ck #3

Merged
besure merged 12 commits from ck into main 2 weeks ago

@ -102,6 +102,9 @@ export const DeviceApi = {
getAvailableList: async () => {
return await request.get({ url: `/iot/device/available-list` })
},
getAvailableListPage: async (params: any) => {
return await request.get({ url: `/iot/device/available-page` , params})
},
// 新增物联设备
createDevice: async (data: DeviceVO) => {
return await request.post({ url: `/iot/device/create`, data })
@ -147,6 +150,10 @@ export const DeviceApi = {
return await request.get({ url: `/iot/device/singleDevice`, params })
},
getSingleDeviceFrom: async (params: SingleDeviceParams) => {
return await request.get({ url: `/iot/device/singleDeviceFrom`, params })
},
getHistoryRecord: async (params: HistoryRecordParams) => {
return await request.get({
url: `/iot/device/historyRecord`,
@ -155,6 +162,15 @@ export const DeviceApi = {
})
},
getHistoryAnalyse: async (params: HistoryRecordParams) => {
return await request.get({
url: `/iot/device/historyAnalyse`,
params,
paramsSerializer: (p) => qs.stringify(p, { allowDots: true, arrayFormat: 'repeat' })
})
},
devicePointList: async () => {
return await request.get({ url: `/iot/device/devicePointList` })
},
@ -170,6 +186,10 @@ export const DeviceApi = {
getDeviceAttributePage: async (params) => {
return await request.get({ url: `/iot/device/device-attribute/page`, params })
},
// 获得设备属性按类型分租
getDeviceAttributeGroupList: async (params) => {
return await request.get({ url: `/iot/device/device-attribute/groupList`, params })
},
// 获得设备属性列表
getDeviceAttributeList: async (deviceId: number | string) => {
return await request.get({ url: `/iot/device/device-attribute/list?deviceId=` + deviceId })

@ -64,13 +64,16 @@ const props = withDefaults(
pageSize?: number
initialRows?: any[]
queryParams?: Record<string, any> //
/** ✅ 新增 */
defaultSelectedKeys?: (string | number)[]
}>(),
{
selectionType: 'multiple',
rowKey: 'id',
pageSize: 10,
initialRows: () => [],
queryParams: () => ({}) //
queryParams: () => ({}), //
defaultSelectedKeys: () => []
}
)
@ -111,6 +114,16 @@ const refreshSelectionOnTable = async () => {
syncingSelection.value = false
}
}
/**
* 新增根据 defaultSelectedKeys 初始化选中状态
*/
const initDefaultSelection = () => {
if (!props.defaultSelectedKeys.length) return
props.defaultSelectedKeys.forEach((key) => {
selectedMap.value.set(key, { [props.rowKey]: key })
})
}
const getList = async () => {
loading.value = true
@ -161,12 +174,30 @@ const handleRowClick = (row: any) => {
}
const open = async (rows?: any[]) => {
/* selectedMap.value.clear()
const initialRows = rows || props.initialRows
const nextRows = isSingleSelect.value ? initialRows.slice(0, 1) : initialRows
nextRows.forEach((row) => {
selectedMap.value.set(resolveRowKey(row), row)
})
queryParams.pageNo = 1
queryParams.pageSize = props.pageSize
dialogVisible.value = true
await getList()*/
selectedMap.value.clear()
// 使 defaultSelectedKeys
if (props.defaultSelectedKeys.length) {
initDefaultSelection()
}
// initialRows / rows
const initialRows = rows || props.initialRows
const nextRows = isSingleSelect.value ? initialRows.slice(0, 1) : initialRows
nextRows.forEach((row) => {
selectedMap.value.set(resolveRowKey(row), row)
})
queryParams.pageNo = 1
queryParams.pageSize = props.pageSize
dialogVisible.value = true

@ -1848,6 +1848,8 @@ export default {
moduleName: 'Product Material Information',
searchNameLabel: 'Name',
searchNamePlaceholder: 'Please enter name',
searchCodeLabel: 'Code',
searchCodePlaceholder: 'Please enter code',
searchButtonText: 'Search',
resetButtonText: 'Reset',
addButtonText: 'Add',
@ -4227,9 +4229,11 @@ export default {
tableCollectionTimeColumn: 'Collection Time',
tableOperateColumn: 'Operation',
tableActionHistoryLabel: 'History',
tableActionHistoryAnalyseLabel: 'History Data Analysis',
dialogTitlePrefix: 'History: ',
dialogCollectionTimeLabel: 'Collection Time',
dialogPointFilterLabel:'Point Filter',
dialogPointFilterPlaceholder: 'Please enter point name',
dialogCollectionTimeStartPlaceholder: 'Start Time',
dialogCollectionTimeEndPlaceholder: 'End Time',
dialogSearchButtonText: 'Search',

@ -2680,6 +2680,8 @@ export default {
moduleName: '产品物料信息',
searchNameLabel: '名称',
searchNamePlaceholder: '请输入名称',
searchCodeLabel: '编码',
searchCodePlaceholder: '请输入编码',
searchButtonText: '搜索',
resetButtonText: '重置',
addButtonText: '新增',
@ -4070,9 +4072,11 @@ export default {
tableCollectionTimeColumn: '采集时间',
tableOperateColumn: '操作',
tableActionHistoryLabel: '历史记录',
tableActionHistoryAnalyseLabel: '数据分析',
dialogTitlePrefix: '历史记录:',
dialogCollectionTimeLabel: '采集时间',
dialogPointFilterLabel: '点位筛选',
dialogPointFilterPlaceholder: '请输入点位名称',
dialogCollectionTimeStartPlaceholder: '开始时间',
dialogCollectionTimeEndPlaceholder: '结束时间',
dialogSearchButtonText: '查询',
@ -4442,9 +4446,11 @@ export default {
tableCollectionTimeColumn: '采集时间',
tableOperateColumn: '操作',
tableActionHistoryLabel: '历史记录',
tableActionHistoryAnalyseLabel: '数据分析',
dialogTitlePrefix: '历史记录:',
dialogCollectionTimeLabel: '采集时间',
dialogPointFilterLabel: '点位筛选',
dialogPointFilterPlaceholder: '请输入点位名称',
dialogCollectionTimeStartPlaceholder: '开始时间',
dialogCollectionTimeEndPlaceholder: '结束时间',
dialogSearchButtonText: '查询',

@ -2,41 +2,29 @@
<template>
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="120px"
@submit.prevent
>
<el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true" label-width="auto" @submit.prevent>
<el-form-item :label="t('FactoryModeling.ProductInformation.searchCodeLabel')" prop="barCode">
<el-input v-model="queryParams.barCode"
:placeholder="t('FactoryModeling.ProductInformation.searchCodePlaceholder')" clearable
@keyup.enter="handleQuery" class="!w-240px" />
</el-form-item>
<el-form-item :label="t('FactoryModeling.ProductInformation.searchNameLabel')" prop="name">
<el-input
v-model="queryParams.name"
:placeholder="t('FactoryModeling.ProductInformation.searchNamePlaceholder')"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
<el-input v-model="queryParams.name"
:placeholder="t('FactoryModeling.ProductInformation.searchNamePlaceholder')" clearable
@keyup.enter="handleQuery" class="!w-240px" />
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> {{ t('FactoryModeling.ProductInformation.searchButtonText') }}</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> {{ t('FactoryModeling.ProductInformation.resetButtonText') }}</el-button>
<el-button
type="primary"
plain
@click="openForm('create')"
v-hasPermi="['erp:product:create']"
>
<el-button @click="handleQuery">
<Icon icon="ep:search" class="mr-5px" /> {{ t('FactoryModeling.ProductInformation.searchButtonText') }}
</el-button>
<el-button @click="resetQuery">
<Icon icon="ep:refresh" class="mr-5px" /> {{ t('FactoryModeling.ProductInformation.resetButtonText') }}
</el-button>
<el-button type="primary" plain @click="openForm('create')" v-hasPermi="['erp:product:create']">
<Icon icon="ep:plus" class="mr-5px" /> {{ t('FactoryModeling.ProductInformation.addButtonText') }}
</el-button>
<el-button
type="success"
plain
@click="handleExport"
:loading="exportLoading"
v-hasPermi="['erp:product:export']"
>
<el-button type="success" plain @click="handleExport" :loading="exportLoading"
v-hasPermi="['erp:product:export']">
<Icon icon="ep:download" class="mr-5px" /> {{ t('FactoryModeling.ProductInformation.exportButtonText') }}
</el-button>
</el-form-item>
@ -47,12 +35,7 @@
<ContentWrap>
<el-tabs v-model="activeName" @tab-click="handleTabClick">
<!-- 使用 v-for 动态生成 el-tab-pane -->
<el-tab-pane
v-for="item in parentList"
:key="item.id"
:label="item.name"
:name="item.id.toString()"
/>
<el-tab-pane v-for="item in parentList" :key="item.id" :label="item.name" :name="item.id.toString()" />
<!-- <el-tab-pane label="产品" name="2" />
<el-tab-pane label="原料" name="1" />
<el-tab-pane label="备件" name="5" />
@ -62,60 +45,46 @@
<el-tab-pane label="其他" name="0" /> -->
</el-tabs>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<el-table-column :label="t('FactoryModeling.ProductInformation.tableBarCodeColumn')" align="center" prop="barCode" sortable/>
<el-table-column :label="t('FactoryModeling.ProductInformation.tableNameColumn')" align="left" prop="name" width="220px" sortable/>
<el-table-column :label="t('FactoryModeling.ProductInformation.tableStandardColumn')" align="center" prop="standard" sortable/>
<el-table-column :label="t('FactoryModeling.ProductInformation.tableCategoryColumn')" align="center" prop="subCategoryName" sortable/>
<el-table-column :label="t('FactoryModeling.ProductInformation.tableUnitColumn')" align="center" prop="unitName" sortable/>
<el-table-column :label="t('FactoryModeling.ProductInformation.tableStatusColumn')" align="center" prop="status" sortable>
<el-table-column :label="t('FactoryModeling.ProductInformation.tableBarCodeColumn')" align="center" prop="barCode"
sortable />
<el-table-column :label="t('FactoryModeling.ProductInformation.tableNameColumn')" align="left" prop="name"
width="220px" sortable />
<el-table-column :label="t('FactoryModeling.ProductInformation.tableStandardColumn')" align="center"
prop="standard" sortable />
<el-table-column :label="t('FactoryModeling.ProductInformation.tableCategoryColumn')" align="center"
prop="subCategoryName" sortable />
<el-table-column :label="t('FactoryModeling.ProductInformation.tableUnitColumn')" align="center" prop="unitName"
sortable />
<el-table-column :label="t('FactoryModeling.ProductInformation.tableStatusColumn')" align="center" prop="status"
sortable>
<template #default="scope">
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column
:label="t('FactoryModeling.ProductInformation.tableCreateTimeColumn')"
align="center"
prop="createTime"
:formatter="dateFormatter"
width="180px"
sortable
/>
<el-table-column :label="t('FactoryModeling.ProductInformation.tableCreateTimeColumn')" align="center"
prop="createTime" :formatter="dateFormatter" width="180px" sortable />
<el-table-column :label="t('FactoryModeling.ProductInformation.tableOperateColumn')" align="center" width="150px">
<template #default="scope">
<!-- <el-button-->
<!-- v-if="scope.row.categoryId ===2"-->
<!-- link-->
<!-- type="primary"-->
<!-- @click="openBomForm('detail', scope.row.id)"-->
<!-- >-->
<!-- BOM-->
<!-- </el-button>-->
<el-button
link
type="primary"
@click="openForm('update', scope.row.id)"
v-hasPermi="['erp:product:update']"
>
<!-- <el-button-->
<!-- v-if="scope.row.categoryId ===2"-->
<!-- link-->
<!-- type="primary"-->
<!-- @click="openBomForm('detail', scope.row.id)"-->
<!-- >-->
<!-- BOM-->
<!-- </el-button>-->
<el-button link type="primary" @click="openForm('update', scope.row.id)" v-hasPermi="['erp:product:update']">
{{ t('FactoryModeling.ProductInformation.tableEditAction') }}
</el-button>
<el-button
link
type="danger"
@click="handleDelete(scope.row.id)"
v-hasPermi="['erp:product:delete']"
>
<el-button link type="danger" @click="handleDelete(scope.row.id)" v-hasPermi="['erp:product:delete']">
{{ t('FactoryModeling.ProductInformation.tableDeleteAction') }}
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
<Pagination :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize"
@pagination="getList" />
</ContentWrap>
<!-- 表单弹窗添加/修改 -->
@ -148,6 +117,7 @@ const queryParams = reactive({
pageNo: 1,
pageSize: 10,
name: undefined,
barCode: undefined,
categoryId: undefined
})
const queryFormRef = ref() //
@ -195,7 +165,7 @@ const handleDelete = async (id: number) => {
message.success(t('common.delSuccess'))
//
await getList()
} catch {}
} catch { }
}
/** 导出按钮操作 */

File diff suppressed because it is too large Load Diff

@ -322,7 +322,7 @@
<!-- 表单弹窗添加/修改 -->
<DeviceForm ref="formRef" @success="getList" />
<!-- 子表的列表 -->
<ContentWrap v-if="ifShow">
<!-- <ContentWrap v-if="ifShow">
<template v-if="attributeDeviceId">
<div class="mb-10px flex items-center justify-between text-sm text-gray-500">
<div>
@ -711,7 +711,7 @@
</el-tabs>
</template>
<el-empty v-else :description="t('DataCollection.Device.emptyDescription')" />
</ContentWrap>
</ContentWrap>-->
<Dialog :title="t('DataCollection.Device.alarmHistoryTitle')" v-model="deviceAlarmDialogVisible" width="1200px">
<el-form
@ -1141,6 +1141,14 @@ const deviceRuleTabLabel = computed(() => {
const handleShowAttribute = (row: any) => {
attributeDeviceId.value = row?.id
attributeDeviceName.value = row?.deviceName ?? ''
router.push({
path: '/iot/pointManagement',
query: {
id: row?.id,
name: row?.deviceName
}
});
}
const ruleLoading = ref(false)

File diff suppressed because it is too large Load Diff

@ -108,12 +108,15 @@
:label="t('DataCollection.HistoryData.tableOperateColumn')"
align="center"
fixed="right"
width="150px"
width="300px"
>
<template #default="scope">
<el-button link type="primary" @click="handleSingleView(scope.row)">
{{ t('DataCollection.HistoryData.tableActionHistoryLabel') }}
</el-button>
<el-button link type="primary" @click="handleSingleAnalyseView(scope.row)">
{{ t('DataCollection.HistoryData.tableActionHistoryAnalyseLabel') }}
</el-button>
</template>
</el-table-column>
</el-table>
@ -137,9 +140,10 @@ import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import { DeviceApi, LineDeviceVO, LineDevicePageParams } from '@/api/iot/device'
import HistorySingleDeviceDialog from './HistorySingleDeviceDialog.vue'
import {useRouter} from "vue-router";
//
const router = useRouter()
defineOptions({ name: 'HistoryData' })
const message = useMessage()
const { t } = useI18n()
@ -225,6 +229,27 @@ const handleSingleView = (row: LineDeviceVO) => {
singleDialogVisible.value = true
}
const handleSingleAnalyseView = (row: LineDeviceVO) => {
const deviceId = (row as any)?.deviceId ?? row?.id
if (deviceId === undefined || deviceId === null || deviceId === '') {
return
}
singleDeviceId.value = deviceId
singleDeviceName.value = row.deviceName || ''
router.push({
path: '/iot/historySingleAnalyse',
query: {
id: deviceId,
name: singleDeviceName.value,
deviceCode: row.deviceCode,
lineNode: row.lineNode,
lineName: row.lineName,
collectionTime: row.collectionTime
}
});
}
onMounted(() => {
getList()
})

@ -7,15 +7,15 @@
</div>
<ContentWrap v-loading="loading">
<div v-if="sections.length" class="single-device-dialog__table-grid">
<div v-for="section in sections" :key="section.key" class="single-device-dialog__section">
<!-- <div v-for="section in sections" :key="section.key" class="single-device-dialog__section">
<div class="single-device-dialog__section-title">
{{ section.title }}
</div>
<el-empty
v-if="!section.columns.length"
</div>-->
<!-- <el-empty
v-if="!section.length"
:description="t('DataCollection.RealTimeMonitoring.emptyDescription')"
/>
<el-table
/>-->
<!-- <el-table
v-else :data="section.rows" :border="true" :header-cell-style="headerCellStyle"
:cell-style="bodyCellStyle" size="small">
<el-table-column
@ -25,8 +25,22 @@ v-for="col in section.columns" :key="col.prop" :prop="col.prop" :label="col.labe
<span>{{ formatCell(scope.row[col.prop]) }}</span>
</template>
</el-table-column>
</el-table>
</div>
</el-table>-->
<div v-for="section in sections" :key="section.id" class="form-section">
<h3 v-if="section.title" class="section-title">{{ section.title }}</h3>
<div class="form-grid">
<div
v-for="item in section.items"
:key="item.prop"
class="form-item"
>
<div class="form-label">{{ item.label }}:</div>
<div class="form-value">{{ formatCellTwo(item.value) }}</div>
</div>
</div>
</div>
<!-- </div>-->
</div>
<el-empty
v-else
@ -49,10 +63,14 @@ type SectionColumn = {
type SectionRow = Record<string, string | number | null>
type Section = {
key: string
title: string
columns: SectionColumn[]
rows: SectionRow[]
id: number
title?: string
items: FormItem[]
}
interface FormItem {
prop: string
label: string
value: any
}
const { t } = useI18n()
@ -83,8 +101,44 @@ const dialogTitle = computed(() => {
const loading = ref(false)
const deviceName = computed(() => props.deviceName)
const sections = ref<Section[]>([])
const sections = ref<Section[]>([
{
id: 1,
title: '工艺参数监控',
items: [
{ prop: 'addressValue', label: '系统运行', value: 1, unit: '℃' },
{ prop: 'attributeName', label: '烘干湿度', value: 2, unit: '%' },
{ prop: 'attributeName', label: '输送速度', value: 3, unit: 'm/min' },
{ prop: 'attributeName', label: '回风温度', value: 4, unit: '℃' },
{ prop: 'attributeName', label: '排风温度', value: 5, unit: '℃' },
{ prop: 'attributeName', label: '呼叫状态', value: 6 },
{ prop: 'attributeName', label: '在线状态', value: 7 },
{ prop: 'attributeName', label: '运行状态', value: 3 },
{ prop: 'attributeName', label: '主缸速度', value: 0, unit: '%' },
{ prop: 'attributeName', label: '提布辊速度', value: 0 },
{ prop: 'fanSpeed', label: '风机速度', value: '暂无数据' },
{ prop: 'mainLevel', label: '主缸水位', value: 1, unit: '%' },
{ prop: 'mainWater', label: '主缸水量', value: 145, unit: 'L' },
{ prop: 'setTemp', label: '设定温度', value: 0.0, unit: '℃' },
{ prop: 'actualTemp', label: '实际温度', value: 0.0, unit: '℃' },
],
},
])
const formatValue = (item: FormItem) => {
let value = item.value
if (value === null || value === undefined) return '--'
if (typeof value === 'number') {
value = Number.isInteger(value) ? value.toString() : value.toFixed(2)
}
if (item.unit) {
value += ` ${item.unit}`
}
return value
}
const toGroupMap = (value: any): Record<string, any[]> => {
if (!value || typeof value !== 'object' || Array.isArray(value)) {
return {}
@ -136,6 +190,14 @@ const formatCell = (value: string | number | null | undefined) => {
return value
}
const formatCellTwo = (value: any) => {
//
if (typeof value === 'number') {
return value.toFixed(2)
}
return value
}
const buildSectionsFromGroups = (groups: Record<string, any[]>): Section[] => {
const result: Section[] = []
for (const [key, list] of Object.entries(groups)) {
@ -150,12 +212,12 @@ const buildSectionsFromGroups = (groups: Record<string, any[]>): Section[] => {
row[prop] = item?.addressValue ?? '-'
})
}
result.push({
/* result.push({
key,
title: key,
columns,
rows: columns.length ? [row] : []
})
})*/
}
return result
}
@ -167,15 +229,16 @@ const fetchList = async () => {
}
loading.value = true
try {
const res: any = await DeviceApi.getSingleDevice({ deviceId: props.deviceId })
const groups = Array.isArray(res)
const res: any = await DeviceApi.getSingleDeviceFrom({ deviceId: props.deviceId })
/* const groups = Array.isArray(res)
? { [t('DataCollection.RealTimeMonitoring.defaultGroupName')]: res }
: {
...toGroupMap(res?.data),
...toGroupMap(res?.data?.data),
...toGroupMap(res)
}
sections.value = buildSectionsFromGroups(groups)
//sections.value = buildSectionsFromGroups(groups)*/
sections.value = res
} finally {
loading.value = false
}
@ -222,4 +285,84 @@ watch(
.single-device-dialog__section :deep(.el-table__inner-wrapper) {
border-radius: 0;
}
/* 表单容器 */
.form-section {
margin-bottom: 24px;
}
/* 标题样式 */
.section-title {
font-size: 13px;
font-weight: bold;
margin-bottom: 12px;
color: var(--el-text-color-primary);
}
/* 三列网格容器 */
.form-grid {
display: grid;
grid-template-columns: repeat(4, 1fr); /* 三列等宽 */
/*gap: 16px;*/ /* 列间距和行间距 */
/* padding: 8px;*/
background-color: #fff;
/* border: 1px solid #dcdfe6; !* 外边框 *!*/
border-radius: 4px;
min-height: 35px;
}
/* 表单项:标签 + 值 */
.form-item {
display: flex;
align-items: center;
justify-content: space-between;
height: 100%;
min-height: 35px;
/* padding: 8px 0;*/
border: 1px solid #dcdfe6;
/* border-right: 1px solid #dcdfe6; !* *!
border-bottom: 1px solid #dcdfe6; !* 下边框 *!*/
}
/* 标签样式 */
.form-label {
font-size: 14px;
display: flex;
align-items: center;
justify-content: center;
color: #666;
height: 100%;
min-height: 35px;
width: 150px; /* 固定标签宽度,避免错位 */
background-color: #f5f7fa; /* 背景色 */
}
/* 值样式 */
.form-value {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
height: 100%;
padding: 6px 12px;
background: white;
min-height: 35px;
box-sizing: border-box;
overflow: hidden;
cursor: default;
transition: all 0.2s;
}
/* 响应式:小屏幕下改为两列或单列 */
@media (max-width: 768px) {
.form-grid {
grid-template-columns: repeat(2, 1fr); /* 平板改为两列 */
}
}
@media (max-width: 480px) {
.form-grid {
grid-template-columns: 1fr; /* 手机改为单列 */
}
}
</style>

@ -79,7 +79,7 @@
</el-tooltip>
</span>
</template>
<el-select v-model="formData.orgType" @change="handleOrgTypeChange" :placeholder="t('FactoryModeling.FactoryStructure.dialogOrgTypePlaceholder')">
<el-select v-model="formData.orgType" :placeholder="t('FactoryModeling.FactoryStructure.dialogOrgTypePlaceholder')">
<el-option
v-for="dict in getStrDictOptions(DICT_TYPE.MES_ORG_TYPE)"
:key="dict.value"
@ -89,25 +89,20 @@
</el-select>
</el-form-item>
<el-form-item :label="t('FactoryModeling.FactoryStructure.dialogMachineLabel')" v-if="formData.orgClass == 'workplace'" prop="machineId">
<!-- <el-tree-select
v-model="formData.machineId"
:data="machineComponentTree"
:props="defaultProps"
check-strictly
default-expand-all
placeholder="请选择机台"
/> -->
<el-select v-model="formData.machineId" :placeholder="t('FactoryModeling.FactoryStructure.dialogMachinePlaceholder')" clearable>
<!-- <el-select v-model="formData.machineId" :placeholder="t('FactoryModeling.FactoryStructure.dialogMachinePlaceholder')" clearable>
<el-option
v-for="item in deviceList"
:key="item.id"
:label="item.deviceName"
:value="item.id"
/>
</el-select>
</el-select>-->
<el-input :model-value="displayItemDevice" readonly clearable class="device-ledger-selection-input"
:placeholder="t('FactoryModeling.FactoryStructure.dialogMachinePlaceholder')"
@click="openCriticalComponentDialog" />
</el-form-item>
<el-form-item :label="t('EquipmentManagement.EquipmentLedger.dvName')" prop="dvId" v-if="formData.orgClass == 'workplace'">
<el-select v-model="formData.dvId" filterable :placeholder="t('EquipmentManagement.EquipmentLedger.dvId')" clearable>
<!-- <el-select v-model="formData.dvId" filterable :placeholder="t('EquipmentManagement.EquipmentLedger.dvId')" clearable>
<el-option
v-for="item in dvList"
:key="item.id"
@ -115,7 +110,10 @@
:value="item.id"
:disabled="item.selected === true"
/>
</el-select>
</el-select>-->
<el-input :model-value="dvIdItemDevice" readonly clearable class="device-ledger-selection-input"
:placeholder="t('EquipmentManagement.EquipmentLedger.dvId')"
@click="openCaijiComponentDialog" />
</el-form-item>
<!-- <el-form-item label="组织状态" prop="status">
<el-radio-group v-model="formData.status">
@ -134,6 +132,61 @@
<el-button @click="dialogVisible = false">{{ t('FactoryModeling.FactoryStructure.dialogCancelButton') }}</el-button>
</template>
</Dialog>
<TableSelectDialog
ref="deviceSelectDialogRef"
title="选择设备"
:columns="deviceColumns"
:fetch-api="fetchDeviceLedgerPage"
row-key="id"
@confirm="handleDeviceSelectConfirm"
:query-params="mergedQueryParams"
:selection-type="'single'"
:default-selected-keys="ids"
>
<!-- 使用 header 插槽插入查询表单 -->
<template #header>
<el-form ref="searchFormRef" :model="searchParams" :inline="true" >
<el-form-item label="设备编号" prop="deviceCode">
<el-input v-model="searchParams.deviceCode" placeholder="请输入编号" clearable />
</el-form-item>
<el-form-item label="设备名称" prop="deviceName">
<el-input v-model="searchParams.deviceName" placeholder="请输入名称" clearable />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">{{ t('FactoryModeling.ProductInformation.searchButtonText') }}</el-button>
<el-button @click="resetSearch">{{ t('FactoryModeling.ProductInformation.resetButtonText') }}</el-button>
</el-form-item>
</el-form>
</template>
</TableSelectDialog>
<TableSelectDialog
ref="caiJiSelectDialogRef"
title="选择采集设备"
:columns="caiJiColumns"
:fetch-api="fetchCaiJILedgerPage"
row-key="id"
@confirm="handleCaiJISelectConfirm"
:query-params="mergedQueryParams"
:selection-type="'single'"
:default-selected-keys="CJIds"
>
<!-- 使用 header 插槽插入查询表单 -->
<template #header>
<el-form ref="searchFormRef" :model="searchParams" :inline="true" >
<el-form-item label="设备编号" prop="deviceCode">
<el-input v-model="searchParams.deviceCode" placeholder="请输入编号" clearable />
</el-form-item>
<el-form-item label="设备名称" prop="deviceName">
<el-input v-model="searchParams.deviceName" placeholder="请输入名称" clearable />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleCJSearch">{{ t('FactoryModeling.ProductInformation.searchButtonText') }}</el-button>
<el-button @click="resetCJSearch">{{ t('FactoryModeling.ProductInformation.resetButtonText') }}</el-button>
</el-form-item>
</el-form>
</template>
</TableSelectDialog>
</template>
<script setup lang="ts">
import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
@ -142,13 +195,14 @@ import { defaultProps, handleTree } from '@/utils/tree'
import {MachineComponentApi} from "@/api/mes/machine";
import {DeviceLedgerApi, DeviceLedgerVO} from "@/api/mes/deviceledger";
import { DeviceApi,DeviceVO } from '@/api/iot/device'
import TableSelectDialog from '@/components/TableSelectDialog/TableSelectDialog.vue'
/** 产线工位 表单 */
defineOptions({ name: 'OrganizationForm' })
const loading = ref(true)
const { t } = useI18n() //
const message = useMessage() //
const deviceSelectDialogRef = ref(false)
const caiJiSelectDialogRef = ref(false)
const dvList = ref<DeviceVO[]>([]) //
const deviceList = ref<DeviceLedgerVO[]>([]) //
const dialogVisible = ref(false) //
@ -179,22 +233,70 @@ const formRules = reactive({
parentId: [{ required: true, message: t('FactoryModeling.FactoryStructure.validatorParentRequired'), trigger: 'blur' }],
orgClass: [{ required: true, message: t('FactoryModeling.FactoryStructure.validatorOrgClassRequired'), trigger: 'blur' }]
})
const itemList = ref<DeviceLedgerVO[]>([])
const CJList = ref<DeviceVO[]>([]) //
const mergedQueryParams = computed(() => ({ ...searchParams }))
const formRef = ref() // Ref
const organizationTree = ref() //
const deviceColumns = [
{ label: '设备编号', prop: 'deviceCode', minWidth: 140 },
{ label: '设备名称', prop: 'deviceName', minWidth: 160 },
{ label: '设备型号', prop: 'deviceModel', minWidth: 140 },
{ label: '所属车间', prop: 'workshop', minWidth: 140 }
]
const caiJiColumns = [
{ label: '设备编号', prop: 'deviceCode', minWidth: 140 },
{ label: '设备名称', prop: 'deviceName', minWidth: 160 },
]
// 1.
const searchParams = reactive({
deviceCode: undefined,
deviceName: undefined,
})
const handleSearch = () => {
//
deviceSelectDialogRef.value?.reload?.()
}
const searchFormRef = ref()
const resetSearch = () => {
searchFormRef.value?.resetFields()
handleSearch()
}
const handleCJSearch = () => {
//
caiJiSelectDialogRef.value?.reload?.()
}
const searchCJFormRef = ref()
const resetCJSearch = () => {
searchFormRef.value?.resetFields()
handleCJSearch()
}
/** 打开弹窗 */
const open = async (type: string, id?: number) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
resetForm()
initSelectedItems()
//
if (id) {
formLoading.value = true
try {
formData.value = await OrganizationApi.getOrganization(id)
ids.value=formData.value.machineId != undefined
? formData.value.machineId.toString().split(",").map(Number)
: []
CJIds.value=formData.value.dvId!=undefined?formData.value.dvId.toString().split(",").map(Number):[]
} finally {
formLoading.value = false
}
}else{
ids.value=[]
CJIds.value=[]
}
await getOrganizationTree()
if (type == 'create' || typeof formData.value.machineId != 'number') {
@ -283,8 +385,115 @@ const getMachineComponentTree = async () => {
root.children = handleTree(data, 'id', 'parentId')
machineComponentTree.value.push(root)
}
const ids = ref([])
const CJIds = ref([])
const displayItemDevice = computed( () => {
if (!ids.value.length)return ''
if (!itemList.value.length) {
return formData.value.machineId ? String(formData.value.machineId) : ''
}
const map = new Map(itemList.value.map((item) => [item.id, item.deviceName]))
const names = ids.value
.map((id) => map.get(id))
.filter((name) => name)
return names.join(',')
})
const dvIdItemDevice = computed( () => {
if (!CJIds.value.length)return ''
if (!CJList.value.length) {
return formData.value.dvId ? String(formData.value.dvId) : ''
}
const map = new Map(CJList.value.map((item) => [item.id, item.deviceName]))
const names = CJIds.value
.map((id) => map.get(id))
.filter((name) => name)
return names.join(',')
})
const fetchDeviceLedgerPage = (params: Record<string, any>) => {
return DeviceLedgerApi.getDeviceLedgerPage({
...params,
})
}
const fetchCaiJILedgerPage = (params: Record<string, any>) => {
return DeviceApi.getAvailableListPage({
...params,
})
}
const selectedDeviceRows = ref<any[]>([])
const selectedCaiJiRows = ref<any[]>([])
/*打开关联设备确认*/
const handleDeviceSelectConfirm = (payload: { ids: (number | string)[]; rows: any[] }) => {
formData.value.devices = payload.rows
.map((item) => {
const id = Number(item.id)
if (!Number.isFinite(id)) return undefined
return {
id,
name: item.deviceName || item.name || item.code || `设备ID:${id}`
}
})
.filter((item): item is { id: number; name: string } => Boolean(item))
selectedDeviceRows.value = payload.rows
formData.value.machineId = payload.ids.join(',')
ids.value = payload.ids.map((id) => Number(id))
}
/*打开关联设备*/
const openCriticalComponentDialog = async () => {
searchParams.deviceCode=''
searchParams.deviceName=''
const rows = selectedDeviceRows.value.map((item) => ({ ...item, id: Number(item.id) }))
deviceSelectDialogRef.value?.open(rows)
let initIds= formData.value.machineId!=undefined?formData.value.machineId.toString().split(","):[]
ids.value=initIds.map((id) => Number(id))
}
/*打开采集设备确认*/
const handleCaiJISelectConfirm = (payload: { ids: (number | string)[]; rows: any[] }) => {
formData.value.devices = payload.rows
.map((item) => {
const id = Number(item.id)
if (!Number.isFinite(id)) return undefined
return {
id,
name: item.deviceName || item.name || item.code || `设备ID:${id}`
}
})
.filter((item): item is { id: number; name: string } => Boolean(item))
selectedCaiJiRows.value = payload.rows
formData.value.dvId = payload.ids.join(',')
CJIds.value = payload.ids.map((id) => Number(id))
}
/*打开采集设备*/
const openCaijiComponentDialog = async () => {
searchParams.deviceCode=''
searchParams.deviceName=''
const rows = selectedCaiJiRows.value.map((item) => ({ ...item, id: Number(item.id) }))
caiJiSelectDialogRef.value?.open(rows)
let initIds= formData.value.dvId!=undefined?formData.value.dvId.toString().split(","):[]
CJIds.value=initIds.map((id) => Number(id))
}
const initSelectedItems = async () => {
if (!itemList.value.length) {
loading.value = true
try {
itemList.value = await DeviceLedgerApi.getDeviceLedgerList()
} finally {
loading.value = false
}
}
if (!CJList.value.length) {
loading.value = true
try {
CJList.value = await DeviceApi.getAvailableList()
} finally {
loading.value = false
}
}
}
/** 初始化 **/
onMounted(async () => {
//console.log("ssss")

@ -34,7 +34,7 @@
</el-form-item>
<el-form-item :label="t('MoldManagement.MoldInspectionPlan.subjectName')" prop="subjectIds">
<el-select
<!-- <el-select
v-model="formData.subjectIds"
multiple
filterable
@ -43,7 +43,10 @@
class="!w-full"
>
<el-option v-for="item in subjectOptions" :key="item.id" :label="item.subjectName" :value="item.id" />
</el-select>
</el-select>-->
<el-input :model-value="displayItemDevice" readonly clearable class="device-ledger-selection-input"
:placeholder="t('MoldManagement.MoldInspectionPlan.placeholderSubjectSelect')"
@click="openCriticalComponentDialog" />
</el-form-item>
</el-form>
@ -52,13 +55,40 @@
<el-button @click="dialogVisible = false">{{ t('common.cancel') }}</el-button>
</template>
</Dialog>
<TableSelectDialog
ref="deviceSelectDialogRef"
title="选择设备"
:columns="deviceColumns"
:fetch-api="fetchDeviceLedgerPage"
row-key="id"
@confirm="handleDeviceSelectConfirm"
:query-params="mergedQueryParams"
:default-selected-keys="ids"
>
<!-- 使用 header 插槽插入查询表单 -->
<template #header>
<el-form ref="searchFormRef" :model="searchParams" :inline="true" >
<el-form-item label="设备编号" prop="deviceCode">
<el-input v-model="searchParams.deviceCode" placeholder="请输入编号" clearable />
</el-form-item>
<el-form-item label="设备名称" prop="deviceName">
<el-input v-model="searchParams.deviceName" placeholder="请输入名称" clearable />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">{{ t('FactoryModeling.ProductInformation.searchButtonText') }}</el-button>
<el-button @click="resetSearch">{{ t('FactoryModeling.ProductInformation.resetButtonText') }}</el-button>
</el-form-item>
</el-form>
</template>
</TableSelectDialog>
</template>
<script setup lang="ts">
import type { FormRules } from 'element-plus'
import { DvSubjectApi, DvSubjectVO } from '@/api/mold/inspectionItems'
import { PlanMaintenanceApi, PlanMaintenanceVO } from '@/api/mold/planmaintenance'
import {DeviceLedgerApi, DeviceLedgerVO} from "@/api/mes/deviceledger";
import TableSelectDialog from '@/components/TableSelectDialog/TableSelectDialog.vue'
defineOptions({ name: 'MoldInspectionPlanForm' })
const { t } = useI18n()
@ -74,6 +104,7 @@ const ensureSubjectOptionsLoaded = async () => {
if (subjectOptions.value.length) return
const res = await DvSubjectApi.getMoldSubjectAllList()
const list = Array.isArray(res) ? res : res?.list
itemList.value = (list ?? []) as DvSubjectVO[]
subjectOptions.value = (list ?? []) as DvSubjectVO[]
}
@ -97,7 +128,94 @@ const parseIds = (value: any): Array<number | string> => {
})
.filter((v): v is number | string => v !== undefined)
}
const ids = ref([])
const itemList = ref<DvSubjectVO[]>([])
const selectedDeviceRows = ref<any[]>([])
const deviceSelectDialogRef = ref(false)
const mergedQueryParams = computed(() => ({ ...searchParams }))
const deviceColumns = [
{ label: '项目编码', prop: 'subjectCode', minWidth: 140 },
{ label: '项目名称', prop: 'subjectName', minWidth: 160 },
{ label: '项目类型', prop: 'subjectType', minWidth: 140 },
{ label: '项目内容', prop: 'subjectContent', minWidth: 140 }
]
const fetchDeviceLedgerPage = async (params: Record<string, any>) => {
const res = await DvSubjectApi.getDvSubjectPage({
...params,
})
const list = res.list || []
//
const selectedMap = new Map(
selectedDeviceRows.value.map((item) => [item.id, item])
)
return {
...res,
list: list.map((item) => ({
...item,
...selectedMap.get(item.id)
}))
}
}
// 1.
const searchParams = reactive({
subjectCode: undefined,
subjectName: undefined,
})
const displayItemDevice = computed( () => {
if (!ids.value.length)return ''
if (!itemList.value.length) {
return formData.value.subjectIds ? String(formData.value.subjectIds) : ''
}
const map = new Map(itemList.value.map((item) => [item.id, item.subjectName]))
const names = ids.value
.map((id) => map.get(id))
.filter((name) => name)
return names.join(',')
})
const openCriticalComponentDialog = async () => {
searchParams.subjectCode=''
searchParams.subjectName=''
const rows = selectedDeviceRows.value.length
//deviceSelectDialogRef.value?.open(rows)
let initIds= formData.value.subjectIds!=undefined?formData.value.subjectIds.toString().split(","):[]
deviceSelectDialogRef.value?.open([], {
defaultSelectedKeys: initIds
})
ids.value=initIds.map((id) => Number(id))
}
const handleDeviceSelectConfirm = (payload: { ids: (number | string)[]; rows: any[] }) => {
formData.value.subjectIds = payload.rows
.map((item) => {
const id = Number(item.id)
if (!Number.isFinite(id)) return undefined
return {
id,
name: item.subjectName || item.name || item.code || `设备ID:${id}`
}
})
.filter((item): item is { id: number; name: string } => Boolean(item))
selectedDeviceRows.value = payload.rows
formData.value.subjectIds = payload.ids.join(',')
ids.value = payload.ids.map((id) => Number(id))
}
const handleSearch = () => {
//
deviceSelectDialogRef.value?.reload?.()
}
const searchFormRef = ref()
const resetSearch = () => {
searchFormRef.value?.resetFields()
handleSearch()
}
const initFormData = () => ({
id: undefined as PlanMaintenanceVO['id'],
planName: '' as string,
@ -141,6 +259,9 @@ const open = async (type: 'create' | 'update', row?: Partial<PlanMaintenanceVO>)
description: (row.description as any) ?? '',
subjectIds: parseIds((row as any).subjectIds ?? (row as any).subjectIdS)
}
ids.value=parseIds((row as any).subjectIds ?? (row as any).subjectIdS)
}else{
ids.value=[]
}
}

Loading…
Cancel
Save