Browse Source

fix:设备功能添加

master
wangqi 2 months ago
parent
commit
a4ec3e384f
  1. 3
      components.d.ts
  2. 8
      src/api/module/eam/customer/index.ts
  3. 34
      src/api/module/eam/device/firmware.ts
  4. 27
      src/api/module/eam/device/index.ts
  5. 28
      src/api/module/eam/device/maintenance.ts
  6. 24
      src/api/module/eam/device/storage.ts
  7. 25
      src/api/module/eam/device/task.ts
  8. 27
      src/api/module/eam/device/template.ts
  9. 2
      src/components/dashboard/Edfs-exception.vue
  10. 1
      src/components/dashboard/FormItemSelect.vue
  11. 182
      src/pages/deviceInfo/components/BasicInfo.vue
  12. 95
      src/pages/deviceInfo/components/DeliveryInfo.vue
  13. 139
      src/pages/deviceInfo/components/MaintainInfo.vue
  14. 92
      src/pages/deviceInfo/components/TestInfo.vue
  15. 63
      src/pages/deviceInfo/index.vue
  16. 106
      src/pages/deviceInfo/info.vue
  17. 48
      src/pages/deviceInfo/utils.ts
  18. 7
      src/pages/deviceTest/testPlan/index.vue
  19. 215
      src/pages/ota/firmware/components/create-firmware-dlg.vue
  20. 183
      src/pages/ota/firmware/index.vue
  21. 32
      src/pages/ota/firmware/utils.ts
  22. 308
      src/pages/ota/upgradeTask/components/create-task-dlg.vue
  23. 244
      src/pages/ota/upgradeTask/components/details-drawer.vue
  24. 372
      src/pages/ota/upgradeTask/index.vue
  25. 44
      src/pages/ota/upgradeTask/utils.ts
  26. 87
      src/pages/system/deviceField/components/Dispose.vue
  27. 15
      src/pages/system/deviceField/index.vue
  28. 2
      src/pages/system/login/index.vue
  29. 13
      src/pages/system/manufacturer/index.vue
  30. 4
      src/pages/system/user/index.vue
  31. 139
      src/router/index.ts

3
components.d.ts vendored

@ -35,6 +35,7 @@ declare module 'vue' { @@ -35,6 +35,7 @@ declare module 'vue' {
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
ElDialog: typeof import('element-plus/es')['ElDialog']
ElDivider: typeof import('element-plus/es')['ElDivider']
ElDrawer: typeof import('element-plus/es')['ElDrawer']
ElDropdown: typeof import('element-plus/es')['ElDropdown']
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
@ -51,6 +52,7 @@ declare module 'vue' { @@ -51,6 +52,7 @@ declare module 'vue' {
ElOption: typeof import('element-plus/es')['ElOption']
ElPagination: typeof import('element-plus/es')['ElPagination']
ElPopover: typeof import('element-plus/es')['ElPopover']
ElProgress: typeof import('element-plus/es')['ElProgress']
ElRadio: typeof import('element-plus/es')['ElRadio']
ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
@ -75,6 +77,7 @@ declare module 'vue' { @@ -75,6 +77,7 @@ declare module 'vue' {
FormItemSelect: typeof import('./src/components/dashboard/FormItemSelect.vue')['default']
Icon: typeof import('./src/components/dashboard/Icon/src/Icon.vue')['default']
IconSelect: typeof import('./src/components/dashboard/Icon/src/IconSelect.vue')['default']
IEpUploadFilled: typeof import('~icons/ep/upload-filled')['default']
ImageViewer: typeof import('./src/components/dashboard/ImageViewer/src/ImageViewer.vue')['default']
InputPassword: typeof import('./src/components/dashboard/InputPassword/src/InputPassword.vue')['default']
LegendItem: typeof import('./src/components/map_tool/legend/LegendItem.vue')['default']

8
src/api/module/eam/customer/index.ts

@ -8,7 +8,7 @@ export interface CustomerVO { @@ -8,7 +8,7 @@ export interface CustomerVO {
email: string
address: string
description: string
status: number
enabled: number
}
export const getCustomerList = (params: PageParam) =>
eamServer<{
@ -21,7 +21,7 @@ export const getCustomerList = (params: PageParam) => @@ -21,7 +21,7 @@ export const getCustomerList = (params: PageParam) =>
})
export const getSimpleCustomerList = () =>
eamServer<Pick<CustomerVO, 'id' | 'name'>>({
eamServer<Array<Pick<CustomerVO, 'id' | 'name'>>>({
url: '/customer/list-all-simple',
method: 'get',
})
@ -38,9 +38,9 @@ export const deleteCustomer = (id: Pick<CustomerVO, 'id'>) => @@ -38,9 +38,9 @@ export const deleteCustomer = (id: Pick<CustomerVO, 'id'>) =>
method: 'delete',
})
export const updateCustomerStatus = (params: Pick<CustomerVO, 'id' | 'status'>) =>
export const updateCustomerStatus = (params: Pick<CustomerVO, 'id' | 'enabled'>) =>
eamServer({
url: `/customer/update-status?id=${params.id}&status=${params.status}`,
url: `/customer/update-status?id=${params.id}&enabled=${params.enabled}`,
method: 'put',
})

34
src/api/module/eam/device/firmware.ts

@ -0,0 +1,34 @@ @@ -0,0 +1,34 @@
import { eamServer } from '../../index'
export function getFirmwareList(params: PageParam) {
return eamServer({
url: '/firmware/page',
method: 'get',
params,
})
}
export function deleteFirmware(id: string) {
return eamServer({
url: '/firmware/delete',
method: 'post',
data: { id },
})
}
export function createFirmware(data: any) {
return eamServer({
url: '/firmware/upload',
method: 'post',
data,
headers: { 'Content-Type': 'multipart/form-data' },
})
}
export function getSimpleFirmwareList(params: { type: number }) {
return eamServer({
url: '/firmware/simple-list',
method: 'get',
params,
})
}

27
src/api/module/eam/device/index.ts

@ -1,50 +1,41 @@ @@ -1,50 +1,41 @@
import { eamServer } from '../../index'
export interface IDevice {
id: string
id: number
name: string
categoryId: string
serialNo: string
sn: string
status: string
status: number
description: string
cost: string
createTime: string
updateTime: string
creator: string
updater: string
testSheetId: string
testSheetId: number
testSheetStatus: string
testSheetDetail: string
}
export interface IDeviceOV {
id?: string
id?: number
name: string
categoryId: string
templateId: string
serialNo: string
serialNo?: string
sn: string
description: string
cost: string
}
export const operantDevice = (type: OperantAction, data: IDeviceOV) => {
if (type === 'create') {
return eamServer({
url: '/device/create',
method: 'post',
data,
})
}
if (type === 'update') {
return eamServer({
url: '/device/update',
method: 'put',
url: `/device/${type}`,
method: type === 'create' ? 'post' : 'put',
data,
})
}
}
export const deleteDevice = (id: string) =>
eamServer({
@ -53,7 +44,7 @@ export const deleteDevice = (id: string) => @@ -53,7 +44,7 @@ export const deleteDevice = (id: string) =>
params: { id },
})
export const getDeviceInfo = (id: string) =>
export const getDeviceInfo = (id: number) =>
eamServer({
url: `/device/get`,
method: 'get',
@ -66,6 +57,8 @@ export const getDevicePage = (params: PageParam) => @@ -66,6 +57,8 @@ export const getDevicePage = (params: PageParam) =>
total: number
}>({ url: '/device/page', method: 'get', params })
// ============== 测试工单相关 ==============
export const operantDeviceTestSheet = (type: OperantAction, params: any) => {

28
src/api/module/eam/device/maintenance.ts

@ -0,0 +1,28 @@ @@ -0,0 +1,28 @@
import { eamServer } from '../../index'
interface IMaintenanceOV {
id?: number
content: string
maintainer?: string
maintenanceTime: string
status: number
}
export const createdMaintenance = (data: IMaintenanceOV) =>
eamServer({
url: '/maintenance/create',
method: 'post',
data,
})
export const updateMaintenance = (data: IMaintenanceOV) =>
eamServer({
url: '/maintenance/update',
method: 'post',
data,
})
export const getMaintenanceList = (params: any) =>
eamServer({
url: '/maintenance/get-by-device-id',
method: 'get',
params,
})

24
src/api/module/eam/device/storage.ts

@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
import { eamServer } from '../../index'
export interface IStorageOutParam {
id?: string
customerId: string
name: string
description: string
deviceId: number
price: string
}
export const storageOutDevice = (params: IStorageOutParam) =>
eamServer({
url: `/device/out-storage`,
method: 'post',
data: params,
})
export const getStorageOutByDeviceId = (deviceId: number) =>
eamServer({
url: `/out-storage/get-by-device-id`,
method: 'get',
params: { deviceId },
})

25
src/api/module/eam/device/task.ts

@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
import { eamServer } from '../../index'
export function getTaskList(params: PageParam) {
return eamServer({
url: '/device/task/page',
method: 'get',
params,
})
}
export function getTaskDetails(id: string) {
return eamServer({
url: '/device/task/details',
method: 'get',
params: { id },
})
}
export function createTask(data: any) {
return eamServer({
url: '/device/firmware/remote-update',
method: 'post',
data,
})
}

27
src/api/module/eam/device/template.ts

@ -37,6 +37,33 @@ export const getDeviceTempPage = (params: PageParam) => @@ -37,6 +37,33 @@ export const getDeviceTempPage = (params: PageParam) =>
total: number
}>({ url: '/template/page', method: 'get', params })
export const getDeviceTempSimpleList = () =>
eamServer<any[]>({
url: '/template/list-all-simple',
method: 'get',
})
export const updateTempStatus = (id: string, enabled: number) =>
eamServer({
url: '/template/update-status',
method: 'put',
data: { id, enabled },
})
export const updateSheetStatus = (id: string, status: number) =>
eamServer({
url: '/sheet-detail/update-result',
method: 'put',
data: { id, status },
})
export const updateTestSheetStatus = (params: { id: number; deviceId: number }) =>
eamServer({
url: '/test-sheet/update-status',
method: 'put',
data: params,
})
// ============== 用例相关 ==============
export interface IDeviceCase {

2
src/components/dashboard/Edfs-exception.vue

@ -10,7 +10,7 @@ @@ -10,7 +10,7 @@
type="primary"
@click="
() => {
router.push('/user/device')
router.push('/device/data')
}
"
>返回首页</el-button

1
src/components/dashboard/FormItemSelect.vue

@ -168,7 +168,6 @@ watch( @@ -168,7 +168,6 @@ watch(
width: 150px;
}
:deep(.el-input__inner) {
color: #4d4d4d;
background-color: transparent;

182
src/pages/deviceInfo/components/BasicInfo.vue

@ -1,28 +1,24 @@ @@ -1,28 +1,24 @@
<template>
{{ info?.status }}
<div class="device-basic-info-from">
<div class="from-input">
<div class="group-box">
<div class="from-row">
<div class="label">设备序列号:</div>
<el-input
v-model="params.number"
:disabled="disabled"
placeholder="请输入设备序列号"
class="input"
v-model="params.serialNo"
:disabled="true"
placeholder="提交信息后自动生成序列号"
clearable
>
<template #append>
<edfs-button
inner-text="自动生成"
:disabled="disabled"
type="success"
@click="onGenerative"
/></template>
</el-input>
</div>
<div class="from-row">
<div class="label">设备名称:</div>
<el-input
v-model="params.deviceName"
class="input"
v-model="params.name"
:disabled="disabled"
placeholder="请输入设备名称"
clearable
@ -32,8 +28,9 @@ @@ -32,8 +28,9 @@
<div class="from-row">
<div class="label">设备SN:</div>
<el-input
class="input"
v-model="params.sn"
:disabled="disabled"
:disabled="disabled || !!params.serialNo"
placeholder="请输入设备sn"
clearable
/>
@ -42,31 +39,36 @@ @@ -42,31 +39,36 @@
<div class="group-box">
<div class="from-row">
<div class="label">设备类别:</div>
<FormItemSelect
<el-cascader
class="input"
v-model="params.categoryId"
:options="categoryTreeData"
:disabled="disabled"
v-model="params.temp"
:options="[]"
placeholder="请选择设备类型"
keyTag="value"
labelTag="label"
:isShowLabel="false"
:props="{
checkStrictly: true,
value: 'id',
label: 'name',
}"
/>
</div>
<div class="from-row">
<div class="label">测试模板:</div>
<FormItemSelect
:disabled="disabled"
v-model="params.temp"
v-model="params.templateId"
placeholder="请选择测试模板"
:options="[]"
keyTag="value"
labelTag="label"
:options="templateList"
keyTag="id"
labelTag="name"
valueTag="id"
class="input"
:isShowLabel="false"
/>
</div>
<div class="from-row">
<div class="label">设备成本:</div>
<number-input
class="input"
:disabled="disabled"
v-model="params.cost"
placeholder="请输入成本"
@ -78,8 +80,9 @@ @@ -78,8 +80,9 @@
<div class="label">备注:</div>
<el-input
:disabled="disabled"
class="input"
type="textarea"
v-model="params.remark"
v-model="params.description"
placeholder="请输入备注"
:autosize="{ minRows: 5, maxRows: 4 }"
resize="none"
@ -87,41 +90,133 @@ @@ -87,41 +90,133 @@
</div>
</div>
<EdfsButton
:disabled="disabled"
inner-text="提交基础信息"
type="success"
class="save-button"
v-if="isShowSaveButton"
@click="onSave"
/>
</div>
</div>
</template>
<script setup lang="ts">
import { isEmpty } from '@/utils/is'
import numberInput from '@/components/dashboard/Edfs-number-item-input.vue'
import EdfsButton from '@/components/dashboard/Edfs-button/index.vue'
import FormItemSelect from '@/components/dashboard/FormItemSelect.vue'
import { operantDevice, type IDevice, type IDeviceOV } from '@/api/module/eam/device'
import { cloneDeep } from 'lodash'
import { isResError, useMessage } from '@/hooks/useMessage'
import { getDeviceTempSimpleList } from '@/api/module/eam/device/template'
import { getCategoryTree, type ICategoryTree } from '@/api/module/eam/device/category'
import { useRoute } from 'vue-router'
import type { OptAction } from '../utils'
const route = useRoute()
const action = route.query.action as OptAction
const message = useMessage()
const props = defineProps<{
type: string
disabled: boolean
info: IDevice | undefined
}>()
const disabled = computed(() => props.type === 'view')
const emit = defineEmits(['on-save'])
console.log('type', props.type)
const params = ref({
number: '',
deviceName: '',
const paramsData: IDeviceOV = {
name: '',
categoryId: '',
templateId: '',
sn: '',
serialNo: '',
description: '',
cost: '',
temp: '',
status: '',
remark: '',
}
const isShowSaveButton = computed(() => {
if(action === 'view') return false
return props.info?.status === undefined ? true : props.info.status < 2
})
function onGenerative() {
console.log('自动生成')
const params = ref(cloneDeep(paramsData))
async function onSave() {
if (!validate()) return
const type = !!params.value.serialNo ? 'update' : 'create'
const options = cloneDeep(params.value)
if (type === 'update') options.id = props.info?.id
options.categoryId = Array.isArray(options.categoryId)
? options.categoryId[options.categoryId.length - 1]
: options.categoryId
const res = await operantDevice(type, options)
if (isResError(res)) return
message.success('基础信息提交成功')
emit('on-save', res.data.id)
}
watch(
() => props.info,
val => {
if (val) {
const info = cloneDeep(val)
for (const key of Object.keys(paramsData)) {
params.value[key] = info[key] ?? ''
}
}
console.log(params.value)
},
{ immediate: true }
)
function validate() {
if (isEmpty(params.value.name)) {
message.error('设备名称不能为空')
return false
}
if (isEmpty(params.value.sn)) {
message.error('设备SN不能为空')
return false
}
if (isEmpty(params.value.categoryId)) {
message.error('设备类别不能为空')
return false
}
if (isEmpty(params.value.templateId)) {
message.error('测试模板不能为空')
return false
}
if (isEmpty(params.value.cost)) {
message.error('设备成本不能为空')
return false
}
return true
}
const templateList = ref<any>([])
async function loadTemplateList() {
const res = await getDeviceTempSimpleList()
if (!isResError(res)) {
templateList.value = res.data
}
}
const categoryTreeData = ref<ICategoryTree[]>([])
async function loadDeviceTypeTree() {
const res = await getCategoryTree()
if (!isResError(res)) {
categoryTreeData.value = res.data
}
}
onMounted(() => {
loadDeviceTypeTree()
loadTemplateList()
})
</script>
<style scoped lang="scss">
@ -142,17 +237,24 @@ function onGenerative() { @@ -142,17 +237,24 @@ function onGenerative() {
row-gap: 10px;
}
.from-row {
min-width: 270px;
width: 320px;
display: flex;
box-sizing: border-box;
min-height: 32px;
align-items: center;
.label {
white-space: nowrap;
width: 80px;
width: 70px;
text-align: right;
color: var(--label-text);
margin-right: 8px;
}
.input {
flex: 1 !important;
}
:deep(.el-cascader) {
flex: 1;
}
}
.save-button {
margin-top: auto;

95
src/pages/deviceInfo/components/DeliveryInfo.vue

@ -5,17 +5,18 @@ @@ -5,17 +5,18 @@
<div class="from-row">
<div class="label">出库流向:</div>
<FormItemSelect
v-model="params.flow"
:options="[]"
v-model="params.customerId"
:options="customerList"
placeholder="请选择出库流向"
keyTag="value"
labelTag="label"
keyTag="id"
labelTag="name"
valueTag="id"
:isShowLabel="false"
/>
</div>
<div class="from-row">
<div class="label">设备价格:</div>
<number-input v-model="params.cost" placeholder="请输入成本" />
<number-input v-model="params.price" placeholder="请输入成本" />
</div>
</div>
<div class="group-box">
@ -23,13 +24,19 @@ @@ -23,13 +24,19 @@
<div class="label">备注:</div>
<el-input
type="textarea"
v-model="params.descr"
v-model="params.description"
placeholder="请输入备注"
:autosize="{ minRows: 3, maxRows: 4 }"
/>
</div>
</div>
<EdfsButton inner-text="出库" type="success" class="save-button" />
<EdfsButton
inner-text="出库"
type="success"
class="save-button"
@click="onSave"
v-if="isShowSaveButton"
/>
</div>
</div>
</template>
@ -38,11 +45,77 @@ @@ -38,11 +45,77 @@
import numberInput from '@/components/dashboard/Edfs-number-item-input.vue'
import EdfsButton from '@/components/dashboard/Edfs-button/index.vue'
import FormItemSelect from '@/components/dashboard/FormItemSelect.vue'
import { getSimpleCustomerList, type CustomerVO } from '@/api/module/eam/customer'
import { isResError } from '@/hooks/useMessage'
import type { IDevice } from '@/api/module/eam/device'
import {
getStorageOutByDeviceId,
storageOutDevice,
type IStorageOutParam,
} from '@/api/module/eam/device/storage'
import { useRoute } from 'vue-router'
import type { OptAction } from '../utils'
const route = useRoute()
const action = route.query.action as OptAction
const props = defineProps<{
disabled: boolean
info: IDevice | undefined
deviceId: number | undefined
}>()
const isShowSaveButton = computed(() => {
if(action === 'view') return false
return props.info?.status === undefined ? true : props.info.status < 3
})
const emit = defineEmits(['onSave'])
const params = ref<Omit<IStorageOutParam, 'deviceId'>>({
customerId: '',
price: '',
name: '',
description: '',
})
const customerList = ref<Array<Pick<CustomerVO, 'id' | 'name'>>>([])
async function loadCustomerList() {
const res = await getSimpleCustomerList()
if (isResError(res)) return
customerList.value = res.data
}
async function onSave() {
if (!props.deviceId) return
const options: IStorageOutParam = {
deviceId: props.deviceId,
customerId: params.value.customerId,
price: params.value.price,
description: params.value.description,
name: '',
}
const res = await storageOutDevice(options)
if (isResError(res)) return
emit('onSave')
}
watch(
() => props.deviceId,
newVal => {
if (newVal) {
loadDeviceStorageInfo()
}
}
)
async function loadDeviceStorageInfo() {
if (!props.deviceId) return
const res = await getStorageOutByDeviceId(props.deviceId)
if (isResError(res)) return
for (const key of Object.keys(params.value)) {
params.value[key] = res.data[key] ?? ''
}
}
const params = ref({
flow: '',
cost: '',
descr: '',
onMounted(() => {
loadCustomerList()
})
</script>

139
src/pages/deviceInfo/components/MaintainInfo.vue

@ -35,6 +35,31 @@ @@ -35,6 +35,31 @@
}}</template>
</template>
</el-table-column>
<el-table-column
v-else-if="col.prop === 'status'"
:prop="col.prop"
:label="col.label"
:min-width="col.minWidth"
>
<template #default="scope">
<template v-if="scope.row.isEdit">
<FormItemSelect
style="height: 32px"
v-model="scope.row[col.prop]"
placeholder="请选择测试模板"
:options="maintainStatus"
keyTag="value"
labelTag="label"
valueTag="value"
class="input"
:isShowLabel="false"
/>
</template>
<template v-else>{{
maintainStatus.find(r => r.value === scope.row[col.prop])?.label ?? ''
}}</template>
</template>
</el-table-column>
<el-table-column
v-else
:prop="col.prop"
@ -52,6 +77,7 @@ @@ -52,6 +77,7 @@
<el-table-column label="操作" width="120" align="center">
<template #default="scope">
<!-- 取消 -->
<template v-if="scope.row.isEdit">
<EdfsButton
link
type="success"
@ -64,16 +90,32 @@ @@ -64,16 +90,32 @@
type="info"
inner-text="取消"
v-if="scope.row.isEdit"
@click="oncancel(scope.$index)"
/>
</template>
<template v-else>
<EdfsButton
link
type="primary"
inner-text="编辑"
@click="
() => {
list.pop()
scope.row.isEdit = true
currentOperateStatus = 'update'
}
"
/>
</template>
</template>
</el-table-column>
</EdfsTable>
<el-button class="mt-4" style="width: 100%" @click="addMaintain" type="success">
<el-button
class="mt-4"
style="width: 100%"
@click="addMaintain"
v-if="action !== 'view'"
type="success"
>
添加维修信息
</el-button>
</div>
@ -81,42 +123,109 @@ @@ -81,42 +123,109 @@
<script setup lang="ts">
import dayjs from 'dayjs'
import { useRoute } from 'vue-router'
import EdfsTable from '@/components/dashboard/Edfs-table/index.vue'
import EdfsButton from '@/components/dashboard/Edfs-button/index.vue'
import { useMessage } from '@/hooks/useMessage'
import { isResError, useMessage } from '@/hooks/useMessage'
import { maintainStatus, type OptAction } from '../utils'
import {
createdMaintenance,
getMaintenanceList,
updateMaintenance,
} from '@/api/module/eam/device/maintenance'
import { isEmpty } from '@/utils/is'
import { cloneDeep } from 'lodash'
const props = defineProps<{
deviceId: number | undefined
}>()
const route = useRoute()
const type = route.query.type as string
const action = route.query.action as OptAction
const dataParams = { content: '', status: '', maintenanceTime: '', description: '' }
const msg = useMessage()
const list = ref<any>([])
const tableCol = [
{ label: '维修内容', prop: 'content', minWidth: '10%', placeholder: '请输入维修内容' },
{ label: '维修状态', prop: 'status', minWidth: '8%', placeholder: '请输入维修状态' },
{ label: '维修时间', prop: 'Time', minWidth: '12%', placeholder: '请输选择时间' },
{ label: '备注', prop: 'descr', minWidth: '16%', placeholder: '请输入备注' },
{
label: '维修时间',
prop: 'maintenanceTime',
minWidth: '12%',
placeholder: '请输选择时间',
},
{ label: '备注', prop: 'description', minWidth: '16%', placeholder: '请输入备注' },
]
function addMaintain() {
currentOperateStatus.value = 'create'
if (list.value.some((item: any) => item.isEdit)) {
msg.error('请先保存当前编辑项')
return
}
list.value.push({
content: '',
status: '',
Time: '',
descr: '',
isEdit: true,
})
list.value.push(Object.assign({}, cloneDeep(dataParams), { isEdit: true }))
}
const currentOperateStatus = ref<Omit<OperantAction, 'delete'>>('create')
function oncancel(index: number) {
if (currentOperateStatus.value === 'create') {
list.value.splice(index, 1)
return
} else {
list.value[index].isEdit = false
}
}
function onSave(row: any) {
for (const [key, val] of Object.entries(row)) {
if (!val && key !== 'descr') {
async function onSave(row: any) {
debugger
for (const key of Object.keys(dataParams)) {
if (isEmpty(row[key]) && key !== 'description') {
msg.error(`请填写${tableCol.find(item => item.prop === key)?.label}`)
return
}
}
const params = {
content: row.content,
maintenanceTime: dayjs(row.maintenanceTime).valueOf(),
status: row.status,
deviceId: props.deviceId,
} as any
let res
if (row.id) {
params.id = row.id
res = await updateMaintenance(params)
} else {
res = await createdMaintenance(params)
}
if (isResError(res)) return
loadMaintainList()
row.isEdit = false
}
watch(
() => props.deviceId,
val => {
if (val) {
loadMaintainList()
}
},
{
immediate: true,
}
)
async function loadMaintainList() {
const res = await getMaintenanceList({
deviceId: props.deviceId,
})
if (isResError(res)) return
list.value = res.data
}
</script>
<style scoped lang="scss">

92
src/pages/deviceInfo/components/TestInfo.vue

@ -3,28 +3,30 @@ @@ -3,28 +3,30 @@
<div class="template-table">
<EdfsTable
class="table"
v-loading="loading"
:data="list"
:usePaging="false"
:highlight-current-row="true"
row-class-name="row"
>
<template v-for="(col, idx) in tableCol" :key="idx">
<!-- <el-table-column
v-if="col.prop === 'status'"
:label="col.label"
:min-width="col.minWidth"
>
<template #default="scope"> </template>
</el-table-column> -->
<template v-for="(col, idx) in testTableCol" :key="idx">
<el-table-column
v-if="col.prop.endsWith('Time')"
v-if="col.prop === 'actualResult'"
:label="col.label"
:min-width="col.minWidth"
>
<template #default="scope">
{{ dayjs(scope.row.createTime).format('YYYY-MM-DD HH:mm:ss') }}
<el-radio-group
v-if="isShowSaveButton"
v-model="scope.row[col.prop]"
@change="onChangeRadio(scope.row)"
>
<template v-for="item in testSheetStatus">
<el-radio :value="item.value">{{ item.label }}</el-radio>
</template>
</el-radio-group>
<div v-else>
{{ testSheetStatus.find(r => r.value === scope.row.status)?.label ?? '' }}
</div>
</template>
</el-table-column>
<el-table-column
@ -35,35 +37,67 @@ @@ -35,35 +37,67 @@
/>
</template>
</EdfsTable>
<EdfsButton inner-text="提交并入库" type="success" class="save-button" />
<EdfsButton
inner-text="提交并入库"
type="success"
class="save-button"
v-if="isShowSaveButton"
@click="onSave"
/>
</div>
</div>
</template>
<script setup lang="ts">
import dayjs from 'dayjs'
import EdfsTable from '@/components/dashboard/Edfs-table/index.vue'
import EdfsButton from '@/components/dashboard/Edfs-button/index.vue'
import FormItemSelect from '@/components/dashboard/FormItemSelect.vue'
const tableCol = [
{ label: '测试内容', prop: 'content', minWidth: '10%' },
{ label: '预计结果', prop: 'result', minWidth: '10%' },
{ label: '测试结果', prop: 'status', minWidth: '10%' },
{ label: '备注', prop: 'entryTime', minWidth: '16%' },
]
import { testSheetStatus, testTableCol, type OptAction } from '../utils'
import {
updateSheetStatus,
updateTestSheetStatus,
} from '@/api/module/eam/device/template'
import { isResError, useMessage } from '@/hooks/useMessage'
import type { IDevice } from '@/api/module/eam/device'
import { useRoute } from 'vue-router'
const route = useRoute()
const action = route.query.action as OptAction
const massage = useMessage()
const loading = ref(true)
const list = ref([])
const props = defineProps<{
disabled: boolean
info: IDevice | undefined
}>()
const getList = async () => {
loading.value = true
const emit = defineEmits(['onSave'])
const list = ref<any>([])
const isShowSaveButton = computed(() => {
if (action === 'view') return false
return props.info?.status === undefined ? true : props.info.status < 2
})
watch(
() => props.info,
val => {
if (val) {
list.value = Array.isArray(val?.testSheetDetail) ? val.testSheetDetail : []
}
},
{ immediate: true }
)
loading.value = false
async function onChangeRadio(row: any) {
const res = await updateSheetStatus(row.id, row.actualResult)
if (isResError(res)) return
massage.success('更新成功')
}
onMounted(() => {
getList()
async function onSave() {
if (!props.info) return
const res = await updateTestSheetStatus({
id: props.info.testSheetId,
deviceId: props.info.id,
})
if (isResError(res)) return
emit('onSave')
}
</script>
<style scoped lang="scss">

63
src/pages/deviceInfo/index.vue

@ -65,7 +65,7 @@ @@ -65,7 +65,7 @@
row-class-name="row"
@pageCurrentChange="handleJump"
>
<template v-for="(col, idx) in tableCol" :key="idx">
<template v-for="(col, idx) in deviceTableCol" :key="idx">
<el-table-column
v-if="col.prop.endsWith('Time')"
:label="col.label"
@ -75,6 +75,16 @@ @@ -75,6 +75,16 @@
{{ dayjs(scope.row.createTime).format('YYYY-MM-DD HH:mm:ss') }}
</template>
</el-table-column>
<el-table-column
v-else-if="col.prop === 'status'"
:prop="col.prop"
:label="col.label"
:min-width="col.minWidth"
>
<template #default="scope">
{{ DeviceStatus[scope.row.status] }}
</template>
</el-table-column>
<el-table-column
v-else
:prop="col.prop"
@ -89,13 +99,36 @@ @@ -89,13 +99,36 @@
link
type="primary"
inner-text="查看"
@click="onView"
@click="onView(scope.row)"
/><el-divider direction="vertical" />
<EdfsButton
link
type="primary"
inner-text="编辑"
@click="addInfo(scope.row)"
inner-text="编辑基础信息"
@click="editDevice(scope.row, 'info')"
v-if="scope.row.status < 2"
/>
<EdfsButton
link
type="primary"
inner-text="测试结果录入"
@click="editDevice(scope.row, 'test')"
v-if="scope.row.status === 1"
/>
<EdfsButton
link
type="primary"
inner-text="出库"
@click="editDevice(scope.row, 'delivery')"
v-if="scope.row.status === 2"
/>
<EdfsButton
link
type="primary"
inner-text="设备报修"
@click="editDevice(scope.row, 'maintain')"
v-if="scope.row.status === 3"
/>
</template>
</el-table-column>
@ -114,14 +147,7 @@ import { dateFormatter } from '@/utils/formatTime' @@ -114,14 +147,7 @@ import { dateFormatter } from '@/utils/formatTime'
import type { TableColumnCtx } from 'element-plus'
import { useRouter } from 'vue-router'
import { getDevicePage, type IDevice } from '@/api/module/eam/device'
const tableCol = [
{ label: '设备名称', prop: 'name', minWidth: '10%' },
{ label: '设备类型', prop: 'categoryId', minWidth: '10%' },
{ label: '设备sn', prop: 'sn', minWidth: '10%' },
{ label: '设备编号', prop: 'serialNo', minWidth: '16%' },
{ label: '创建时间', prop: 'createTime', minWidth: '10%' },
]
import { deviceTableCol, DeviceStatus } from './utils'
const loading = ref(true)
const total = ref(0)
@ -182,14 +208,17 @@ onMounted(() => { @@ -182,14 +208,17 @@ onMounted(() => {
const router = useRouter()
function addInfo(row: any) {
router.push({ path: '/user/deviceInfo', query: { type: 'edit' } })
function editDevice(row: any, type: string) {
router.push({
path: '/device/deviceOperation',
query: { action: 'update', type, id: row.id },
})
}
function addDevice() {
router.push({ path: '/user/deviceInfo', query: { type: 'add' } })
router.push({ path: '/device/deviceOperation', query: { action: 'create' } })
}
function onView() {
router.push({ path: '/user/deviceInfo', query: { type: 'view' } })
function onView(row: any) {
router.push({ path: '/device/deviceOperation', query: { action: 'view', id: row.id } })
}
</script>

106
src/pages/deviceInfo/info.vue

@ -3,7 +3,6 @@ @@ -3,7 +3,6 @@
<el-collapse v-model="activeNames" @change="handleChange">
<template v-for="item in collapses">
<el-collapse-item :name="item.name" :icon="CaretRight">
<template #title>
<div class="title">
<div class="title-rect"></div>
@ -12,10 +11,26 @@ @@ -12,10 +11,26 @@
</div>
</div>
</template>
<BasicInfo v-show="item.name === 'info'" :type="type"/>
<TestInfo v-show="item.name === 'test'" />
<DeliveryInfo v-show="item.name === 'delivery'" />
<MaintainInfo v-show="item.name === 'maintain'" />
<BasicInfo
v-if="item.name === 'info'"
:info="deviceInfo"
:disabled="isDisabledInfo"
@onSave="loadDeviceInfo"
/>
<TestInfo
v-else-if="item.name === 'test'"
:info="deviceInfo"
:disabled="isDisabledTest"
@onSave="loadDeviceInfo"
/>
<DeliveryInfo
:info="deviceInfo"
:disabled="false"
:deviceId="deviceInfoId"
v-else-if="item.name === 'delivery'"
@onSave="loadDeviceInfo"
/>
<MaintainInfo :deviceId="deviceInfoId" v-else-if="item.name === 'maintain'" />
</el-collapse-item>
</template>
</el-collapse>
@ -28,29 +43,83 @@ import { CaretRight } from '@element-plus/icons-vue' @@ -28,29 +43,83 @@ import { CaretRight } from '@element-plus/icons-vue'
import BasicInfo from './components/BasicInfo.vue'
import TestInfo from './components/TestInfo.vue'
import DeliveryInfo from './components/DeliveryInfo.vue'
import { collapses, openNotMsg, type OptAction } from './utils'
import MaintainInfo from './components/MaintainInfo.vue'
import { useRoute } from 'vue-router'
const collapses = [
{ title: '基础信息', name: 'info' },
{ title: '测试信息', name: 'test' },
{ title: '出库信息', name: 'delivery' },
{ title: '维修信息', name: 'maintain' },
]
import { getDeviceInfo } from '@/api/module/eam/device'
import { isResError, useMessage } from '@/hooks/useMessage'
import { isEmpty } from '@/utils/is'
const route = useRoute()
const type = route.query.type as string
console.log(type)
const activeNames = ref(['info', 'test'])
const action = route.query.action as OptAction
const deviceInfoId = ref()
const message = useMessage()
const isDisabledInfo = computed(() => {
if (action === 'view') return true
if (action === 'update') return deviceInfo.value?.status > 1
return false
})
const isDisabledTest = computed(() => {
if (action === 'view') return true
if (action === 'update') return deviceInfo.value?.status >= 2
return false
})
const isDisabledDelivery = computed(() => {
if (action === 'view') return true
if (action === 'update') return deviceInfo.value?.status >= 3
return false
})
const deviceInfo = ref()
const activeNames = ref()
function handleChange(val: CollapseModelValue) {
if (action === 'view') return
const actives = val as CollapseActiveName[]
if (actives[actives.length - 1] === 'delivery') {
//
if (true) {
activeNames.value = activeNames.value.filter(item => item !== 'delivery')
const active = actives[actives.length - 1] as CollapseActiveName
if (active) {
const activeIdx = collapses.findIndex(item => item.name === active)
const status = deviceInfo.value?.status ?? 0
if (activeIdx > status) {
message.notifyWarning(`请先填写${collapses[activeIdx - 1].title}`)
activeNames.value = actives.filter(item => item !== active)
return
}
}
}
async function loadDeviceInfo(id: number) {
if (!isEmpty(id) && typeof id !== 'undefined') {
deviceInfoId.value = id
}
const res = await getDeviceInfo(deviceInfoId.value)
if (!isResError(res)) {
deviceInfo.value = res.data
}
}
onMounted(async () => {
switch (action) {
case 'create':
activeNames.value = [collapses[0].name]
break
case 'view':
activeNames.value = collapses.map(item => item.name)
break
case 'update':
activeNames.value = [type]
deviceInfoId.value = Number(route.query.id)
break
}
if (action !== 'create') {
loadDeviceInfo(Number(route.query.id))
}
})
</script>
<style scoped lang="scss">
@ -62,6 +131,7 @@ function handleChange(val: CollapseModelValue) { @@ -62,6 +131,7 @@ function handleChange(val: CollapseModelValue) {
background-color: var(--warp-bg);
}
:deep(.el-collapse-item__content) {
padding-top: 10px;
padding-left: 30px;
}
.title {

48
src/pages/deviceInfo/utils.ts

@ -0,0 +1,48 @@ @@ -0,0 +1,48 @@
export enum DeviceStatus {
,
,
,
,
}
export const deviceTableCol = [
{ label: '设备编号', prop: 'serialNo', minWidth: '16%' },
{ label: '设备名称', prop: 'name', minWidth: '10%' },
{ label: '设备类型', prop: 'categoryId', minWidth: '10%' },
{ label: '设备状态', prop: 'status', minWidth: '10%' },
{ label: '设备sn', prop: 'sn', minWidth: '10%' },
{ label: '创建时间', prop: 'createTime', minWidth: '10%' },
]
export type OptAction = 'create' | 'update' | 'view'
export const collapses = [
{ title: '基础信息', name: 'info' },
{ title: '测试信息', name: 'test' },
{ title: '出库信息', name: 'delivery' },
{ title: '维修信息', name: 'maintain' },
]
export const openNotMsg = (msg: string) => {
ElNotification({
title: 'Warning',
message: msg,
type: 'warning',
})
}
export const maintainStatus = [
{ label: '待维修', value: 0 },
{ label: '维修中', value: 1 },
{ label: '已维修', value: 2 },
]
export const testSheetStatus = [
{ label: '未测试', value: 0 },
{ label: '通过', value: 1 },
{ label: '未通过', value: 2 },
]
export const testTableCol = [
{ label: '测试内容', prop: 'content', minWidth: '10%' },
{ label: '预计结果', prop: 'expectResult', minWidth: '10%' },
{ label: '测试结果', prop: 'actualResult', minWidth: '20%' },
{ label: '备注', prop: 'description', minWidth: '16%' },
]

7
src/pages/deviceTest/testPlan/index.vue

@ -78,8 +78,8 @@ @@ -78,8 +78,8 @@
<template #default="scope">
<el-switch
v-model="scope.row.enabled"
:active-value="0"
:inactive-value="1"
:active-value="CommonStatusEnum.ENABLE"
:inactive-value="CommonStatusEnum.DISABLE"
@change="handleStatusChange(scope.row)"
/>
</template>
@ -136,6 +136,7 @@ import Icon from '@/components/dashboard/Icon/src/Icon.vue' @@ -136,6 +136,7 @@ import Icon from '@/components/dashboard/Icon/src/Icon.vue'
import {
deleteDeviceTemp,
getDeviceTempPage,
updateTempStatus,
type IDeviceTemp,
} from '@/api/module/eam/device/template'
import { getCategoryTree, type ICategoryTree } from '@/api/module/eam/device/category'
@ -239,7 +240,7 @@ const handleStatusChange = async (row: IDeviceTemp) => { @@ -239,7 +240,7 @@ const handleStatusChange = async (row: IDeviceTemp) => {
const text = row.enabled === CommonStatusEnum.ENABLE ? '启用' : '停用'
await message.confirm('确认要"' + text + '""' + row.name + '"模板吗?')
//
// await updateUserStatus(row.id, row.enabled)
await updateTempStatus(row.id as string, row.enabled)
await getList()
} catch {

215
src/pages/ota/firmware/components/create-firmware-dlg.vue

@ -0,0 +1,215 @@ @@ -0,0 +1,215 @@
<template>
<EdfsDialog
@on-close="onClone"
@on-save="onSave"
title="新建固件"
:is-show="isShow"
width="44%"
class="create-firmware-dlg"
>
<div class="dlg-body">
<el-row>
<FormItemInput label="固件名称" v-model="formData.name" require />
</el-row>
<el-row>
<FormItemInput label="固件版本" v-model="formData.version" require />
</el-row>
<el-row>
<FormItemSelect
label="固件类型"
v-model="formData.type"
require
:options="getIntDictOptions('device_entity_type')"
keyTag="value"
valueTag="value"
labelTag="label"
/>
</el-row>
<!-- 固件文件 -->
<el-row class="upload">
<span class="label"
><span data-v-fbd7b238="" class="require">*</span>固件文件:</span
>
<el-upload
v-model:fileList="fileList"
drag
action=""
:limit="1"
:on-exceed="handleExceed"
:auto-upload="false"
ref="upload"
class="upload-container"
>
<el-icon class="upload-icon">
<IEpUploadFilled />
</el-icon>
<div class="text">拖拽文件或者 <em>点击上传</em></div>
<template #tip v-if="!fileList.length">
<div class="el-upload__tip">上传限制一个文件新文件会覆盖旧文件</div>
</template>
</el-upload>
</el-row>
<el-row>
<FormItemInput label="固件描述" type="textarea" v-model="formData.description" />
</el-row>
</div>
</EdfsDialog>
</template>
<script setup lang="ts">
import { createFirmware } from '@/api/module/eam/device/firmware'
import { getTenant } from '@/api/module/system/tenant'
import EdfsDialog from '@/components/dashboard/Edfs-dialog.vue'
import FormItemInput from '@/components/dashboard/FormItemInput.vue'
import { isResError, useMessage } from '@/hooks/useMessage'
import FormItemSelect from '@/components/dashboard/FormItemSelect.vue'
import { getTenantId } from '@/utils/auth'
import { getIntDictOptions } from '@/utils/dict'
const message = useMessage()
import type {
UploadInstance,
UploadProps,
UploadRawFile,
UploadUserFile,
} from 'element-plus'
import { cloneDeep } from 'lodash'
interface Props {
isShow: boolean
// action: operantAction
}
// const title = computed(() =>
// props.action === 'create' ? '' : ''
// )
const emits = defineEmits(['on-save', 'on-close'])
const props = withDefaults(defineProps<Props>(), {
isShow: false,
})
const fileList = ref<UploadUserFile[]>([])
const data = {
name: '',
version: '',
description: '',
type: '',
}
const formData = ref(cloneDeep(data))
const upload = ref<UploadInstance>()
const handleExceed: UploadProps['onExceed'] = files => {
upload.value!.clearFiles()
const file = files[0] as UploadRawFile
upload.value!.handleStart(file)
}
function validate() {
if (!formData.value.name) {
message.error('请输入固件名称')
return true
}
if (!formData.value.version) {
message.error('请输入固件版本')
return true
}
if (!formData.value.type) {
message.error('请选择固件类型')
return true
}
if (!fileList.value.length) {
message.error('请上传固件文件')
return true
}
return false
}
async function onSave() {
if (validate()) return
const data = new FormData()
data.append('path', getTenantId())
data.append('file', fileList.value[0].raw as File)
data.append('name', formData.value.name)
data.append('version', formData.value.version)
data.append('type', formData.value.type)
data.append('description', formData.value.description)
const res = await createFirmware(data)
if (isResError(res)) return
emits('on-save')
clearData()
}
function clearData() {
formData.value = cloneDeep(data)
fileList.value = []
}
function onClone() {
clearData()
emits('on-close')
}
</script>
<style scoped lang="scss">
.create-firmware-dlg {
.dlg-body {
height: 470px;
}
.el-row {
height: 32px;
margin-bottom: 20px;
:deep(.label) {
margin-right: 10px;
}
span {
width: 110px;
height: 32px;
line-height: 32px;
text-align: right;
}
:deep(.el-radio-group) {
height: 100%;
}
:deep(.el-radio) {
height: 100%;
}
}
.require {
color: red;
margin-right: 4px;
}
.upload {
height: 156px;
:deep(.upload-container) {
width: calc(100% - 210px);
height: calc(100% - 20px);
}
:deep(.el-upload) {
height: 100%;
width: 100%;
}
:deep(.el-upload-dragger) {
height: 100%;
width: 100%;
padding-top: 30px;
box-sizing: border-box;
}
.upload-icon {
font-size: 54px;
color: #c0c4cc;
}
.text {
color: #6c6c6c;
font-size: 14px;
text-align: center;
}
em {
color: #409eff;
cursor: pointer;
}
}
}
</style>

183
src/pages/ota/firmware/index.vue

@ -0,0 +1,183 @@ @@ -0,0 +1,183 @@
<template>
<div class="firmware-management-wrap">
<EdfsWrap class="firmware-table" title="固件列表">
<template #title-right>
<div class="firmware-table-title">
<EdfsButton
type="success"
inner-text="新增固件"
:icon="createIcon"
@click="addFirmware"
/>
</div>
</template>
<edfs-table
:data="dataList"
:highlight-current-row="true"
ref="tableRef"
row-class-name="row"
:page-size="pageSize"
:page-total="pageTotal"
class="table"
:current-page="pageIndex"
@pageCurrentChange="handleJump"
>
<template v-for="(col, idx) in tableColumns" :key="idx">
<!-- <el-table-column
v-if="col.prop == 'status'"
:label="col.label"
:prop="col.prop"
:min-width="col.minWidth"
>
<template #default="scope">
<div style="display: flex; align-items: center; column-gap: 4px">
<img
:src="deviceStatusImgMap[scope.row.status]"
alt=""
width="16"
height="16"
/>
{{ getDictObj('device_entity_status', scope.row.status)?.label }}
</div>
</template>
</el-table-column> -->
<el-table-column
v-if="col.prop == 'createTime'"
:label="col.label"
:prop="col.prop"
:min-width="col.width"
>
<template #default="scope">
{{ dayjs(scope.row.createTime).format('YYYY-MM-DD HH:mm:ss') }}
</template>
</el-table-column>
<el-table-column
v-else
:label="col.label"
:prop="col.prop"
:min-width="col.width"
/>
</template>
<el-table-column label="操作" min-width="14%">
<template #default="scope">
<!-- <EdfsButton
type="success"
link
inner-text="下载固件"
@click="onDownload(scope.row)"
/> -->
<EdfsButton
type="danger"
link
inner-text="删除"
@click="onDelete(scope.row.id)"
/>
</template>
</el-table-column>
</edfs-table>
</EdfsWrap>
<createFirmwareDlg :is-show="isShowDlg" @on-close="onClose" @on-save="onSave" />
</div>
</template>
<script setup lang="ts">
import { tableColumns } from './utils'
import EdfsButton from '@/components/dashboard/Edfs-button/index.vue'
import EdfsTable from '@/components/dashboard/Edfs-table/index.vue'
import EdfsWrap from '@/components/dashboard/Edfs-wrap.vue'
import { useIcon } from '@/utils/useIcon'
import createFirmwareDlg from './components/create-firmware-dlg.vue'
import { deleteFirmware, getFirmwareList } from '@/api/module/eam/device/firmware'
import { isResError, useMessage } from '@/hooks/useMessage.js'
import dayjs from 'dayjs'
import download from '@/utils/download'
const message = useMessage()
const createIcon = useIcon({ icon: 'gravity-ui:plus' })
const tableRef = ref<any>(null)
const dataList = ref([])
const pageIndex = ref(1)
const pageSize = ref()
const pageTotal = ref(0)
const pageJump = ref(1)
async function loadData() {
if (!pageSize.value) {
pageSize.value = tableRef.value.getSize()
}
const res = await getFirmwareList({
pageNo: pageIndex.value,
pageSize: pageSize.value,
})
if (isResError(res)) return
const { list, total } = res.data
dataList.value = list
pageTotal.value = total
}
watch(pageIndex, () => {
pageJump.value = pageIndex.value
loadData()
})
function handleJump(page: number) {
pageIndex.value = page
}
async function onDelete(id: string) {
try {
await message.delConfirm()
const res = await deleteFirmware(id)
if (isResError(res)) return
message.success('删除成功')
loadData()
} catch (error) {}
}
function onDownload(row: any) {
// download.word(row.downloadPath, row.name)
// const downA = document.createElement('a')
// downA.href = row.downloadPath
// downA.download = row.name
// downA.click()
// downA.remove()
}
const isShowDlg = ref(false)
function addFirmware() {
isShowDlg.value = true
}
function onClose() {
isShowDlg.value = false
}
function onSave() {
isShowDlg.value = false
loadData()
}
onMounted(() => {
loadData()
})
</script>
<style scoped lang="scss">
.firmware-management-wrap {
width: 100%;
height: 100%;
.firmware-table {
height: 100%;
box-sizing: border-box;
background: #fff;
padding-bottom: 0;
.table {
height: 100%;
width: 100%;
}
}
}
</style>

32
src/pages/ota/firmware/utils.ts

@ -0,0 +1,32 @@ @@ -0,0 +1,32 @@
export const tableColumns = [
{
label: '固件名称',
prop: 'name',
width: '10%',
},
{
label: '固件版本',
prop: 'version',
width: '10%',
},
{
label: '下载路径',
prop: 'downloadPath',
width: '30%',
},
{
label: '创建时间',
prop: 'createTime',
width: '20%',
},
{
label: '固件状态',
prop: 'status',
width: '10%',
},
{
label: '描述',
prop: 'description',
width: '20%',
},
]

308
src/pages/ota/upgradeTask/components/create-task-dlg.vue

@ -0,0 +1,308 @@ @@ -0,0 +1,308 @@
<template>
<EdfsDialog
@on-close="onClone"
@on-save="onSave"
title="新建升级任务"
:is-show="isShow"
width="44%"
class="create-task-dlg"
>
<div class="dlg-body">
<el-row>
<FormItemInput label="任务名称" v-model="formData.name" require />
</el-row>
<el-row>
<span class="label">固件选择</span>
<el-cascader
:props="cascaderFirmware"
v-model="firmwareSelect"
ref="firmwareCascaderRef"
:show-all-levels="false"
placeholder="请选择"
/>
</el-row>
<el-row v-if="isShowDeviceSelect">
<!-- <span class="label">设备选择</span>
<el-cascader
style="flex: 1"
v-model="deviceSelect"
:props="cascaderDevice"
@change="devChange"
ref="deviceCascaderRef"
:show-all-levels="false"
popper-class="task-device-select-cascader"
placeholder="请选择"
collapse-tags
:max-collapse-tags="4"
collapse-tags-tooltip
@expand-change="changeSourceType"
/> -->
</el-row>
<el-row>
<span class="label">是否立即执行</span>
<el-switch
v-model="formData.executeNow"
style="--el-switch-on-color: #13ce66; --el-switch-off-color: #ff4949"
/>
</el-row>
<el-row v-if="!formData.executeNow">
<span class="label">任务执行时间</span>
<el-date-picker
v-model="formData.executeTime"
type="datetime"
value-format="YYYY-MM-DD HH:mm"
format="YYYY-MM-DD HH:mm"
placeholder="Pick a day"
:disabled-date="disabledExecuteDate"
/>
</el-row>
<el-row>
<EdfsNumberInput labelName="监控超时" v-model="formData.monitorTimeout" require />
</el-row>
<el-row>
<EdfsNumberInput labelName="重试次数" v-model="formData.retryCount" require />
</el-row>
<el-row>
<EdfsNumberInput labelName="重试间隔" v-model="formData.retryInterval" require />
</el-row>
</div>
</EdfsDialog>
</template>
<script setup lang="ts">
import EdfsDialog from '@/components/dashboard/Edfs-dialog.vue'
import FormItemInput from '@/components/dashboard/FormItemInput.vue'
import { isResError, useMessage } from '@/hooks/useMessage'
import EdfsNumberInput from '@/components/dashboard/Edfs-number-item-input.vue'
import FormItemSelect from '@/components/dashboard/FormItemSelect.vue'
import type { CascaderProps, CascaderValue, ElCascader } from 'element-plus'
// import { getSimpleStationList } from '@/api/module/app/station'
// import { getSimpleDeviceList } from '@/api/module/app/device'
import { getIntDictOptions } from '@/utils/dict'
import { getSimpleFirmwareList } from '@/api/module/eam/device/firmware'
import { isEmpty } from '@/utils/is'
import { clone, cloneDeep } from 'lodash'
import { createTask } from '@/api/module/eam/device/task'
const message = useMessage()
interface Props {
isShow: boolean
}
const emits = defineEmits(['on-save', 'on-close'])
const props = withDefaults(defineProps<Props>(), {
isShow: false,
})
const firmwareSelect = ref<CascaderValue>([])
const firmwareCascaderRef = ref<InstanceType<typeof ElCascader>>()
const cascaderFirmware: CascaderProps = {
lazy: true,
async lazyLoad(node, resolve) {
const { level } = node
if (level === 0) {
const deviceTypes = getIntDictOptions('device_entity_type').map(item => ({
value: item.value,
label: item.label,
isLeaf: false,
}))
resolve(deviceTypes)
}
if (level === 1) {
const res = await getSimpleFirmwareList({
type: node.data?.value as number,
})
const data = Array.isArray(res?.data) ? res.data : []
const firmware = data.map((item: any) => ({
value: item.id,
label: item.name,
isLeaf: true,
leaf: true,
}))
resolve(firmware)
}
},
}
const selectFirmwareType = ref()
watch(firmwareSelect, (value: any) => {
selectFirmwareType.value = value[0] ?? ''
formData.value.firmwareId = String(value[1]) ?? ''
})
const isShowDeviceSelect = ref(false)
watch(
() => selectFirmwareType.value,
() => {
isShowDeviceSelect.value = false
nextTick(() => {
isShowDeviceSelect.value =
!isEmpty(selectFirmwareType.value) &&
typeof selectFirmwareType.value !== 'undefined'
})
}
)
// const deviceCascaderRef = ref<InstanceType<typeof ElCascader>>()
// const cascaderDevice: CascaderProps = {
// lazy: true,
// multiple: true,
// async lazyLoad(node, resolve) {
// const { level } = node
// if (level === 0) {
// const res = await getSimpleStationList()
// if (isResError(res)) return
// const data = Array.isArray(res?.data) ? res.data : []
// const nodes = data.map((item: any) => ({
// value: item.id,
// label: item.name,
// isLeaf: false,
// }))
// resolve(nodes)
// }
// if (level === 1) {
// const res = await getSimpleDeviceList({
// siteId: node.data?.value as number,
// type: selectFirmwareType.value,
// })
// const data = Array.isArray(res?.data) ? res.data : []
// const devices = data.map((item: any) => ({
// value: item.sn,
// label: item.name,
// isLeaf: true,
// leaf: true,
// }))
// resolve(devices)
// }
// },
// }
const deviceSelect = ref<CascaderValue>([])
watch(deviceSelect, (value: any) => {
formData.value.deviceSns = value.map((item: any) => item[1])
})
function devChange() {}
const cascaderTag = ref<any[]>([])
function changeSourceType(selectItem: any) {
if (!cascaderTag.value.includes(selectItem[0])) {
deviceSelect.value = []
}
cascaderTag.value = selectItem
}
const form = {
name: '',
executeNow: false, //
executeTime: '', //
retryCount: 3, //
retryInterval: 1000, //
monitorTimeout: 1000, //
firmwareId: '', // ID
deviceSns: [], // SN
}
const formData = ref(cloneDeep(form))
function validate() {
if (!formData.value.name) {
message.error('请输入任务名称')
return true
}
if (!formData.value.firmwareId) {
message.error('请选择固件')
return true
}
if (!formData.value.deviceSns.length) {
message.error('请选择设备')
return true
}
if (!formData.value.executeNow && !formData.value.executeTime) {
message.error('请选择执行时间')
return true
}
if (!formData.value.monitorTimeout) {
message.error('请输入监控超时')
return true
}
if (!formData.value.retryCount) {
message.error('请输入重试次数')
return true
}
if (!formData.value.retryInterval) {
message.error('请输入重试间隔')
return true
}
return false
}
function disabledExecuteDate(time: Date) {
return time.getTime() < Date.now() - 8.64e7
}
async function onSave() {
if (validate()) return
const res = await createTask(formData.value)
if (isResError(res)) return
emits('on-save')
clearData()
}
function onClone() {
clearData()
emits('on-close')
}
function clearData() {
formData.value = cloneDeep(form)
firmwareSelect.value = []
cascaderTag.value = []
deviceSelect.value = []
}
</script>
<style lang="scss">
.task-device-select-cascader {
.el-cascader-panel {
.el-cascader-menu:first-child {
.el-cascader-node {
.el-checkbox {
display: none !important;
}
}
}
}
}
</style>
<style scoped lang="scss">
.create-task-dlg {
.dlg-body {
max-height: 400px;
}
.el-row {
height: 32px;
width: 74%;
margin-bottom: 20px;
:deep(.label) {
margin-right: 10px;
}
span {
width: 110px;
height: 32px;
line-height: 32px;
text-align: right;
}
:deep(.el-radio-group) {
height: 100%;
}
:deep(.el-radio) {
height: 100%;
}
}
.require {
color: red;
margin-right: 4px;
}
}
</style>

244
src/pages/ota/upgradeTask/components/details-drawer.vue

@ -0,0 +1,244 @@ @@ -0,0 +1,244 @@
<template>
<div class="details-drawer">
<el-drawer
v-model="isShow"
:title="`${props.rowData.taskName}任务详情`"
direction="rtl"
size="64%"
>
<main class="drawer-box">
<el-scrollbar>
<div class="device-status">
<template v-for="(item, idx) in deviceDetails" :key="idx">
<div class="device-info">
<div class="title">站点名称{{ item.siteName }}</div>
<div class="body">
<div class="info">
<div class="item">
<span class="label">设备:</span><span>{{ item.deviceName }}</span>
</div>
<div class="item">
<span class="label">状态:</span
><span>{{ TaskStatus[item.node] ?? '未开始' }}</span>
</div>
<div class="item" v-if="item.node === 1">
<span class="label">原因:</span><span>{{ item.reason }}</span>
</div>
</div>
<div class="progress" v-if="!isEmpty(item.node)">
<el-progress
type="circle"
v-if="item.node === 0"
:percentage="100"
:stroke-width="10"
color="#16a34a"
>
<div class="progress-content" style="color: #16a34a">
<div>
<Icon icon="mingcute:check-fill" :size="20" />
</div>
<span class="percentage-label">升级成功</span>
</div>
</el-progress>
<el-progress
type="circle"
v-if="item.node === 1"
:percentage="100"
:stroke-width="10"
color="#ef4444"
>
<div class="progress-content" style="color: #ef4444">
<div>
<Icon icon="mingcute:close-fill" :size="20" />
</div>
<span class="percentage-label">升级失败</span>
</div>
</el-progress>
<el-progress
type="circle"
v-if="item.node === 2"
:percentage="item.subNode"
:stroke-width="10"
color="#3b82f6"
/>
<el-progress
type="circle"
v-if="item.node === 3"
:percentage="item.subNode"
:stroke-width="10"
color="#f97316"
:indeterminate="true"
/>
<el-progress
type="circle"
v-if="item.node === 4"
:percentage="item.subNode"
:stroke-width="10"
color="#10b981"
:duration="6"
/>
</div>
</div>
</div>
</template>
</div>
</el-scrollbar>
</main>
</el-drawer>
</div>
</template>
<script setup lang="ts">
import { isResError } from '@/hooks/useMessage'
import { getTaskDetails } from '@/api/module/eam/device/task'
import { isEmpty } from '@/utils/is'
import Icon from '@/components/dashboard/Icon/src/Icon.vue'
enum TaskStatus {
'升级成功',
'升级失败',
'接收中...',
'校验中...',
'升级中...',
}
interface Props {
rowData: any
taskMap: any
}
const props = withDefaults(defineProps<Props>(), {
taskMap: new Map(),
})
const isShow = defineModel<boolean>()
const deviceDetails = ref<any>([])
async function loadData() {
const res = await getTaskDetails(props.rowData.id)
if (!isResError(res)) {
deviceDetails.value = Array.isArray(res.data) ? res.data : []
}
}
function updateData(data: any[]) {
console.log('object', data)
for (const d of data) {
const find = deviceDetails.value.find((item: any) => item.deviceSn == d.deviceSn)
if (find) {
find.node = d.node
find.subNode = d.subNode
find.reason = d.reason
}
}
}
onMounted(async () => {
await loadData()
const taskId = props.rowData.id
let data = props.taskMap.get(taskId)
data = data ? Array.from(data.values()) : []
updateData(data)
})
defineExpose({
updateData,
})
</script>
<style lang="scss" scoped>
.details-drawer {
font-size: 16px;
:deep(.el-overlay) {
// background-color: transparent !important;
}
// :deep(.el-drawer) {
// width: calc(100% - 280px) !important;
// width: 70%;
// }
:deep(.el-drawer__header) {
margin-bottom: 10px;
padding: 10px;
border-bottom: 1px solid #ccc;
}
:deep(.el-drawer__body) {
padding: 20px;
padding-top: 0;
}
:deep(.el-drawer__title) {
font-family: Alibaba-PuHuiTi-M;
font-size: 16px;
color: #4d4d4d;
line-height: 24px;
font-weight: 500;
}
.drawer-box {
width: 100%;
height: 100%;
.device-status {
width: 100%;
height: 100%;
display: flex;
flex-wrap: wrap;
column-gap: 20px;
row-gap: 24px;
.device-info {
width: 280px;
height: 192px;
background: #ffffff;
border: 1px solid #e9e9e9;
box-shadow: 0px 2px 8px 0px rgba(0, 0, 0, 0.09);
border-radius: 4px;
display: flex;
flex-direction: column;
box-sizing: border-box;
.title {
display: flex;
align-items: center;
height: 48px;
width: 100%;
background-color: #f1f1f1;
box-sizing: border-box;
font-family: Alibaba-PuHuiTi-M;
font-size: 16px;
color: #030303;
font-weight: 500;
justify-content: space-between;
padding: 0 12px;
}
.body {
padding: 10px;
display: flex;
column-gap: 14px;
flex: 1;
.info {
width: 130px;
}
.progress {
flex: 1;
.progress-content {
display: flex;
flex-direction: column;
align-items: center;
row-gap: 8px;
}
}
.item {
display: flex;
min-height: 32px;
.label {
color: #6a6a6a;
font-size: 14px;
flex: 0.5;
}
span {
word-wrap: break-word;
word-break: break-all;
white-space: normal;
font-size: 14px;
flex: 1;
color: #3c3c3c;
}
}
}
}
}
}
}
</style>

372
src/pages/ota/upgradeTask/index.vue

@ -0,0 +1,372 @@ @@ -0,0 +1,372 @@
<template>
<div class="task-management-wrap">
<EdfsWrap class="task-table" title="任务列表">
<template #title-right>
<div class="task-table-title">
<EdfsButton
type="success"
inner-text="创建升级任务"
:icon="createIcon"
@click="addTask"
/>
</div>
</template>
<edfs-table
:data="dataList"
:highlight-current-row="true"
ref="tableRef"
row-class-name="row"
:page-size="pageSize"
:page-total="pageTotal"
class="table"
:current-page="pageIndex"
@pageCurrentChange="handleJump"
>
<template v-for="(col, idx) in tableColumns" :key="idx">
<el-table-column
v-if="col.prop === 'createTime'"
:label="col.label"
:prop="col.prop"
:min-width="col.width"
>
<template #default="scope">
{{ dayjs(scope.row.createTime).format('YYYY-MM-DD HH:mm:ss') }}
</template>
</el-table-column>
<el-table-column
v-else-if="col.prop === 'status'"
:label="col.label"
:prop="col.prop"
:min-width="col.width"
>
<template #default="scope">
{{ taskStatusMap[scope.row.status] }}
</template>
</el-table-column>
<el-table-column
v-else-if="col.prop === 'executeTime'"
:label="col.label"
:prop="col.prop"
:min-width="col.width"
>
<template #default="scope">
{{ dayjs(scope.row.executeTime).format('YYYY-MM-DD HH:mm:ss') }}
</template>
</el-table-column>
<el-table-column
v-else-if="col.prop === 'deviceUpgradeDetail'"
:label="col.label"
:prop="col.prop"
:min-width="col.width"
>
<template #default="scope">
<div>
<div>升级中{{ scope.row.processingCount }}</div>
<div>失败数量: {{ scope.row.failCount }}</div>
<div>升级完成: {{ scope.row.successCount }}</div>
</div>
</template>
</el-table-column>
<el-table-column
v-else
:label="col.label"
:prop="col.prop"
:min-width="col.width"
/>
</template>
<el-table-column label="操作" min-width="8%">
<template #default="scope">
<EdfsButton
type="success"
link
v-if="scope.row.status === 1 || scope.row.status === 2"
inner-text="详情"
@click="onDetails(scope.row)"
/>
</template>
</el-table-column>
</edfs-table>
</EdfsWrap>
<createTaskDlg :isShow="isShowDlg" @on-close="onClose" @on-save="onSave" />
<detailsDrawer
ref="detailsRef"
:rowData="rowData"
:taskMap="taskMap"
v-model="isShowDetails"
v-if="isShowDetails"
/>
</div>
</template>
<script setup lang="ts">
import {
addListener,
removeListener,
type SocketMsgListener,
} from '@/tools/event/socketEvent'
import { tableColumns, taskStatusMap } from './utils'
import EdfsButton from '@/components/dashboard/Edfs-button/index.vue'
import EdfsTable from '@/components/dashboard/Edfs-table/index.vue'
import EdfsWrap from '@/components/dashboard/Edfs-wrap.vue'
import { useIcon } from '@/utils/useIcon'
import createTaskDlg from './components/create-task-dlg.vue'
import detailsDrawer from './components/details-drawer.vue'
import { isResError, useMessage } from '@/hooks/useMessage.js'
import dayjs from 'dayjs'
import { getTaskList } from '@/api/module/eam/device/task'
const message = useMessage()
const createIcon = useIcon({ icon: 'gravity-ui:plus' })
const tableRef = ref<any>(null)
const dataList = ref<any>([])
const pageIndex = ref(1)
const pageSize = ref()
const pageTotal = ref(0)
const pageJump = ref(1)
type DeviceMap = Map<number, any>
type TaskMap = Map<number, DeviceMap>
const taskMap: TaskMap = new Map<number, DeviceMap>()
async function loadData() {
if (!pageSize.value) {
pageSize.value = tableRef.value.getSize()
}
const res = await getTaskList({
pageNo: pageIndex.value,
pageSize: pageSize.value,
})
if (isResError(res)) return
const { list, total } = res.data
dataList.value = list
pageTotal.value = total
updateTaskData()
}
watch(pageIndex, () => {
pageJump.value = pageIndex.value
loadData()
})
function handleJump(page: number) {
pageIndex.value = page
}
const isShowDlg = ref(false)
function addTask() {
isShowDlg.value = true
}
function onClose() {
isShowDlg.value = false
}
function onSave() {
isShowDlg.value = false
loadData()
}
const rowData = ref<any>({})
const isShowDetails = ref(false)
async function onDetails(row: any) {
rowData.value = row
isShowDetails.value = true
}
onMounted(async () => {
await loadData()
addListener(socketMsgListener)
})
onBeforeUnmount(() => {
removeListener(socketMsgListener)
})
const detailsRef = ref<any>(null)
const tasksStatusMap = new Map()
const socketMsgListener: SocketMsgListener = {
onCmdPost: function (type: string, stationId: number, data?: any, time?: string) {
if (type === 'upgrade') {
const { deviceSn, taskId } = data
if (!taskId || !deviceSn) return
let deviceMap = taskMap.get(taskId)
if (!deviceMap) {
deviceMap = new Map<number, any>()
taskMap.set(taskId, deviceMap)
}
deviceMap.set(deviceSn, data)
if (detailsRef.value && rowData.value?.id) {
const taskId = rowData.value?.id
let taskData = taskMap.get(taskId)
const dataArray = taskData ? Array.from(taskData.values()) : []
detailsRef.value.updateData(dataArray)
}
}
if (type === 'upgrade-task' && data.id) {
tasksStatusMap.set(data.id, data)
updateTaskData()
}
},
}
function updateTaskData() {
for (const data of dataList.value) {
const taskData = tasksStatusMap.get(data.id)
if (!taskData) continue
data.status = taskData.status
data.processingCount = taskData.processingCount
data.failCount = taskData.failCount
data.successCount = taskData.successCount
}
}
// let a = [
// {
// type: 'upgrade',
// stationId: 1,
// time: 1726129158000,
// data: {
// id: 1,
// taskId: 32,
// deviceId: 1,
// node: 0,
// subNode: 10,
// reason: null,
// deviceSn: '4',
// tenantId: 162,
// createTime: 1725533993000,
// updateTime: 1725533993000,
// creator: '145',
// updater: '145',
// deleted: false,
// },
// },
// {
// type: 'upgrade',
// stationId: 1,
// time: 1726129158000,
// data: {
// id: 1,
// taskId: 32,
// deviceId: 1,
// node: 1,
// subNode: 10,
// reason: 'null',
// deviceSn: '5',
// tenantId: 162,
// createTime: 1725533993000,
// updateTime: 1725533993000,
// creator: '145',
// updater: '145',
// deleted: false,
// },
// },
// {
// type: 'upgrade',
// stationId: 1,
// time: 1726129158000,
// data: {
// id: 1,
// taskId: 32,
// deviceId: 1,
// node: 2,
// subNode: 10,
// reason: 'null',
// deviceSn: '6',
// tenantId: 162,
// createTime: 1725533993000,
// updateTime: 1725533993000,
// creator: '145',
// updater: '145',
// deleted: false,
// },
// },
// {
// type: 'upgrade',
// stationId: 1,
// time: 1726129158000,
// data: {
// id: 1,
// taskId: 1,
// deviceId: 1,
// node: 3,
// subNode: 10,
// reason: null,
// deviceSn: '6',
// tenantId: 162,
// createTime: 1725533993000,
// updateTime: 1725533993000,
// creator: '145',
// updater: '145',
// deleted: false,
// },
// },
// {
// type: 'upgrade',
// stationId: 1,
// time: 1726129158000,
// data: {
// id: 1,
// taskId: 1,
// deviceId: 1,
// node: 1,
// subNode: 10,
// reason: null,
// deviceSn: '6',
// tenantId: 162,
// createTime: 1725533993000,
// updateTime: 1725533993000,
// creator: '145',
// updater: '145',
// deleted: false,
// },
// },
// ]
// function test(type: string, stationId: number, data?: any, time?: number) {
// if (type === 'upgrade') {
// const { deviceSn, taskId } = data
// if (!taskId || !deviceSn) return
// //
// let deviceMap = taskMap.get(taskId)
// if (!deviceMap) {
// deviceMap = new Map<number, any>()
// taskMap.set(taskId, deviceMap)
// }
// deviceMap.set(deviceSn, data)
// if (detailsRef.value && rowData.value?.id) {
// const taskId = rowData.value?.id
// let taskData = taskMap.get(taskId)
// const dataArray = taskData ? Array.from(taskData.values()) : []
// detailsRef.value.updateData(dataArray)
// }
// }
// }
// setInterval(() => {
// for (const item of a) {
// test(item.type, item.stationId, item.data, item.time)
// }
// }, 1000)
</script>
<style scoped lang="scss">
.task-management-wrap {
width: 100%;
height: 100%;
.task-table {
height: 100%;
box-sizing: border-box;
background: #fff;
padding-bottom: 0;
.table {
height: 100%;
width: 100%;
}
}
}
</style>

44
src/pages/ota/upgradeTask/utils.ts

@ -0,0 +1,44 @@ @@ -0,0 +1,44 @@
export const tableColumns = [
{
label: '任务名称',
prop: 'taskName',
width: '10%',
},
{
label: '固件名称',
prop: 'name',
width: '8%',
},
{
label: '固件版本',
prop: 'version',
width: '6%',
},
{
label: '创建时间',
prop: 'createTime',
width: '18%',
},
{
label: '任务执行时间',
prop: 'executeTime',
width: '18%',
},
{
label: '升级详情',
prop: 'deviceUpgradeDetail',
width: '10%',
},
{
label: '任务状态',
prop: 'status',
width: '8%',
},
]
export const taskStatusMap = {
0: '未开始',
1: '进行中',
2: '已完成',
}

87
src/pages/system/deviceField/components/Dispose.vue

@ -0,0 +1,87 @@ @@ -0,0 +1,87 @@
<template>
<div class="device-dispose-warp">
<div class="top-filter">
<FormItemSelect
:options="[]"
placeholder="请选择"
keyTag="value"
labelTag="label"
:isShowLabel="false"
class="filter-select"
/>
<el-input class="filter-input" placeholder="请输入" />
</div>
<div class="content-box">
<div class="item-info">
<div class="info">
<div class="type">
<div class="reason">处置原因</div>
<div class="name">报废</div>
</div>
<div class="describe">
<div>添加人: 向向</div>
<div>添加时间: 2022-10-31 18:00:00</div>
</div>
</div>
<div class="operation">
<EdfsButton link type="primary" :icon="editIcon" />
<EdfsButton link type="danger" :icon="delIcon" style="margin-left: 0px" />
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import FormItemSelect from '@/components/dashboard/FormItemSelect.vue'
import { useIcon } from '@/utils/useIcon'
const editIcon = useIcon({ icon: 'fluent:edit-16-regular' })
const delIcon = useIcon({ icon: 'fluent:delete-16-regular' })
</script>
<style scoped lang="scss">
.device-dispose-warp {
.top-filter {
display: flex;
height: 32px;
.filter-select {
flex: 0.7;
margin-right: 4px;
}
.filter-input {
flex: 1;
}
}
.content-box {
width: 100%;
height: calc(100% - 32px);
.item-info {
border-bottom: 1px solid var(--el-border-color);
padding: 10px 0;
display: flex;
.info {
.type {
display: flex;
column-gap: 6px;
}
.describe {
display: flex;
column-gap: 6px;
color: var(--text-desc);
}
border-left: 2px solid #048b87;
padding-left: 6px;
}
.operation {
flex: 1;
display: flex;
justify-content: end;
:deep(.el-button) {
margin-top: auto;
}
}
}
}
}
</style>

15
src/pages/system/deviceField/index.vue

@ -5,7 +5,7 @@ @@ -5,7 +5,7 @@
<EdfsButton
type="success"
:icon="plusIcon"
inner-text="新"
inner-text="新"
@click="addCategory"
/>
</template>
@ -15,7 +15,16 @@ @@ -15,7 +15,16 @@
@on-delete="onDeleteCategory"
/>
</EdfsWrap>
<EdfsWrap title="设备处置/报废/故障 常用原因管理" class="cause-box"></EdfsWrap>
<EdfsWrap title="设备处置/报废/故障 常用原因管理" class="cause-box">
<template #title-right>
<EdfsButton
type="success"
:icon="plusIcon"
inner-text="新增"
/>
</template>
<Dispose />
</EdfsWrap>
<EdfsWrap title="设备信息字段管理" class="info-box"></EdfsWrap>
</div>
<CategoryDlg
@ -40,6 +49,7 @@ import EdfsButton from '@/components/dashboard/Edfs-button/index.vue' @@ -40,6 +49,7 @@ import EdfsButton from '@/components/dashboard/Edfs-button/index.vue'
import CategoryTree from './components/CategoryTree.vue'
import { useIcon } from '@/utils/useIcon'
import CategoryDlg from './components/CategoryDlg.vue'
import Dispose from './components/Dispose.vue'
const message = useMessage()
const plusIcon = useIcon({ icon: 'gravity-ui:plus' })
@ -100,6 +110,7 @@ onMounted(() => { @@ -100,6 +110,7 @@ onMounted(() => {
}
.cause-box {
flex: 0.6;
min-width: 400px;
}
.info-box {
flex: 1;

2
src/pages/system/login/index.vue

@ -128,7 +128,7 @@ async function onLogin() { @@ -128,7 +128,7 @@ async function onLogin() {
}
isLoading.value = false
router.push('/user/device')
router.push('/device/data')
}
async function loginByAccount() {

13
src/pages/system/manufacturer/index.vue

@ -79,8 +79,8 @@ @@ -79,8 +79,8 @@
<template #default="scope">
<el-switch
v-model="scope.row.enabled"
:active-value="0"
:inactive-value="1"
:active-value="CommonStatusEnum.ENABLE"
:inactive-value="CommonStatusEnum.DISABLE"
@change="handleStatusChange(scope.row)"
/>
</template>
@ -231,19 +231,20 @@ const handleDelete = async (id: number) => { @@ -231,19 +231,20 @@ const handleDelete = async (id: number) => {
const handleStatusChange = async (row: CustomerVO) => {
try {
//
const text = row.status === CommonStatusEnum.ENABLE ? '启用' : '停用'
const text = row.enabled === CommonStatusEnum.ENABLE ? '启用' : '停用'
await message.confirm('确认要"' + text + '""' + row.name + '"厂商吗?')
//
await updateCustomerStatus({
id: row.id,
status: row.status,
enabled: row.enabled,
})
await getList()
} catch {
//
row.status =
row.status === CommonStatusEnum.ENABLE
row.enabled =
row.enabled === CommonStatusEnum.ENABLE
? CommonStatusEnum.DISABLE
: CommonStatusEnum.ENABLE
}

4
src/pages/system/user/index.vue

@ -119,8 +119,8 @@ @@ -119,8 +119,8 @@
<template #default="scope">
<el-switch
v-model="scope.row.status"
:active-value="0"
:inactive-value="1"
:active-value="CommonStatusEnum.ENABLE"
:inactive-value="CommonStatusEnum.DISABLE"
@change="handleStatusChange(scope.row)"
/>
</template>

139
src/router/index.ts

@ -47,66 +47,83 @@ export const defaultRoute = [ @@ -47,66 +47,83 @@ export const defaultRoute = [
},
],
},
{
path: '/user',
component: () => import('@/pages/layout.vue'),
name: 'UserInfo',
meta: {
canTo: true,
hidden: false,
noTagsView: false,
icon: 'ep:user',
title: '系统管理',
},
children: [
{
path: 'deviceField',
component: () => import('@/pages/system/deviceField/index.vue'),
name: 'DeviceField',
meta: {
icon: 'ep:user',
title: '字段管理',
},
},
{
path: 'device',
component: () => import('@/pages/deviceInfo/index.vue'),
name: 'Device',
meta: {
icon: 'ep:user',
title: '设备管理',
},
},
{
path: 'deviceInfo',
component: () => import('@/pages/deviceInfo/info.vue'),
name: 'DeviceInfo',
meta: {
icon: 'ep:user',
hidden: true,
title: '设备xx',
},
},
{
path: 'manufacturer',
component: () => import('@/pages/system/manufacturer/index.vue'),
name: 'Manufacturer',
meta: {
icon: 'ep:user',
title: '厂商管理',
},
},
{
path: 'deviceTemplate',
component: () => import('@/pages/deviceTest/testPlan/index.vue'),
name: 'DeviceTemplate',
meta: {
icon: 'ep:user',
title: '模板管理',
},
}
],
},
// {
// path: '/device',
// component: () => import('@/pages/layout.vue'),
// name: 'UserInfo',
// meta: {
// canTo: true,
// hidden: true,
// noTagsView: false,
// icon: 'ep:user',
// title: '系统管理',
// },
// children: [
// {
// path: 'deviceOperation',
// component: () => import('@/pages/deviceInfo/info.vue'),
// name: 'deviceOperation',
// meta: {
// icon: 'ep:user',
// hidden: true,
// title: '设备xx',
// },
// },
// ],
// },
// {
// path: '/user',
// component: () => import('@/pages/layout.vue'),
// name: 'UserInfo',
// meta: {
// canTo: true,
// hidden: false,
// noTagsView: false,
// icon: 'ep:user',
// title: '系统管理',
// },
// children: [
// {
// path: 'deviceField',
// component: () => import('@/pages/system/deviceField/index.vue'),
// name: 'DeviceField',
// meta: {
// icon: 'ep:user',
// title: '字段管理',
// },
// },
// {
// path: 'device',
// component: () => import('@/pages/deviceInfo/index.vue'),
// name: 'Device',
// meta: {
// icon: 'ep:user',
// title: '设备管理',
// },
// },
// },
// {
// path: 'manufacturer',
// component: () => import('@/pages/system/manufacturer/index.vue'),
// name: 'Manufacturer',
// meta: {
// icon: 'ep:user',
// title: '厂商管理',
// },
// },
// {
// path: 'deviceTemplate',
// component: () => import('@/pages/deviceTest/testPlan/index.vue'),
// name: 'DeviceTemplate',
// meta: {
// icon: 'ep:user',
// title: '模板管理',
// },
// }
// ],
// },
]
const router = createRouter({
@ -127,7 +144,7 @@ router.beforeEach(async (to, from, next) => { @@ -127,7 +144,7 @@ router.beforeEach(async (to, from, next) => {
}
if (to.path === '/login' || to.path === '/') {
next('/user/device')
next('/device/data')
return
}
const userStore = useUserStore()

Loading…
Cancel
Save