|
|
@@ -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>
|