pqdif
This commit is contained in:
@@ -19,6 +19,7 @@ export namespace DevType {
|
|||||||
id: string; //设备类型ID
|
id: string; //设备类型ID
|
||||||
name: string;//设备类型名称
|
name: string;//设备类型名称
|
||||||
icd: string| null;//设备关联的ICD
|
icd: string| null;//设备关联的ICD
|
||||||
|
pqdif: string| null;//设备关联的PQDIF
|
||||||
power: string| null;//工作电源
|
power: string| null;//工作电源
|
||||||
devVolt: number; //额定电压(V)
|
devVolt: number; //额定电压(V)
|
||||||
devCurr: number; //额定电流(A)
|
devCurr: number; //额定电流(A)
|
||||||
|
|||||||
26
frontend/src/api/device/interface/pqdif.ts
Normal file
26
frontend/src/api/device/interface/pqdif.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import type { ReqPage, ResPage } from '@/api/interface'
|
||||||
|
|
||||||
|
export namespace PQDIF {
|
||||||
|
export interface ReqPQDIFParams extends ReqPage {
|
||||||
|
name?: string
|
||||||
|
result?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ResPQDIF {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
filePath?: string | null
|
||||||
|
recordCount?: number | null
|
||||||
|
observationCount?: number | null
|
||||||
|
sampleValueCount?: number | null
|
||||||
|
result?: number | null
|
||||||
|
msg?: string | null
|
||||||
|
state: number
|
||||||
|
createBy?: string | null
|
||||||
|
createTime?: string | null
|
||||||
|
updateBy?: string | null
|
||||||
|
updateTime?: string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ResPQDIFPage extends ResPage<ResPQDIF> {}
|
||||||
|
}
|
||||||
14
frontend/src/api/device/pqdif/index.ts
Normal file
14
frontend/src/api/device/pqdif/index.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import type { PQDIF } from '@/api/device/interface/pqdif'
|
||||||
|
import http from '@/api'
|
||||||
|
|
||||||
|
export const getPQDIFList = (params: PQDIF.ReqPQDIFParams) => {
|
||||||
|
return http.post<PQDIF.ResPQDIFPage>('/pqdif/list', params)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getPQDIFAllList = () => {
|
||||||
|
return http.get<PQDIF.ResPQDIF[]>('/pqdif/listAll')
|
||||||
|
}
|
||||||
|
|
||||||
|
export const importPQDIFJson = (params: FormData) => {
|
||||||
|
return http.upload('/pqdif/import/json', params, { loading: false })
|
||||||
|
}
|
||||||
171
frontend/src/components/JsonImportDialog/index.vue
Normal file
171
frontend/src/components/JsonImportDialog/index.vue
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
v-model="dialogVisible"
|
||||||
|
:title="title"
|
||||||
|
width="460px"
|
||||||
|
destroy-on-close
|
||||||
|
:close-on-click-modal="!loading"
|
||||||
|
:show-close="!loading"
|
||||||
|
>
|
||||||
|
<div v-loading="loading" :element-loading-text="loadingText" class="json-import-content">
|
||||||
|
<el-upload
|
||||||
|
ref="uploadRef"
|
||||||
|
action="#"
|
||||||
|
class="upload"
|
||||||
|
:auto-upload="false"
|
||||||
|
:limit="1"
|
||||||
|
accept=".json,application/json"
|
||||||
|
:on-exceed="handleExceed"
|
||||||
|
:on-change="handleChange"
|
||||||
|
:on-remove="handleRemove"
|
||||||
|
:disabled="loading"
|
||||||
|
>
|
||||||
|
<el-button type="primary" :icon="Upload" :disabled="loading">选择文件</el-button>
|
||||||
|
<template #tip>
|
||||||
|
<div class="el-upload__tip">{{ tip }}</div>
|
||||||
|
</template>
|
||||||
|
</el-upload>
|
||||||
|
<div v-if="loading" class="json-import-loading-text">{{ loadingText }}</div>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button :disabled="loading" @click="close">取消</el-button>
|
||||||
|
<el-button type="primary" :loading="loading" @click="submit">开始导入</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { Upload } from '@element-plus/icons-vue'
|
||||||
|
import {
|
||||||
|
ElMessage,
|
||||||
|
ElNotification,
|
||||||
|
genFileId,
|
||||||
|
type UploadFile,
|
||||||
|
type UploadInstance,
|
||||||
|
type UploadProps,
|
||||||
|
type UploadRawFile
|
||||||
|
} from 'element-plus'
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
title: string
|
||||||
|
requestApi: (params: FormData) => Promise<unknown>
|
||||||
|
tip?: string
|
||||||
|
loadingText?: string
|
||||||
|
successMessage?: string
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
tip: '请上传 .json 文件',
|
||||||
|
loadingText: '导入中,请稍候...',
|
||||||
|
successMessage: '导入成功'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'success'): void
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const dialogVisible = ref(false)
|
||||||
|
const loading = ref(false)
|
||||||
|
const uploadRef = ref<UploadInstance | null>(null)
|
||||||
|
const selectedFile = ref<File | null>(null)
|
||||||
|
|
||||||
|
const open = () => {
|
||||||
|
dialogVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const close = () => {
|
||||||
|
if (loading.value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dialogVisible.value = false
|
||||||
|
selectedFile.value = null
|
||||||
|
uploadRef.value?.clearFiles()
|
||||||
|
}
|
||||||
|
|
||||||
|
const isJsonFile = (file: UploadRawFile | File) => {
|
||||||
|
return file.name.toLowerCase().endsWith('.json') || file.type === 'application/json'
|
||||||
|
}
|
||||||
|
|
||||||
|
const notifyInvalidFile = () => {
|
||||||
|
ElNotification({
|
||||||
|
title: '温馨提示',
|
||||||
|
message: '上传文件只能是 json 格式!',
|
||||||
|
type: 'warning'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const setSelectedFile = (file: UploadRawFile | File | undefined) => {
|
||||||
|
if (!file) {
|
||||||
|
selectedFile.value = null
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!isJsonFile(file)) {
|
||||||
|
notifyInvalidFile()
|
||||||
|
selectedFile.value = null
|
||||||
|
uploadRef.value?.clearFiles()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
selectedFile.value = file
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleExceed: UploadProps['onExceed'] = files => {
|
||||||
|
uploadRef.value?.clearFiles()
|
||||||
|
const file = files[0] as UploadRawFile
|
||||||
|
file.uid = genFileId()
|
||||||
|
uploadRef.value?.handleStart(file)
|
||||||
|
setSelectedFile(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleChange = (uploadFile: UploadFile) => {
|
||||||
|
setSelectedFile(uploadFile.raw as File | undefined)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleRemove = () => {
|
||||||
|
selectedFile.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
const submit = async () => {
|
||||||
|
if (loading.value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!selectedFile.value) {
|
||||||
|
ElMessage.warning('请选择文件!')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('file', selectedFile.value)
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
await props.requestApi(formData)
|
||||||
|
ElMessage.success(props.successMessage)
|
||||||
|
dialogVisible.value = false
|
||||||
|
selectedFile.value = null
|
||||||
|
uploadRef.value?.clearFiles()
|
||||||
|
emit('success')
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
open,
|
||||||
|
close
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.json-import-content {
|
||||||
|
min-height: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.json-import-loading-text {
|
||||||
|
margin-top: 12px;
|
||||||
|
color: var(--el-text-color-secondary);
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -192,7 +192,7 @@ export default class SocketService {
|
|||||||
* WebSocket连接配置
|
* WebSocket连接配置
|
||||||
*/
|
*/
|
||||||
private config: SocketConfig = {
|
private config: SocketConfig = {
|
||||||
url: `ws://127.0.0.1:7778/hello`,
|
url: `ws://127.0.0.1:7777/hello`,
|
||||||
heartbeatInterval: 9000, // 9秒心跳间隔
|
heartbeatInterval: 9000, // 9秒心跳间隔
|
||||||
reconnectDelay: 5000, // 5秒重连延迟
|
reconnectDelay: 5000, // 5秒重连延迟
|
||||||
maxReconnectAttempts: 5, // 最多重连5次
|
maxReconnectAttempts: 5, // 最多重连5次
|
||||||
|
|||||||
@@ -65,6 +65,16 @@
|
|||||||
:value="item.id"
|
:value="item.id"
|
||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="设备关联PQDIF" prop="pqdif" >
|
||||||
|
<el-select v-model="formContent.pqdif" clearable placeholder="请选择PQDIF">
|
||||||
|
<el-option
|
||||||
|
v-for="item in pqdifOptions"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.name"
|
||||||
|
:value="item.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="录波指令" prop="waveCmd" v-if="modeStore.currentMode == '比对式'">
|
<el-form-item label="录波指令" prop="waveCmd" v-if="modeStore.currentMode == '比对式'">
|
||||||
<el-input v-model='formContent.waveCmd' placeholder="请输入录波指令" maxlength="32" show-word-limit/>
|
<el-input v-model='formContent.waveCmd' placeholder="请输入录波指令" maxlength="32" show-word-limit/>
|
||||||
@@ -89,6 +99,7 @@
|
|||||||
import { ref,computed, Ref } from 'vue'
|
import { ref,computed, Ref } from 'vue'
|
||||||
import { type DevType } from '@/api/device/interface/devType'
|
import { type DevType } from '@/api/device/interface/devType'
|
||||||
import { type ICD } from '@/api/device/interface/icd'
|
import { type ICD } from '@/api/device/interface/icd'
|
||||||
|
import { type PQDIF } from '@/api/device/interface/pqdif'
|
||||||
import {dialogMiddle} from '@/utils/elementBind'
|
import {dialogMiddle} from '@/utils/elementBind'
|
||||||
import { useDictStore } from '@/stores/modules/dict'
|
import { useDictStore } from '@/stores/modules/dict'
|
||||||
import {addDevType,updateDevType} from '@/api/device/devType'
|
import {addDevType,updateDevType} from '@/api/device/devType'
|
||||||
@@ -100,6 +111,7 @@
|
|||||||
const dialogFormRef = ref()
|
const dialogFormRef = ref()
|
||||||
const scene = ref('')
|
const scene = ref('')
|
||||||
const icdOptions = ref<ICD.StandardOption[]>([])
|
const icdOptions = ref<ICD.StandardOption[]>([])
|
||||||
|
const pqdifOptions = ref<PQDIF.ResPQDIF[]>([])
|
||||||
function useMetaInfo() {
|
function useMetaInfo() {
|
||||||
const dialogVisible = ref(false)
|
const dialogVisible = ref(false)
|
||||||
const titleType = ref('add')
|
const titleType = ref('add')
|
||||||
@@ -107,6 +119,7 @@
|
|||||||
id: '', //设备类型ID
|
id: '', //设备类型ID
|
||||||
name: '',//设备类型名称
|
name: '',//设备类型名称
|
||||||
icd: '',//设备关联的ICD
|
icd: '',//设备关联的ICD
|
||||||
|
pqdif: '',//设备关联的PQDIF
|
||||||
power: 'AC/DC 110V-220V',//工作电源
|
power: 'AC/DC 110V-220V',//工作电源
|
||||||
devVolt: 57.74, //额定电压(V)
|
devVolt: 57.74, //额定电压(V)
|
||||||
devCurr: 5, //额定电流(A)
|
devCurr: 5, //额定电流(A)
|
||||||
@@ -127,6 +140,7 @@ const resetFormContent = () => {
|
|||||||
id: '', //设备类型ID
|
id: '', //设备类型ID
|
||||||
name: '',//设备类型名称
|
name: '',//设备类型名称
|
||||||
icd: '',//设备关联的ICD
|
icd: '',//设备关联的ICD
|
||||||
|
pqdif: '',//设备关联的PQDIF
|
||||||
power: 'AC/DC 110V-220V',//工作电源
|
power: 'AC/DC 110V-220V',//工作电源
|
||||||
devVolt: 57.74, //额定电压(V)
|
devVolt: 57.74, //额定电压(V)
|
||||||
devCurr: 5, //额定电流(A)
|
devCurr: 5, //额定电流(A)
|
||||||
@@ -174,6 +188,9 @@ const resetFormContent = () => {
|
|||||||
],
|
],
|
||||||
icd: [
|
icd: [
|
||||||
{ required: true, message: '设备关联ICD必选!', trigger: 'change' }
|
{ required: true, message: '设备关联ICD必选!', trigger: 'change' }
|
||||||
|
],
|
||||||
|
pqdif: [
|
||||||
|
{ required: true, message: '设备关联PQDIF必选!', trigger: 'change' }
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -209,13 +226,14 @@ const close = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 打开弹窗,可能是新增,也可能是编辑
|
// 打开弹窗,可能是新增,也可能是编辑
|
||||||
const open = async (sign: string, data: DevType.ResPqDevType,icd: ICD.StandardOption[],currentScene: string) => {
|
const open = async (sign: string, data: DevType.ResPqDevType,icd: ICD.StandardOption[],pqdif: PQDIF.ResPQDIF[],currentScene: string) => {
|
||||||
// 重置表单
|
// 重置表单
|
||||||
dialogFormRef.value?.resetFields()
|
dialogFormRef.value?.resetFields()
|
||||||
titleType.value = sign
|
titleType.value = sign
|
||||||
dialogVisible.value = true
|
dialogVisible.value = true
|
||||||
scene.value = currentScene
|
scene.value = currentScene
|
||||||
icdOptions.value = icd
|
icdOptions.value = icd
|
||||||
|
pqdifOptions.value = pqdif
|
||||||
|
|
||||||
if (data.id) {
|
if (data.id) {
|
||||||
formContent.value = { ...data }
|
formContent.value = { ...data }
|
||||||
|
|||||||
@@ -45,6 +45,8 @@ import { onBeforeMount, reactive, ref } from 'vue'
|
|||||||
import { useModeStore, useAppSceneStore } from '@/stores/modules/mode'
|
import { useModeStore, useAppSceneStore } from '@/stores/modules/mode'
|
||||||
import { getICDAllList } from '@/api/device/icd'
|
import { getICDAllList } from '@/api/device/icd'
|
||||||
import type { ICD } from '@/api/device/interface/icd'
|
import type { ICD } from '@/api/device/interface/icd'
|
||||||
|
import { getPQDIFAllList } from '@/api/device/pqdif'
|
||||||
|
import type { PQDIF } from '@/api/device/interface/pqdif'
|
||||||
|
|
||||||
// defineOptions({
|
// defineOptions({
|
||||||
// name: 'devType',
|
// name: 'devType',
|
||||||
@@ -56,6 +58,7 @@ import type { ICD } from '@/api/device/interface/icd'
|
|||||||
const proTable = ref<ProTableInstance>()
|
const proTable = ref<ProTableInstance>()
|
||||||
const devTypePopup = ref()
|
const devTypePopup = ref()
|
||||||
const icdOptions = ref<ICD.StandardOption[]>([])
|
const icdOptions = ref<ICD.StandardOption[]>([])
|
||||||
|
const pqdifOptions = ref<PQDIF.ResPQDIF[]>([])
|
||||||
|
|
||||||
const getTableList = async (params: any) => {
|
const getTableList = async (params: any) => {
|
||||||
let newParams = JSON.parse(JSON.stringify(params))
|
let newParams = JSON.parse(JSON.stringify(params))
|
||||||
@@ -111,6 +114,15 @@ import type { ICD } from '@/api/device/interface/icd'
|
|||||||
return <span>{name?.name}</span>
|
return <span>{name?.name}</span>
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
prop: 'pqdif',
|
||||||
|
label: '设备关联PQDIF',
|
||||||
|
minWidth: 200,
|
||||||
|
render: (scope) => {
|
||||||
|
const name = pqdifOptions.value.find(option => option.id === scope.row.pqdif)
|
||||||
|
return <span>{name?.name || '--'}</span>
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
prop: 'createTime',
|
prop: 'createTime',
|
||||||
label: '创建时间',
|
label: '创建时间',
|
||||||
@@ -136,7 +148,7 @@ import type { ICD } from '@/api/device/interface/icd'
|
|||||||
|
|
||||||
// 打开 drawer(新增、编辑)
|
// 打开 drawer(新增、编辑)
|
||||||
const openDialog = (titleType: string, row: Partial<DevType.ResPqDevType> = {}) => {
|
const openDialog = (titleType: string, row: Partial<DevType.ResPqDevType> = {}) => {
|
||||||
devTypePopup.value?.open(titleType, row,icdOptions.value,appSceneStore.currentScene)
|
devTypePopup.value?.open(titleType, row,icdOptions.value,pqdifOptions.value,appSceneStore.currentScene)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -156,11 +168,12 @@ import type { ICD } from '@/api/device/interface/icd'
|
|||||||
|
|
||||||
|
|
||||||
onBeforeMount(async () => {
|
onBeforeMount(async () => {
|
||||||
const response = await getICDAllList()
|
const [response, pqdifResponse] = await Promise.all([getICDAllList(), getPQDIFAllList()])
|
||||||
icdOptions.value = (response.data as ICD.ResICD[]).map(item => ({
|
icdOptions.value = (response.data as ICD.ResICD[]).map(item => ({
|
||||||
id: item.id,
|
id: item.id,
|
||||||
name: item.name
|
name: item.name
|
||||||
}))
|
}))
|
||||||
|
pqdifOptions.value = pqdifResponse.data as PQDIF.ResPQDIF[]
|
||||||
})
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -33,50 +33,20 @@
|
|||||||
</ProTable>
|
</ProTable>
|
||||||
</div>
|
</div>
|
||||||
<IcdPopup ref="icdPopup" :refresh-table="proTable?.getTableList" />
|
<IcdPopup ref="icdPopup" :refresh-table="proTable?.getTableList" />
|
||||||
<el-dialog
|
<JsonImportDialog
|
||||||
v-model="importDialogVisible"
|
ref="jsonImportDialog"
|
||||||
title="导入ICD"
|
title="导入ICD"
|
||||||
width="460px"
|
:request-api="importICDJson"
|
||||||
destroy-on-close
|
@success="handleImportSuccess"
|
||||||
:close-on-click-modal="!importLoading"
|
/>
|
||||||
:show-close="!importLoading"
|
|
||||||
>
|
|
||||||
<div v-loading="importLoading" element-loading-text="导入中,请稍候..." class="icd-import-content">
|
|
||||||
<el-upload
|
|
||||||
ref="importUploadRef"
|
|
||||||
action="#"
|
|
||||||
class="upload"
|
|
||||||
:auto-upload="false"
|
|
||||||
:limit="1"
|
|
||||||
accept=".json,application/json"
|
|
||||||
:on-exceed="handleImportExceed"
|
|
||||||
:before-upload="beforeImportUpload"
|
|
||||||
:on-change="handleImportChange"
|
|
||||||
:on-remove="handleImportRemove"
|
|
||||||
:disabled="importLoading"
|
|
||||||
>
|
|
||||||
<el-button type="primary" :icon="Upload" :disabled="importLoading">选择文件</el-button>
|
|
||||||
<template #tip>
|
|
||||||
<div class="el-upload__tip">请上传 .json 文件</div>
|
|
||||||
</template>
|
|
||||||
</el-upload>
|
|
||||||
<div v-if="importLoading" class="icd-import-loading-text">导入中,请稍候...</div>
|
|
||||||
</div>
|
|
||||||
<template #footer>
|
|
||||||
<div class="dialog-footer">
|
|
||||||
<el-button :disabled="importLoading" @click="closeImportDialog">取消</el-button>
|
|
||||||
<el-button type="primary" :loading="importLoading" @click="submitImport">开始导入</el-button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</el-dialog>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="tsx" name="useIcd">
|
<script setup lang="tsx" name="useIcd">
|
||||||
import { reactive, ref } from 'vue'
|
import { reactive, ref } from 'vue'
|
||||||
import { CirclePlus, Delete, Download, EditPen, Upload } from '@element-plus/icons-vue'
|
import { CirclePlus, Delete, Download, EditPen, Upload } from '@element-plus/icons-vue'
|
||||||
import { ElMessage, ElNotification, genFileId, type UploadFile, type UploadInstance, type UploadProps, type UploadRawFile } from 'element-plus'
|
|
||||||
import type { ICD } from '@/api/device/interface/icd'
|
import type { ICD } from '@/api/device/interface/icd'
|
||||||
import ProTable from '@/components/ProTable/index.vue'
|
import ProTable from '@/components/ProTable/index.vue'
|
||||||
|
import JsonImportDialog from '@/components/JsonImportDialog/index.vue'
|
||||||
import type { ColumnProps, ProTableInstance } from '@/components/ProTable/interface'
|
import type { ColumnProps, ProTableInstance } from '@/components/ProTable/interface'
|
||||||
import { useHandleData } from '@/hooks/useHandleData'
|
import { useHandleData } from '@/hooks/useHandleData'
|
||||||
import { useDownloadWithServerFileName } from '@/hooks/useDownload'
|
import { useDownloadWithServerFileName } from '@/hooks/useDownload'
|
||||||
@@ -90,10 +60,7 @@ defineOptions({
|
|||||||
|
|
||||||
const proTable = ref<ProTableInstance>()
|
const proTable = ref<ProTableInstance>()
|
||||||
const icdPopup = ref<InstanceType<typeof IcdPopup>>()
|
const icdPopup = ref<InstanceType<typeof IcdPopup>>()
|
||||||
const importDialogVisible = ref(false)
|
const jsonImportDialog = ref<InstanceType<typeof JsonImportDialog>>()
|
||||||
const importLoading = ref(false)
|
|
||||||
const importUploadRef = ref<UploadInstance | null>(null)
|
|
||||||
const selectedImportFile = ref<File | null>(null)
|
|
||||||
|
|
||||||
const getTableList = async (params: ICD.ReqICDParams) => {
|
const getTableList = async (params: ICD.ReqICDParams) => {
|
||||||
return getICDList({ ...params })
|
return getICDList({ ...params })
|
||||||
@@ -128,7 +95,7 @@ const columns = reactive<ColumnProps<ICD.ResICD>[]>([
|
|||||||
prop: 'type',
|
prop: 'type',
|
||||||
label: '类型',
|
label: '类型',
|
||||||
minWidth: 180,
|
minWidth: 180,
|
||||||
enum: ICD_TYPE_OPTIONS,
|
enum: [...ICD_TYPE_OPTIONS],
|
||||||
fieldNames: { label: 'label', value: 'value' },
|
fieldNames: { label: 'label', value: 'value' },
|
||||||
search: {
|
search: {
|
||||||
el: 'select',
|
el: 'select',
|
||||||
@@ -180,79 +147,10 @@ const handleExport = async (row: ICD.ResICD) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const openImportDialog = () => {
|
const openImportDialog = () => {
|
||||||
importDialogVisible.value = true
|
jsonImportDialog.value?.open()
|
||||||
}
|
}
|
||||||
|
|
||||||
const closeImportDialog = () => {
|
const handleImportSuccess = () => {
|
||||||
if (importLoading.value) {
|
proTable.value?.getTableList()
|
||||||
return
|
|
||||||
}
|
|
||||||
importDialogVisible.value = false
|
|
||||||
selectedImportFile.value = null
|
|
||||||
importUploadRef.value?.clearFiles()
|
|
||||||
}
|
|
||||||
|
|
||||||
const beforeImportUpload = (file: UploadRawFile) => {
|
|
||||||
const isJsonFile = file.name.toLowerCase().endsWith('.json') || file.type === 'application/json'
|
|
||||||
if (!isJsonFile) {
|
|
||||||
ElNotification({
|
|
||||||
title: '温馨提示',
|
|
||||||
message: '上传文件只能是 json 格式!',
|
|
||||||
type: 'warning'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return isJsonFile
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleImportExceed: UploadProps['onExceed'] = files => {
|
|
||||||
importUploadRef.value?.clearFiles()
|
|
||||||
const file = files[0] as UploadRawFile
|
|
||||||
file.uid = genFileId()
|
|
||||||
importUploadRef.value?.handleStart(file)
|
|
||||||
selectedImportFile.value = file
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleImportChange: UploadProps['onChange'] = uploadFile => {
|
|
||||||
selectedImportFile.value = (uploadFile.raw as File | undefined) ?? null
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleImportRemove = () => {
|
|
||||||
selectedImportFile.value = null
|
|
||||||
}
|
|
||||||
|
|
||||||
const submitImport = async () => {
|
|
||||||
if (importLoading.value) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (!selectedImportFile.value) {
|
|
||||||
ElMessage.warning('请选择文件!')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const formData = new FormData()
|
|
||||||
formData.append('file', selectedImportFile.value)
|
|
||||||
importLoading.value = true
|
|
||||||
try {
|
|
||||||
await importICDJson(formData)
|
|
||||||
ElMessage.success('导入成功')
|
|
||||||
importDialogVisible.value = false
|
|
||||||
selectedImportFile.value = null
|
|
||||||
importUploadRef.value?.clearFiles()
|
|
||||||
await proTable.value?.getTableList()
|
|
||||||
} finally {
|
|
||||||
importLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.icd-import-content {
|
|
||||||
min-height: 120px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icd-import-loading-text {
|
|
||||||
margin-top: 12px;
|
|
||||||
color: var(--el-text-color-secondary);
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
142
frontend/src/views/machine/pqdif/index.vue
Normal file
142
frontend/src/views/machine/pqdif/index.vue
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
<template>
|
||||||
|
<div class="table-box">
|
||||||
|
<ProTable ref="proTable" :columns="columns" :request-api="getTableList">
|
||||||
|
<template #tableHeader>
|
||||||
|
<el-button v-auth.pqdif="'import'" type="primary" plain :icon="Upload" @click="openImportDialog">
|
||||||
|
导入
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</ProTable>
|
||||||
|
</div>
|
||||||
|
<JsonImportDialog
|
||||||
|
ref="jsonImportDialog"
|
||||||
|
title="导入PQDIF"
|
||||||
|
:request-api="importPQDIFJson"
|
||||||
|
@success="handleImportSuccess"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="tsx" name="usePqdif">
|
||||||
|
import { reactive, ref } from 'vue'
|
||||||
|
import { Upload } from '@element-plus/icons-vue'
|
||||||
|
import type { PQDIF } from '@/api/device/interface/pqdif'
|
||||||
|
import { getPQDIFList, importPQDIFJson } from '@/api/device/pqdif'
|
||||||
|
import ProTable from '@/components/ProTable/index.vue'
|
||||||
|
import JsonImportDialog from '@/components/JsonImportDialog/index.vue'
|
||||||
|
import type { ColumnProps, ProTableInstance } from '@/components/ProTable/interface'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'pqdif'
|
||||||
|
})
|
||||||
|
|
||||||
|
const proTable = ref<ProTableInstance>()
|
||||||
|
const jsonImportDialog = ref<InstanceType<typeof JsonImportDialog>>()
|
||||||
|
|
||||||
|
const getTableList = async (params: PQDIF.ReqPQDIFParams) => {
|
||||||
|
return getPQDIFList({ ...params })
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatDateTime = (value?: string | null) => {
|
||||||
|
if (!value) {
|
||||||
|
return '--'
|
||||||
|
}
|
||||||
|
const date = new Date(value)
|
||||||
|
if (Number.isNaN(date.getTime())) {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
const year = date.getFullYear()
|
||||||
|
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||||
|
const day = String(date.getDate()).padStart(2, '0')
|
||||||
|
const hours = String(date.getHours()).padStart(2, '0')
|
||||||
|
const minutes = String(date.getMinutes()).padStart(2, '0')
|
||||||
|
const seconds = String(date.getSeconds()).padStart(2, '0')
|
||||||
|
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
|
||||||
|
}
|
||||||
|
|
||||||
|
const RESULT_OPTIONS = [
|
||||||
|
{ label: '成功', value: 1 },
|
||||||
|
{ label: '失败', value: 0 }
|
||||||
|
]
|
||||||
|
|
||||||
|
const columns = reactive<ColumnProps<PQDIF.ResPQDIF>[]>([
|
||||||
|
{ type: 'index', fixed: 'left', width: 70, label: '序号' },
|
||||||
|
{
|
||||||
|
prop: 'name',
|
||||||
|
label: 'PQDIF文件名称',
|
||||||
|
minWidth: 180,
|
||||||
|
search: { el: 'input' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: 'filePath',
|
||||||
|
label: '原始文件路径',
|
||||||
|
minWidth: 260,
|
||||||
|
render: scope => scope.row.filePath || '--'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: 'recordCount',
|
||||||
|
label: 'Record总数',
|
||||||
|
width: 130,
|
||||||
|
render: scope => String(scope.row.recordCount ?? '--')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: 'observationCount',
|
||||||
|
label: 'Observation总数',
|
||||||
|
width: 150,
|
||||||
|
render: scope => String(scope.row.observationCount ?? '--')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: 'sampleValueCount',
|
||||||
|
label: '样例采样值数量',
|
||||||
|
width: 160,
|
||||||
|
render: scope => String(scope.row.sampleValueCount ?? '--')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: 'result',
|
||||||
|
label: '解析结果',
|
||||||
|
width: 120,
|
||||||
|
enum: RESULT_OPTIONS,
|
||||||
|
fieldNames: { label: 'label', value: 'value' },
|
||||||
|
search: {
|
||||||
|
el: 'select',
|
||||||
|
props: {
|
||||||
|
clearable: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
render: scope => {
|
||||||
|
if (scope.row.result === 1) {
|
||||||
|
return <el-tag type="success">成功</el-tag>
|
||||||
|
}
|
||||||
|
if (scope.row.result === 0) {
|
||||||
|
return <el-tag type="danger">失败</el-tag>
|
||||||
|
}
|
||||||
|
return '--'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: 'msg',
|
||||||
|
label: '解析提示',
|
||||||
|
minWidth: 220,
|
||||||
|
render: scope => scope.row.msg || '--'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: 'createTime',
|
||||||
|
label: '创建时间',
|
||||||
|
width: 180,
|
||||||
|
render: scope => formatDateTime(scope.row.createTime)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: 'updateTime',
|
||||||
|
label: '更新时间',
|
||||||
|
width: 180,
|
||||||
|
render: scope => formatDateTime(scope.row.updateTime)
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
const openImportDialog = () => {
|
||||||
|
jsonImportDialog.value?.open()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleImportSuccess = () => {
|
||||||
|
proTable.value?.getTableList()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
14
frontend/vitest.config.ts
Normal file
14
frontend/vitest.config.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { defineConfig } from "vitest/config";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
test: {
|
||||||
|
environment: "jsdom",
|
||||||
|
include: ["frontend/src/**/*.spec.ts"]
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
"@": path.resolve(__dirname, "src")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user