main
kkk-ops 1 month ago
commit 59552f1b3c

@ -150,6 +150,11 @@ export const DeviceApi = {
return await request.get({ url: `/iot/device/devicePointList` })
},
updateDeviceEnabled: async (id: number | string, enabled: string) => {
const data = { id, enabled }
return await request.put({ url: `/iot/device/update-enabled`, data })
},
// ==================== 子表(设备属性) ====================
// 获得设备属性分页

@ -176,7 +176,7 @@ export const RecipeConfigApi = {
},
getRecipePointDetailPage: async (params: any) => {
return await request.get({ url: `/iot/recipe-device-attribute/page`, params })
return await request.get({ url: `/iot/recipe-device-attribute/getList`, params })
},
updateRecipeDeviceAttribute: async (data: { recipeId: string | number; ids: number[] }) => {
return await request.put({ url: `/iot/recipe-device-attribute/update`, data })

@ -2657,6 +2657,7 @@ export default {
readDialogSubmitButtonText: 'Read',
readDialogCancelButton: 'Cancel',
readDeviceConfirmMessage: 'Do you want to read device data?',
readDialogOverwriteTip: 'Reading again will overwrite existing device data and manual parameters',
detailTabDeviceDataLabel: 'Device Data',
detailTabManualLabel: 'Manual Parameters',
detailDevicePointNameColumn: 'Point Name',
@ -3591,11 +3592,13 @@ export default {
placeholderUrl: 'Please enter endpoint URL',
placeholderUsername: 'Please enter username',
placeholderPassword: 'Please enter password',
placeholderTopic: 'Please enter subscription topic',
model: 'Device Model',
url: 'Endpoint URL',
username: 'Username',
password: 'Password',
topic: 'Subscription Topic',
settingDialogTitle: 'Device Settings',

@ -2240,6 +2240,7 @@ export default {
readDialogSubmitButtonText: '读 取',
readDialogCancelButton: '取 消',
readDeviceConfirmMessage: '是否读取设备数据?',
readDialogOverwriteTip: '再次读取会覆盖已有设备数据和手动录入参数',
detailTabDeviceDataLabel: '设备数据',
detailTabManualLabel: '手动录入参数',
detailDevicePointNameColumn: '点位名称',
@ -3415,7 +3416,7 @@ export default {
protocol: '采集协议',
status: '连接状态',
sampleCycle: '采集周期(s)',
isEnable: '是否启用',
isEnable: '启用',
collectionTime: '采集时间',
operate: '操作',
@ -3432,11 +3433,13 @@ export default {
placeholderUrl: '请输入端点URL',
placeholderUsername: '请输入用户名',
placeholderPassword: '请输入密码',
placeholderTopic: '请输入订阅主题',
model: '设备模型',
url: '端点URL',
username: '用户名',
password: '密码',
topic: '订阅主题',
settingDialogTitle: '设备设置',

@ -13,7 +13,7 @@ v-loading="typeTreeLoading" :data="brandTreeData" node-key="id" highlight-curren
<el-input
v-model="queryParams.code" :placeholder="t('MoldManagement.Mold.code')" clearable @keyup.enter="handleQuery"
class="!w-240px" />
</el-form-item>
</el-form-item>
<el-form-item :label="t('MoldManagement.Mold.name')" prop="name">
<el-input
v-model="queryParams.name" :placeholder="t('MoldManagement.Mold.name')" clearable @keyup.enter="handleQuery"

@ -323,10 +323,16 @@ const getList = async () => {
pageSize,
recipeId: props.recipeId
})
list.value = (data.list ?? []).map(normalizeDetail)
total.value = data.total
//
unitList.value = await ProductUnitApi.getProductUnitSimpleList()
const rawList = Array.isArray((data as any)?.list)
? (data as any).list
: Array.isArray((data as any)?.data)
? (data as any).data
: Array.isArray(data)
? data
: []
list.value = rawList.map(normalizeDetail)
total.value = (data as any)?.total ?? rawList.length
unitList.value = await ProductUnitApi.getProductUnitSimpleList()
} finally {
loading.value = false
}

@ -663,7 +663,7 @@ const buildTransferLabel = (item: any) => {
const loadConfigCandidatesByDevice = async (deviceId: number) => {
const data = await DeviceApi.getDeviceAttributeList(deviceId)
const list = (data?.list ?? data?.data ?? []) as any[]
const list = (data ?? []) as any[]
const deviceItems = (list ?? [])
.map((item: any) => {
const key = normalizeKey(item?.attributeId ?? item?.id)
@ -674,12 +674,7 @@ const loadConfigCandidatesByDevice = async (deviceId: number) => {
} as TransferItem
})
.filter((it: any) => it && typeof it.key === 'number') as TransferItem[]
const merged = new Map<number, TransferItem>()
for (const it of deviceItems) merged.set(it.key, it)
for (const it of configSelectedItems.value) {
if (!merged.has(it.key)) merged.set(it.key, it)
}
configCandidates.value = Array.from(merged.values())
configCandidates.value = deviceItems
}
const openConfigDialog = async (recipeId: number | string) => {

@ -1,6 +1,14 @@
<template>
<Dialog v-model="visible" :title="title" width="920px">
<div class="formula-library-read-content">
<el-alert
v-if="hasExistingRecords"
type="warning"
:closable="false"
:title="t('RecipeManagement.RecipeLibrary.readDialogOverwriteTip')"
show-icon
class="mb-15px"
/>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" row-key="id">
<!-- <el-table-column type="index" :label="t('RecipeManagement.RecipeLibrary.tableIndexColumn')" align="center" width="70" /> -->
<el-table-column
@ -94,6 +102,7 @@ type PageResult<T> = { list: T[]; total: number }
const visible = ref(false)
const title = ref('')
const loading = ref(false)
const hasExistingRecords = ref(false)
const recipeId = ref<string | number | undefined>(undefined)
const list = ref<RecipePointVO[]>([])
@ -144,11 +153,13 @@ const open = async (options: {
recipeId: string | number
recipeName?: string
data?: Array<RecipePointVO>
id : undefined | number
id: number | undefined
hasExistingRecords?: boolean
}) => {
visible.value = true
formulaLibraryId.value = options.id
recipeId.value = options.recipeId
hasExistingRecords.value = !!options.hasExistingRecords
const baseTitle = t('RecipeManagement.RecipeLibrary.readDialogTitle')
title.value = options.recipeName ? `${baseTitle}-${options.recipeName}` : baseTitle
queryParams.pageNo = 1

@ -223,7 +223,8 @@ import { RecipeConfigApi } from '@/api/iot/recipeConfig'
import { RecipePointApi } from '@/api/iot/recipePoint'
import FormulaLibraryDetailTabs from './components/FormulaLibraryDetailTabs.vue'
import FormulaLibraryReadDialog from './components/FormulaLibraryReadDialog.vue'
import { RecipeDeviceRecordApi} from '@/api/iot/recipeDeviceRecord'
import { RecipeDeviceRecordApi } from '@/api/iot/recipeDeviceRecord'
import { RecipePointRecordApi } from '@/api/iot/recipepointrecord'
type SelectOption<T = any> = { label: string; value: T }
@ -398,11 +399,37 @@ const openDialog = async (type: 'create' | 'update', row?: RecipePlanDetailVO) =
const readDialogRef = ref<InstanceType<typeof FormulaLibraryReadDialog>>()
const checkRecipeRecords = async (recipeId: string | number) => {
const params = {
pageNo: 1,
pageSize: 1,
recipeId: String(recipeId)
}
try {
const [devicePage, manualPage] = await Promise.all([
RecipeDeviceRecordApi.getRecipeDeviceRecordPage(params),
RecipePointRecordApi.getRecipePointRecordPage(params)
])
const deviceTotal = Number((devicePage as any)?.total ?? 0)
const manualTotal = Number((manualPage as any)?.total ?? 0)
return {
hasDeviceRecords: deviceTotal > 0,
hasManualRecords: manualTotal > 0
}
} catch {
return {
hasDeviceRecords: false,
hasManualRecords: false
}
}
}
const handleRead = async (row: RecipePlanDetailVO) => {
const recipeId = row?.recipeId
const id = row?.id
if (!recipeId) return
const recipeName = row?.recipeName
const { hasDeviceRecords, hasManualRecords } = await checkRecipeRecords(recipeId)
const data = await RecipePointApi.getRecipePointList(Number(recipeId))
if (!data?.length) {
await message.confirm(t('RecipeManagement.RecipeLibrary.readDeviceConfirmMessage'))
@ -412,7 +439,13 @@ const handleRead = async (row: RecipePlanDetailVO) => {
return
}
}
await readDialogRef.value?.open({ recipeId, recipeName, data, id })
await readDialogRef.value?.open({
recipeId,
recipeName,
data,
id,
hasExistingRecords: hasDeviceRecords || hasManualRecords
})
}
const submitDialog = async () => {

@ -79,12 +79,6 @@
<el-option v-for="item in modelList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item :label="t('DataCollection.Device.sampleCycle')" prop="sampleCycle">
<el-input
v-model="formData.sampleCycle"
:placeholder="t('DataCollection.Device.placeholderSampleCycle')"
/>
</el-form-item>
<!-- <el-form-item label="备注" prop="remark">
<el-input v-model="formData.remark" placeholder="请输入备注" />
</el-form-item> -->
@ -102,12 +96,6 @@
</template>
<template v-else-if="formType === 'update'">
<el-form-item :label="t('DataCollection.Device.sampleCycle')" prop="sampleCycle">
<el-input
v-model="formData.sampleCycle"
:placeholder="t('DataCollection.Device.placeholderSampleCycle')"
/>
</el-form-item>
<el-form-item :label="t('DataCollection.Device.isEnable')" prop="isEnable">
<el-radio-group v-model="formData.isEnable">
<el-radio
@ -122,23 +110,10 @@
</template>
<template v-else>
<el-form-item :label="t('DataCollection.Device.url')" prop="url">
<el-input
v-model="formData.url"
:placeholder="t('DataCollection.Device.placeholderUrl')"
/>
</el-form-item>
<el-form-item :label="t('DataCollection.Device.username')" prop="username">
<el-input
v-model="formData.username"
:placeholder="t('DataCollection.Device.placeholderUsername')"
/>
</el-form-item>
<el-form-item :label="t('DataCollection.Device.password')" prop="password">
<el-form-item :label="t('DataCollection.Device.topic')" prop="topic">
<el-input
v-model="formData.password"
:placeholder="t('DataCollection.Device.placeholderPassword')"
show-password
v-model="formData.topic"
:placeholder="t('DataCollection.Device.placeholderTopic')"
/>
</el-form-item>
</template>
@ -187,20 +162,19 @@ const formData = ref({
url: undefined,
username: undefined,
password: undefined,
topic: undefined,
})
const formRules = reactive({
create: {
deviceCode: [{ required: true, message: '设备编码不能为空', trigger: 'blur' }],
deviceName: [{ required: true, message: '设备名称不能为空', trigger: 'blur' }],
sampleCycle: [{ required: true, message: '采集周期不能为空', trigger: 'blur' }],
isEnable: [{ required: true, message: '是否启用不能为空', trigger: 'blur' }]
},
update: {
sampleCycle: [{ required: true, message: '采集周期不能为空', trigger: 'blur' }],
isEnable: [{ required: true, message: '是否启用不能为空', trigger: 'blur' }]
},
setting: {
url: [{ required: true, message: '端点URL不能为空', trigger: 'blur' }]
topic: [{ required: true, message: '订阅主题不能为空', trigger: 'blur' }]
}
})
@ -236,7 +210,7 @@ const submitForm = async () => {
//
formLoading.value = true
try {
const { id, deviceCode, deviceName, deviceModelId, sampleCycle, remark, isEnable, url, username, password } = formData.value as any
const { id, deviceCode, deviceName, deviceModelId, sampleCycle, remark, isEnable, topic } = formData.value as any
if (formType.value === 'create') {
const data: Partial<DeviceVO> = { deviceCode, deviceName, deviceModelId, sampleCycle, remark, isEnable }
@ -247,7 +221,7 @@ const submitForm = async () => {
await DeviceApi.updateDevice(data)
message.success(t('common.updateSuccess'))
} else {
const data: any = { id, deviceCode, deviceName, deviceModelId, isEnable, url, username, password }
const data: any = { id, deviceCode, deviceName, deviceModelId, isEnable, topic }
await DeviceApi.updateDevice(data)
message.success(t('common.updateSuccess'))
}

@ -101,8 +101,6 @@
<dict-tag :type="DICT_TYPE.IOT_GATEWAY_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column :label="t('DataCollection.Device.sampleCycle')" align="center" prop="sampleCycle" width="120px" />
<!-- <el-table-column label="读主题" align="center" prop="readTopic" />
<el-table-column label="写主题" align="center" prop="writeTopic" />
<el-table-column label="网关id" align="center" prop="gatewayId" /> -->
@ -125,7 +123,10 @@
/> -->
<el-table-column :label="t('DataCollection.Device.isEnable')" align="center" prop="isEnable" width="120px">
<template #default="scope">
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.isEnable" />
<el-switch
:model-value="isDeviceEnabled(scope.row)"
@change="(val) => handleDeviceEnableChange(scope.row, val)"
/>
</template>
</el-table-column>
<el-table-column
@ -162,11 +163,6 @@
>
{{ t('action.edit') }}
</el-button>
<el-button
link :type="isRowConnected(scope.row) ? 'warning' : 'success'"
:loading="!!connectLoadingMap[scope.row.id]" @click.stop="handleToggleConnect(scope.row)">
{{ isRowConnected(scope.row) ? t('DataCollection.Device.disconnect') : t('DataCollection.Device.connect') }}
</el-button>
<el-button
link
type="danger"
@ -651,6 +647,34 @@ const getOperatingStatusType = (value: string | number | undefined) => {
return 'info'
}
const isDeviceEnabled = (row: DeviceVO) => {
const value = (row as any)?.isEnable
if (typeof value === 'boolean') return value
const text = String(value ?? '').toLowerCase().trim()
if (!text) return false
if (text === 'true' || text === '1' || text === 'yes') return true
return false
}
const handleDeviceEnableChange = async (row: DeviceVO, value: boolean) => {
if (!row.id) return
const oldValue = (row as any).isEnable
;(row as any).isEnable = value
try {
await DeviceApi.updateDeviceEnabled(row.id, value ? 'true' : 'false')
const name = (row as any).deviceName ?? (row as any).deviceCode ?? ''
const suffix = value ? '已启用' : '已停用'
if (name) {
message.success(`${name}${suffix}`)
} else {
message.success(suffix)
}
} catch {
;(row as any).isEnable = oldValue
message.error(t('common.updateFail'))
}
}
const selectedIds = ref<number[]>([])
const handleSelectionChange = (rows: any[]) => {
selectedIds.value = rows?.map((row) => row.id).filter((id) => id !== undefined) ?? []

Loading…
Cancel
Save