| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203 |
- <template>
- <div class="account-warning-container" v-if="platformNames.length > 0 || isLoading">
- <a-popover placement="bottom" trigger="hover">
- <div class="warning-tags">
- <icon-loading v-if="isLoading" style="margin-right: 8px;" />
- <a-tag
- v-for="platform in platformNames"
- :key="platform"
- :color="getPlatformStatus(platform).hasOffline ? '#f53f3f' : '#165dff'"
- :style="{ marginRight: '8px' }"
- :class="{ 'warning-pulse': getPlatformStatus(platform).hasOffline }"
- >
- {{ getPlatformDisplayName(platform) }}:{{ getPlatformStatus(platform).status }}
- <icon-exclamation-circle v-if="getPlatformStatus(platform).hasOffline"
- style="margin-left: 4px;"
- class="warning-icon" />
- </a-tag>
- </div>
- <template #content>
- <div class="warning-detail">
- <div v-for="platform in platformNames" :key="platform" class="platform-detail">
- <div class="platform-title">{{ getPlatformDisplayName(platform) }}</div>
- <div class="platform-stats">
- <a-tag
- v-for="(stats, type) in accountWarningData[platform]"
- :key="type"
- :color="stats[1] === 0 ? '#f53f3f' : '#165dff'"
- size="small"
- :style="{ marginRight: '4px', marginBottom: '4px' }"
- >
- {{ type }}: {{ stats[1] }}/{{ stats[0] }}
- </a-tag>
- </div>
- </div>
- </div>
- </template>
- </a-popover>
- </div>
- </template>
- <script lang="ts" setup>
- import { ref, computed, onMounted, onUnmounted } from 'vue';
- import { queryAccountWarning, type AccountWarningStats } from '@/api/message';
- const accountWarningData = ref<AccountWarningStats>({});
- const isLoading = ref(false);
- let intervalId: ReturnType<typeof setInterval> | null = null;
- let loadingTimer: ReturnType<typeof setTimeout> | null = null;
- const fetchAccountWarning = async () => {
- const startTime = Date.now();
- isLoading.value = true;
-
- if (loadingTimer) {
- clearTimeout(loadingTimer);
- }
-
- try {
- const { data } = await queryAccountWarning();
- console.log(data);
- accountWarningData.value = data;
-
- const elapsed = Date.now() - startTime;
- const remainingTime = Math.max(500 - elapsed, 0);
-
- loadingTimer = setTimeout(() => {
- isLoading.value = false;
- }, remainingTime);
-
- } catch (error) {
- console.error('获取账户警告数据失败:', error);
-
- const elapsed = Date.now() - startTime;
- const remainingTime = Math.max(500 - elapsed, 0);
-
- loadingTimer = setTimeout(() => {
- isLoading.value = false;
- }, remainingTime);
- }
- };
- const platformNames = computed(() => Object.keys(accountWarningData.value || {}));
- const getPlatformDisplayName = (platform: string) => {
- const nameMap: Record<string, string> = {
- 'tb': '淘宝',
- 'jd': '京东'
- };
- return nameMap[platform] || platform;
- };
- const getPlatformStatus = (platform: string) => {
- const platformData = accountWarningData.value[platform];
- if (!platformData) return { status: '无数据', hasOffline: false };
-
- const hasOffline = Object.values(platformData).some(([, onlineCount]) => onlineCount === 0);
-
- if (hasOffline) {
- const offlineTypes = Object.entries(platformData)
- .filter(([, [, onlineCount]]) => onlineCount === 0)
- .map(([type]) => type);
- return {
- status: `异常(${offlineTypes.join(',')})`,
- hasOffline: true
- };
- }
- return { status: '正常', hasOffline: false };
- };
- onMounted(() => {
- fetchAccountWarning();
- intervalId = setInterval(fetchAccountWarning, 5000);
- });
- onUnmounted(() => {
- if (intervalId) {
- clearInterval(intervalId);
- intervalId = null;
- }
- if (loadingTimer) {
- clearTimeout(loadingTimer);
- loadingTimer = null;
- }
- });
- </script>
- <style scoped lang="less">
- .account-warning-container {
- display: flex;
- align-items: center;
- flex-wrap: wrap;
- .warning-tags {
- display: flex;
- align-items: center;
- }
- }
- .warning-detail {
- min-width: 200px;
-
- .platform-detail {
- margin-bottom: 12px;
-
- &:last-child {
- margin-bottom: 0;
- }
-
- .platform-title {
- font-weight: bold;
- margin-bottom: 6px;
- color: var(--color-text-1);
- }
-
- .platform-stats {
- display: flex;
- flex-wrap: wrap;
- }
- }
- }
- .warning-pulse {
- animation: pulse 1s infinite;
- box-shadow: 0 0 12px rgba(245, 63, 63, 0.8);
- font-weight: bold;
- border: 2px solid rgba(245, 63, 63, 0.6);
- }
- .warning-icon {
- animation: shake 1s infinite;
- filter: drop-shadow(0 0 4px rgba(245, 63, 63, 0.8));
- }
- @keyframes pulse {
- 0% {
- box-shadow: 0 0 12px rgba(245, 63, 63, 0.8);
- border-color: rgba(245, 63, 63, 0.6);
- transform: scale(1);
- }
- 50% {
- box-shadow: 0 0 24px rgba(245, 63, 63, 1);
- border-color: rgba(245, 63, 63, 1);
- transform: scale(1.05);
- }
- 100% {
- box-shadow: 0 0 12px rgba(245, 63, 63, 0.8);
- border-color: rgba(245, 63, 63, 0.6);
- transform: scale(1);
- }
- }
- @keyframes shake {
- 0%, 100% {
- transform: translateX(0);
- }
- 10%, 30%, 50%, 70%, 90% {
- transform: translateX(-2px);
- }
- 20%, 40%, 60%, 80% {
- transform: translateX(2px);
- }
- }
- </style>
|