Ver Fonte

🦄 refactor(设备监控): 优化

Zimo há 6 dias atrás
pai
commit
cfe7c5877d

+ 31 - 16
src/config/axios/service.ts

@@ -194,34 +194,46 @@ service.interceptors.response.use(
 
       const requestUrl = response.config.url || ''
       // 判断是否包含rq/iot路径
-      if (requestUrl.includes('rq/')||requestUrl.includes('system/dict')||requestUrl.includes('system/auth/get-permission-info')||requestUrl.includes('system/dept/list')
-        ||requestUrl.includes('system/menu/simple-list')||requestUrl.includes('system/menu/list')||requestUrl.includes('system/dept/simple-list')
-        ||requestUrl.includes('pms/')||requestUrl.includes('system/user/page')||requestUrl.includes('supplier/base/page')||requestUrl.includes('system/dept/get')
-        ||requestUrl.includes('system/user/simpleUserList')||requestUrl.includes('system/dept/companyLevelDepts')||requestUrl.includes('system/dept/companyLevelChildrenDepts')
-        ||requestUrl.includes('system/user/companyDeptsEmployee')||requestUrl.includes('system/dept/specifiedSimpleDepts')) {
+      if (
+        requestUrl.includes('rq/') ||
+        requestUrl.includes('system/dict') ||
+        requestUrl.includes('system/auth/get-permission-info') ||
+        requestUrl.includes('system/dept/list') ||
+        requestUrl.includes('system/menu/simple-list') ||
+        requestUrl.includes('system/menu/list') ||
+        requestUrl.includes('system/dept/simple-list') ||
+        requestUrl.includes('pms/') ||
+        requestUrl.includes('system/user/page') ||
+        requestUrl.includes('supplier/base/page') ||
+        requestUrl.includes('system/dept/get') ||
+        requestUrl.includes('system/user/simpleUserList') ||
+        requestUrl.includes('system/dept/companyLevelDepts') ||
+        requestUrl.includes('system/dept/companyLevelChildrenDepts') ||
+        requestUrl.includes('system/user/companyDeptsEmployee') ||
+        requestUrl.includes('system/dept/specifiedSimpleDepts')
+      ) {
         const localeStore = useLocaleStore()
         const lang = localeStore.getCurrentLocale.lang
-        if (data&& data.data) {
+        if (data && data.data) {
           if (data.data.list) {
             if (Array.isArray(data.data.list)) {
               const list = langHelper.transformArray(data.data.list, lang)
-              data.data.list = list;
-              return data;
+              data.data.list = list
+              return data
             }
-          }else if (data &&Array.isArray(data.data)) {
+          } else if (data && Array.isArray(data.data)) {
             const list = langHelper.transformArray(data.data, lang)
-            data.data = list;
-            return data;
-          }else if (data && typeof data.data === 'object') {
-            const object =  langHelper.transformObject(data, lang)
+            data.data = list
+            return data
+          } else if (data && typeof data.data === 'object') {
+            const object = langHelper.transformObject(data, lang)
             data = object
             return data
           } else {
             return data
           }
         }
-
-      }else {
+      } else {
         return data
       }
       // return data
@@ -230,6 +242,7 @@ service.interceptors.response.use(
   (error: AxiosError) => {
     console.log('err' + error) // for debug
     let { message } = error
+    console.log('message :>> ', message)
     const { t } = useI18n()
     if (message === 'Network Error') {
       message = t('sys.api.errorMessage')
@@ -237,13 +250,15 @@ service.interceptors.response.use(
       message = t('sys.api.apiTimeoutMessage')
     } else if (message.includes('Request failed with status code')) {
       message = t('sys.api.apiRequestFailed') + message.substr(message.length - 3)
+    } else if (message.includes('canceled')) {
+      return Promise.reject(error)
     }
     ElMessage.error(message)
     return Promise.reject(error)
   }
 )
 
-const isSystemPagePath = (path: string): boolean=> {
+const isSystemPagePath = (path: string): boolean => {
   // 正则说明:
   // ^.*system/ 匹配开头任意字符直到system/
   // (?:[^/]+/)* 匹配零个或多个非斜杠字符组成的路径段

+ 79 - 41
src/views/pms/device/monitor/TdDeviceInfo.vue

@@ -25,6 +25,7 @@ interface Dimensions {
   name: string
   value: string
   color?: string
+  response?: boolean
 }
 
 const dimensions = ref<Dimensions[]>([])
@@ -41,6 +42,12 @@ const selectedDimension = ref<SelectedDimension>({})
 
 const dimensionLoading = ref(false)
 
+const disabledDimension = computed(() => (identifier: string) => {
+  const response = dimensions.value.find((item) => item.identifier === identifier)?.response
+
+  return { disabled: disabledDimensions.value.includes(identifier) || response, loading: response }
+})
+
 async function loadDimensions() {
   if (!query.id) return
 
@@ -110,10 +117,11 @@ function genderIntervalArr(init: boolean = false) {
   }
 
   const maxVal = values.length === 0 ? 10000 : Math.max(...values)
-  const minVal = values.length === 0 ? 0 : Math.min(...values)
+  const minVal = values.length === 0 ? 0 : Math.min(...values) > 0 ? 0 : Math.min(...values)
 
   const maxDigits = (Math.floor(maxVal) + '').length
-  const minDigits = (Math.floor(Math.abs(minVal)) + '').length
+  const minDigits = minVal === 0 ? 0 : (Math.floor(Math.abs(minVal)) + '').length
+
   const interval = Math.max(maxDigits, minDigits)
 
   maxInterval.value = interval
@@ -157,9 +165,9 @@ function render() {
 
   chart.setOption({
     grid: {
-      left: '8%',
-      top: '0%',
-      right: '8%',
+      left: '6%',
+      top: '5%',
+      right: '6%',
       bottom: '12%'
     },
     tooltip: {
@@ -209,7 +217,7 @@ function render() {
           return num.toLocaleString()
         }
       },
-      show: false
+      show: true
     },
     legend: {
       data: dimensions.value.map((item) => item.name),
@@ -260,28 +268,36 @@ function updateSingleSeries(name: string) {
 const lastTsMap = ref<Record<Dimensions['name'], number>>({})
 
 async function fetchIncrementData() {
-  for (const { identifier, name } of dimensions.value) {
+  for (const item of dimensions.value) {
+    const { identifier, name } = item
+
     const lastTs = lastTsMap.value[name]
     if (!lastTs) continue
 
+    item.response = true
+
     IotStatApi.getDeviceInfoChart(
       data.value.deviceCode,
       identifier,
       dayjs(lastTs).format('YYYY-MM-DD HH:mm:ss'),
       dayjs().format('YYYY-MM-DD HH:mm:ss')
-    ).then((res) => {
-      if (!res.length) return
-
-      const sorted = res.sort((a, b) => a.ts - b.ts)
-
-      // push 到本地
-      chartData.value[name].push(...sorted)
-      // 更新 lastTs
-      lastTsMap.value[identifier] = sorted.at(-1).ts
-
-      // 更新图表
-      updateSingleSeries(name)
-    })
+    )
+      .then((res) => {
+        if (!res.length) return
+
+        const sorted = res.sort((a, b) => a.ts - b.ts)
+
+        // push 到本地
+        chartData.value[name].push(...sorted)
+        // 更新 lastTs
+        lastTsMap.value[identifier] = sorted.at(-1).ts
+
+        // 更新图表
+        updateSingleSeries(name)
+      })
+      .finally(() => {
+        item.response = false
+      })
   }
 }
 
@@ -306,28 +322,38 @@ async function initLoadChartData(real_time: boolean = true) {
 
   chartLoading.value = true
 
-  for (const { identifier, name } of dimensions.value) {
-    const res = await IotStatApi.getDeviceInfoChart(
-      data.value.deviceCode,
-      identifier,
-      selectedDate.value[0],
-      selectedDate.value[1]
-    )
+  dimensions.value = dimensions.value.map((item) => {
+    item.response = true
+    return item
+  })
+
+  for (const item of dimensions.value) {
+    const { identifier, name } = item
+    try {
+      const res = await IotStatApi.getDeviceInfoChart(
+        data.value.deviceCode,
+        identifier,
+        selectedDate.value[0],
+        selectedDate.value[1]
+      )
 
-    const sorted = res
-      .sort((a, b) => a.ts - b.ts)
-      .map((item) => ({ ts: item.ts, value: item.value }))
+      const sorted = res
+        .sort((a, b) => a.ts - b.ts)
+        .map((item) => ({ ts: item.ts, value: item.value }))
 
-    chartData.value[name] = sorted
+      chartData.value[name] = sorted
 
-    lastTsMap.value[name] = sorted.at(-1)?.ts ?? 0
+      lastTsMap.value[name] = sorted.at(-1)?.ts ?? 0
 
-    updateSingleSeries(name)
+      updateSingleSeries(name)
 
-    chartLoading.value = false
+      chartLoading.value = false
 
-    if (selectedDimension.value[name]) {
-      genderIntervalArr()
+      if (selectedDimension.value[name]) {
+        genderIntervalArr()
+      }
+    } finally {
+      item.response = false
     }
   }
 
@@ -487,12 +513,18 @@ onUnmounted(() => {
       <button
         v-for="item in gatewayDimensions"
         :key="item.identifier"
-        class="border-none h-12 bg-white dark:bg-[#1d1e1f] rounded-lg px-6 shadow flex items-center hover:scale-103 transition-all cursor-pointer"
+        class="border-none h-12 bg-white dark:bg-[#1d1e1f] rounded-lg px-8 shadow flex items-center hover:scale-103 transition-all cursor-pointer"
         :class="{ 'bg-blue-200': selectedDimension[item.name] }"
-        :disabled="disabledDimensions.includes(item.identifier)"
+        :disabled="disabledDimension(item.identifier).disabled"
         @click="handleClickSpec(item.name)"
       >
-        <span class="text-sm text-[var(--el-text-color-regular)]">{{ item.name }}</span>
+        <span class="text-sm text-[var(--el-text-color-regular)] flex items-center gap-2 relative">
+          <i
+            v-show="disabledDimension(item.identifier).loading"
+            class="i-line-md:loading-loop size-5 absolute -left-6"
+          ></i>
+          {{ item.name }}
+        </span>
         <span class="text-lg font-medium ms-a">{{ item.value }}</span>
       </button>
     </div>
@@ -508,10 +540,16 @@ onUnmounted(() => {
         :key="item.identifier"
         class="border-none h-12 bg-white dark:bg-[#1d1e1f] rounded-lg px-6 shadow flex items-center hover:scale-103 transition-all cursor-pointer"
         :class="{ 'bg-blue-200': selectedDimension[item.name] }"
-        :disabled="disabledDimensions.includes(item.identifier)"
+        :disabled="disabledDimension(item.identifier).disabled"
         @click="handleClickSpec(item.name)"
       >
-        <span class="text-sm text-[var(--el-text-color-regular)]">{{ item.name }}</span>
+        <span class="text-sm text-[var(--el-text-color-regular)] flex items-center gap-2">
+          <i
+            v-show="disabledDimension(item.identifier).loading"
+            class="i-line-md:loading-loop size-5"
+          ></i>
+          {{ item.name }}
+        </span>
         <span class="text-lg font-medium ms-a">{{ item.value }}</span>
       </button>
     </div>

+ 239 - 50
src/views/pms/device/monitor/color.ts

@@ -1,52 +1,241 @@
 export const colors = [
-  '#5470C6',
-  '#91CC75',
-  '#FAC858',
-  '#EE6666',
-  '#73C0DE',
-  '#3BA272',
-  '#FC8452',
-  '#9A60B4',
-  '#EA7CCC',
-  '#2E91E5',
-  '#1CA71C',
-  '#FB0D0D',
-  '#DA16FF',
-  '#222A2A',
-  '#B68100',
-  '#750D86',
-  '#EB663B',
-  '#0D2A63',
-  '#87BC45',
-  '#F58518',
-  '#8C564B',
-  '#7F7F7F',
-  '#BCBD22',
-  '#17BECF',
-  '#4C72B0',
-  '#55A868',
-  '#C44E52',
-  '#8172B2',
-  '#CCB974',
-  '#64B5CD',
-  '#4E79A7',
-  '#F28E2B',
-  '#E15759',
-  '#76B7B2',
-  '#59A14F',
-  '#EDC948',
-  '#B07AA1',
-  '#FF9DA7',
-  '#9C755F',
-  '#BAB0AC',
-  '#1F77B4',
-  '#AEC7E8',
-  '#FF7F0E',
-  '#FFBB78',
-  '#2CA02C',
-  '#98DF8A',
-  '#D62728',
-  '#FF9896',
-  '#9467BD',
-  '#C5B0D5'
+  // --- 第 1 组 (高对比度起手) ---
+  '#5470C6', // 蓝
+  '#FAC858', // 黄
+  '#EE6666', // 红
+  '#3BA272', // 绿
+  '#9A60B4', // 紫
+  '#FC8452', // 橙
+  '#73C0DE', // 浅蓝
+  '#91CC75', // 浅绿
+  '#222A2A', // 深灰
+  '#EA7CCC', // 粉
+
+  // --- 第 2 组 ---
+  '#0D2A63', // 深蓝
+  '#F58518', // 鲜橙
+  '#1CA71C', // 纯绿
+  '#DA16FF', // 亮紫
+  '#B68100', // 暗金
+  '#C44E52', // 砖红
+  '#17BECF', // 青
+  '#8C564B', // 棕
+  '#2E91E5', // 亮蓝
+  '#EDC948', // 芥末黄
+
+  // --- 第 3 组 ---
+  '#750D86', // 深紫
+  '#87BC45', // 草绿
+  '#FB0D0D', // 鲜红
+  '#76B7B2', // 蓝绿
+  '#EB663B', // 珊瑚橙
+  '#8172B2', // 灰紫
+  '#CCB974', // 沙黄
+  '#59A14F', // 森林绿
+  '#F28E2B', // 橘黄
+  '#4E79A7', // 钢蓝
+
+  // --- 第 4 组 ---
+  '#E15759', // 绯红
+  '#55A868', // 叶绿
+  '#FF9DA7', // 嫩粉
+  '#4C72B0', // 靛蓝
+  '#BCBD22', // 橄榄
+  '#64B5CD', // 天蓝
+  '#B07AA1', // 兰花紫
+  '#9C755F', // 咖啡
+  '#FF7F0E', // 胡萝卜橙
+  '#1F77B4', // 经典蓝
+
+  // --- 第 5 组 (加入 Material Design 鲜艳色) ---
+  '#00FFCC', // 荧光青
+  '#D62728', // 宝石红
+  '#98DF8A', // 薄荷绿
+  '#9467BD', // 薰衣草
+  '#AEC7E8', // 淡蓝
+  '#FFBB78', // 杏黄
+  '#2CA02C', // 熟绿
+  '#C5B0D5', // 浅紫
+  '#FF9896', // 鲑鱼粉
+  '#7F7F7F', // 中灰
+
+  // --- 第 6 组 ---
+  '#240046', // 午夜紫
+  '#FFD166', // 奶酪黄
+  '#06D6A0', // 碧绿
+  '#EF476F', // 玫瑰红
+  '#118AB2', // 太平洋蓝
+  '#F72585', // 霓虹粉
+  '#43AA8B', // 丛林绿
+  '#F8961E', // 琥珀
+  '#577590', // 蓝灰
+  '#F94144', // 珊瑚红
+
+  // --- 第 7 组 ---
+  '#2B2D42', // 炭黑蓝
+  '#A7C957', // 鳄梨绿
+  '#6A4C93', // 皇家紫
+  '#FF595E', // 糖果红
+  '#8AC926', // 酸橙绿
+  '#1982C4', // 蔚蓝
+  '#FFCA3A', // 香蕉黄
+  '#003566', // 海军蓝
+  '#FF5400', // 国际橙
+  '#8338EC', // 电光紫
+
+  // --- 第 8 组 ---
+  '#001219', // 墨绿黑
+  '#E9D8A6', // 香槟
+  '#BB3E03', // 铁锈红
+  '#0A9396', // 孔雀蓝
+  '#EE9B00', // 姜黄
+  '#9B2226', // 血红
+  '#94D2BD', // 蒂芙尼蓝
+  '#CA6702', // 焦糖
+  '#005F73', // Petrol蓝
+  '#AE2012', // 砖红
+
+  // --- 第 9 组 ---
+  '#355070', // 深丹宁
+  '#E56B6F', // 柔粉红
+  '#6D597A', // 葡萄紫
+  '#B56576', // 豆沙红
+  '#EAAC8B', // 桃皮绒
+  '#E07A5F', // 陶土
+  '#3D405B', // 墨水蓝
+  '#81B29A', // 鼠尾草绿
+  '#F2CC8F', // 奶油黄
+  '#F4F1DE', // 羊皮纸色
+
+  // --- 第 10 组 (高饱和度 赛博朋克风) ---
+  '#FF006E', // 亮玫红
+  '#3A86FF', // 亮蓝
+  '#8338EC', // 蓝紫
+  '#FFBE0B', // 金盏花
+  '#FB5607', // 熔岩橙
+  '#70D6FF', // 冰蓝
+  '#FF70A6', // 泡泡糖粉
+  '#FF9770', // 浅橙
+  '#E9FF70', // 柠檬黄
+  '#001524', // 极夜黑
+
+  // --- 第 11 组 ---
+  '#5F0F40', // 酒红
+  '#9A031E', // 深绯
+  '#FB8B24', // 橘红
+  '#E36414', // 柿子色
+  '#0F4C5C', // 深青
+  '#590D22', // 波尔多红
+  '#FFCCD5', // 淡粉
+  '#A8DADC', // 浅湖蓝
+  '#457B9D', // 钢青
+  '#1D3557', // 普鲁士蓝
+
+  // --- 第 12 组 ---
+  '#D9ED92', // 嫩芽绿
+  '#B5E48C', // 苹果绿
+  '#99D98C', // 抹茶
+  '#76C893', // 草地
+  '#52B69A', // 翡翠
+  '#34A0A4', // 蓝玉
+  '#168AAD', // 蔚蓝
+  '#1A759F', // 深湖蓝
+  '#1E6091', // 靛青
+  '#184E77', // 深海蓝
+
+  // --- 第 13 组 ---
+  '#003049', // 普鲁士深蓝
+  '#D62828', // 消防红
+  '#F77F00', // 橙皮
+  '#FCBF49', // 日光黄
+  '#EAE2B7', // 米白
+  '#264653', // 炭青
+  '#2A9D8F', // 波斯绿
+  '#E9C46A', // 沙金
+  '#F4A261', // 浅褐
+  '#E76F51', // 焦赭
+
+  // --- 第 14 组 ---
+  '#606C38', // 橄榄褐
+  '#283618', // 森林黑绿
+  '#FEFAE0', // 奶油白
+  '#DDA15E', // 鹿皮
+  '#BC6C25', // 树皮褐
+  '#CCD5AE', // 浅橄榄
+  '#E9EDC9', // 浅豆绿
+  '#FAEDCD', // 杏仁
+  '#D4A373', // 拿铁
+  '#8D99AE', // 冷灰
+
+  // --- 第 15 组 ---
+  '#7400B8', // 电紫
+  '#6930C3', // 葡萄
+  '#5E60CE', // 蓝鸢尾
+  '#5390D9', // 矢车菊
+  '#4EA8DE', // 天空
+  '#48BFE3', // 浅蓝
+  '#56CFE1', // 青空
+  '#64DFDF', // 绿松石
+  '#72EFDD', // 薄荷
+  '#80FFDB', // 冰绿
+
+  // --- 第 16 组 ---
+  '#FF595E', // 珊瑚粉
+  '#FFCA3A', // 芥末
+  '#8AC926', // 梨绿
+  '#1982C4', // 纯蓝
+  '#6A4C93', // 茄紫
+  '#F9C74F', // 蛋黄
+  '#90BE6D', // 开心果
+  '#43AA8B', // 丛林
+  '#577590', // 军舰灰
+  '#F3722C', // 胡萝卜
+
+  // --- 第 17 组 ---
+  '#F94144', // 烈焰红
+  '#F3722C', // 柿子
+  '#F8961E', // 橙黄
+  '#F9844A', // 橙粉
+  '#F9C74F', // 玉米黄
+  '#90BE6D', // 抹茶
+  '#43AA8B', // 绿松石
+  '#4D908E', // 藻绿
+  '#577590', // 蓝灰
+  '#277DA1', // 远洋蓝
+
+  // --- 第 18 组 ---
+  '#006466', // 深墨绿
+  '#065A60', // 深青
+  '#0B525B', // 油蓝
+  '#144552', // 墨蓝
+  '#1B3A4B', // 深夜蓝
+  '#212F45', // 极深蓝
+  '#272640', // 极深紫
+  '#312244', // 墨紫
+  '#3E1F47', // 李子黑
+  '#4D194D', // 葡萄黑
+
+  // --- 第 19 组 ---
+  '#F08080', // 淡珊瑚
+  '#20B2AA', // 浅海绿
+  '#778899', // 娄石蓝
+  '#9370DB', // 适中紫
+  '#3CB371', // 春绿
+  '#FF6347', // 番茄
+  '#4682B4', // 钢蓝
+  '#DAA520', // 金麒麟
+  '#CD5C5C', // 栗色
+  '#008B8B', // 深青
+
+  // --- 第 20 组 ---
+  '#800000', // 栗红
+  '#808000', // 橄榄
+  '#008000', // 纯绿
+  '#008080', // 鸭翅青
+  '#000080', // 海军蓝
+  '#4B0082', // 靛蓝
+  '#DC143C', // 猩红
+  '#00CED1', // 暗青
+  '#FFD700', // 金色
+  '#FF00FF' // 洋红
 ]