Compare commits

...

3 Commits

@ -17,5 +17,18 @@ export const RecipePointRecordApi = {
},
getRecipePointRecordPage: async (params: any) => {
return await request.get({ url: `/iot/recipe-point-record/page`, params })
},
updateRecipePointRecord: async (params: {
id: string | number
recipeId?: string | number
name?: string
max?: string | number
min?: string | number
dataType?: string | number
dataUnit?: string | number
remark?: string
refer?: string
}) => {
return await request.put({ url: `/iot/recipe-point-record/update`, data:params })
}
}

@ -129,6 +129,11 @@
prop="remark"
min-width="180"
/>
<el-table-column align="center" fixed="right" label="操作" width="120">
<template #default="scope">
<el-button link type="primary" @click="openReferDialog(scope.row)"></el-button>
</template>
</el-table-column>
</el-table>
<Pagination
:total="total"
@ -138,16 +143,44 @@
/>
</el-tab-pane>
</el-tabs>
<Dialog v-model="referDialogVisible" title="补录采集值" width="520px">
<el-form :model="referForm" label-width="90px">
<el-form-item label="名称">
<el-input :model-value="referForm.name" disabled />
</el-form-item>
<el-form-item label="数据类型">
<el-input :model-value="referForm.dataType" disabled />
</el-form-item>
<el-form-item label="单位">
<el-input :model-value="referForm.dataUnitLabel" disabled />
</el-form-item>
<el-form-item label="备注">
<el-input :model-value="referForm.remark" type="textarea" disabled />
</el-form-item>
<el-form-item label="参考值">
<el-input v-model="referForm.refer" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="referDialogVisible = false" :disabled="referSubmitting">{{ t('common.cancel') }}</el-button>
<el-button type="primary" @click="submitRefer" :loading="referSubmitting" :disabled="referSubmitting">
{{ t('common.ok') }}
</el-button>
</template>
</Dialog>
</template>
<script setup lang="ts">
import { RecipePointRecordApi } from '@/api/iot/recipepointrecord'
import { RecipeDeviceRecordApi } from '@/api/iot/recipeDeviceRecord'
import { RecipePointVO } from '@/api/iot/recipePoint'
import { ProductUnitApi, ProductUnitVO } from '@/api/erp/product/unit'
defineOptions({ name: 'FormulaLibraryDetailTabs' })
const { t } = useI18n()
const message = useMessage()
const props = defineProps({
recipeId: {
@ -257,6 +290,60 @@ const resetQuery = () => {
handleQuery()
}
const referDialogVisible = ref(false)
const referSubmitting = ref(false)
const referForm = reactive({
id: '' as string,
recipeId: '' as string,
name: '' as string,
max: '' as string,
min: '' as string,
dataType: '' as string,
dataUnit: '' as string,
dataUnitLabel: '' as string,
remark: '' as string,
refer: '' as string
})
const openReferDialog = (row: RecipePointVO) => {
const id = row?.id === undefined || row?.id === null ? '' : String(row.id)
referForm.id = id
referForm.recipeId = row?.recipeId === undefined || row?.recipeId === null ? '' : String(row.recipeId)
referForm.name = row?.name ?? ''
referForm.max = row?.max ?? ''
referForm.min = row?.min ?? ''
referForm.dataType = row?.dataType ?? ''
referForm.dataUnit = row?.dataUnit ?? ''
referForm.dataUnitLabel = getUnitLabel(row?.dataUnit)
referForm.remark = row?.remark ?? ''
referForm.refer = row?.refer ?? ''
referDialogVisible.value = true
}
const submitRefer = async () => {
if (referSubmitting.value) return
if (!referForm.id) return
referSubmitting.value = true
try {
await RecipePointRecordApi.updateRecipePointRecord({
id: referForm.id,
recipeId: referForm.recipeId ? referForm.recipeId : undefined,
name: referForm.name,
max: referForm.max,
min: referForm.min,
dataType: referForm.dataType,
dataUnit: referForm.dataUnit,
remark: referForm.remark,
refer: referForm.refer
})
message.success(t('common.updateSuccess'))
referDialogVisible.value = false
await refreshManual()
} finally {
referSubmitting.value = false
}
}
watch(
() => props.recipeId,
() => {

@ -33,6 +33,7 @@
<ContentWrap v-for="group in selectedGroups" :key="group.id">
<el-form class="-mb-15px device-param-analysis-form" :inline="true" label-width="auto">
<el-form-item :label="t('DataCollection.DeviceParamAnalysis.formTimeLabel')">
<div>
<el-date-picker
v-model="group.dateRange"
type="datetimerange"
@ -41,7 +42,10 @@
value-format="YYYY-MM-DD HH:mm:ss"
:shortcuts="dateShortcuts"
class="!w-360px"
:disabled-date="disableFutureDate"
@change="(value) => handleDateRangeChange(group, value)"
/>
</div>
</el-form-item>
<el-form-item>
<el-button :disabled="group.chartLoading" @click="handleQuery(group)">
@ -131,45 +135,37 @@ const keyword = ref('')
const treeProps = { children: 'children', label: 'label', disabled: 'disabled' }
const treeData = ref<DeviceTreeNode[]>([])
const DATE_TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss'
const MAX_DATE_RANGE_HOURS = 8
const MAX_DATE_RANGE_MS = MAX_DATE_RANGE_HOURS * 60 * 60 * 1000
const lastValidDateRange = new Map<string, [string, string]>()
const disableFutureDate = (date: Date) => {
return dayjs(date).isAfter(dayjs(), 'day')
}
const buildDefaultDateRange = (): [string, string] => {
const end = dayjs()
const start = end.subtract(2, 'hour')
return [start.format('YYYY-MM-DD HH:mm:ss'), end.format('YYYY-MM-DD HH:mm:ss')]
return [start.format(DATE_TIME_FORMAT), end.format(DATE_TIME_FORMAT)]
}
const dateShortcuts = [
{
text: t('DataCollection.DeviceParamAnalysis.shortcutLast7Days'),
value: () => {
const end = dayjs().endOf('day').toDate()
const start = dayjs().subtract(6, 'day').startOf('day').toDate()
return [start, end]
}
},
{
text: t('DataCollection.DeviceParamAnalysis.shortcutLastWeek'),
value: () => {
const start = dayjs().subtract(1, 'week').startOf('week').toDate()
const end = dayjs().subtract(1, 'week').endOf('week').toDate()
return [start, end]
}
},
{
text: t('DataCollection.DeviceParamAnalysis.shortcutLastMonth'),
value: () => {
const start = dayjs().subtract(1, 'month').startOf('month').toDate()
const end = dayjs().subtract(1, 'month').endOf('month').toDate()
return [start, end]
}
},
{
text: t('DataCollection.DeviceParamAnalysis.shortcutLast3Months'),
const createLastHoursShortcut = (hours: number) => {
return {
text: `最近${hours}小时`,
value: () => {
const end = dayjs().endOf('day').toDate()
const start = dayjs().subtract(3, 'month').startOf('day').toDate()
const end = dayjs().toDate()
const start = dayjs().subtract(hours, 'hour').toDate()
return [start, end]
}
}
}
const dateShortcuts = [
createLastHoursShortcut(1),
createLastHoursShortcut(2),
createLastHoursShortcut(4),
createLastHoursShortcut(8)
]
type ChartState = 'idle' | 'loading' | 'empty' | 'ready'
@ -346,10 +342,78 @@ const loadTree = async () => {
}
}
const normalizeDateRangeValue = (value: any): [string, string] | undefined => {
if (!Array.isArray(value) || value.length !== 2) return undefined
const start = value[0]
const end = value[1]
if (!start || !end) return undefined
return [String(start), String(end)]
}
const ensureDateRangeWithin8Hours = (group: SelectedGroup, dateRange: [string, string]) => {
const now = dayjs()
let start = dayjs(dateRange[0])
let end = dayjs(dateRange[1])
if (!start.isValid() || !end.isValid()) return dateRange
if (start.isAfter(now) && end.isAfter(now)) {
end = now
start = now.subtract(2, 'hour')
} else {
if (end.isAfter(now)) end = now
if (start.isAfter(now)) start = end.subtract(2, 'hour')
}
const diffMs = end.valueOf() - start.valueOf()
if (diffMs < 0) {
end = now
start = now.subtract(2, 'hour')
const nextRange: [string, string] = [start.format(DATE_TIME_FORMAT), end.format(DATE_TIME_FORMAT)]
return nextRange
}
if (diffMs <= MAX_DATE_RANGE_MS) {
return [start.format(DATE_TIME_FORMAT), end.format(DATE_TIME_FORMAT)]
}
const clampedEnd = start.add(MAX_DATE_RANGE_HOURS, 'hour')
const finalEnd = clampedEnd.isAfter(now) ? now : clampedEnd
return [start.format(DATE_TIME_FORMAT), finalEnd.format(DATE_TIME_FORMAT)]
}
const ensureDateRange = (group: SelectedGroup) => {
if (!group.dateRange || group.dateRange.length !== 2) {
group.dateRange = buildDefaultDateRange()
lastValidDateRange.set(group.id, group.dateRange)
return
}
const normalized = normalizeDateRangeValue(group.dateRange)
if (!normalized) {
group.dateRange = buildDefaultDateRange()
lastValidDateRange.set(group.id, group.dateRange)
return
}
const nextRange = ensureDateRangeWithin8Hours(group, normalized)
group.dateRange = nextRange
lastValidDateRange.set(group.id, nextRange)
}
const handleDateRangeChange = (group: SelectedGroup, value: any) => {
const normalized = normalizeDateRangeValue(value) || normalizeDateRangeValue(group.dateRange)
if (!normalized) return
const now = dayjs()
const selectedStart = dayjs(normalized[0])
const selectedEnd = dayjs(normalized[1])
const hasFuture = (selectedStart.isValid() && selectedStart.isAfter(now)) || (selectedEnd.isValid() && selectedEnd.isAfter(now))
const nextRange = ensureDateRangeWithin8Hours(group, normalized)
const isClamped = nextRange[0] !== normalized[0] || nextRange[1] !== normalized[1]
if (hasFuture) {
message.warning('不能选择未来日期')
} else if (isClamped) {
message.warning('时间范围最多只能选择 8 小时')
}
group.dateRange = nextRange
lastValidDateRange.set(group.id, nextRange)
}
const resetChartData = (group: SelectedGroup) => {
@ -536,6 +600,7 @@ const syncGroupsByCheckedNodes = (checkedNodes: DeviceTreeNode[]) => {
chartRenderKey: 0
}
selectedGroups.value.push(group)
lastValidDateRange.set(group.id, group.dateRange)
addedGroups.push(group)
}
return addedGroups

Loading…
Cancel
Save