This commit is contained in:
njdaoyehu 2025-05-08 11:37:08 +08:00
parent 213b0b8a9e
commit f539747c03
9 changed files with 347 additions and 140 deletions

View File

@ -85,6 +85,7 @@
"lodash-es": "^4.17.21",
"lodash.get": "^4.4.2",
"mockjs": "^1.1.0",
"moment": "^2.30.1",
"nprogress": "^0.2.0",
"path-to-regexp": "^6.2.1",
"pdfjs-dist": "^4.10.38",

BIN
public/预埋件信息.xlsx Normal file

Binary file not shown.

View File

@ -256,6 +256,10 @@
height: 36px !important;
}
:deep(.ant-select-disabled .ant-select-selection-item) {
color: #bbbbbb !important;
}
:deep(.ant-pagination-options .ant-select-selector) {
height: 28px !important;
}

View File

@ -36,6 +36,17 @@
border: none;
}
.ant-btn-primary:disabled,
.ant-btn-primary:disabled:hover {
color: #AAAAAA !important;
background-color: #3793d4;
box-shadow: inset 0 0 20px 2px #006CC6;
}
.ant-btn-primary:disabled svg {
color: #AAAAAA !important;
}
.ant-btn-default {
font-family: "Noto Sans SC", serif;
font-size: 14px;
@ -59,13 +70,16 @@
}
.ant-btn-default:disabled,
.ant-btn-default:disabled:hover,
.ant-btn-default:disabled svg {
.ant-btn-default:disabled:hover {
color: #AAAAAA !important;
background-color: rgba(58,98,203,0.8);
box-shadow: inset 0 0 20px 2px #3A62CB;
}
.ant-btn-default:disabled svg {
color: #AAAAAA !important;
}
.ant-spin-container {
margin: 0 5px;
}

View File

@ -35,7 +35,7 @@
data() {
return {
open: false,
zoomLevel: 1,
zoomLevel: 1.75,
start: false,
selection: null,
startPoint: null,
@ -59,7 +59,6 @@
const viewport = page.getViewport({scale: this.zoomLevel});
const canvas = this.$refs.pdfCanvas;
const context = canvas.getContext('2d');
console.log(viewport.width + '' + viewport.height);
canvas.width = viewport.width;
canvas.height = viewport.height;
await page.render({

View File

@ -1,12 +1,11 @@
<template>
<BasicDrawer v-bind="$attrs" @register="registerDrawer" showFooter :title="getTitle" width="960px" @ok="handleSubmit">
<BasicForm @register="registerForm" />
<BasicForm @register="registerForm" @field-value-change="handleValuesChanged" />
<BasicTable @register="registerTable">
<template #toolbar>
<a-button type="primary" @click="handleCreate" :icon="h(PlusOutlined)"> </a-button>
<div style="width: 5px" />
<a-button type="primary" :icon="h(EyeOutlined)" @click="handleOpenFileDialog">OCR识别</a-button>
<div style="width: 5px" />
<a-button type="primary" :icon="h(PlusOutlined)" @click="handleCreate"> </a-button>
<a-button type="primary" :icon="h(EyeOutlined)" :disabled="importDisabled" @click="handleOpenFileDialog">OCR识别</a-button>
<a-button type="primary" :icon="h(FileExcelOutlined)" :disabled="importDisabled" @click="handleImport">导入Excel</a-button>
<Popconfirm
title="确定要清空预埋件列表吗?"
ok-text="确定"
@ -15,7 +14,6 @@
>
<a-button type="primary" :icon="h(MinusOutlined)">清空列表</a-button>
</Popconfirm>
<div style="width: 5px" />
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'x'">
@ -63,25 +61,23 @@
</BasicDrawer>
</template>
<script lang="ts" setup>
import {defineEmits, ref, computed, unref, h} from 'vue';
import { defineEmits, ref, computed, unref, h } from 'vue';
import { BasicForm, useForm } from '@/components/Form/index';
import { formSchema } from './schema';
import { BasicDrawer, useDrawerInner } from '@/components/Drawer';
import * as TaskApi from '@/api/data/taskApi';
import { BasicTable, useTable, TableAction } from '@/components/Table';
import { EyeOutlined, PlusOutlined, MinusOutlined } from "@ant-design/icons-vue";
import { EyeOutlined, PlusOutlined, MinusOutlined, FileExcelOutlined } from "@ant-design/icons-vue";
import { Popconfirm } from 'ant-design-vue';
import Modal from "@/views/data/task/modal.vue";
import { useModal } from '/@/components/Modal';
const [register, { openModal }] = useModal();
const columns = [
{
title: '序号',
dataIndex: 'sn',
key: 'sn',
width: '50px',
// customRender: ({ index }) => `${index + 1}`,
},
{
title: '预埋件编号',
@ -129,9 +125,10 @@ const paramData = ref<any>([]);
const emit = defineEmits(['success', 'register', 'ocrClick']);
const isUpdate = ref(true);
const importDisabled = ref(true);
const entity = ref();
const [registerForm, { resetFields, setFieldsValue, validate, getFieldsValue }] = useForm({
const [registerForm, { resetFields, setFieldsValue, validate, getFieldsValue, updateSchema }] = useForm({
labelWidth: 120,
schemas: formSchema,
showActionButtonGroup: false,
@ -139,7 +136,7 @@ const [registerForm, { resetFields, setFieldsValue, validate, getFieldsValue }]
const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
await resetFields();
setDrawerProps({ confirmLoading: false });
setDrawerProps({confirmLoading: false});
isUpdate.value = !!data?.isUpdate;
entity.value = data?.record;
paramData.value = [];
@ -148,28 +145,28 @@ const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (
if (Object.keys(d).includes('sn')) {
paramData.value.push({...d});
} else {
paramData.value.push({ sn: (index + 1) + '', ...d});
paramData.value.push({sn: (index + 1) + '', ...d});
}
});
await setFieldsValue({
...data.record,
});
await updateSchema([
{
field: 'wallType',
componentProps: {
disabled: unref(isUpdate)
}
},
{
field: 'direction',
componentProps: {
disabled: unref(isUpdate)
}
}]);
handleValuesChanged();
});
const setParamData = (items: any) => {
if (!paramData.value)
paramData.value = [];
items.forEach((d: any) => {
const items = paramData.value.filter((p: any) => p.code === d.code);
if (items.length === 0) {
paramData.value.push({...d});
}
else {
items[0] = {...d};
}
});
};
const [registerTable] = useTable({
title: '预埋件列表',
columns,
@ -210,8 +207,12 @@ async function handleSubmit() {
);
let p: any = [];
paramData.value.forEach((d: any) => {
delete d.index;
p.push(d);
//
d.wallType = data.wallType;
d.direction = data.direction;
d.coreA = data.coreA;
d.coreB = data.coreB;
p.push({ ...d });
});
data.paramJson = JSON.stringify(p);
await action(data);
@ -247,6 +248,41 @@ const handleOpenFileDialog = () => {
emit("ocrClick", data);
};
const handleImport = () => {
WebViewService.importExcel().then((res: any) => {
const data = getFieldsValue();
if (res === "") {
WebViewService.setMessage('没有预埋件数据!', 'information').then(() => {});
return;
}
const items = JSON.parse(res);
if (items.length === 0) {
WebViewService.setMessage('没有预埋件数据!', 'information').then(() => {});
return;
}
if (!paramData.value) {
paramData.value = [];
}
items.forEach((d: any) => {
if (d.wallType.toUpperCase() === data.wallType && d.direction === data.direction) {
const items = paramData.value.filter((p: any) => p.code === d.code);
// if (data.wallType === "B") {
// const nX: any = d["y"];
// const nY: any = d["x"];
// d.x = nX;
// d.y = nY;
// }
if (items.length === 0) {
paramData.value.push({...d});
} else {
items[0] = {...d};
}
}
});
WebViewService.setMessage('数据导入成功!', "success").then(() => {});
})
}
const handleDeleteAll = (e: MouseEvent) => {
paramData.value = [];
};
@ -275,6 +311,71 @@ const markField = computed(() => {
};
});
const handleValuesChanged = () => {
const data = getFieldsValue();
importDisabled.value = data.wallType === undefined || data.direction === undefined;
};
const setParamData = (data: any) => {
const items = handleParamData(data);
if (!paramData.value)
paramData.value = [];
items.forEach((d: any) => {
if (d.hasOwnProperty("zm")) {
delete d.zm;
}
if (d.hasOwnProperty("nX")) {
delete d.nX;
}
if (d.hasOwnProperty("nY")) {
delete d.nY;
}
const items = paramData.value.filter((p: any) => p.code === d.code);
if (items.length === 0) {
paramData.value.push({...d});
}
else {
items[0] = {...d};
}
});
WebViewService.setMessage('数据导入成功!', "success").then(() => {});
};
const handleParamData = (items: any) => {
const data = getFieldsValue();
const wallType = data.wallType;
const direction = data.direction;
// x
const uniqueListX = Array.from(new Map(items.map((item: any) => [item.x, item])).values());
// y
const uniqueListY = Array.from(new Map(items.map((item: any) => [item.y, item])).values());
// B
items.forEach((d: any) => {
if (wallType === "B类" && uniqueListX.length <= 3 && uniqueListY.length > 3) {
const nX: any = d["y"];
const nY: any = d["x"];
d.nX = nX;
d.nY = nY;
}
});
//
const uniqueY = Array.from(new Map(items.map((item: any) => [item.nY, item])).values());
const maxY = Math.max(...uniqueY.map((item: any) => Number(item.nY)));
const minY = Math.min(...uniqueY.map((item: any) => Number(item.nY)));
items.forEach((d: any) => {
if (Number(d.nY) === minY) {
d.zm = 1;
} else if (Number(d.nY) === maxY) {
d.zm = 0;
} else if (maxY - Number(d.nY) < Number(d.nY) - minY) {
d.zm = 0;
} else {
d.zm = 1;
}
});
return direction === "正面" ? items.filter((d: any) => { return d.zm === 1 }).map((d: any) => ({...d})) : items.filter((d: any) => { return d.zm === 0 }).map((d: any) => ({...d}));
}
defineExpose({
setParamData
});

View File

@ -7,8 +7,8 @@
<BasicTable @register="registerTable">
<template #toolbar>
<a-button type="primary" @click="handleCreate" :icon="h(PlusOutlined)"> </a-button>
<div style="width: 5px" />
<a-button type="default" :icon="h(EyeOutlined)" @click="handleOpenFileDialog(null)">OCR识别</a-button>
<!-- <div style="width: 5px" />-->
<!-- <a-button type="default" :icon="h(EyeOutlined)" @click="handleOpenFileDialog(null)">OCR识别</a-button>-->
<input
type="file"
ref="fileInput"
@ -30,6 +30,11 @@
:icon="h(SyncOutlined)"
:disabled="checkedKeys.length === 0"
>同步数据</a-button>
<a-button
type="default"
@click="handleDownloadTemplate"
:icon="h(CloudDownloadOutlined)"
>下载模板</a-button>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'action'">
@ -60,7 +65,7 @@
icon: 'ant-design:eye-outlined',
onClick: handleView.bind(null, record),
divider: true,
disabled: record.endTime === undefined || record.endTime === null,
disabled: record.paramJson === undefined || record.paramJson === null || record.paramJson === '',
},
{
label: '下发数据',
@ -109,12 +114,13 @@
import { columns, searchFormSchema } from './schema';
import { SvgIcon } from '@/components/Icon';
import { computed, h, onMounted, onUnmounted, ref } from 'vue';
import { DownloadOutlined, EyeOutlined, PlusOutlined, SyncOutlined } from '@ant-design/icons-vue';
import { DownloadOutlined, PlusOutlined, SyncOutlined, CloudDownloadOutlined } from '@ant-design/icons-vue';
import PdfViewer from '@/components/PdfViewer/index.vue';
import { useModal } from '@/components/Modal';
import Result from '@/views/data/task/result.vue';
import { Loading } from '@/components/Loading';
import { SizeEnum } from '@/enums/sizeEnum';
import axios from 'axios';
const [register, { openModal }] = useModal();
@ -217,40 +223,20 @@
reload();
};
const getCode = (code1: string, code2: string) => {
const hasPt = code1.indexOf("PT") !== -1;
if (hasPt) {
let code = code1.split("PT")[0] + "PT";
for (let i = 0; i < code1.split("PT")[1].length - code2.length; i++) {
code += "0";
}
code += code2;
return code
} else {
return code2
}
};
const getResults = (record: any) => {
const params = JSON.parse(record.paramJson);
if (record.results === undefined || record.results === null)
return undefined;
const results = JSON.parse(record.results);
const results = !record.results || record.results === "" || record.results === "[]" ? [] : JSON.parse(record.results);
params.forEach((param: any) => {
const l = results.filter((d: any) => param["code"] === d["code"] || param["code"] === getCode(param["code"], d["code"]+''));
if (l.length > 0) {
param["x1"] = l[0]["x1"];
param["y1"] = l[0]["y1"];
param["x2"] = l[0]["x2"];
param["y2"] = l[0]["y2"];
param["x3"] = l[0]["x3"];
param["y3"] = l[0]["y3"];
param["x4"] = l[0]["x4"];
param["y4"] = l[0]["y4"];
param["base"] = l[0]["base"];
param["actual_value"] = l[0]["actual_value"];
param["status"] = l[0]["status"];
}
const l = results.filter((d: any) => param["code"] === d["code"]);
param["w"] = l.length == 0 ? "" : l[0]["best_cal_width"];
param["h"] = l.length == 0 ? "" : l[0]["best_cal_height"];
param["average_deviation"] = l.length == 0 ? "" : l[0]["average_deviation"];
param["m_x"] = l.length == 0 ? "" : l[0]["m_x"];
param["m_y"] = l.length == 0 ? "" : l[0]["m_y"];
param["x_deviation"] = l.length == 0 ? "" : l[0]["x_deviation"];
param["y_deviation"] = l.length == 0 ? "" : l[0]["y_deviation"];
param["base"] = l.length == 0 ? "" : l[0]["base"];
param["status"] = l.length == 0 ? "" : l[0]["status"];
});
return params;
}
@ -258,7 +244,7 @@
const handleView = (record: any) => {
const results = getResults(record);
if (!results) return;
record.resultJson1 = JSON.stringify(results);
record.reportJson = JSON.stringify(results);
openModal(true, { record });
};
@ -294,18 +280,15 @@
(res: any) => {
if (res === "") {
isLoading.value = false;
WebViewService.setMessage('没有预埋件数据!', 'information').then(() => {});
return;
}
const data = JSON.parse(res);
if (data.length === 0) {
isLoading.value = false;
WebViewService.setMessage('没有预埋件数据!', 'information').then(() => {});
return;
}
// if (data[0].length > 5) {
// wallCode = data[0].code.substring(0, data.length - 5);
// } else {
// wallCode = null;
// }
if (ocrData === null) {
const record = {
name: '',
@ -358,6 +341,25 @@
);
};
const handleDownloadTemplate = async () => {
try {
const response = await axios({
url: `${import.meta.env.VITE_PUBLIC_PATH}预埋件信息.xlsx`, //
method: 'GET',
responseType: 'blob' // blob
});
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', '预埋件信息.xlsx'); //
document.body.appendChild(link);
link.click();
link.remove(); //
} catch (error) {
console.error('下载失败', error);
}
}
const deviceConnectedList = ref([]);
const getDeviceConnected = computed(() => {

View File

@ -29,11 +29,43 @@ import { CheckOutlined, CloseOutlined, FileExcelOutlined, FileTextOutlined } fro
import ExcelJS from 'exceljs';
import { saveAs } from 'file-saver';
// # '': item['code'],
// # '': item['type'],
// # 'x': item['x'],
// # 'y': item['y'],
// # '': item['center'],
// # '': item['best_cal_width'],
// # '': item['best_cal_height'],
// # '': item['average_deviation'],
// # 'x': item['m_x'],
// # '': item['m_z'],
// # '': item['x_deviation'],
// # '': item['y_deviation'],
// # '': item['base'],
// # '': item['status'], # good or bad
// layout.addWidget(self.add_param_item("", 180, True), row, 0)
// layout.addWidget(self.add_param_item("", -1, True), row, 1)
// layout.addWidget(self.add_param_item("X", 65, True), row, 2)
// layout.addWidget(self.add_param_item("Y", 65, True), row, 3)
// layout.addWidget(self.add_param_item("", 100, True), row, 4)
// layout.addWidget(self.add_param_item("", 65, True), row, 5)
// layout.addWidget(self.add_param_item("", 65, True), row, 6)
// layout.addWidget(self.add_param_item("", 100, True), row, 7)
// layout.addWidget(self.add_param_item("X", 100, True), row, 8)
// layout.addWidget(self.add_param_item("", 140, True), row, 9)
// layout.addWidget(self.add_param_item("", 140, True), row, 10)
// layout.addWidget(self.add_param_item("", 140, True), row, 11)
// layout.addWidget(self.add_param_item("", 140, True), row, 12)
// layout.addWidget(self.add_param_item("", 140, True), row, 13)
const columns = [
{
title: '序号',
dataIndex: 'sn',
key: 'sn',
width: '60px',
},
{
title: '预埋件编号',
@ -41,105 +73,80 @@ const columns = [
key: 'code',
},
{
title: '类型',
title: '预埋件类型',
dataIndex: 'type',
key: 'type',
},
{
title: 'X(mm)',
title: '中心x',
dataIndex: 'x',
key: 'x',
width: '80px',
},
{
title: 'Y(mm)',
title: '中心y',
dataIndex: 'y',
key: 'y',
width: '80px',
},
{
title: 'w(mm)',
dataIndex: 'w',
key: 'w',
width: '80px',
},
{
title: 'h(mm)',
dataIndex: 'h',
key: 'h',
width: '80px',
},
{
title: '中心(m)',
title: '中心标高',
dataIndex: 'center',
key: 'center',
width: '110px',
},
{
title: 'X1',
dataIndex: 'x1',
key: 'x1',
width: '70px',
title: '宽度',
dataIndex: 'w',
key: 'w',
width: '80px',
},
{
title: 'Y1',
dataIndex: 'y1',
key: 'y1',
width: '70px',
title: '高度',
dataIndex: 'h',
key: 'h',
width: '80px',
},
{
title: 'X2',
dataIndex: 'x2',
key: 'x2',
width: '70px',
title: '大概偏差',
dataIndex: 'average_deviation',
key: 'average_deviation',
width: '80px',
},
{
title: 'Y2',
dataIndex: 'y2',
key: 'y2',
width: '70px',
title: '人工测量X',
dataIndex: 'm_x',
key: 'm_x',
width: '100px',
},
{
title: 'X3',
dataIndex: 'x3',
key: 'x3',
width: '70px',
title: '人工测量标高',
dataIndex: 'm_y',
key: 'm_y',
width: '100px',
},
{
title: 'Y3',
dataIndex: 'y3',
key: 'y3',
width: '70px',
title: '水平位置偏差',
dataIndex: 'x_deviation',
key: 'x_deviation',
width: '100px',
},
{
title: 'X4',
dataIndex: 'x4',
key: 'x4',
width: '70px',
title: '垂直位置偏差',
dataIndex: 'y_deviation',
key: 'y_deviation',
width: '100px',
},
{
title: 'Y4',
dataIndex: 'y4',
key: 'y4',
width: '70px',
},
{
title: '标准值',
title: '偏差基准值',
dataIndex: 'base',
key: 'base',
width: '60px',
width: '90px',
},
{
title: '实际值',
dataIndex: 'actual_value',
key: 'actual_value',
width: '70px',
},
{
title: '结果',
title: '是否需要调整',
dataIndex: 'status',
key: 'status',
width: '50px',
},
];
@ -154,7 +161,7 @@ const [register, { closeModal, setModalProps }] = useModalInner(async (data) =>
entity.value = data?.record;
title.value = data?.record.name + " - 任务结果详情";
paramData.value = [];
const items = entity.value && entity.value.resultJson ? JSON.parse(entity.value.resultJson1) : [];
const items = JSON.parse(entity.value.reportJson);
items.forEach((d: any, index: number) => {
paramData.value.push({ index, ...d });
});
@ -197,8 +204,6 @@ const handleExport = async () => {
worksheet.addRow(data);
setCellStyle(worksheet.getRow(index + 2));
});
worksheet.getColumn(1).width = 22;
worksheet.getColumn(2).width = 22;
//
const buffer = await workbook.xlsx.writeBuffer();
const blob = new Blob([buffer], {type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'});

View File

@ -9,6 +9,7 @@
import { BasicColumn } from '@/components/Table';
import { FormSchema } from '@/components/Table';
import * as DeviceApi from '@/api/data/deviceApi'
import moment from "moment";
@ -49,6 +50,7 @@ export const schema = {
rules: [{ required: true, message: '请输入任务名称!' }],
},
table: {
width: 200,
},
},
{
@ -73,6 +75,81 @@ export const schema = {
table: {
},
},
{
field: 'wallType',
label: '墙体类型',
defaultValue: undefined,
form: {
colProps,
component: 'Select',
componentProps: {
allowClear: true,
placeholder: '请选择墙体类型',
options: [{value: 'A类', label: 'A类'}, {value: 'B类', label: 'B类'}],
labelField: 'label',
valueField: 'value',
disabled: true,
},
rules: [{ required: true, message: '请选择墙体类型!' }],
},
table: {
width: 80,
},
},
{
field: 'direction',
label: '方向',
defaultValue: undefined,
form: {
colProps,
component: 'Select',
componentProps: {
allowClear: true,
placeholder: '请选择方向',
options: [{value: '正面', label: '正面'}, {value: '背面', label: '背面'}],
labelField: 'label',
valueField: 'value',
},
rules: [{ required: true, message: '请选择方向!' }],
},
table: {
width: 80,
},
},
{
field: 'coreA',
label: '堆芯A点',
defaultValue: undefined,
form: {
componentProps: {
allowClear: false,
placeholder: '堆芯A点',
},
colProps,
component: 'Input',
rules: [{ required: true, message: '请输入堆芯A点' }],
},
table: {
width: 80,
},
},
{
field: 'coreB',
label: '堆芯B点',
defaultValue: undefined,
form: {
componentProps: {
allowClear: false,
placeholder: '堆芯B点',
},
colProps,
component: 'Input',
rules: [{ required: true, message: '请输入堆芯B点' }],
},
table: {
width: 80,
},
},
{
field: 'paramJson',
label: '任务参数',
@ -189,6 +266,7 @@ export const schema = {
rules: [{ required: true, message: '请选择状态!' }],
},
table: {
width: 60,
customRender: ({ text, record }) => {
if (record.state === 0)
return '未开始';
@ -216,6 +294,9 @@ export const schema = {
rules: [{ required: true, message: '请输入CreateTime' }],
},
table: {
customRender: ({ text, record }) => {
return moment(record.createTime).format('YYYY-MM-DD HH:mm');
},
},
},
{
@ -242,9 +323,9 @@ export const schema = {
const queryFields = ['name','deviceSn','startTimeQuery','state'];
// const editFields = ['name','deviceSn','paramJson'];
const editFields = ['name', 'deviceSn'];
const tableFields = ['name','deviceSn','startTime','endTime','state','createTime'];
const descriptionFields = ['name','deviceSn','paramJson','resultJson','startTime','endTime','state'];
const editFields = ['name','deviceSn','wallType','direction','coreA','coreB'];
const tableFields = ['name','deviceSn','wallType','direction','coreA','coreB','startTime','endTime','state','createTime'];
const descriptionFields = ['name','deviceSn','wallType','direction','coreA','coreB','paramJson','resultJson','startTime','endTime','state'];
const queryFieldsIndexMap = new Map(queryFields.map((field, index) => [field, index]));
export const searchFormSchema: FormSchema[] = schema.properties.filter(item => queryFields.includes(item.field))