Merge remote-tracking branch 'origin/main'

main
liutao 5 days ago
commit 0fc091dbfb

@ -1913,6 +1913,7 @@ export default {
// Equipment Ledger
EquipmentLedger: {
lineCategory: 'Production Line Category',
placeholderLineCategory: 'Please select production line category',
lineCategoryAll: 'All',
createLineCategory: 'Add Production Line Category',
updateLineCategory: 'Edit Production Line Category',
@ -5241,6 +5242,7 @@ export default {
WorkReportRecord: {
moduleName: 'Work Report Record',
tabProductionRecord: 'Production Record',
searchBaogongTimeLabel: 'Report Date',
searchBaogongTimeStartPlaceholder: 'Start Date',

@ -1929,6 +1929,7 @@ export default {
// 设备台账
EquipmentLedger: {
lineCategory: '产线分类',
placeholderLineCategory: '请选择产线分类',
lineCategoryAll: '全部',
createLineCategory: '新增产线分类',
updateLineCategory: '修改产线分类',
@ -4223,8 +4224,8 @@ export default {
},
WorkTeam: {
moduleName: '班组管理',
searchTeamNameLabel: '班名称',
searchTeamNamePlaceholder: '请输入班名称',
searchTeamNameLabel: '班名称',
searchTeamNamePlaceholder: '请输入班名称',
searchProcessLabel: '工序',
searchProcessPlaceholder: '请选择工序',
searchRemarkLabel: '备注',
@ -4237,7 +4238,7 @@ export default {
tabsDay: '白班',
tabsNight: '夜班',
tabsLongDay: '长白班',
tableTeamNameColumn: '班名称',
tableTeamNameColumn: '班名称',
tableLeaderColumn: '组长',
tableGroupTypeColumn: '班组类型',
tableWorkTimeColumn: '工作时间',
@ -4249,8 +4250,8 @@ export default {
tableEditAction: '编辑',
tableDeleteAction: '删除',
detailTabLabel: '班组成员',
dialogTeamNameLabel: '班名称',
dialogTeamNamePlaceholder: '请输入班名称',
dialogTeamNameLabel: '班名称',
dialogTeamNamePlaceholder: '请输入班名称',
dialogProcessLabel: '工序',
dialogProcessTooltip: '来源:数据字典-工序',
dialogGroupTypeLabel: '班组类型',
@ -5136,6 +5137,7 @@ export default {
},
WorkReportRecord: {
moduleName: '报工记录',
tabProductionRecord: '生产记录',
searchBaogongTimeLabel: '报工日期',
searchBaogongTimeStartPlaceholder: '开始日期',

@ -49,6 +49,14 @@
v-if="formData.primaryFlag === 'N'"
prop="changeRate"
>
<template #label>
<span>
{{ t('FactoryModeling.ProductUnit.dialogChangeRateLabel') }}
<el-tooltip content="与主单位换算比例s = 1000 ms换算比例为1000" placement="top">
<Icon icon="ep:question-filled" />
</el-tooltip>
</span>
</template>
<el-input-number
v-model="formData.changeRate"
:step="0.1"

@ -4,17 +4,19 @@
<el-form-item :label="t('DataCollection.Device.attributeCode')" prop="attributeCode">
<el-input
v-model="formData.attributeCode" :placeholder="t('DataCollection.Device.placeholderAttributeCode')"
@input="handleAttributeCodeInput" :disabled="formType === 'update'" />
:disabled="formType === 'update'" />
</el-form-item>
<el-form-item :label="t('DataCollection.Device.attributeName')" prop="attributeName">
<el-input v-model="formData.attributeName" :placeholder="t('DataCollection.Device.placeholderAttributeName')" />
</el-form-item>
<el-form-item :label="t('DataCollection.Device.attributeType')" prop="attributeType">
<el-select
v-model="formData.attributeType" clearable filterable
:placeholder="t('DataCollection.Device.placeholderAttributeType')" @change="handleAttributeTypeChange">
<el-option v-for="item in typeList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
<el-input
v-model="formData.typeName"
readonly
:placeholder="t('DataCollection.Device.placeholderAttributeType')"
class="cursor-pointer"
@click="openAttributeTypeSelectDialog"
/>
</el-form-item>
<el-form-item :label="t('DataCollection.Device.dataType')" prop="dataType">
<el-select
@ -53,11 +55,21 @@ v-model="formData.remark" :placeholder="t('DataCollection.Device.placeholderRema
</el-button>
</template>
</Dialog>
<TableSelectDialog
ref="attributeTypeSelectDialogRef"
:title="t('DataCollection.Device.attributeType')"
:columns="attributeTypeColumns"
:fetch-api="fetchAttributeTypePage"
selection-type="single"
row-key="id"
@confirm="handleAttributeTypeSelectConfirm"
/>
</template>
<script setup lang="ts">
import { getStrDictOptions, DICT_TYPE } from '@/utils/dict'
import { DeviceApi } from '@/api/iot/device'
import { DeviceAttributeTypeApi, DeviceAttributeTypeVO } from '@/api/iot/deviceattributetype'
import TableSelectDialog from '@/components/TableSelectDialog/TableSelectDialog.vue'
import { ProductUnitApi, ProductUnitVO } from '@/api/erp/product/unit'
const { t } = useI18n() //
@ -68,18 +80,6 @@ const dialogTitle = ref('') // 弹窗的标题
const formLoading = ref(false) // 12
const formType = ref('') // create - update -
const unitList = ref<ProductUnitVO[]>([]) //
const typeList = ref<DeviceAttributeTypeVO[]>([])
const loadTypeList = async () => {
if (typeList.value.length > 0) {
return
}
try {
const data = await DeviceAttributeTypeApi.getDeviceAttributeTypePage({ pageNo: 1, pageSize: 10 })
typeList.value = data?.list ?? []
} catch {
typeList.value = []
}
}
const formData = ref({
id: undefined as number | undefined,
@ -95,18 +95,8 @@ const formData = ref({
deviceId: undefined as number | undefined
})
const handleAttributeCodeInput = (val: string) => {
const sanitized = (val || '').replace(/[^A-Za-z0-9_]/g, '')
formData.value.attributeCode = sanitized.replace(/^[0-9]+/, '')
}
const handleAttributeTypeChange = (val: number | string) => {
const matched = typeList.value.find(
(item) => item.id === val || String(item.id) === String(val)
)
formData.value.typeName = matched?.name
}
const formRules = reactive({
attributeCode: [
@ -194,7 +184,6 @@ const open = async (type: string, id?: number, deviceId: number) => {
//
unitList.value = await ProductUnitApi.getProductUnitSimpleList()
formData.value.deviceId = deviceId
await loadTypeList()
//
if (id) {
formLoading.value = true
@ -203,20 +192,18 @@ const open = async (type: string, id?: number, deviceId: number) => {
if (!(formData.value as any)?.deviceId) {
; (formData.value as any).deviceId = deviceId
}
const currentType = (formData.value as any)?.attributeType
if (currentType !== undefined && currentType !== null && currentType !== '') {
const matched = typeList.value.find(
(item) =>
item.id === currentType ||
String(item.id) === String(currentType) ||
item.name === currentType ||
item.code === currentType
)
if (matched) {
; (formData.value as any).attributeType = matched.id
; (formData.value as any).typeName = matched.name
const currentAttributeType = (formData.value as any)?.attributeType
const currentTypeId = Number(currentAttributeType)
if (Number.isFinite(currentTypeId) && currentTypeId > 0) {
try {
const selectedType = await DeviceAttributeTypeApi.getDeviceAttributeType(currentTypeId)
;(formData.value as any).attributeType = selectedType.id
;(formData.value as any).typeName = selectedType.name
} catch {
// Keep the value returned by the detail API when the type no longer exists.
}
} else if (!(formData.value as any)?.typeName && currentAttributeType != null) {
;(formData.value as any).typeName = String(currentAttributeType)
}
} finally {
formLoading.value = false
@ -249,6 +236,35 @@ const submitForm = async () => {
}
}
const attributeTypeSelectDialogRef = ref()
const attributeTypeColumns = [
{ label: t('DataCollection.DeviceAttributeType.code'), prop: 'code', minWidth: 140 },
{ label: t('DataCollection.DeviceAttributeType.name'), prop: 'name', minWidth: 160 },
{ label: t('DataCollection.DeviceAttributeType.sort'), prop: 'sort', minWidth: 100 },
{ label: t('DataCollection.DeviceAttributeType.remark'), prop: 'remark', minWidth: 160 },
{ label: t('DataCollection.DeviceAttributeType.createTime'), prop: 'createTime', minWidth: 180 }
]
const fetchAttributeTypePage = (params: Record<string, any>) => {
return DeviceAttributeTypeApi.getDeviceAttributeTypePage(params)
}
const openAttributeTypeSelectDialog = () => {
const currentTypeId = formData.value.attributeType
const currentTypeName = formData.value.typeName
const selectedRows = currentTypeId != null && currentTypeName
? [{ id: currentTypeId, name: currentTypeName }]
: []
attributeTypeSelectDialogRef.value?.open(selectedRows)
}
const handleAttributeTypeSelectConfirm = (payload: { rows: DeviceAttributeTypeVO[] }) => {
const row = payload.rows?.[0]
if (!row) return
formData.value.attributeType = row.id
formData.value.typeName = row.name
formRef.value?.clearValidate('attributeType')
}
/** 重置表单 */
const resetForm = () => {
formData.value = {

@ -592,6 +592,7 @@
:placeholder="t('DataCollection.DeviceModel.ruleDialogRuleAttributePlaceholder')"
class="!w-240px"
clearable
filterable
>
<el-option
v-for="item in ruleAttributeOptions"

@ -19,15 +19,13 @@
:placeholder="t('DataCollection.DeviceModel.placeholderAttributeName')" />
</el-form-item>
<el-form-item :label="t('DataCollection.DeviceModel.attributeType')" prop="attributeType">
<el-select
v-model="formData.attributeType"
clearable
filterable
<el-input
v-model="formData.typeName"
readonly
:placeholder="t('DataCollection.DeviceModel.placeholderAttributeType')"
@change="handleAttributeTypeChange"
>
<el-option v-for="item in typeList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
class="cursor-pointer"
@click="openAttributeTypeSelectDialog"
/>
</el-form-item>
<el-form-item :label="t('DataCollection.DeviceModel.dataType')" prop="dataType">
<el-select
@ -78,11 +76,21 @@
<el-button @click="dialogVisible = false">{{ t('DataCollection.DeviceModel.dialogCancel') }}</el-button>
</template>
</Dialog>
<TableSelectDialog
ref="attributeTypeSelectDialogRef"
:title="t('DataCollection.DeviceModel.attributeType')"
:columns="attributeTypeColumns"
:fetch-api="fetchAttributeTypePage"
selection-type="single"
row-key="id"
@confirm="handleAttributeTypeSelectConfirm"
/>
</template>
<script setup lang="ts">
import { getStrDictOptions, DICT_TYPE } from '@/utils/dict'
import { DeviceModelAttributeApi } from '@/api/iot/devicemodelattribute'
import { DeviceAttributeTypeApi, DeviceAttributeTypeVO } from '@/api/iot/deviceattributetype'
import TableSelectDialog from '@/components/TableSelectDialog/TableSelectDialog.vue'
import { ProductUnitApi, ProductUnitVO } from '@/api/erp/product/unit'
/** 采集设备模型-点位管理 表单 */
@ -91,20 +99,6 @@ defineOptions({ name: 'DeviceModelAttributeForm' })
const { t } = useI18n() //
const message = useMessage() //
const typeList = ref<DeviceAttributeTypeVO[]>([])
const typeListLoaded = ref(false)
const loadTypeList = async () => {
if (typeListLoaded.value) {
return
}
try {
const data = await DeviceAttributeTypeApi.getDeviceAttributeTypePage({ pageNo: 1, pageSize: 10 })
typeList.value = data?.list ?? []
typeListLoaded.value = true
} catch {
typeList.value = []
}
}
const dialogVisible = ref(false) //
const dialogTitle = ref('') //
@ -124,7 +118,19 @@ const formData = ref({
deviceModelId: undefined,
})
const formRules = reactive({
attributeCode: [{ required: true, message: t('DataCollection.DeviceModel.validatorAttributeCodeRequired'), trigger: 'blur' }],
attributeCode: [
{ required: true, message: t('DataCollection.DeviceModel.validatorAttributeCodeRequired'), trigger: 'blur' },
{
validator: (_rule: any, value: string, callback: any) => {
if (!value || /^[A-Za-z_][A-Za-z0-9_]*$/.test(value)) {
callback()
return
}
callback(new Error('仅支持英文字母、数字和下划线,且必须以字母或下划线开头'))
},
trigger: ['blur', 'change']
}
],
attributeName: [{ required: true, message: t('DataCollection.DeviceModel.validatorAttributeNameRequired'), trigger: 'blur' }],
dataType: [{ required: true, message: t('DataCollection.DeviceModel.validatorDataTypeRequired'), trigger: 'change' }],
attributeType: [{ required: true, message: '点位类型不能为空', trigger: 'change' }]
@ -189,7 +195,6 @@ const open = async (type: string, id: number, modelId: number) => {
dialogTitle.value = t('action.' + type)
formType.value = type
resetForm()
await loadTypeList()
//
unitList.value = await ProductUnitApi.getProductUnitSimpleList()
formData.value.deviceModelId = modelId
@ -197,21 +202,19 @@ const open = async (type: string, id: number, modelId: number) => {
formLoading.value = true
try {
formData.value = await DeviceModelAttributeApi.getDeviceModelAttribute(id)
const currentType = (formData.value as any)?.attributeType
if (currentType !== undefined && currentType !== null && currentType !== '') {
const matched = typeList.value.find(
(item) =>
item.id === currentType ||
String(item.id) === String(currentType) ||
item.name === currentType ||
item.code === currentType
)
if (matched) {
;(formData.value as any).attributeType = matched.id
;(formData.value as any).typeName = matched.name
}
}
const currentAttributeType = (formData.value as any)?.attributeType
const currentTypeId = Number(currentAttributeType)
if (Number.isFinite(currentTypeId) && currentTypeId > 0) {
try {
const selectedType = await DeviceAttributeTypeApi.getDeviceAttributeType(currentTypeId)
;(formData.value as any).attributeType = selectedType.id
;(formData.value as any).typeName = selectedType.name
} catch {
// Keep the value returned by the detail API when the type no longer exists.
}
} else if (!(formData.value as any)?.typeName && currentAttributeType != null) {
;(formData.value as any).typeName = String(currentAttributeType)
}
} finally {
formLoading.value = false
}
@ -243,13 +246,36 @@ const submitForm = async () => {
}
}
const handleAttributeTypeChange = (val: number | string) => {
const matched = typeList.value.find(
(item) => item.id === val || String(item.id) === String(val)
)
;(formData.value as any).typeName = matched?.name
const attributeTypeSelectDialogRef = ref()
const attributeTypeColumns = [
{ label: t('DataCollection.DeviceAttributeType.code'), prop: 'code', minWidth: 140 },
{ label: t('DataCollection.DeviceAttributeType.name'), prop: 'name', minWidth: 160 },
{ label: t('DataCollection.DeviceAttributeType.sort'), prop: 'sort', minWidth: 100 },
{ label: t('DataCollection.DeviceAttributeType.remark'), prop: 'remark', minWidth: 160 },
{ label: t('DataCollection.DeviceAttributeType.createTime'), prop: 'createTime', minWidth: 180 }
]
const fetchAttributeTypePage = (params: Record<string, any>) => {
return DeviceAttributeTypeApi.getDeviceAttributeTypePage(params)
}
const openAttributeTypeSelectDialog = () => {
const currentTypeId = formData.value.attributeType
const currentTypeName = formData.value.typeName
const selectedRows = currentTypeId != null && currentTypeName
? [{ id: currentTypeId, name: currentTypeName }]
: []
attributeTypeSelectDialogRef.value?.open(selectedRows)
}
const handleAttributeTypeSelectConfirm = (payload: { rows: DeviceAttributeTypeVO[] }) => {
const row = payload.rows?.[0]
if (!row) return
formData.value.attributeType = row.id as any
formData.value.typeName = row.name
formRef.value?.clearValidate('attributeType')
}
/** 重置表单 */
const resetForm = () => {
formData.value = {

@ -249,6 +249,7 @@ link type="primary" @click="openForm('update', scope.row.id)"
<el-select
v-model="ruleForm.ruleAttributeId"
:placeholder="t('DataCollection.DeviceModel.ruleDialogRuleAttributePlaceholder')"
filterable
class="!w-240px"
>
<el-option

@ -47,6 +47,27 @@
</el-col>
</el-row>
</el-form-item>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item
:label="t('EquipmentManagement.EquipmentLedger.lineCategory')"
prop="deviceLine"
required
>
<el-tree-select
v-model="formData.deviceLine"
:data="deviceLineTree"
:props="treeSelectProps"
check-strictly
default-expand-all
value-key="id"
clearable
:placeholder="t('EquipmentManagement.EquipmentLedger.placeholderLineCategory')"
class="!w-full"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
@ -765,6 +786,7 @@
import { DeviceLedgerApi, DeviceLedgerVO } from '@/api/mes/deviceledger'
import { CriticalComponentApi, CriticalComponentVO } from '@/api/mes/criticalComponent'
import { DeviceTypeApi, DeviceTypeTreeVO } from '@/api/mes/devicetype'
import { DeviceLineApi, DeviceLineTreeVO } from '@/api/mes/deviceline'
import { getSimpleUserList, UserVO } from '@/api/system/user'
import { formatDate } from '@/utils/formatTime'
import type { FormRules } from 'element-plus'
@ -980,6 +1002,13 @@ const formRules = reactive<FormRules>({
trigger: 'change'
}
],
deviceLine: [
{
required: true,
message: t('EquipmentManagement.EquipmentLedger.placeholderLineCategory'),
trigger: 'change'
}
],
ratedCapacity: [
{ validator: validateScheduledRequired('棰濆畾浜ц兘'), trigger: ['blur', 'change'] }
]
@ -999,6 +1028,7 @@ watch(
const treeSelectProps = { label: 'name', children: 'children' }
const deviceTypeTree = ref<DeviceTypeTreeVO[]>([])
const deviceLineTree = ref<DeviceLineTreeVO[]>([])
const findDeviceTypeName = (id: string | number): string | undefined => {
const stack = [...deviceTypeTree.value]
while (stack.length) {
@ -1287,13 +1317,15 @@ const handleQrcodeRefreshSuccess = async (data: any) => {
}
const ensureOptionsLoaded = async () => {
const [deviceTypeRes, userRes, criticalRes, beijianRes] = await Promise.all([
const [deviceTypeRes, deviceLineRes, userRes, criticalRes, beijianRes] = await Promise.all([
DeviceTypeApi.getDeviceTypeTree({ pageNo: 1, pageSize: 10 }),
DeviceLineApi.getDeviceLineTree(),
getSimpleUserList(),
CriticalComponentApi.getCriticalComponentList(),
ProductApi.getProductPage({ pageNo: 1, pageSize: 10, categoryType: SPARE_PART_CATEGORY_TYPE })
])
deviceTypeTree.value = deviceTypeRes
deviceLineTree.value = deviceLineRes ?? []
users.value = userRes ?? []
criticalComponentOptions.value = buildCriticalComponentOptions(criticalRes ?? [])
beijianOptions.value = buildBeijianOptions(beijianRes?.list ?? [])

@ -1,4 +1,12 @@
<template>
<ContentWrap>
<el-tabs v-model="activeRecordTab">
<el-tab-pane :label="t('ProductionPlan.WorkReportRecord.moduleName')" name="work-report" />
<el-tab-pane :label="t('ProductionPlan.WorkReportRecord.tabProductionRecord')" name="production-record" />
</el-tabs>
</ContentWrap>
<template v-if="activeRecordTab === 'work-report'">
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form
@ -70,18 +78,22 @@
@pagination="getList"
/>
</ContentWrap>
</template>
<ProductionReport v-else />
</template>
<script setup lang="ts">
import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import { BaogongRecordApi, BaogongRecordStatVO } from '@/api/mes/baogongrecord'
import ProductionReport from '../productionReport/index.vue'
/** 报工记录统计 列表 */
defineOptions({ name: 'WorkReportRecord' })
const message = useMessage() //
const { t } = useI18n() //
const activeRecordTab = ref('work-report')
const loading = ref(true) //
const list = ref<BaogongRecordStatVO[]>([]) //

@ -10,25 +10,7 @@
<el-form-item :label="t('FactoryModeling.WorkTeam.dialogTeamNameLabel')" prop="teamName">
<el-input v-model="formData.teamName" :placeholder="t('FactoryModeling.WorkTeam.dialogTeamNamePlaceholder')" />
</el-form-item>
<el-form-item prop="groupType">
<template #label>
<span>
{{ t('FactoryModeling.WorkTeam.dialogGroupTypeLabel') }}
<el-tooltip :content="t('FactoryModeling.WorkTeam.dialogGroupTypeTooltip')" placement="top">
<Icon icon="ep:question-filled" />
</el-tooltip>
</span>
</template>
<el-radio-group v-model="formData.groupType">
<el-radio
v-for="dict in getStrDictOptions(DICT_TYPE.MES_GROUP_TYPE)"
:key="dict.value"
:label="dict.value"
>
{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('FactoryModeling.WorkTeam.dialogWorkTimeLabel')" prop="工作时间">
<el-time-picker
v-model="formData.dateRange"
@ -61,7 +43,7 @@
</Dialog>
</template>
<script setup lang="ts">
import {getStrDictOptions, DICT_TYPE, getBoolDictOptions} from '@/utils/dict'
import { DICT_TYPE, getBoolDictOptions } from '@/utils/dict'
import { WorkTeamApi, WorkTeamVO } from '@/api/mes/workteam'
/** 生产班组 表单 */
@ -87,7 +69,6 @@ const formData = ref({
})
const formRules = reactive({
groupType: [{ required: true, message: t('FactoryModeling.WorkTeam.validatorGroupTypeRequired'), trigger: 'blur' }],
isEnable: [{ required: true, message: t('FactoryModeling.WorkTeam.validatorEnableRequired'), trigger: 'blur' }],
teamName: [{ required: true, message: t('FactoryModeling.WorkTeam.validatorTeamNameRequired'), trigger: 'blur' }]
})
@ -99,7 +80,6 @@ const open = async (type: string, id?: number) => {
dialogTitle.value = t('action.' + type)
formType.value = type
resetForm()
formData.value.groupType = '1'
formData.value.isEnable = true
formData.value.dateRange=[new Date(), new Date()]
//
@ -147,6 +127,7 @@ const resetForm = () => {
id: undefined,
teamName: undefined,
teamLeaderId: undefined,
groupType: '1',
remark: undefined,
isEnable: undefined,
}

@ -53,12 +53,7 @@
<!-- 列表 -->
<ContentWrap>
<el-tabs v-model="activeName" @tab-click="handleTabClick">
<el-tab-pane :label="t('FactoryModeling.WorkTeam.tabsAll')" name="" />
<el-tab-pane :label="t('FactoryModeling.WorkTeam.tabsDay')" name="1" />
<el-tab-pane :label="t('FactoryModeling.WorkTeam.tabsNight')" name="2" />
<el-tab-pane :label="t('FactoryModeling.WorkTeam.tabsLongDay')" name="3" />
</el-tabs>
<el-table
v-loading="loading"
:data="list"
@ -158,7 +153,6 @@ const queryParams = reactive({
pageSize: 10,
teamName: undefined,
teamLeaderId: undefined,
groupType: undefined,
remark: undefined,
isEnable: undefined,
createTime: [],
@ -234,12 +228,6 @@ const handleCurrentChange = (row) => {
onMounted(() => {
getList()
})
/** tab 切换 */
let activeName = ''
const handleTabClick = (tab: TabsPaneContext) => {
queryParams.groupType = tab.paneName
handleQuery()
}
const getHour = (timeStamp) =>{
let now = new Date(timeStamp);

Loading…
Cancel
Save