Merge remote-tracking branch 'origin/main'

main
liutao 18 hours ago
commit ab727dcbb0

@ -3,11 +3,14 @@ import qs from 'qs'
// 物联设备 VO
export interface DeviceVO {
id: number // ID
id: string | number // ID
deviceCode: string // 设备编号
deviceName: string // 设备名称
boundLedgerName?: string // 已绑定的设备台账名称
deviceType: string // 设备类型
ledgerBound?: boolean
selectable?: boolean
boundLedgerId?: string | number | null
status: string // 状态
isConnect?: string | number
operatingStatus?: string | number
@ -91,7 +94,7 @@ export const DeviceApi = {
return await request.get({ url: `/iot/device/deviceList`, params })
},
// 查询物联设备详情
getDevice: async (id: number) => {
getDevice: async (id: string | number) => {
return await request.get({ url: `/iot/device/get?id=` + id })
},
getDeviceListByNoUsed: async () => {

@ -700,6 +700,11 @@ export default {
exportName: '产品库存.xls'
},
In: {
all: '全部',
waitIn: '待入库',
rejected: '已驳回',
auditing: '审核中',
inStock: '已入库',
no: '入库单号',
product: '产品',
inTime: '入库时间',
@ -762,6 +767,11 @@ export default {
addItem: '添加入库产品'
},
Out: {
all: '全部',
waitOut: '待出库',
rejected: '已驳回',
auditing: '审核中',
outStock: '已出库',
no: '出库单号',
customer: '客户',
product: '产品',
@ -907,6 +917,13 @@ export default {
stockCountExceededWarning: '数量不能超出可用库存,已自动调整为可用库存数量'
},
Check: {
all: '全部',
waitSubmit: '待提交',
rejected: '已驳回',
auditing: '审核中',
approved: '审核通过',
checked: '已盘点',
unchecked: '未盘点',
no: '盘点单号',
product: '产品',
checkTime: '盘点时间',
@ -1045,6 +1062,11 @@ export default {
exportName: '产品库存明细.xls'
},
Move: {
all: '全部',
waitSubmit: '待提交',
rejected: '已驳回',
auditing: '审核中',
approved: '审核通过',
no: '调拨单号',
product: '产品',
moveTime: '调度时间',
@ -1117,6 +1139,10 @@ export default {
validatorStatusRequired: '状态不能为空'
},
Pallet: {
all: '全部',
idle: '空闲',
occupied: '占用',
scrapped: '报废',
code: '托盘编码',
palletType: '托盘类型',
specification: '规格',

@ -1,5 +1,13 @@
<template>
<ContentWrap>
<!-- 统计卡片 -->
<div class="stock-check__header">
<div v-for="card in statusCards" :key="card.key" class="stock-check__stat"
:class="{ 'is-active': currentStatusKey === card.key }" @click="changeStatus(card.key, card.statusValue, card.checkStatusValue)">
<div class="stock-check__stat-title">{{ card.label }}</div>
<div class="stock-check__stat-value">{{ card.value }}</div>
</div>
</div>
<el-form
ref="queryFormRef"
:model="queryParams"
@ -42,7 +50,7 @@
class="!w-240px"
/>
</el-form-item>
<el-form-item :label="t('ErpStock.Check.status')" prop="status">
<el-form-item :label="t('ErpStock.Check.status')" prop="status" v-show="showAllFilters">
<el-select
v-model="queryParams.status"
:placeholder="t('ErpStock.Check.placeholderStatus')"
@ -57,6 +65,13 @@
/>
</el-select>
</el-form-item>
<el-form-item v-if="filterCount > 3">
<el-button type="text" class="text-primary" @click="toggleFilters">
<Icon :icon="showAllFilters ? 'ep:arrow-up' : 'ep:arrow-down'" class="mr-5px" />
{{ showAllFilters ? t('FactoryModeling.FactoryStructure.collapseText') :
t('FactoryModeling.FactoryStructure.expandText') }}
</el-button>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery">
<Icon icon="ep:search" class="mr-5px" />
@ -295,6 +310,37 @@ const updatePlanTableMaxHeight = async () => {
const warehouseList = ref<WarehouseVO[]>([])
const selectionList = ref<StockCheckVO[]>([])
const queryFormRef = ref()
const currentStatusKey = ref('all')
const counters = reactive({
allCount: 0,
waitSubmitCount: 0,
rejectedCount: 0,
auditingCount: 0,
approvedCount: 0,
checkedCount: 0,
uncheckedCount: 0
})
const statusCards = computed(() => [
{ key: 'all', label: t('ErpStock.Check.all'), value: counters.allCount, statusValue: undefined, checkStatusValue: undefined },
{ key: 'waitSubmit', label: t('ErpStock.Check.waitSubmit'), value: counters.waitSubmitCount, statusValue: 0, checkStatusValue: undefined },
{ key: 'rejected', label: t('ErpStock.Check.rejected'), value: counters.rejectedCount, statusValue: 1, checkStatusValue: undefined },
{ key: 'auditing', label: t('ErpStock.Check.auditing'), value: counters.auditingCount, statusValue: 10, checkStatusValue: undefined },
{ key: 'approved', label: t('ErpStock.Check.approved'), value: counters.approvedCount, statusValue: 20, checkStatusValue: undefined },
{ key: 'checked', label: t('ErpStock.Check.checked'), value: counters.checkedCount, statusValue: undefined, checkStatusValue: 1 },
{ key: 'unchecked', label: t('ErpStock.Check.unchecked'), value: counters.uncheckedCount, statusValue: undefined, checkStatusValue: 0 }
])
const changeStatus = (key: string, statusValue: number | undefined, checkStatusValue: number | undefined) => {
currentStatusKey.value = key
queryParams.status = statusValue
queryParams.checkStatus = checkStatusValue
handleQuery()
}
const showAllFilters = ref(false)
const filterCount = 4
const toggleFilters = () => {
showAllFilters.value = !showAllFilters.value
updatePlanTableMaxHeight()
}
const formRef = ref()
const submitDialogVisible = ref(false)
const submitLoading = ref(false)
@ -322,7 +368,8 @@ const queryParams = reactive({
no: undefined as string | undefined,
warehouseId: undefined as number | undefined,
checkTime: [] as string[],
status: undefined as number | undefined
status: undefined as number | undefined,
checkStatus: undefined as number | undefined
})
const getList = async () => {
@ -331,6 +378,13 @@ const getList = async () => {
const data = await StockCheckApi.getStockCheckPage(queryParams)
list.value = data?.list || []
total.value = data?.total || 0
counters.allCount = Number(data?.allCount ?? data?.total ?? 0)
counters.waitSubmitCount = Number(data?.waitSubmitCount ?? 0)
counters.rejectedCount = Number(data?.rejectedCount ?? 0)
counters.auditingCount = Number(data?.auditingCount ?? 0)
counters.approvedCount = Number(data?.approvedCount ?? 0)
counters.checkedCount = Number(data?.checkedCount ?? 0)
counters.uncheckedCount = Number(data?.uncheckedCount ?? 0)
} finally {
loading.value = false
updatePlanTableMaxHeight()
@ -353,6 +407,7 @@ const handleQuery = () => {
}
const resetQuery = () => {
currentStatusKey.value = 'all'
queryFormRef.value?.resetFields()
handleQuery()
}
@ -478,3 +533,44 @@ onBeforeUnmount(() => {
window.removeEventListener('resize', updatePlanTableMaxHeight)
})
</script>
<style scoped lang="scss">
.stock-check__header {
display: grid;
grid-template-columns: repeat(7, minmax(0, 1fr));
gap: 12px;
width: 100%;
margin-bottom: 16px;
}
.stock-check__stat {
padding: 16px 18px;
border: 1px solid var(--el-border-color-light);
border-radius: 12px;
cursor: pointer;
transition: box-shadow 0.2s ease;
background: #fff;
&:hover {
box-shadow: 0 10px 24px rgba(64, 158, 255, 0.12);
}
&.is-active {
border-color: var(--el-color-primary);
box-shadow: 0 10px 24px rgba(64, 158, 255, 0.12);
}
}
.stock-check__stat-title {
color: var(--el-text-color-regular);
font-size: 14px;
}
.stock-check__stat-value {
margin-top: 8px;
color: var(--el-text-color-primary);
font-size: 28px;
font-weight: 700;
line-height: 1;
}
</style>

@ -3,9 +3,16 @@
<template v-if="!formVisible">
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form
class="-mb-15px"
:model="queryParams"
<div class="stock-in__header">
<div v-for="card in statusCards" :key="card.key" class="stock-in__stat"
:class="{ 'is-active': currentStatusKey === card.key }" @click="changeStatus(card.key, card.statusList)">
<div class="stock-in__stat-title">{{ card.label }}</div>
<div class="stock-in__stat-value">{{ card.value }}</div>
</div>
</div>
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="auto"
@ -374,6 +381,26 @@ const inTypeToInfoKey = (inType: string) => {
}
const activeName = ref('')
const queryFormRef = ref() //
const currentStatusKey = ref('all')
const counters = reactive({
allCount: 0,
waitInCount: 0,
rejectedCount: 0,
auditingCount: 0,
inStockCount: 0
})
const statusCards = computed(() => [
{ key: 'all', label: t('ErpStock.In.all'), value: counters.allCount, statusList: [] },
{ key: 'waitIn', label: t('ErpStock.In.waitIn'), value: counters.waitInCount, statusList: [0] },
{ key: 'rejected', label: t('ErpStock.In.rejected'), value: counters.rejectedCount, statusList: [1] },
{ key: 'auditing', label: t('ErpStock.In.auditing'), value: counters.auditingCount, statusList: [10] },
{ key: 'inStock', label: t('ErpStock.In.inStock'), value: counters.inStockCount, statusList: [20] }
])
const changeStatus = (key: string, statusList: number[]) => {
currentStatusKey.value = key
queryParams.statusList = statusList
handleQuery()
}
const exportLoading = ref(false) //
const showAllFilters = ref(false)
const DEFAULT_STATUS_LIST = [0, 1, 10]
@ -426,6 +453,11 @@ const getList = async () => {
const data = await StockInApi.getStockInPage(buildQueryParams())
list.value = data.list
total.value = data.total
counters.allCount = Number(data?.allCount ?? data?.total ?? 0)
counters.waitInCount = Number(data?.waitInCount ?? 0)
counters.rejectedCount = Number(data?.rejectedCount ?? 0)
counters.auditingCount = Number(data?.auditingCount ?? 0)
counters.inStockCount = Number(data?.inStockCount ?? 0)
} finally {
loading.value = false
updatePlanTableMaxHeight()
@ -451,6 +483,7 @@ const handleQuery = () => {
/** 重置按钮操作 */
const resetQuery = () => {
useInitialStatusFilter.value = false
currentStatusKey.value = 'all'
queryFormRef.value.resetFields()
queryParams.statusList = []
queryParams.inType = activeName.value || stockInTypeOptions.value[0]?.value
@ -602,3 +635,44 @@ const handleTabClick = (tab: TabsPaneContext) => {
handleQuery()
}
</script>
<style scoped lang="scss">
.stock-in__header {
display: grid;
grid-template-columns: repeat(5, minmax(0, 1fr));
gap: 12px;
width: 100%;
margin-bottom: 16px;
}
.stock-in__stat {
padding: 16px 18px;
border: 1px solid var(--el-border-color-light);
border-radius: 12px;
cursor: pointer;
transition: box-shadow 0.2s ease;
background: #fff;
&:hover {
box-shadow: 0 10px 24px rgba(64, 158, 255, 0.12);
}
&.is-active {
border-color: var(--el-color-primary);
box-shadow: 0 10px 24px rgba(64, 158, 255, 0.12);
}
}
.stock-in__stat-title {
color: var(--el-text-color-regular);
font-size: 14px;
}
.stock-in__stat-value {
margin-top: 8px;
color: var(--el-text-color-primary);
font-size: 28px;
font-weight: 700;
line-height: 1;
}
</style>

@ -1,6 +1,14 @@
<template>
<ContentWrap>
<!-- 统计卡片 -->
<div class="stock-move__header">
<div v-for="card in statusCards" :key="card.key" class="stock-move__stat"
:class="{ 'is-active': currentStatusKey === card.key }" @click="changeStatus(card.key, card.statusValue)">
<div class="stock-move__stat-title">{{ card.label }}</div>
<div class="stock-move__stat-value">{{ card.value }}</div>
</div>
</div>
<!-- 搜索工作栏 -->
<el-form
class="-mb-15px"
@ -34,7 +42,7 @@
/>
</el-select>
</el-form-item>
<el-form-item :label="t('ErpStock.Move.moveTime')" prop="moveTime">
<el-form-item :label="t('ErpStock.Move.moveTime')" prop="moveTime" v-show="showAllFilters">
<el-date-picker
v-model="queryParams.moveTime"
value-format="YYYY-MM-DD HH:mm:ss"
@ -45,7 +53,7 @@
class="!w-240px"
/>
</el-form-item>
<el-form-item :label="t('ErpStock.Move.fromWarehouse')" prop="fromWarehouseId">
<el-form-item :label="t('ErpStock.Move.fromWarehouse')" prop="fromWarehouseId" v-show="showAllFilters">
<el-select
v-model="queryParams.fromWarehouseId"
clearable
@ -61,7 +69,7 @@
/>
</el-select>
</el-form-item>
<el-form-item :label="t('ErpStock.Move.creator')" prop="creator">
<el-form-item :label="t('ErpStock.Move.creator')" prop="creator" v-show="showAllFilters">
<el-select
v-model="queryParams.creator"
clearable
@ -77,7 +85,7 @@
/>
</el-select>
</el-form-item>
<el-form-item :label="t('ErpStock.Move.status')" prop="status">
<el-form-item :label="t('ErpStock.Move.status')" prop="status" v-show="showAllFilters">
<el-select v-model="queryParams.status" :placeholder="t('ErpStock.Move.placeholderStatus')" clearable class="!w-240px">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.ERP_AUDIT_STATUS)"
@ -96,6 +104,13 @@
class="!w-240px"
/>
</el-form-item> -->
<el-form-item v-if="filterCount > 3">
<el-button type="text" class="text-primary" @click="toggleFilters">
<Icon :icon="showAllFilters ? 'ep:arrow-up' : 'ep:arrow-down'" class="mr-5px" />
{{ showAllFilters ? t('FactoryModeling.FactoryStructure.collapseText') :
t('FactoryModeling.FactoryStructure.expandText') }}
</el-button>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> {{ t('common.query') }}</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> {{ t('common.reset') }}</el-button>
@ -324,6 +339,32 @@ const queryParams = reactive({
creator: undefined
})
const queryFormRef = ref() //
const currentStatusKey = ref('all')
const counters = reactive({
allCount: 0,
waitSubmitCount: 0,
rejectedCount: 0,
auditingCount: 0,
approvedCount: 0
})
const statusCards = computed(() => [
{ key: 'all', label: t('ErpStock.Move.all'), value: counters.allCount, statusValue: undefined },
{ key: 'waitSubmit', label: t('ErpStock.Move.waitSubmit'), value: counters.waitSubmitCount, statusValue: 0 },
{ key: 'rejected', label: t('ErpStock.Move.rejected'), value: counters.rejectedCount, statusValue: 1 },
{ key: 'auditing', label: t('ErpStock.Move.auditing'), value: counters.auditingCount, statusValue: 10 },
{ key: 'approved', label: t('ErpStock.Move.approved'), value: counters.approvedCount, statusValue: 20 }
])
const changeStatus = (key: string, statusValue: number | undefined) => {
currentStatusKey.value = key
queryParams.status = statusValue
handleQuery()
}
const showAllFilters = ref(false)
const filterCount = 6
const toggleFilters = () => {
showAllFilters.value = !showAllFilters.value
updatePlanTableMaxHeight()
}
const exportLoading = ref(false) //
const productList = ref<ProductVO[]>([]) //
const warehouseList = ref<WarehouseVO[]>([]) //
@ -363,6 +404,11 @@ const getList = async () => {
const data = await StockMoveApi.getStockMovePage(queryParams)
list.value = data.list
total.value = data.total
counters.allCount = Number(data?.allCount ?? data?.total ?? 0)
counters.waitSubmitCount = Number(data?.waitSubmitCount ?? 0)
counters.rejectedCount = Number(data?.rejectedCount ?? 0)
counters.auditingCount = Number(data?.auditingCount ?? 0)
counters.approvedCount = Number(data?.approvedCount ?? 0)
} finally {
loading.value = false
updatePlanTableMaxHeight()
@ -377,6 +423,7 @@ const handleQuery = () => {
/** 重置按钮操作 */
const resetQuery = () => {
currentStatusKey.value = 'all'
queryFormRef.value.resetFields()
handleQuery()
}
@ -503,3 +550,44 @@ onBeforeUnmount(() => {
// TODO
// TODO
</script>
<style scoped lang="scss">
.stock-move__header {
display: grid;
grid-template-columns: repeat(5, minmax(0, 1fr));
gap: 12px;
width: 100%;
margin-bottom: 16px;
}
.stock-move__stat {
padding: 16px 18px;
border: 1px solid var(--el-border-color-light);
border-radius: 12px;
cursor: pointer;
transition: box-shadow 0.2s ease;
background: #fff;
&:hover {
box-shadow: 0 10px 24px rgba(64, 158, 255, 0.12);
}
&.is-active {
border-color: var(--el-color-primary);
box-shadow: 0 10px 24px rgba(64, 158, 255, 0.12);
}
}
.stock-move__stat-title {
color: var(--el-text-color-regular);
font-size: 14px;
}
.stock-move__stat-value {
margin-top: 8px;
color: var(--el-text-color-primary);
font-size: 28px;
font-weight: 700;
line-height: 1;
}
</style>

@ -2,6 +2,14 @@
<div class="dv-repair-page">
<template v-if="!formVisible">
<ContentWrap>
<!-- 统计卡片 -->
<div class="stock-out__header">
<div v-for="card in statusCards" :key="card.key" class="stock-out__stat"
:class="{ 'is-active': currentStatusKey === card.key }" @click="changeStatus(card.key, card.statusList)">
<div class="stock-out__stat-title">{{ card.label }}</div>
<div class="stock-out__stat-value">{{ card.value }}</div>
</div>
</div>
<!-- 鎼滅储宸ヤ綔鏍?-->
<el-form
class="-mb-15px"
@ -384,6 +392,26 @@ const getProductCategoryType = () => {
}
const activeName = ref('')
const queryFormRef = ref() // ?
const currentStatusKey = ref('all')
const counters = reactive({
allCount: 0,
waitOutCount: 0,
rejectedCount: 0,
auditingCount: 0,
outStockCount: 0
})
const statusCards = computed(() => [
{ key: 'all', label: t('ErpStock.Out.all'), value: counters.allCount, statusList: [] },
{ key: 'waitOut', label: t('ErpStock.Out.waitOut'), value: counters.waitOutCount, statusList: [0] },
{ key: 'rejected', label: t('ErpStock.Out.rejected'), value: counters.rejectedCount, statusList: [1] },
{ key: 'auditing', label: t('ErpStock.Out.auditing'), value: counters.auditingCount, statusList: [10] },
{ key: 'outStock', label: t('ErpStock.Out.outStock'), value: counters.outStockCount, statusList: [20] }
])
const changeStatus = (key: string, statusList: number[]) => {
currentStatusKey.value = key
queryParams.statusList = statusList
handleQuery()
}
const exportLoading = ref(false) //
const showAllFilters = ref(false)
const DEFAULT_STATUS_LIST = [0, 1, 10]
@ -445,6 +473,11 @@ const getList = async () => {
const data = await StockOutApi.getStockOutPage(buildQueryParams())
list.value = data.list
total.value = data.total
counters.allCount = Number(data?.allCount ?? data?.total ?? 0)
counters.waitOutCount = Number(data?.waitOutCount ?? 0)
counters.rejectedCount = Number(data?.rejectedCount ?? 0)
counters.auditingCount = Number(data?.auditingCount ?? 0)
counters.outStockCount = Number(data?.outStockCount ?? 0)
} finally {
loading.value = false
updatePlanTableMaxHeight()
@ -469,6 +502,7 @@ const handleQuery = () => {
/** 閲嶇疆鎸夐挳鎿嶄綔 */
const resetQuery = async () => {
useInitialStatusFilter.value = false
currentStatusKey.value = 'all'
queryFormRef.value.resetFields()
queryParams.statusList = []
queryParams.outType = activeName.value || stockOutTypeOptions.value[0]?.value
@ -622,3 +656,44 @@ const handleTabClick = async (tab: TabsPaneContext) => {
handleQuery()
}
</script>
<style scoped lang="scss">
.stock-out__header {
display: grid;
grid-template-columns: repeat(5, minmax(0, 1fr));
gap: 12px;
width: 100%;
margin-bottom: 16px;
}
.stock-out__stat {
padding: 16px 18px;
border: 1px solid var(--el-border-color-light);
border-radius: 12px;
cursor: pointer;
transition: box-shadow 0.2s ease;
background: #fff;
&:hover {
box-shadow: 0 10px 24px rgba(64, 158, 255, 0.12);
}
&.is-active {
border-color: var(--el-color-primary);
box-shadow: 0 10px 24px rgba(64, 158, 255, 0.12);
}
}
.stock-out__stat-title {
color: var(--el-text-color-regular);
font-size: 14px;
}
.stock-out__stat-value {
margin-top: 8px;
color: var(--el-text-color-primary);
font-size: 28px;
font-weight: 700;
line-height: 1;
}
</style>

@ -1,5 +1,13 @@
<template>
<ContentWrap>
<!-- 统计卡片 -->
<div class="pallet__header">
<div v-for="card in statusCards" :key="card.key" class="pallet__stat"
:class="{ 'is-active': currentStatusKey === card.key }" @click="changeStatus(card.key, card.statusValue)">
<div class="pallet__stat-title">{{ card.label }}</div>
<div class="pallet__stat-value">{{ card.value }}</div>
</div>
</div>
<el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true" label-width="auto">
<el-form-item :label="t('ErpStock.Pallet.code')" prop="code">
<el-input v-model="queryParams.code" :placeholder="t('ErpStock.Pallet.placeholderCode')" clearable
@ -151,6 +159,24 @@ const queryParams = reactive({
planCode: undefined
})
const queryFormRef = ref()
const currentStatusKey = ref('all')
const counters = reactive({
allCount: 0,
idleCount: 0,
occupiedCount: 0,
scrappedCount: 0
})
const statusCards = computed(() => [
{ key: 'all', label: t('ErpStock.Pallet.all'), value: counters.allCount, statusValue: undefined },
{ key: 'idle', label: t('ErpStock.Pallet.idle'), value: counters.idleCount, statusValue: 1 },
{ key: 'occupied', label: t('ErpStock.Pallet.occupied'), value: counters.occupiedCount, statusValue: 2 },
{ key: 'scrapped', label: t('ErpStock.Pallet.scrapped'), value: counters.scrappedCount, statusValue: 3 }
])
const changeStatus = (key: string, statusValue: number | undefined) => {
currentStatusKey.value = key
queryParams.status = statusValue
handleQuery()
}
const showAllFilters = ref(false)
const filterCount = 6
const warehouseList = ref<any[]>([])
@ -185,6 +211,10 @@ const getList = async () => {
const data = await PalletApi.getPalletPage(queryParams)
list.value = data.list ?? []
total.value = data.total ?? 0
counters.allCount = Number(data?.allCount ?? data?.total ?? 0)
counters.idleCount = Number(data?.idleCount ?? 0)
counters.occupiedCount = Number(data?.occupiedCount ?? 0)
counters.scrappedCount = Number(data?.scrappedCount ?? 0)
} finally {
loading.value = false
updatePlanTableMaxHeight()
@ -210,6 +240,7 @@ const handleQuery = () => {
}
const resetQuery = () => {
currentStatusKey.value = 'all'
queryFormRef.value.resetFields()
handleQuery()
}
@ -240,3 +271,44 @@ onBeforeUnmount(() => {
window.removeEventListener('resize', updatePlanTableMaxHeight)
})
</script>
<style scoped lang="scss">
.pallet__header {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 12px;
width: 100%;
margin-bottom: 16px;
}
.pallet__stat {
padding: 16px 18px;
border: 1px solid var(--el-border-color-light);
border-radius: 12px;
cursor: pointer;
transition: box-shadow 0.2s ease;
background: #fff;
&:hover {
box-shadow: 0 10px 24px rgba(64, 158, 255, 0.12);
}
&.is-active {
border-color: var(--el-color-primary);
box-shadow: 0 10px 24px rgba(64, 158, 255, 0.12);
}
}
.pallet__stat-title {
color: var(--el-text-color-regular);
font-size: 14px;
}
.pallet__stat-value {
margin-top: 8px;
color: var(--el-text-color-primary);
font-size: 28px;
font-weight: 700;
line-height: 1;
}
</style>

@ -95,33 +95,6 @@ const formData = ref({
deviceId: undefined as number | undefined
})
const ratioEnabledTypes = new Set([
'uint8',
'uint16',
'uint32',
'uint64',
'int8',
'int16',
'int32',
'float32',
'float64'
])
const ratioEnabled = computed(() => {
const v = formData.value.dataType
if (!v) return false
return ratioEnabledTypes.has(v)
})
watch(
() => formData.value.dataType,
() => {
if (!ratioEnabled.value) {
formData.value.ratio = undefined
}
}
)
const handleAttributeCodeInput = (val: string) => {
const sanitized = (val || '').replace(/[^A-Za-z0-9_]/g, '')
formData.value.attributeCode = sanitized.replace(/^[0-9]+/, '')
@ -202,7 +175,7 @@ const buildSubmitData = () => {
dataType,
address,
dataUnit,
ratio: ratioEnabled.value ? ratio : undefined,
ratio,
remark,
deviceId
}

@ -173,7 +173,7 @@ const buildSubmitData = () => {
dataType,
address,
dataUnit,
ratio: ratioEnabled.value ? ratio : undefined,
ratio,
remark,
deviceModelId
}

@ -425,8 +425,8 @@
<el-table-column width="56" align="center">
<template #default="scope">
<el-radio
:model-value="iotDeviceDraftId"
:label="scope.row.id"
v-model="iotDeviceDraftId"
:value="String(scope.row.id)"
:disabled="!isIotDeviceSelectable(scope.row)"
@change="selectIotDevice(scope.row)"
@click.stop
@ -796,7 +796,7 @@ const iotDeviceDialogVisible = ref(false)
const iotDeviceList = ref<DeviceVO[]>([])
const iotDeviceTotal = ref(0)
const iotDeviceSelectedLabel = ref('')
const iotDeviceDraftId = ref<number | undefined>()
const iotDeviceDraftId = ref<string | undefined>()
const iotDeviceDraftRow = ref<DeviceVO | undefined>()
const list = ref<CriticalComponentVO[]>([])
const bjList = ref<ProductVO[]>([])
@ -825,6 +825,12 @@ const normalizeNumberish = (value: any): number | undefined => {
}
return undefined
}
const normalizeId = (value: unknown): string | undefined => {
if (value === null || value === undefined) return undefined
const id = String(value).trim()
return id || undefined
}
const normalizeYmd = (value: any): string | undefined => {
if (value === null || value === undefined || value === '') return undefined
@ -1094,7 +1100,7 @@ const buildIotDeviceLabel = (item: any) => {
}
const getIotDeviceIdFromDetail = (detail: any) =>
normalizeNumberish(
normalizeId(
detail?.dvId ?? detail?.iotDeviceId ?? detail?.iotDevice?.id ?? detail?.device?.id
)
@ -1118,8 +1124,19 @@ const getIotDeviceLabelFromDetail = (detail: any) => {
const iotDeviceDisplay = computed(() => iotDeviceSelectedLabel.value)
const isCurrentIotDevice = (row: any) => {
const rowId = normalizeId(row?.id)
const selectedId = normalizeId(formData.value.dvId)
if (rowId !== undefined && rowId === selectedId) return true
const boundLedgerId = normalizeId(row?.boundLedgerId)
const currentLedgerId = normalizeId(formData.value.id)
return boundLedgerId !== undefined && boundLedgerId === currentLedgerId
}
const isIotDeviceSelectable = (row: any) => {
const value = row?.selectable
if (isCurrentIotDevice(row)) return true
return value !== false && String(value ?? 'true').toLowerCase() !== 'false'
}
@ -1134,7 +1151,7 @@ const isIotDeviceEnabled = (row: any) => {
const selectIotDevice = (row: DeviceVO) => {
if (!isIotDeviceSelectable(row)) return
const id = normalizeNumberish(row?.id)
const id = normalizeId(row?.id)
if (id === undefined) return
iotDeviceDraftId.value = id
iotDeviceDraftRow.value = row
@ -1149,7 +1166,7 @@ const getIotDeviceRowClassName = ({ row }: { row: DeviceVO }) => {
}
const openIotDeviceDialog = async () => {
const id = normalizeNumberish(formData.value.dvId)
const id = normalizeId(formData.value.dvId)
iotDeviceDraftId.value = id
iotDeviceDraftRow.value =
id === undefined ? undefined : ({ id, deviceName: iotDeviceSelectedLabel.value } as DeviceVO)

@ -730,8 +730,8 @@
<el-table-column width="56" align="center">
<template #default="scope">
<el-radio
:model-value="iotDeviceDraftId"
:label="scope.row.id"
v-model="iotDeviceDraftId"
:value="String(scope.row.id)"
:disabled="!isIotDeviceSelectable(scope.row)"
@change="selectIotDevice(scope.row)"
@click.stop
@ -894,7 +894,7 @@ const iotDeviceDialogVisible = ref(false)
const iotDeviceList = ref<DeviceVO[]>([])
const iotDeviceTotal = ref(0)
const iotDeviceSelectedLabel = ref('')
const iotDeviceDraftId = ref<number | undefined>()
const iotDeviceDraftId = ref<string | undefined>()
const iotDeviceDraftRow = ref<DeviceVO | undefined>()
const parseIdsValue = (value: any): number[] => {
if (!value) return []
@ -1158,6 +1158,12 @@ const normalizeNumberish = (value: any): number | undefined => {
}
return undefined
}
const normalizeId = (value: unknown): string | undefined => {
if (value === null || value === undefined) return undefined
const id = String(value).trim()
return id || undefined
}
const normalizeYmd = (value: any): string | undefined => {
if (value === null || value === undefined || value === '') return undefined
@ -1428,7 +1434,7 @@ const buildIotDeviceLabel = (item: any) => {
}
const getIotDeviceIdFromDetail = (detail: any) =>
normalizeNumberish(
normalizeId(
detail?.dvId ?? detail?.iotDeviceId ?? detail?.iotDevice?.id ?? detail?.device?.id
)
@ -1452,8 +1458,19 @@ const getIotDeviceLabelFromDetail = (detail: any) => {
const iotDeviceDisplay = computed(() => iotDeviceSelectedLabel.value)
const isCurrentIotDevice = (row: any) => {
const rowId = normalizeId(row?.id)
const selectedId = normalizeId(formData.value.dvId)
if (rowId !== undefined && rowId === selectedId) return true
const boundLedgerId = normalizeId(row?.boundLedgerId)
const currentLedgerId = normalizeId(formData.value.id)
return boundLedgerId !== undefined && boundLedgerId === currentLedgerId
}
const isIotDeviceSelectable = (row: any) => {
const value = row?.selectable
if (isCurrentIotDevice(row)) return true
return value !== false && String(value ?? 'true').toLowerCase() !== 'false'
}
@ -1468,7 +1485,7 @@ const isIotDeviceEnabled = (row: any) => {
const selectIotDevice = (row: DeviceVO) => {
if (!isIotDeviceSelectable(row)) return
const id = normalizeNumberish(row?.id)
const id = normalizeId(row?.id)
if (id === undefined) return
iotDeviceDraftId.value = id
iotDeviceDraftRow.value = row
@ -1483,7 +1500,7 @@ const getIotDeviceRowClassName = ({ row }: { row: DeviceVO }) => {
}
const openIotDeviceDialog = async () => {
const id = normalizeNumberish(formData.value.dvId)
const id = normalizeId(formData.value.dvId)
iotDeviceDraftId.value = id
iotDeviceDraftRow.value =
id === undefined ? undefined : ({ id, deviceName: iotDeviceSelectedLabel.value } as DeviceVO)

Loading…
Cancel
Save