| | |
| | | ] |
| | | }, |
| | | { |
| | | //第三方玻璃优化 |
| | | path: 'glassOptimizeThirdParty', |
| | | name: 'glassOptimizeThirdParty', |
| | | component: () => import('../views/pp/glassOptimizeThirdParty/Optimize.vue'), |
| | | children:[ |
| | | { |
| | | path: 'optimizeProject', |
| | | name: 'optimizeProject', |
| | | component: () => import('../views/pp/glassOptimizeThirdParty/OptimizeProject.vue'), |
| | | }, |
| | | { |
| | | path: 'optimizePrint', |
| | | name: 'optimizePrint', |
| | | component: () => import('../views/pp/glassOptimizeThirdParty/OptimizePrint.vue'), |
| | | }, |
| | | { |
| | | path: 'OptimizationRectPrint', |
| | | name: 'OptimizationRectPrint', |
| | | component: () => import('../views/pp/glassOptimizeThirdParty/OptimizationRectPrint.vue'), |
| | | }, |
| | | { |
| | | path: 'OptimizeControl', |
| | | name: 'OptimizeControl', |
| | | component: () => import('../views/pp/glassOptimizeThirdParty/OptimizeControl.vue'), |
| | | }, |
| | | { |
| | | path: 'Optimization', |
| | | name: 'Optimization', |
| | | component: () => import('../views/pp/glassOptimizeThirdParty/Optimization.vue'), |
| | | }, |
| | | { |
| | | path: 'optimizeparms', |
| | | name: 'optimizeparms', |
| | | component: () => import('../views/pp/glassOptimizeThirdParty/OptimizeParms.vue'), |
| | | }, |
| | | { |
| | | path: '/optimizeProject/:projectNo/:thickNess/:model', |
| | | name: 'optimizeInfo', |
| | | component: () => import('../views/pp/glassOptimizeThirdParty/OptimizeProject.vue') |
| | | }, |
| | | { |
| | | path: '', |
| | | redirect:'/main/glassOptimizeThirdParty/optimizeProject' |
| | | } |
| | | ] |
| | | }, |
| | | { |
| | | path: '', |
| | | redirect:'/main/order' |
| | | } |
| | |
| | | "order_number": 33, |
| | | "process_id": "NG24120028A005", |
| | | "technology_number": 2, |
| | | "tempering_feed_sequence": 14, |
| | | "tempering_layout_id": 15, |
| | | "tempering_feed_sequence": 1, |
| | | "tempering_layout_id": 1, |
| | | "width": 814, |
| | | "x_coordinate": 1582.0, |
| | | "y_coordinate": 3768.0 |
| | |
| | | "order_number": 33, |
| | | "process_id": "NG24120028A005", |
| | | "technology_number": 2, |
| | | "tempering_feed_sequence": 14, |
| | | "tempering_layout_id": 15, |
| | | "tempering_feed_sequence": 2, |
| | | "tempering_layout_id": 1, |
| | | "width": 814, |
| | | "x_coordinate": 1582.0, |
| | | "y_coordinate": 3768.0 |
| | |
| | | const handleSave = () => { |
| | | |
| | | if (props.data) { |
| | | |
| | | console.log(props.data) |
| | | |
| | | let projectData = ref({ |
| | | projectdetail: props.data, |
| | | projectdetail: props.data.data[0].glass_details, |
| | | ratioResult: props.data.data[0].ratioResult, |
| | | rackinfos: props.data.data[0].rackinfos, |
| | | resultSum: props.data.data[0].resultSum, |
| | | guidance: props.data.data[0].guidance, |
| | | userName : username, |
| | | inputValues:inputValues |
| | | }) |
New file |
| | |
| | | <script setup> |
| | | import ComputeCard from "@/views/pp/glassOptimizeThirdParty/page/ComputeCard.vue"; |
| | | import ComputeDetail from "@/views/pp/glassOptimizeThirdParty/page/ComputeDetail.vue"; |
| | | import Compute from "@/views/pp/glassOptimizeThirdParty/page/Compute.vue"; |
| | | import {onMounted, ref} from 'vue'; |
| | | import {ElMessage, ElLoading} from "element-plus"; |
| | | import request from "@/utils/request"; |
| | | |
| | | const props = defineProps({ |
| | | project : null |
| | | }); |
| | | |
| | | const computed = ref(null); |
| | | const computedCard = ref(null); |
| | | const computedData = ref({ |
| | | // 要传递给子组件的数据 |
| | | }); |
| | | |
| | | // 监听子组件ComputeCard的数据 |
| | | |
| | | |
| | | const computeCardRef=ref(null) |
| | | |
| | | |
| | | const handleCardData = (data) => { |
| | | |
| | | computedCard.value = data; |
| | | // 将 computed.value 合并到 computedCard.value 的最外层 |
| | | computedCard.value = { |
| | | ...computed.value, |
| | | ...computedCard.value |
| | | }; |
| | | }; |
| | | |
| | | // 监听子组件ComputeDetail的数据 |
| | | const handleData = (data) => { |
| | | computed.value = data; |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | if (props.project) { |
| | | console.log(props.project) |
| | | handleFetchData(props.project.projectNumber); |
| | | //handleTableData(data); |
| | | //handleDataReceive(data); |
| | | } |
| | | }); |
| | | |
| | | // 在父组件中定义处理接收数据的函数 |
| | | const handleDataReceive= async (data) => { |
| | | console.log('接收到子组件的数据2:', data); |
| | | // 处理数据,例如更新父组件的状态或调用后端API |
| | | |
| | | } |
| | | |
| | | |
| | | // 用于存储从后端获取到的数据,初始化为空数组 |
| | | const receivedData = ref([]); |
| | | |
| | | |
| | | console.log(receivedData) |
| | | const handleFetchData = async (projectNumber) => { |
| | | try { |
| | | const res = await request.post(`/glassOptimize/selectProjectComputeMpThirdParty/${projectNumber}`); |
| | | if (Number(res.code) === 200 && res.data && res.data.data) { |
| | | receivedData.value = res.data.data; |
| | | } else { |
| | | console.error('请求出现问题,状态码:', res.code, ',错误信息:', res.msg); |
| | | if (res.code === 404) { |
| | | ElMessage.error('未找到对应工程号的数据,请检查输入是否正确'); |
| | | } else if (res.code === 500) { |
| | | ElMessage.error('服务器内部出现错误,请稍后再试'); |
| | | } else { |
| | | ElMessage.warning(res.msg); |
| | | } |
| | | } |
| | | } catch (error) { |
| | | console.error('请求出错', error); |
| | | ElMessage.error('请输入工程号!'); |
| | | } |
| | | }; |
| | | |
| | | const handleTableData= async (data) => { |
| | | // 处理接收到的表格数据 |
| | | console.log('父组件接收到的数据1:', data); |
| | | } |
| | | |
| | | const handleSimulation = async () => { |
| | | const data=computeCardRef.value.selectFullData(); |
| | | if(props.project!=null){ |
| | | computed.value.glass_thickness=props.project.glass_thickness |
| | | computed.value.glass_type=props.project.glass_type |
| | | } |
| | | data.forEach(item=>{ |
| | | const processCard = { |
| | | process_no: item.processId, |
| | | layers: item.technologyNumber, |
| | | total_layers: item.total_layers, |
| | | total_num: item.total_num, |
| | | total_area: item.total_area, |
| | | is_must: true, |
| | | allow_rotate: item.allow_rotate, |
| | | priority_level: 0, |
| | | tempering: item.tempering, |
| | | curtain_wall: item.curtain_wall, |
| | | glass_details: [] |
| | | }; |
| | | |
| | | request.post(`/glassOptimize/selectComputeDetailThirdParty/${item.processId}/${item.technologyNumber}`).then((res) => { |
| | | if(Number(res.code) === 200){ |
| | | res.data.data.forEach(items=>{ |
| | | const detail={ |
| | | process_id:null, |
| | | technology_number:null, |
| | | order_number:null, |
| | | layers_number:null, |
| | | max_width:null, |
| | | max_height:null, |
| | | child_width:null, |
| | | child_height:null, |
| | | quantity:null, |
| | | patch_state:null |
| | | } |
| | | detail.process_id=items.process_id |
| | | detail.technology_number=items.technology_number |
| | | detail.order_number=items.order_number |
| | | detail.layers_number=item.total_layers |
| | | detail.max_width=items.maxwidth |
| | | detail.max_height=items.maxheight |
| | | detail.child_width=items.width |
| | | detail.child_height=items.height |
| | | detail.quantity=items.quantity |
| | | detail.patch_state=item.patch_state |
| | | |
| | | processCard.glass_details.push(detail) |
| | | }) |
| | | |
| | | |
| | | }else{ |
| | | ElMessage.warning(res.msg) |
| | | } |
| | | }) |
| | | computed.value.process_cards.push(processCard) |
| | | }) |
| | | console.log(computed.value) |
| | | |
| | | |
| | | |
| | | |
| | | const loading = ElLoading.service({ |
| | | lock: true, |
| | | text: '正在计算中,请稍候...', |
| | | background: 'rgba(0, 0, 0, 0.7)' |
| | | }); |
| | | console.log('发送到后端的数据',computedCard.value) |
| | | const mockData = { |
| | | code: 200, |
| | | data: [ |
| | | { |
| | | "glass_details": [ |
| | | { |
| | | "angle": 0.0, |
| | | "glass_type": 3, |
| | | "height": 862, |
| | | "layers_number": 2, |
| | | "order_number": 33, |
| | | "process_id": "NG24120028A005", |
| | | "technology_number": 2, |
| | | "tempering_feed_sequence": 1, |
| | | "tempering_layout_id": 1, |
| | | "width": 814, |
| | | "x_coordinate": 1582.0, |
| | | "y_coordinate": 3768.0 |
| | | }, |
| | | { |
| | | "angle": 0.1, |
| | | "glass_type": 4, |
| | | "height": 862, |
| | | "layers_number": 2, |
| | | "order_number": 33, |
| | | "process_id": "NG24120028A005", |
| | | "technology_number": 2, |
| | | "tempering_feed_sequence": 2, |
| | | "tempering_layout_id": 1, |
| | | "width": 814, |
| | | "x_coordinate": 1582.0, |
| | | "y_coordinate": 3768.0 |
| | | } |
| | | ], |
| | | "guidance": 15, |
| | | "rackinfos": [ |
| | | "NG24120026A003-2", |
| | | "NG24120027A003-2", |
| | | "NG24120028A005-2" |
| | | ], |
| | | "ratioResult": [ |
| | | { |
| | | "area": 10.53, |
| | | "glass_total": 15, |
| | | "ratio": 0.74, |
| | | "tempering_layout_id": "13" |
| | | }, |
| | | { |
| | | "area": 10.53, |
| | | "glass_total": 15, |
| | | "ratio": 0.74, |
| | | "tempering_layout_id": "14" |
| | | }, |
| | | { |
| | | "area": 9.82, |
| | | "glass_total": 14, |
| | | "ratio": 0.69, |
| | | "tempering_layout_id": "15" |
| | | } |
| | | ], |
| | | "resultSum": [15.0, 0.74] |
| | | } |
| | | ] |
| | | }; |
| | | if (mockData.code === 200) { |
| | | loading.close(); |
| | | ElMessage.success('模拟计算成功!'); |
| | | } |
| | | // 更新computedData |
| | | computedData.value = mockData; |
| | | // try { |
| | | // if (!computed.value ) { |
| | | // ElMessage.warning('请先加载数据再进行模拟计算'); |
| | | // return; |
| | | // } |
| | | |
| | | // // 显示加载提示 |
| | | // const loading = ElLoading.service({ |
| | | // lock: true, |
| | | // text: '正在计算中,请稍候...', |
| | | // background: 'rgba(0, 0, 0, 0.7)' |
| | | // }); |
| | | |
| | | // try { |
| | | // // 将数据提交到后端 |
| | | // const response = await request.post('/glassOptimize/simulationCalculate', { |
| | | |
| | | // data: computedCard.value |
| | | |
| | | // }); |
| | | |
| | | // if (response.code === 200) { |
| | | // ElMessage.success('模拟计算成功!'); |
| | | // } else { |
| | | // ElMessage.error(response.msg || '模拟计算失败'); |
| | | // } |
| | | // } catch (error) { |
| | | // console.error('请求失败:', error); |
| | | // ElMessage.error('网络异常,请稍后再试'); |
| | | // } finally { |
| | | // // 关闭加载提示 |
| | | // loading.close(); |
| | | // } |
| | | // } catch (error) { |
| | | // console.error('请求失败:', error); |
| | | // ElMessage.error('网络异常,请稍后再试'); |
| | | // } |
| | | }; |
| | | |
| | | |
| | | |
| | | //接受子组件ComputeCard的流程卡号 |
| | | let projectRow = ref({ |
| | | processId:null, |
| | | patchState:null, |
| | | technologyNumber:null |
| | | }) |
| | | const handleUpdateProcessId = (processId,technologyNumber) => { |
| | | projectRow.value.processId = processId; |
| | | }; |
| | | |
| | | const handleTechnologyNumberUpdate = (processId,technologyNumber) => { |
| | | projectRow.value.technologyNumber = technologyNumber; |
| | | }; |
| | | |
| | | |
| | | |
| | | </script> |
| | | |
| | | <template> |
| | | <div style="width: 100%; height: 100%;"> |
| | | |
| | | <div id="compute"> |
| | | <compute :data="computedData" @fetch-data="handleFetchData" :project="props.project" @sendData="handleData" @simulate-click="handleSimulation" /> |
| | | </div> |
| | | |
| | | <div id="computeCard"> |
| | | <compute-card ref="computeCardRef" :table-data="receivedData" |
| | | :process-id="projectRow.processId===null?null:projectRow.processId" |
| | | :technology-number="projectRow.technologyNumber===null?null:projectRow.technologyNumber" |
| | | @upProcessId="handleUpdateProcessId" @updateTechnologyNumber="handleTechnologyNumberUpdate" @sendData="handleCardData" |
| | | /> |
| | | </div> |
| | | |
| | | <div id="computeDetail"> |
| | | <compute-detail :process-id="projectRow.processId===null?null:projectRow.processId" |
| | | :technology-number="projectRow.technologyNumber===null?null:projectRow.technologyNumber" |
| | | :patch-state="projectRow.patchState===null?null:projectRow.patchState"/> |
| | | </div> |
| | | |
| | | </div> |
| | | </template> |
| | | |
| | | <style scoped> |
| | | #compute{ |
| | | float: left; |
| | | margin-top: -30px; |
| | | margin-bottom: 2%; |
| | | width: 100%; |
| | | height: 37%; |
| | | } |
| | | #computeCard{ |
| | | width: 64%; |
| | | height: 55%; |
| | | float: left; |
| | | } |
| | | #computeDetail{ |
| | | margin-left: 1%; |
| | | float: left; |
| | | width: 35%; |
| | | height: 55%; |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <div> |
| | | <RectRenderer |
| | | v-if="dataLoaded" |
| | | :layoutData="layoutData" |
| | | :gw="1150" |
| | | :gh="850" |
| | | style="width: 1000px; height: 800px; position: relative;" |
| | | /> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted } from 'vue'; |
| | | import RectRenderer from './page/OptimizationRect.vue'; |
| | | import mockLayoutData from '../../../components/pp/MockData'; |
| | | import request from "@/utils/request"; |
| | | import { useI18n } from "vue-i18n"; |
| | | import { ElMessage } from "element-plus"; |
| | | |
| | | const { t } = useI18n(); |
| | | const savedProjectNo = localStorage.getItem('projectNo'); |
| | | const processId = savedProjectNo; |
| | | const layoutData = ref(null); |
| | | const dataLoaded = ref(false); |
| | | |
| | | const selectLayout = () => { |
| | | request.post(`/glassOptimize/selectOptimizeResult/${processId}`) |
| | | .then((res) => { |
| | | if (res.code == 200) { |
| | | try { |
| | | const parsedData = JSON.parse(res.data.data[0].Layouts); |
| | | layoutData.value = parsedData; |
| | | dataLoaded.value = true; |
| | | ElMessage.success("打开版图成功"); |
| | | } catch (error) { |
| | | ElMessage.error("解析数据时出错:", error); |
| | | } |
| | | } else { |
| | | ElMessage.error(t('basicData.msg.requestFailed')); |
| | | } |
| | | }) |
| | | .catch((error) => { |
| | | console.error("请求失败:", error); |
| | | ElMessage.error(t('basicData.msg.requestFailed')); |
| | | }); |
| | | } |
| | | |
| | | onMounted(() => { |
| | | selectLayout(); |
| | | }); |
| | | </script> |
New file |
| | |
| | | <template> |
| | | <div> |
| | | <el-button id="button" type="primary" @click="handlePrint">打印版图</el-button> |
| | | <el-select |
| | | v-model="printLayout" |
| | | placeholder="选择打印布局" |
| | | @change="handleLayoutChange" |
| | | style="width: 150px; margin-bottom: 10px;"> |
| | | <el-option label="四行两列" value="4rows-2cols"></el-option> |
| | | <el-option label="三行两列" value="3rows-2cols"></el-option> |
| | | <el-option label="三行一列" value="3rows-1col"></el-option> |
| | | <el-option label="两行两列" value="2rows-2cols"></el-option> |
| | | </el-select> |
| | | |
| | | <div ref="printContainer" style="position: relative;"> |
| | | <RectRenderer |
| | | ref="rectRenderer" |
| | | :layoutData="layoutData" |
| | | :gw="currentGw" |
| | | :gh="currentGh" |
| | | :printLayout="printLayout" |
| | | :printWidth="currentPrintWidth" |
| | | :printHeight="currentPrintHeight" |
| | | style="position: absolute;" |
| | | v-if="dataLoaded" |
| | | /> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted, watch } from 'vue'; |
| | | import RectRenderer from './page/RectRenderer.vue'; |
| | | import request from "@/utils/request"; |
| | | |
| | | const printLayout = ref('2rows-2cols'); |
| | | const rectRenderer = ref(null); |
| | | const savedProjectNo = localStorage.getItem('projectNo'); |
| | | const processId = savedProjectNo; |
| | | const layoutData = ref(null); |
| | | const dataLoaded = ref(false); |
| | | // 定义不同布局对应的尺寸 |
| | | const layoutDimensions = { |
| | | '4rows-2cols': { width: 1000, height: 1000 }, |
| | | '3rows-2cols': { width: 1000, height: 1000 }, |
| | | '3rows-1col': { width: 1000, height: 1000 }, |
| | | '2rows-2cols': { width: 1200, height: 1200 } |
| | | }; |
| | | |
| | | // 当前布局的尺寸 |
| | | const currentGw = ref(layoutDimensions[printLayout.value].width); |
| | | const currentGh = ref(layoutDimensions[printLayout.value].height); |
| | | const currentPrintWidth = ref(layoutDimensions[printLayout.value].width); |
| | | const currentPrintHeight = ref(layoutDimensions[printLayout.value].height); |
| | | |
| | | const selectLayout = () => { |
| | | request.post(`/glassOptimize/selectOptimizeResult/${processId}`) |
| | | .then((res) => { |
| | | if (res.code == 200) { |
| | | try { |
| | | const parsedData = JSON.parse(res.data.data[0].Layouts); |
| | | layoutData.value = parsedData; |
| | | dataLoaded.value = true; |
| | | } catch (error) { |
| | | console.error("解析布局数据失败:", error); |
| | | } |
| | | } else { |
| | | console.error("请求失败,状态码:", res.code); |
| | | } |
| | | }) |
| | | .catch((error) => { |
| | | console.error("请求失败:", error); |
| | | }); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | selectLayout(); |
| | | }); |
| | | |
| | | const handlePrint = () => { |
| | | if (rectRenderer.value) { |
| | | rectRenderer.value.print(); |
| | | } |
| | | }; |
| | | |
| | | const handleLayoutChange = () => { |
| | | // 更新布局尺寸 |
| | | const dimensions = layoutDimensions[printLayout.value]; |
| | | currentGw.value = dimensions.width; |
| | | currentGh.value = dimensions.height; |
| | | currentPrintWidth.value = dimensions.width; |
| | | currentPrintHeight.value = dimensions.height; |
| | | |
| | | if (rectRenderer.value) { |
| | | rectRenderer.value.updateLayout(); |
| | | } |
| | | }; |
| | | |
| | | // 监听布局变化 |
| | | watch(printLayout, (newVal) => { |
| | | handleLayoutChange(); |
| | | }); |
| | | </script> |
New file |
| | |
| | | <script setup> |
| | | import {ArrowDown, ArrowLeftBold, ArrowRight, Search} from "@element-plus/icons-vue" |
| | | import {useRouter,useRoute,onBeforeRouteUpdate} from "vue-router" |
| | | import {useI18n} from "vue-i18n" |
| | | const { t } = useI18n() |
| | | const router = useRouter() |
| | | const route = useRoute() |
| | | let indexFlag=$ref(1) |
| | | function changeRouter(index){ |
| | | indexFlag=index |
| | | |
| | | } |
| | | |
| | | </script> |
| | | |
| | | <template> |
| | | <div id="main-div"> |
| | | <div id="div-title"> |
| | | <el-breadcrumb :separator-icon="ArrowRight"> |
| | | <el-breadcrumb-item @click="changeRouter(1)" :class="indexFlag===1?'indexTag':''" :to="{ path: '/main/glassOptimizeThirdParty/optimizeProject' }">{{'工程信息'}}</el-breadcrumb-item> |
| | | <el-breadcrumb-item v-show="false" @click="changeRouter(2)" :class="indexFlag===2?'indexTag':''" :to="{ path: '/main/order/createOrder' }">{{$t('order.page.createOrder')}}</el-breadcrumb-item> |
| | | <el-breadcrumb-item @click="changeRouter(5)" :class="indexFlag===5?'indexTag':''" :to="{ path: '/main/glassOptimizeThirdParty/OptimizeControl' }">数控 |
| | | </el-breadcrumb-item> |
| | | <el-breadcrumb-item @click="changeRouter(6)" :class="indexFlag===6?'indexTag':''" :to="{ path: '/main/glassOptimizeThirdParty/optimizeparms' }">设置 |
| | | </el-breadcrumb-item> |
| | | <el-breadcrumb-item></el-breadcrumb-item> |
| | | </el-breadcrumb> |
| | | </div> |
| | | |
| | | <div id="main-body"> |
| | | <router-view :key="route.fullPath" /> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <style scoped> |
| | | #main-div{ |
| | | width: 99%; |
| | | height: 100%; |
| | | } |
| | | #div-title{ |
| | | height: 2%; |
| | | width: 100%; |
| | | } |
| | | #searchButton{ |
| | | margin-top: -5px; |
| | | margin-left: 1rem; |
| | | } |
| | | #searchButton1{ |
| | | //margin-left: 10rem; |
| | | } |
| | | /*main-body样式*/ |
| | | #main-body{ |
| | | width: 100%; |
| | | height: 95%; |
| | | margin-top: 1%; |
| | | } |
| | | #select{ |
| | | margin-left:0.5rem; |
| | | } |
| | | :deep(.indexTag .el-breadcrumb__inner){ |
| | | color: #5CADFE !important; |
| | | } |
| | | |
| | | .example-showcase .el-dropdown-link { |
| | | cursor: pointer; |
| | | color: var(--el-color-primary); |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <div > |
| | | <RectRenderer |
| | | :layoutData="layoutData" |
| | | :gw="1400" |
| | | :gh="1100" |
| | | style="width: 1500px; height: 800px; position: relative;" |
| | | v-if="dataLoaded" |
| | | /> |
| | | |
| | | |
| | | </div> |
| | | <button @click="submitLayouts" style="position: fixed; bottom: 20px; right: 20px; padding: 10px; background: #4CAF50; color: white; border: none; border-radius: 5px; cursor: pointer;"> |
| | | 保存OPT |
| | | </button> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref,onMounted } from 'vue'; |
| | | import RectRenderer from './page/RectRenderer.vue'; |
| | | import mockLayoutData from '../../../components/pp/MockData'; |
| | | import request from "@/utils/request"; |
| | | import { useI18n } from "vue-i18n"; |
| | | import { ElMessage } from "element-plus"; |
| | | |
| | | const { t } = useI18n(); |
| | | // const layoutData = ref(mockLayoutData); |
| | | |
| | | const savedProjectNo = localStorage.getItem('projectNo'); |
| | | const processId = savedProjectNo; |
| | | const layoutData = ref(null); |
| | | const dataLoaded = ref(false); |
| | | |
| | | |
| | | const selectLayout = () => { |
| | | request.post(`/glassOptimize/selectOptimizeResult/${processId}`) |
| | | .then((res) => { |
| | | if (res.code == 200) { |
| | | try { |
| | | const parsedData = JSON.parse(res.data.data[0].Layouts); |
| | | layoutData.value = parsedData; |
| | | dataLoaded.value = true; |
| | | } catch (error) { |
| | | |
| | | |
| | | } |
| | | } else { |
| | | |
| | | } |
| | | }) |
| | | .catch((error) => { |
| | | console.error("请求失败:", error); |
| | | ElMessage.error(t('basicData.msg.requestFailed')); |
| | | }); |
| | | } |
| | | |
| | | onMounted(() => { |
| | | selectLayout(); |
| | | |
| | | }); |
| | | |
| | | const submitLayouts = async () => { |
| | | try { |
| | | const response = await request.post('/glassOptimize/generateOpt', { |
| | | Layouts: layoutData.value.Layouts |
| | | }, { |
| | | headers: { |
| | | 'Content-Type': 'application/json' |
| | | }, |
| | | responseType: 'blob' // 以 blob 形式接收响应 |
| | | }); |
| | | |
| | | // 处理下载 |
| | | const downloadUrl = window.URL.createObjectURL(response); |
| | | const a = document.createElement('a'); |
| | | a.href = downloadUrl; |
| | | a.download = 'output.opt'; |
| | | document.body.appendChild(a); |
| | | a.click(); |
| | | document.body.removeChild(a); |
| | | window.URL.revokeObjectURL(downloadUrl); |
| | | ElMessage.success('OPT文件下载成功,请选择文件路径'); |
| | | } catch (error) { |
| | | console.error('下载失败:', error); |
| | | // 显示错误消息给用户 |
| | | ElMessage.error('下载失败,请稍后再试'); |
| | | } |
| | | }; |
| | | |
| | | |
| | | |
| | | </script> |
| | | |
New file |
| | |
| | | <template> |
| | | <div > |
| | | <RectRenderer |
| | | :layoutData="layoutData" |
| | | :gw="1400" |
| | | :gh="1100" |
| | | style="width: 1500px; height: 800px; position: relative;" |
| | | /> |
| | | |
| | | |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref } from 'vue'; |
| | | import RectRenderer from './page/RectRenderer.vue'; |
| | | import mockLayoutData from '../../../components/pp/MockData'; |
| | | |
| | | const layoutData = ref(mockLayoutData); |
| | | |
| | | |
| | | </script> |
| | | |
New file |
| | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, watch } from 'vue'; |
| | | import { useI18n } from "vue-i18n"; |
| | | import { ElMessage } from "element-plus"; |
| | | import request from "@/utils/request"; |
| | | import useUserInfoStore from "@/stores/userInfo"; |
| | | |
| | | const { t } = useI18n(); |
| | | const userStore = useUserInfoStore() |
| | | const username = userStore.user.userName |
| | | |
| | | const currentComponent = ref('optimization'); |
| | | |
| | | const settings = reactive({ |
| | | optimization: {}, |
| | | display: {}, |
| | | cutting: {}, |
| | | server: {}, |
| | | tempering: {} |
| | | }); |
| | | |
| | | const selectComponent = (component) => { |
| | | currentComponent.value = component; |
| | | }; |
| | | |
| | | const saveToDatabase = () => { |
| | | request.post(`/glassOptimize/optimizeParms`, settings).then((res) => { |
| | | if (res.code == 200 && res.data === true) { |
| | | ElMessage.success(t('basicData.msg.saveSuccess')) |
| | | } else { |
| | | ElMessage.warning(res.msg) |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const fetchSettings = async (username) => { |
| | | try { |
| | | const response = await request.post(`/glassOptimize/selectOptimizeParms/${username}`); |
| | | if (response.code == 200) { |
| | | if (!response.data) { |
| | | console.error('响应数据为空'); |
| | | return; |
| | | } |
| | | const parsedData = JSON.parse(response.data); |
| | | Object.assign(settings, parsedData); |
| | | console.log('设置已更新:', settings); |
| | | } else { |
| | | console.error('请求失败,状态码:', response.code); |
| | | } |
| | | } catch (error) { |
| | | console.error('请求发生错误:', error); |
| | | } |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | fetchSettings(username); |
| | | }); |
| | | |
| | | // 参数映射表,用于将英文参数名映射为中文名称和类型 |
| | | const paramMapping = { |
| | | yShapeJoinOptimization: { name: '异形拼接优化', type: 'checkbox' }, |
| | | autoMiddleEmptyPairing: { name: '自动中空配对', type: 'checkbox' }, |
| | | smallPieceRotationProhibited: { name: '小片禁止旋转', type: 'checkbox' }, |
| | | maxFramesOnSite: { name: '现场最大可放架子数量', type: 'text' }, |
| | | bendEdgeDistance: { name: '掰边距(mm)', type: 'text' }, |
| | | positiveTolerance: { name: '正公差(mm)', type: 'text' }, |
| | | negativeTolerance: { name: '负公差(mm)', type: 'text' }, |
| | | cutterOriginPosition: { name: '切割机原点位置', type: 'select' }, |
| | | uniformShapeEdgeTrimAmount: { name: '统一设置异形修边量', type: 'text' }, |
| | | edgeTrimMode: { name: '修边模式', type: 'select' }, |
| | | parallelOptimizationChannels: { name: '并行优化的通道数量', type: 'text' }, |
| | | optimizationIterations: { name: '重复优化次数', type: 'text' }, |
| | | finishedProductGrindingAmount: { name: '成品默认磨量', type: 'text' }, |
| | | rawPieceEdgeTrimAmount: { name: '原片默认修边量', type: 'text' }, |
| | | finishedSinglePieceBelowGrindingAmount: { name: '成品单片小于多少时磨量为', type: 'text' } |
| | | }; |
| | | |
| | | // 动态生成参数列表 |
| | | const parameterList = ref(Object.keys(paramMapping).map(key => ({ |
| | | key, |
| | | ...paramMapping[key] |
| | | }))); |
| | | </script> |
| | | |
| | | <template> |
| | | <div class="settings-container"> |
| | | <div class="sidebar"> |
| | | <div |
| | | @click="selectComponent('optimization')" |
| | | :class="{ active: currentComponent === 'optimization' }" |
| | | class="sidebar-item" |
| | | > |
| | | <div class="sidebar-header">优化参数</div> |
| | | <div class="sidebar-content">优化参数设置</div> |
| | | </div> |
| | | |
| | | <div |
| | | @click="selectComponent('display')" |
| | | :class="{ active: currentComponent === 'display' }" |
| | | class="sidebar-item" |
| | | > |
| | | <div class="sidebar-header">版图显示</div> |
| | | <div class="sidebar-content">版图显示设置</div> |
| | | </div> |
| | | |
| | | <div |
| | | @click="selectComponent('cutting')" |
| | | :class="{ active: currentComponent === 'cutting' }" |
| | | class="sidebar-item" |
| | | > |
| | | <div class="sidebar-header">刀路</div> |
| | | <div class="sidebar-content">刀路设置</div> |
| | | </div> |
| | | |
| | | <div |
| | | @click="selectComponent('server')" |
| | | :class="{ active: currentComponent === 'server' }" |
| | | class="sidebar-item" |
| | | > |
| | | <div class="sidebar-header">输出</div> |
| | | <div class="sidebar-content">结果输出设置</div> |
| | | </div> |
| | | |
| | | <div |
| | | @click="selectComponent('tempering')" |
| | | :class="{ active: currentComponent === 'tempering' }" |
| | | class="sidebar-item" |
| | | > |
| | | <div class="sidebar-header">钢化</div> |
| | | <div class="sidebar-content">钢化设置</div> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="main-content"> |
| | | <template v-if="currentComponent === 'display'"> |
| | | <div class="display-settings"> |
| | | <h2>版图显示设置</h2> |
| | | <div class="form-group"> |
| | | <label>矩形成品显示颜色</label> |
| | | <input type="color" v-model="settings.display.themeColor" /> |
| | | </div> |
| | | <div class="form-group"> |
| | | <label>成品尺寸包含磨边量</label> |
| | | <input type="checkbox" v-model="settings.display.includeProductEdge" /> |
| | | </div> |
| | | <div class="form-group"> |
| | | <label>异形尺寸包含磨边量</label> |
| | | <input type="checkbox" v-model="settings.display.includeIrregularEdge" /> |
| | | </div> |
| | | <div class="form-group"> |
| | | <label>按架号合并版图</label> |
| | | <input type="checkbox" v-model="settings.display.mergeByFrameNumber" /> |
| | | </div> |
| | | <div style="border: 1px solid #d2d0d0; margin-top: 20px;"> |
| | | <div style="background-color: #D5EAFF;">小片信息</div> |
| | | <div class="form-group"> |
| | | <label>架号</label> |
| | | <input type="checkbox" v-model="settings.display.frameNumber" /> |
| | | </div> |
| | | <div class="form-group"> |
| | | <label>订单编号</label> |
| | | <input type="checkbox" v-model="settings.display.orderNumber" /> |
| | | </div> |
| | | <div class="form-group"> |
| | | <label>产品名称</label> |
| | | <input type="checkbox" v-model="settings.display.productName" /> |
| | | </div> |
| | | <div class="form-group"> |
| | | <label>加工信息</label> |
| | | <input type="checkbox" v-model="settings.display.processingInfo" /> |
| | | </div> |
| | | <div class="form-group"> |
| | | <label>备注</label> |
| | | <input type="checkbox" v-model="settings.display.remarks" /> |
| | | </div> |
| | | <div class="form-group"> |
| | | <label>楼层编号</label> |
| | | <input type="checkbox" v-model="settings.display.floorNumber" /> |
| | | </div> |
| | | <div class="form-group"> |
| | | <label>边长</label> |
| | | <input type="checkbox" v-model="settings.display.edgeLength" /> |
| | | </div> |
| | | <div class="form-group"> |
| | | <label>工艺</label> |
| | | <input type="checkbox" v-model="settings.display.manufacturingProcess" /> |
| | | </div> |
| | | <div class="form-group"> |
| | | <label>ID</label> |
| | | <input type="checkbox" v-model="settings.display.identifier" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <template v-else-if="currentComponent === 'cutting'"> |
| | | <div class="display-settings"> |
| | | <h2>刀路设置</h2> |
| | | <div class="form-group"> |
| | | <label>正向切割</label> |
| | | <input type="checkbox" v-model="settings.cutting.cutting_direction" /> |
| | | </div> |
| | | <div class="form-group"> |
| | | <label>显示切割方向</label> |
| | | <input type="checkbox" v-model="settings.cutting.show_cutting_path" /> |
| | | </div> |
| | | <div class="form-group"> |
| | | <label>强制弧线和直线连续切割</label> |
| | | <input type="checkbox" v-model="settings.cutting.force_continuous_cutting" /> |
| | | </div> |
| | | <div class="form-group"> |
| | | <label>孔洞最小切割半径</label> |
| | | <input type="number" v-model="settings.cutting.min_radius" /> |
| | | </div> |
| | | <div class="form-group"> |
| | | <label>两条平行线最小间隔</label> |
| | | <input type="number" v-model="settings.cutting.min_parallel_offset" /> |
| | | </div> |
| | | <div class="form-group"> |
| | | <label>T型退缩</label> |
| | | <input type="number" v-model="settings.cutting.t_shaped_recess" /> |
| | | </div> |
| | | <div class="form-group"> |
| | | <label>下刀间距</label> |
| | | <input type="number" v-model="settings.cutting.down_cut_spacing" /> |
| | | </div> |
| | | <div class="form-group"> |
| | | <label>抬刀退缩</label> |
| | | <input type="number" v-model="settings.cutting.lift_retract" /> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <template v-else-if="currentComponent === 'server'"> |
| | | <div class="display-settings"> |
| | | <h2>结果输出设置</h2> |
| | | <div class="form-group"> |
| | | <label>工程文件保存路径</label> |
| | | <input type="text" v-model="settings.server.output_format" /> |
| | | </div> |
| | | <div class="form-group"> |
| | | <label>切割代码保存路径</label> |
| | | <input type="text" v-model="settings.server.cutting_code_save_path" /> |
| | | </div> |
| | | <div class="form-group"> |
| | | <label>保存文件后打开所在文件夹</label> |
| | | <input type="text" v-model="settings.server.open_folder_after_save" /> |
| | | </div> |
| | | <div class="form-group"> |
| | | <label>G代码文件格式</label> |
| | | <input type="text" v-model="settings.server.g_code_file_format" /> |
| | | </div> |
| | | <div class="form-group"> |
| | | <label>TRF文件保存路径</label> |
| | | <input type="text" v-model="settings.server.trf_file_save_path" /> |
| | | </div> |
| | | <div class="form-group"> |
| | | <label>保特罗文件保存路径</label> |
| | | <input type="text" v-model="settings.server.btl_file_save_path" /> |
| | | </div> |
| | | <div class="form-group"> |
| | | <label>OPTIMA文件保存路径</label> |
| | | <input type="text" v-model="settings.server.optima_file_save_path" /> |
| | | </div> |
| | | <div class="form-group"> |
| | | <label>优化深度限制启用</label> |
| | | <input type="text" v-model="settings.server.optimization_depth_limit_enable" /> |
| | | </div> |
| | | <div class="form-group"> |
| | | <label>原片切材率计算</label> |
| | | <input type="text" v-model="settings.server.original_sheet_material_calculation" /> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <template v-else-if="currentComponent === 'tempering'"> |
| | | <div class="display-settings"> |
| | | <h2>钢化设置</h2> |
| | | <div class="form-group"> |
| | | <label>炉长(mm)</label> |
| | | <input type="number" v-model="settings.tempering.furnaceLength" step="1" /> |
| | | </div> |
| | | <div class="form-group"> |
| | | <label>炉宽(mm)</label> |
| | | <input type="number" v-model="settings.tempering.furnaceWidth" step="1" /> |
| | | </div> |
| | | <div class="form-group"> |
| | | <label>最大装载率(%)</label> |
| | | <input type="number" v-model="settings.tempering.maxLoadingRate" step="1" /> |
| | | </div> |
| | | <div class="form-group"> |
| | | <label>混乱程度(%)</label> |
| | | <input type="number" v-model="settings.tempering.chaosLevel" step="1" /> |
| | | </div> |
| | | <div class="form-group"> |
| | | <label>钢化加热时间(秒)</label> |
| | | <input type="number" v-model="settings.tempering.temperingTime" step="1" /> |
| | | </div> |
| | | <div class="form-group"> |
| | | <label>默认钢化推荐模式</label> |
| | | <select v-model="settings.tempering.defaultTemperingMode"> |
| | | <option value="auto">自动推荐</option> |
| | | <option value="manual">手动选择</option> |
| | | </select> |
| | | </div> |
| | | <div class="form-group"> |
| | | <label>最大面积设置(㎡)</label> |
| | | <input type="number" v-model="settings.tempering.maxArea" step="0.1" /> |
| | | </div> |
| | | <div class="form-group"> |
| | | <label>最大片数设置(片)</label> |
| | | <input type="number" v-model="settings.tempering.maxPieceCount" step="1" /> |
| | | </div> |
| | | <div class="form-group"> |
| | | <label>长轴默认间隔(mm)</label> |
| | | <input type="number" v-model="settings.tempering.xAxisInterval" step="1" /> |
| | | </div> |
| | | <div class="form-group"> |
| | | <label>宽轴默认间隔(mm)</label> |
| | | <input type="number" v-model="settings.tempering.yAxisInterval" step="1" /> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <template v-else> |
| | | <div class="display-settings"> |
| | | <h2>优化参数设置</h2> |
| | | <div class="parameter-list"> |
| | | <div |
| | | class="form-group" |
| | | v-for="(param, index) in parameterList" |
| | | :key="index" |
| | | > |
| | | <label class="parameter-name">{{ param.name }}</label> |
| | | <div class="parameter-control"> |
| | | <input |
| | | v-if="param.type === 'checkbox'" |
| | | type="checkbox" |
| | | :checked="settings.optimization[param.key] === '1'" |
| | | @click="() => { settings.optimization[param.key] = settings.optimization[param.key] === '1' ? '0' : '1' }" |
| | | > |
| | | <select |
| | | v-else-if="param.type === 'select'" |
| | | :name="param.key" |
| | | class="cs" |
| | | > |
| | | <option |
| | | v-for="option in getOptions(param.key)" |
| | | :key="option.value" |
| | | :selected="option.value === settings.optimization[param.key]" |
| | | > |
| | | {{ option.label }} |
| | | </option> |
| | | </select> |
| | | <input |
| | | v-else |
| | | :type="param.type || 'text'" |
| | | :name="param.key" |
| | | class="cs" |
| | | v-model="settings.optimization[param.key]" |
| | | > |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | </div> |
| | | |
| | | <button class="button" @click="saveToDatabase">保存</button> |
| | | </div> |
| | | </template> |
| | | |
| | | <style> |
| | | .settings-container { |
| | | display: flex; |
| | | gap: 20px; |
| | | padding: 20px; |
| | | background-color: #f0f2f5; |
| | | } |
| | | |
| | | .sidebar { |
| | | width: 300px; |
| | | background-color: white; |
| | | padding: 15px; |
| | | border-radius: 8px; |
| | | box-shadow: 0 2px 4 rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | .sidebar-item { |
| | | cursor: pointer; |
| | | padding: 10px; |
| | | margin-bottom: 5px; |
| | | border-radius: 6px; |
| | | transition: all 0.3s ease; |
| | | } |
| | | |
| | | .sidebar-item.active { |
| | | background-color: #f8f9fa; |
| | | transform: translateX(5px); |
| | | } |
| | | |
| | | .sidebar-header { |
| | | font-size: 12px; |
| | | color: #666; |
| | | margin-bottom: 5px; |
| | | } |
| | | |
| | | .sidebar-content { |
| | | font-size: 14px; |
| | | color:#333; |
| | | } |
| | | |
| | | .main-content { |
| | | flex-grow: 1; |
| | | padding: 20px; |
| | | } |
| | | |
| | | button { |
| | | padding: 10px 20px; |
| | | background-color: #5cadfe; |
| | | color: white; |
| | | border: none; |
| | | border-radius: 4px; |
| | | cursor: pointer; |
| | | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | @media (max-width: 768px) { |
| | | .settings-container { |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .sidebar { |
| | | order: -1; |
| | | } |
| | | |
| | | .main-content { |
| | | margin-top: 20px; |
| | | } |
| | | } |
| | | |
| | | .form-group { |
| | | margin-bottom: 15px; |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 10px; |
| | | } |
| | | |
| | | .form-group label { |
| | | font-weight: normal; |
| | | min-width: 120px; |
| | | } |
| | | |
| | | .form-group input, |
| | | .form-group select { |
| | | flex: 1; |
| | | padding: 5px; |
| | | border: 1px solid #ddd; |
| | | border-radius: 4px; |
| | | } |
| | | |
| | | .form-group select { |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .display-settings { |
| | | background-color: white; |
| | | padding: 20px; |
| | | border-radius: 8px; |
| | | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | .parameter-list { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 0px; |
| | | } |
| | | |
| | | .parameter-item { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 0px; |
| | | } |
| | | |
| | | .parameter-name { |
| | | font-weight: normal; |
| | | min-width: 120px; |
| | | } |
| | | |
| | | .parameter-control { |
| | | flex: 1; |
| | | } |
| | | |
| | | .button { |
| | | text-align: center; |
| | | width: 70px; |
| | | height: 28px; |
| | | border: none; |
| | | background-color: #5CADFE; |
| | | box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0), 0 6px 5px 0 rgba(0, 0, 0, 0.19); |
| | | margin-left: 500px; |
| | | } |
| | | </style> |
| | | |
| | | <script> |
| | | // 辅助函数,根据参数键获取选项 |
| | | function getOptions(key) { |
| | | const optionsMapping = { |
| | | cutterOriginPosition: [ |
| | | { value: 'topLeft', label: '左上角' }, |
| | | { value: 'bottomLeft', label: '左下角' }, |
| | | { value: 'topRight', label: '右上角' }, |
| | | { value: 'bottomRight', label: '右下角' } |
| | | ], |
| | | edgeTrimMode: [ |
| | | { value: 'intelligent', label: '智能修边' }, |
| | | { value: 'complete', label: '完全修边' } |
| | | ] |
| | | }; |
| | | return optionsMapping[key] || []; |
| | | } |
| | | </script> |
New file |
| | |
| | | <script setup> |
| | | import {ref} from "vue"; |
| | | |
| | | const layout=ref([ |
| | | {"x":0,"y":0,"w":50,"h":50,"i":"0"}, |
| | | /*{"x":2,"y":0,"w":2,"h":4,"i":"1"}, |
| | | {"x":4,"y":0,"w":2,"h":5,"i":"2"}, |
| | | {"x":6,"y":0,"w":2,"h":3,"i":"3"}, |
| | | {"x":8,"y":0,"w":2,"h":3,"i":"4"}, |
| | | {"x":10,"y":0,"w":2,"h":3,"i":"5"}, |
| | | {"x":0,"y":5,"w":2,"h":5,"i":"6"}, |
| | | {"x":2,"y":5,"w":2,"h":5,"i":"7"}, |
| | | {"x":4,"y":5,"w":2,"h":5,"i":"8"}, |
| | | {"x":6,"y":3,"w":2,"h":4,"i":"9"}, |
| | | {"x":8,"y":4,"w":2,"h":4,"i":"10"}, |
| | | {"x":10,"y":4,"w":2,"h":4,"i":"11"}, |
| | | {"x":0,"y":10,"w":2,"h":5,"i":"12"}, |
| | | {"x":2,"y":10,"w":2,"h":5,"i":"13"}, |
| | | {"x":4,"y":8,"w":2,"h":4,"i":"14"}, |
| | | {"x":6,"y":8,"w":2,"h":4,"i":"15"}, |
| | | {"x":8,"y":10,"w":2,"h":5,"i":"16"}, |
| | | {"x":10,"y":4,"w":2,"h":2,"i":"17"}, |
| | | {"x":0,"y":9,"w":2,"h":3,"i":"18"}, |
| | | {"x":2,"y":6,"w":2,"h":2,"i":"19"}*/ |
| | | ]) |
| | | |
| | | const layoutUpdated = (newLayout) => { |
| | | //checkLayoutBounds(newLayout) |
| | | } |
| | | const checkLayoutBounds = (layout1) => { |
| | | layout1.forEach(item => { |
| | | // 检查边界,例如确保x和y不小于0,并且w和h不超出最大值等。 |
| | | if (item.x < 0) item.x = 0; |
| | | if (item.y < 0) item.y = 0; |
| | | if (item.w > 12) item.w = 12; // 假设最大列数为12 |
| | | if (item.h > 10) item.h = 10; // 假设最大行为10(根据需要调整) |
| | | }); |
| | | layout.value = layout1; // 应用边界检查后的布局 |
| | | console.log(layout1) |
| | | } |
| | | const moveEvent = (i, newX, newY) => { |
| | | console.log(`移动元素 ${i} 到 (${newX}, ${newY})`) |
| | | } |
| | | |
| | | </script> |
| | | |
| | | <template> |
| | | <grid-layout |
| | | style="border:1px solid black;height: 244px;width: 366px;overflow: hidden;grid-template-columns: 1px" |
| | | :layout.sync="layout" |
| | | :col-num="366" |
| | | :row-height="1" |
| | | :is-draggable="true" |
| | | :is-resizable="false" |
| | | :is-mirrored="false" |
| | | :vertical-compact="true" |
| | | :margin="[0, 0]" |
| | | :use-css-transforms="true" |
| | | :autoSize="false" |
| | | @layout-updated="layoutUpdated" |
| | | > |
| | | |
| | | <grid-item v-for="item in layout" |
| | | :x="item.x" |
| | | :y="item.y" |
| | | :w="item.w" |
| | | :h="item.h" |
| | | :i="item.i" |
| | | :key="item.i" |
| | | @move="moveEvent" |
| | | style="background-color: white" |
| | | > |
| | | {{item.i}} |
| | | </grid-item> |
| | | </grid-layout> |
| | | </template> |
| | | |
| | | <style scoped> |
| | | |
| | | </style> |
New file |
| | |
| | | <script setup> |
| | | import GlassInventory from "@/views/pp/glassOptimizeThirdParty/page/GlassInventory.vue" |
| | | import ProjectDetail from "@/views/pp/glassOptimizeThirdParty/page/ProjectDetail.vue" |
| | | import GlassComputed from "@/views/pp/glassOptimizeThirdParty/GlassComputed.vue"; |
| | | import ProjectMange from "@/views/pp/glassOptimizeThirdParty/ProjectMange.vue"; |
| | | |
| | | import {ref} from "vue"; |
| | | let dialogVisible = ref(false) |
| | | let detailPage = ref(0) |
| | | const changeDialog = (value) => { |
| | | dialogVisible.value = true |
| | | detailPage.value = value |
| | | } |
| | | |
| | | //从工程管理获取工程号,并跳转 |
| | | const projectNumber = ref(); |
| | | const switchDialog = (number) => { |
| | | projectNumber.value = number; |
| | | detailPage.value = 3; |
| | | }; |
| | | |
| | | //工程管理关闭弹窗 |
| | | const handlePopupClose = () => { |
| | | dialogVisible.value = false; |
| | | }; |
| | | |
| | | //获取SetTrimming的值 |
| | | const dataForGlassInventory = ref(); |
| | | const handleProjectDetailData = (data) => { |
| | | dataForGlassInventory.value = data; |
| | | }; |
| | | |
| | | //获取GlassInventory的值(查询库存) |
| | | const sendDataGlassInventory = ref() |
| | | const handleInventoyData = (selectedLabel1,selectedLabel2,type) => { |
| | | sendDataGlassInventory.value = { |
| | | selectedLabel1, selectedLabel2,type |
| | | }; |
| | | }; |
| | | |
| | | //右键菜单打开修边 |
| | | const isTrimmingDialogVisible = ref(false); |
| | | const sendTrimming = (value) => { |
| | | isTrimmingDialogVisible.value = value; |
| | | }; |
| | | |
| | | </script> |
| | | |
| | | <template > |
| | | <div style="width: 100%;height: 100%"> |
| | | <div id="main-body"> |
| | | <project-detail @changeDialog="changeDialog" |
| | | @forward-data-to-grandparent="handleProjectDetailData" |
| | | @send-inventory-to-op="handleInventoyData" |
| | | :TrimmingDialogVisible="isTrimmingDialogVisible" /> |
| | | </div> |
| | | <div id="main-footer"> |
| | | <glass-inventory :receivedData="dataForGlassInventory" |
| | | :InventoryData="sendDataGlassInventory" |
| | | @select-trimming="sendTrimming"/> |
| | | </div> |
| | | |
| | | |
| | | <el-dialog |
| | | v-model="dialogVisible" |
| | | :title=" detailPage ===2? '工程管理' : detailPage ===3? '模拟计算':''" |
| | | destroy-on-close |
| | | style="width: 90%;height:90%;margin-top: 3vh" |
| | | z-index="100" |
| | | > |
| | | <project-mange v-if="detailPage===2" @switch-dialog="switchDialog" @closeDetailPage="handlePopupClose"/> |
| | | <glass-computed v-if="detailPage===3" :project="projectNumber"/> |
| | | <div v-else></div> |
| | | </el-dialog> |
| | | |
| | | </div> |
| | | </template> |
| | | |
| | | <style scoped> |
| | | #main-body{ |
| | | width: 100%; |
| | | height: 60%; |
| | | } |
| | | #main-footer{ |
| | | margin-top: 10px; |
| | | width: 100%; |
| | | height: calc(40% - 10px); |
| | | } |
| | | :deep( .el-dialog__body){ |
| | | height: calc(100% - 55px); |
| | | width: 100%; |
| | | margin-top: 30px; |
| | | //padding: 0; |
| | | } |
| | | |
| | | </style> |
New file |
| | |
| | | <script setup> |
| | | import ProcessCard from "@/views/pp/glassOptimize/page/ProcessCard.vue"; |
| | | import ProcessCardDetail from "@/views/pp/glassOptimize/page/ProcessCardDetail.vue"; |
| | | import ProjectList from "@/views/pp/glassOptimize/page/ProjectList.vue"; |
| | | |
| | | import {defineEmits, nextTick, onMounted, reactive, ref} from "vue"; |
| | | import {useI18n} from "vue-i18n"; |
| | | import GlassComputed from "@/views/pp/glassOptimize/GlassComputed.vue"; |
| | | import ProjectMange from "@/views/pp/glassOptimize/ProjectMange.vue"; |
| | | import request from "@/utils/request"; |
| | | import {ElMessage, ElMessageBox} from "element-plus"; |
| | | import deepClone from "@/utils/deepClone"; |
| | | import {useRouter} from "vue-router"; |
| | | import {changeFilterEvent, filterChanged} from "@/hook"; |
| | | |
| | | const router = useRouter(); |
| | | let projectRow = ref({ |
| | | processId:null, |
| | | technologyNumber:null |
| | | }) |
| | | |
| | | const processCardRef=ref(null) |
| | | |
| | | const handleProcessIdUpdate = newProcessId => { |
| | | projectRow.value.processId = newProcessId; |
| | | |
| | | }; |
| | | |
| | | const handleTechnologyNumberUpdate = newTechnologyNumber => { |
| | | projectRow.value.technologyNumber = newTechnologyNumber; |
| | | }; |
| | | |
| | | const updateState = state => { |
| | | getProject(); |
| | | |
| | | }; |
| | | |
| | | |
| | | const { t } = useI18n() |
| | | |
| | | const xGrid = ref() |
| | | |
| | | |
| | | const gridOptions = reactive({ |
| | | height:'100%', |
| | | loading: false, |
| | | border: "full",//表格加边框 |
| | | keepSource: true,//保持源数据 |
| | | align: 'center',//文字居中 |
| | | stripe:true,//斑马纹 |
| | | rowConfig: {isCurrent: true, isHover: true,height: 30, useKey: true},//鼠标移动或选择高亮 |
| | | id: 'ProjectList', |
| | | scrollX:{enabled: true}, |
| | | scrollY:{ enabled: true ,gt:0},//开启虚拟滚动 |
| | | showOverflow:true, |
| | | columnConfig: { |
| | | resizable: true, |
| | | useKey: true |
| | | }, |
| | | filterConfig: { //筛选配置项 |
| | | //remote: true |
| | | }, |
| | | customConfig: { |
| | | storage: true |
| | | }, |
| | | editConfig: { |
| | | trigger: 'click', |
| | | mode: 'row', |
| | | showStatus: true |
| | | }, |
| | | columns:[ |
| | | {field: 'id',width: 150, title: 'ID'}, |
| | | {field: 'projectNumber',width: 150, title: '工程号',filters:[{ data: '' }],slots: { filter: 'num1_filter' }, sortable: true,filterMethod:filterChanged}, |
| | | {field: 'project_name',width: 150, title: '项目名称',filters:[{ data: '' }],slots: { filter: 'num1_filter' }, sortable: true,filterMethod:filterChanged}, |
| | | {field: 'glass_type',width: 150, title: '膜系',filters:[{ data: '' }],slots: { filter: 'num1_filter' }, sortable: true,filterMethod:filterChanged}, |
| | | {field: 'glass_thickness',width: 150, title: '厚度',filters:[{ data: '' }],slots: { filter: 'num1_filter' }, sortable: true,filterMethod:filterChanged}, |
| | | {field: 'type',width: 150, title: '类型',filters:[{ data: '' }],slots: { filter: 'num1_filter' }, sortable: true,filterMethod:filterChanged}, |
| | | {field: 'state',width: 150, title: '状态',filters:[{ data: '' }],slots: { filter: 'num1_filter' }, sortable: true,filterMethod:filterChanged}, |
| | | {field: 'quantity',width: 150, title: t('order.quantity')}, |
| | | {field: 'glass_total_area',width: 150, title: t('order.grossArea'),filters:[{ data: '' }],slots: { filter: 'num1_filter' }, sortable: true,filterMethod:filterChanged}, |
| | | {field: 'process_qty',width: 150, title: '流程数量',filters:[{ data: '' }],slots: { filter: 'num1_filter' }, sortable: true,filterMethod:filterChanged}, |
| | | {field: 'process_cards',width: 150, title: '流程卡号',filters:[{ data: '' }],slots: { filter: 'num1_filter' }, sortable: true,filterMethod:filterChanged}, |
| | | |
| | | ],//表头参数 |
| | | data:null,//表格数据 |
| | | toolbarConfig: { |
| | | buttons: [ |
| | | ], |
| | | import: false, |
| | | // export: true, |
| | | // print: true, |
| | | zoom: true, |
| | | custom: true |
| | | }, |
| | | //右键菜单选项 |
| | | menuConfig: { |
| | | body: { |
| | | options: [ |
| | | [ |
| | | {code: 'openProject', name: '打开工程', prefixIcon: 'vxe-icon-folder-open'}, |
| | | {code: 'updateProject', name: '修改工程', prefixIcon: 'vxe-icon-folder-open'}, |
| | | {code: 'compute', name: '模拟计算', prefixIcon: 'vxe-icon-subtable'}, |
| | | {code: 'delProject', name: '删除工程', prefixIcon: 'vxe-icon-delete'}, |
| | | ], |
| | | [] |
| | | ] |
| | | } |
| | | } |
| | | |
| | | }) |
| | | |
| | | // 定义操作配置对象数组,集中管理不同操作选项对应的参数 |
| | | const operationConfigs = [ |
| | | { |
| | | code: 'openProject', // 打开工程 |
| | | initialState: ['1'], // |
| | | targetState: null, |
| | | successMsg: '已打开!', |
| | | checkMessage: '当前工程状态不符合条件,请确认工程状态后再操作!', |
| | | requiresRow: true, |
| | | openFile: async ({row}) => { |
| | | const projectNumber = row.projectNumber; |
| | | const thickness = row.glass_thickness; |
| | | const glassType = row.glass_type; |
| | | await router.push({ |
| | | name: 'optimizeInfo', |
| | | params: { |
| | | projectNo: projectNumber, |
| | | thickNess: thickness, |
| | | model: glassType |
| | | } |
| | | }); |
| | | } |
| | | }, |
| | | { |
| | | code: 'compute', // 打开模拟计算操作 |
| | | initialState: ['1'], // |
| | | targetState: null, |
| | | successMsg: '模拟计算已启动!', |
| | | checkMessage: '当前工程状态不符合模拟计算条件,请确认工程状态后再操作!', |
| | | requiresRow: true, |
| | | actionFunction: async ({row}) => { |
| | | const projectNo = row.projectNumber; |
| | | emit('switch-dialog', row); |
| | | } |
| | | }, |
| | | { |
| | | code: 'delProject', |
| | | initialState: ['1'], |
| | | targetState: null, |
| | | successMsg: '工程删除成功!', |
| | | checkMessage: '当前工程状态不符合删除条件,请确认工程状态后再操作!', |
| | | }, |
| | | { |
| | | code: 'updateProject', |
| | | initialState: ['1'], |
| | | targetState: null, |
| | | successMsg: '', |
| | | checkMessage: '当前工程状态不符合删除条件,请确认工程状态后再操作!', |
| | | } |
| | | |
| | | ]; |
| | | |
| | | //定义切换模拟计算弹窗 |
| | | const emit = defineEmits(['switch-dialog']); |
| | | |
| | | onMounted(async () => { |
| | | getProject(); |
| | | }) |
| | | |
| | | // 定义数据返回结果,使用ref创建响应式数据,初始化为空数组 |
| | | let produceList = ref([]) |
| | | |
| | | const getProject = ()=>{ |
| | | request.post(`/glassOptimize/getProjectList`).then((res) => { |
| | | if(res.code==200){ |
| | | produceList.value = deepClone(res.data.data); |
| | | xGrid.value.loadData(produceList.value) |
| | | }else{ |
| | | ElMessage.warning(res.msg) |
| | | } |
| | | }) |
| | | } |
| | | |
| | | // 公共处理函数,处理相同数据时的操作,并返回targetRoute对象(右键菜单和双击打开) |
| | | const handleSameDataOperation = async ({projectNumber, thickness, glassType}) => { |
| | | const targetRoute = { |
| | | name: 'optimizeInfo', |
| | | params: { |
| | | projectNo: projectNumber, |
| | | thickNess: String(thickness), |
| | | model: glassType |
| | | } |
| | | }; |
| | | const currentRoute = router.currentRoute.value; |
| | | const isRoutesEqual = currentRoute.name === targetRoute.name && |
| | | currentRoute.params.projectNo === targetRoute.params.projectNo && |
| | | currentRoute.params.thickNess === targetRoute.params.thickNess && |
| | | currentRoute.params.model === targetRoute.params.model; |
| | | if (isRoutesEqual) { |
| | | handleConfirm(); |
| | | } |
| | | return {isRoutesEqual}; |
| | | }; |
| | | |
| | | //选中相同数据时弹窗提示 |
| | | const handleConfirm = () => { |
| | | const currentRoute = router.currentRoute.value; |
| | | const projectNumber = currentRoute.params.projectNo; |
| | | ElMessageBox.confirm(`当前工程(工程号:${projectNumber})已打开,是否重新打开?`, '确认操作', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | }) |
| | | .then(() => { |
| | | emit('close-detail-page'); |
| | | ElMessage.success('已打开!'); |
| | | }) |
| | | .catch(() => { |
| | | ElMessage.info('已取消操作'); |
| | | }); |
| | | }; |
| | | |
| | | const gridEvents = { |
| | | menuClick({menu, row}) { |
| | | const $grid = xGrid.value; |
| | | if ($grid) { |
| | | const config = operationConfigs.find(c => c.code === menu.code); |
| | | if (config) { |
| | | if (config.requiresRow && !row) { |
| | | ElMessage.warning('未选中工程,请选中工程后再进行当前操作!'); |
| | | return; |
| | | } |
| | | if (config.code === 'compute') { |
| | | config.actionFunction({row}); |
| | | return; |
| | | } |
| | | if (config.code === 'updateProject') { |
| | | if (!row) { |
| | | ElMessage.warning(config.checkMessage); |
| | | return; |
| | | } |
| | | processCardRef.value.getUpdateFlowCardList(row.projectNumber,row.glass_type,row.glass_thickness); |
| | | return; |
| | | } |
| | | if (config.code === 'openProject') { |
| | | handleSameDataOperation(row).then(({isRoutesEqual}) => { |
| | | if (!isRoutesEqual) { |
| | | config.openFile({row}); |
| | | ElMessage.success(config.successMsg); |
| | | } |
| | | }); |
| | | } |
| | | // 添加确认提示弹窗,询问用户是否进行当前操作 |
| | | ElMessageBox.confirm('是否进行当前操作?', '确认操作', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | }).then(() => { |
| | | if (config.code === 'delProject') { |
| | | if (!row) { |
| | | ElMessage.warning(config.checkMessage); |
| | | return; |
| | | } |
| | | const isInitialStateMatched = config.initialState.includes(String(row.state)); |
| | | if (!isInitialStateMatched) { |
| | | ElMessage.warning(config.checkMessage); |
| | | return; |
| | | } |
| | | deleteProject(row.projectNumber, config); |
| | | } |
| | | }).catch(() => { |
| | | // 用户点击取消后执行的逻辑 |
| | | ElMessage.info('已取消操作'); |
| | | }); |
| | | } else { |
| | | console.error(`未找到操作选项 ${menu.code} 对应的配置,请检查配置项`); |
| | | } |
| | | } |
| | | } |
| | | }; |
| | | |
| | | |
| | | function deleteProject(projectNumber, config) { |
| | | request.post(`/glassOptimize/deleteProject/${projectNumber}`, { |
| | | headers: { |
| | | 'Content-Type': 'application/json' |
| | | } |
| | | }).then((res) => { |
| | | if (res.code==200 && res.data===true) { |
| | | ElMessage.success(config.successMsg); |
| | | // 从列表中移除已删除的工程数据 |
| | | const index = produceList.value.findIndex(item => item.projectNumber === projectNumber); |
| | | if (index !== -1) { |
| | | produceList.value.splice(index, 1); |
| | | xGrid.value.reloadData(produceList.value); |
| | | } |
| | | //刷新工程号 |
| | | processCardRef.value.getProjectId(); |
| | | processCardRef.value.selectGlassType(); |
| | | processCardRef.value.selectFlowCardList(); |
| | | } else { |
| | | console.log('res.code 的值:', res.code, ', 类型:', typeof res.code); |
| | | console.log('res.msg 的值:', res.msg, ', 类型:', typeof res.msg); |
| | | const errorMsg = res.data ? res.data.errorMessage : config.failureMsg; |
| | | ElMessage.error(`操作失败,原因: ${errorMsg}`); |
| | | } |
| | | }).catch((error) => { |
| | | console.error('请求出错,工程删除未完成,详细错误信息:', error); |
| | | ElMessage.error(`请求出错,工程删除未完成,原因: ${errorMsg}`); |
| | | }); |
| | | } |
| | | |
| | | </script> |
| | | |
| | | <template> |
| | | <div style="width: 100%; height: 100%;"> |
| | | |
| | | <div id="processCard"> |
| | | <process-card ref="processCardRef" :process-id="projectRow.processId===null?null:projectRow.processId" |
| | | :technology-number="projectRow.technologyNumber===null?null:projectRow.technologyNumber" |
| | | @updateProcessId="handleProcessIdUpdate" |
| | | @updateTechnologyNumber="handleTechnologyNumberUpdate" |
| | | @updateState="updateState" |
| | | /> |
| | | </div> |
| | | |
| | | <div id="processCard-detail" > |
| | | <process-card-detail :process-id="projectRow.processId===null?null:projectRow.processId" |
| | | :technology-number="projectRow.technologyNumber===null?null:projectRow.technologyNumber"/> |
| | | </div> |
| | | |
| | | <div id="project-list"> |
| | | <div style="width: 100%;height: 100%"> |
| | | <h1>工程列表</h1> |
| | | <vxe-grid |
| | | size="small" |
| | | height="100%" |
| | | class="mytable-scrollbar" |
| | | ref="xGrid" |
| | | v-bind="gridOptions" |
| | | v-on="gridEvents" |
| | | > |
| | | |
| | | <template #num2_filter="{ column, $panel }"> |
| | | <div> |
| | | <div v-for="(option, index) in column.filters" :key="index"> |
| | | <vxe-select v-model="option.data" :placeholder="$t('processCard.pleaseSelect')" @change="changeFilterEvent($event, option, $panel)"> |
| | | <vxe-option value="0" :label="$t('basicData.unchecked')"></vxe-option> |
| | | <vxe-option value="1" :label="$t('basicData.selected')"></vxe-option> |
| | | </vxe-select> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <template #num1_filter="{ column, $panel }"> |
| | | <div> |
| | | <div v-for="(option, index) in column.filters" :key="index"> |
| | | <input type="type" v-model="option.data" @keyup.enter.native="$panel.confirmFilter()" @input="changeFilterEvent($event, option, $panel)"/> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | </vxe-grid> |
| | | </div> |
| | | </div> |
| | | |
| | | |
| | | |
| | | </div> |
| | | </template> |
| | | |
| | | |
| | | <style scoped> |
| | | #processCard{ |
| | | width: 64%; |
| | | height: 55%; |
| | | float: left; |
| | | } |
| | | #processCard-detail{ |
| | | margin-left: 1%; |
| | | float: left; |
| | | width: 35%; |
| | | height: 55%; |
| | | } |
| | | #project-list{ |
| | | float: left; |
| | | margin-top: 2%; |
| | | width: 100%; |
| | | height: 30%; |
| | | } |
| | | </style> |
New file |
| | |
| | | <script setup> |
| | | import {computed, nextTick, reactive, ref} from "vue"; |
| | | import {useI18n} from "vue-i18n"; |
| | | import deepClone from "@/utils/deepClone" |
| | | import {Search} from "@element-plus/icons-vue"; |
| | | import request from "@/utils/request"; |
| | | import {ElDatePicker, ElMessage, ElMessageBox} from "element-plus"; |
| | | import useOrderInfoStore from "@/stores/sd/order/orderInfo"; |
| | | import useUserInfoStore from "@/stores/userInfo"; |
| | | import userInfo from "@/stores/userInfo"; |
| | | import {useRouter} from 'vue-router'; |
| | | import {defineEmits} from 'vue'; |
| | | import {changeFilterEvent, filterChanged} from "@/hook"; |
| | | |
| | | |
| | | const {t} = useI18n() |
| | | const userStore = useUserInfoStore() |
| | | const user = userInfo() |
| | | const orderInfo = useOrderInfoStore() |
| | | |
| | | const router = useRouter(); |
| | | |
| | | const xGrid = ref() |
| | | const gridOptions = reactive({ |
| | | height: '100%', |
| | | loading: false, |
| | | border: "full",//表格加边框 |
| | | keepSource: true,//保持源数据 |
| | | align: 'center',//文字居中 |
| | | stripe: true,//斑马纹 |
| | | rowConfig: {isCurrent: true, isHover: true, height: 30, useKey: true},//鼠标移动或选择高亮 |
| | | id: 'ProjectMange', |
| | | scrollX: {enabled: true}, |
| | | scrollY: {enabled: true, gt: 0},//开启虚拟滚动 |
| | | showOverflow: true, |
| | | columnConfig: { |
| | | resizable: true, |
| | | useKey: true |
| | | }, |
| | | filterConfig: { //筛选配置项 |
| | | //remote: true |
| | | }, |
| | | customConfig: { |
| | | storage: true |
| | | }, |
| | | editConfig: { |
| | | trigger: 'click', |
| | | mode: 'row', |
| | | showStatus: true |
| | | }, |
| | | |
| | | columns: [ |
| | | {type: 'seq', title: t('basicData.Number'), width: 80}, |
| | | {field: 'projectNumber', width: 100, title: '工程号',sortable: true,showOverflow:'ellipsis' ,filters:[{ data: '' }],slots: { filter: 'num1_filter' },filterMethod:filterChanged}, |
| | | {field: 'projectName', width: 50, title: '名称',}, |
| | | {field: 'glassType', width: 50, title: '膜系',}, |
| | | {field: 'thickness', width: 50, title: '厚度',}, |
| | | {field: 'type', width: 50, title: '类型',}, |
| | | {field: 'state', width: 50, title: '状态',}, |
| | | {field: 'quantity', width: 50, title: '数量',}, |
| | | {field: 'area', width: 50, title: '面积',}, |
| | | {field: 'processCardQuantity', width: 100, title: '流程卡数量',}, |
| | | {field: 'usingQuantity', width: 100, title: '原片使用数',}, |
| | | {field: 'processCardCollection', width: 70, title: '流程卡',filters: [{data: ''}], slots: {filter: 'num1_filter'},filterMethod:filterChanged}, |
| | | {field: 'temperedLoadingRate', width: 100, title: '钢化装载率',}, |
| | | {field: 'numberOfTemperingFurnaces', width: 100, title: '钢化炉数',}, |
| | | {field: 'averageCuttingRate', width: 100, title: '平均切裁率',}, |
| | | {field: 'effectiveCuttingRate', width: 100, title: '有效切裁率',}, |
| | | {field: 'residueGlassCuttingRate', width: 100, title: '尾片切裁率',}, |
| | | {field: 'amountOfOriginalGlassUsage1', width: 100, title: '原料使用数',}, |
| | | {field: 'averageCuttingRate1', width: 100, title: '平均切裁率',}, |
| | | {field: 'notes', width: 50, title: '备注',}, |
| | | {field: 'creator', width: 100, title: '创建人',}, |
| | | {field: 'createTime', width: 100, title: '创建时间',}, |
| | | {field: 'modifyTime', width: 100, title: '修改时间',}, |
| | | ],//表头参数 |
| | | data: null,//表格数据 |
| | | toolbarConfig: { |
| | | buttons: [ |
| | | ], |
| | | import: false, |
| | | // export: true, |
| | | // print: true, |
| | | zoom: true, |
| | | custom: true |
| | | }, |
| | | //右键菜单选项 |
| | | menuConfig: { |
| | | body: { |
| | | options: [ |
| | | [ |
| | | {code: 'openProject', name: '打开工程', prefixIcon: 'vxe-icon-folder-open'}, |
| | | {code: 'compute', name: '模拟计算', prefixIcon: 'vxe-icon-subtable'}, |
| | | {code: 'production', name: '允许生产', prefixIcon: 'vxe-icon-square-checked'}, |
| | | {code: 'novisible', name: '生产不可见', prefixIcon: 'vxe-icon-eye-fill-close'}, |
| | | {code: 'undoCompute', name: '撤销模拟计算', prefixIcon: 'vxe-icon-error-circle-fill'}, |
| | | {code: 'delProject', name: '删除工程', prefixIcon: 'vxe-icon-delete'}, |
| | | {code: 'viewTempered', name: '查看钢化版图', prefixIcon: 'vxe-icon-custom-column'}, |
| | | {code: 'Export', name: '数据导出', prefixIcon: 'vxe-icon-download', visible: true, disabled: false}, |
| | | |
| | | ], |
| | | [] |
| | | ] |
| | | }, |
| | | }, |
| | | }) |
| | | // 定义操作配置对象数组,集中管理不同操作选项对应的参数 |
| | | const operationConfigs = [ |
| | | { |
| | | code: 'openProject', // 打开工程 |
| | | initialState: ['2','10', '100', '200'], // |
| | | targetState: null, |
| | | successMsg: '已打开!', |
| | | checkMessage: '当前工程状态不符合条件,请确认工程状态后再操作!', |
| | | requiresRow: true, |
| | | openFile: async ({row}) => { |
| | | const projectNumber = row.projectNumber; |
| | | const thickness = row.thickness; |
| | | const glassType = row.glassType; |
| | | await router.push({ |
| | | name: 'optimizeInfo', |
| | | params: { |
| | | projectNo: projectNumber, |
| | | thickNess: thickness, |
| | | model: glassType |
| | | } |
| | | }); |
| | | } |
| | | }, |
| | | { |
| | | code: 'compute', // 打开模拟计算操作 |
| | | initialState: '2', |
| | | targetState: null, |
| | | successMsg: '模拟计算已启动!', |
| | | checkMessage: '当前工程状态不符合模拟计算条件,请确认工程状态后再操作!', |
| | | requiresRow: true, |
| | | actionFunction: async ({row}) => { |
| | | const projectNo = row.projectNumber; |
| | | emit('switch-dialog', row); |
| | | } |
| | | }, |
| | | { |
| | | code: 'undoCompute', |
| | | initialState: '10', |
| | | targetState: 2, |
| | | successMsg: '撤销模拟计算成功,数据已更新!', |
| | | checkMessage: '当前工程状态不符合撤销模拟计算条件,请确认工程状态后再操作!', |
| | | requiresRow: true, |
| | | }, |
| | | { |
| | | code: 'production', |
| | | initialState: '10', |
| | | targetState: 100, |
| | | successMsg: '设置成功,允许生产!', |
| | | checkMessage: '当前工程状态不符合允许生产条件,请确认工程状态后再操作!', |
| | | requiresRow: true, |
| | | }, |
| | | { |
| | | code: 'novisible', |
| | | initialState: '100', |
| | | targetState: 10, |
| | | successMsg: '设置成功,生产不可见!', |
| | | checkMessage: '当前工程状态不符合生产不可见条件,请确认工程状态后再操作!', |
| | | requiresRow: true, |
| | | }, |
| | | { |
| | | code: 'delProject', |
| | | initialState: ['2', '10'], |
| | | targetState: null, |
| | | successMsg: '工程删除成功!', |
| | | checkMessage: '当前工程状态不符合删除条件,请确认工程状态后再操作!', |
| | | requiresRow: true, |
| | | }, |
| | | { |
| | | code: 'Export', // 导出文件操作的配置 |
| | | initialState: [], |
| | | targetState: null, |
| | | successMsg: '文件导出成功!', |
| | | gridRef: xGrid, |
| | | requiresRow: false, |
| | | }, |
| | | { |
| | | code: 'viewTempered', |
| | | initialState: [], |
| | | targetState: null, |
| | | successMsg: '钢化版图已打开!', |
| | | failureMsg: '工程删除失败,请联系管理员!', |
| | | } |
| | | ]; |
| | | |
| | | function getOriginalState(targetState) { |
| | | // 根据工程状态返回对应的原始状态 |
| | | const stateMapping = { |
| | | 2: '10', |
| | | 10: '100', |
| | | 100: '200', |
| | | 200: '100' |
| | | }; |
| | | return stateMapping[targetState] || targetState; |
| | | } |
| | | |
| | | //右键菜单条件判断 |
| | | function checkOperationCondition(config, row) { |
| | | if (!config.requiresRow || !row) { |
| | | return false; |
| | | } |
| | | if (Array.isArray(config.initialState)) { |
| | | return config.initialState.includes(String(row.state)); |
| | | } |
| | | return config.initialState === String(row.state); |
| | | } |
| | | |
| | | // 右键菜单点击事件 |
| | | const gridEvents = { |
| | | menuClick({menu, row}) { |
| | | const $grid = xGrid.value; |
| | | if ($grid) { |
| | | const config = operationConfigs.find(c => c.code === menu.code); |
| | | if (config) { |
| | | if (config.requiresRow && !row) { |
| | | ElMessage.warning('未选中工程,请选中工程后再进行当前操作!'); |
| | | return; |
| | | } |
| | | if (!checkOperationCondition(config, row)) { |
| | | ElMessage.warning(config.checkMessage); |
| | | return; |
| | | } |
| | | if (config.code === 'Export') { |
| | | config.gridRef.value.exportData(); |
| | | ElMessage.success(config.successMsg); |
| | | return; |
| | | } |
| | | if (config.code === 'compute') { |
| | | config.actionFunction({row}); |
| | | return; |
| | | } |
| | | // 添加确认提示弹窗,询问用户是否进行当前操作 |
| | | ElMessageBox.confirm('是否进行当前操作?', '确认操作', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | }).then(() => { |
| | | if (config.code === 'viewTempered') { |
| | | ElMessageBox.alert('当前点击的是查看钢化版图功能,目前暂时仅做提示,暂无实际查看操作!', '功能提示', { |
| | | confirmButtonText: '我知道了' |
| | | }); |
| | | return; |
| | | } |
| | | if (config.code === 'viewOptimize') { |
| | | ElMessageBox.alert('当前点击的是查看钢化版图功能,目前暂时仅做提示,暂无实际查看操作!', '功能提示', { |
| | | confirmButtonText: '我知道了' |
| | | }); |
| | | return; |
| | | } |
| | | if (config.code === 'delProject') { |
| | | if (!row) { |
| | | ElMessage.warning(config.checkMessage); |
| | | return; |
| | | } |
| | | deleteProject(row.projectNumber, config); |
| | | } else { |
| | | if (config.code === 'openProject') { |
| | | handleSameDataOperation(row).then(({isRoutesEqual}) => { |
| | | if (!isRoutesEqual) { |
| | | config.openFile({row}); |
| | | ElMessage.success(config.successMsg); |
| | | } |
| | | }); |
| | | } else if (config.code === 'compute') { |
| | | config.actionFunction({row}); |
| | | } else if (config.code === 'optimizeTypography') { |
| | | handleSameDataOperation(row).then(({isRoutesEqual}) => { |
| | | if (!isRoutesEqual) { |
| | | config.Typography({row}); |
| | | ElMessage.success(config.successMsg); |
| | | } |
| | | }); |
| | | } |
| | | else { |
| | | row.state = config.targetState; |
| | | let code=0 |
| | | const index = produceList.value.findIndex(item => item === row); |
| | | if (index !== -1) { |
| | | produceList.value.splice(index, 1, {...row}); |
| | | xGrid.value.reloadData(produceList.value); |
| | | } |
| | | if(config.code === 'undoCompute'){ |
| | | code=1 |
| | | }else if(config.code === 'undoOptimize'){ |
| | | code=2 |
| | | } |
| | | else if(config.code === 'production'){ |
| | | code=3 |
| | | } |
| | | else if(config.code === 'novisible'){ |
| | | code=4 |
| | | } |
| | | else if(config.code === 'InitializeProject'){ |
| | | code=5 |
| | | } |
| | | updateProjectStateAndHandleResponse(row, row.projectNumber, config.targetState,code, config.successMsg); |
| | | } |
| | | } |
| | | }).catch(() => { |
| | | // 用户点击取消后执行的逻辑 |
| | | ElMessage.info('已取消操作'); |
| | | }); |
| | | } else { |
| | | console.error(`未找到操作选项 ${menu.code} 对应的配置,请检查配置项`); |
| | | } |
| | | } |
| | | }, |
| | | cellDblclick: ({row}) => { |
| | | const menu = {code: 'openProject'}; |
| | | nextTick(() => { |
| | | handleSameDataOperation(row).then(({isRoutesEqual}) => { |
| | | if (!isRoutesEqual) { |
| | | gridEvents.menuClick({menu, row}); |
| | | } |
| | | }); |
| | | }); |
| | | }, |
| | | }; |
| | | |
| | | // 公共处理函数,处理相同数据时的操作,并返回targetRoute对象(右键菜单和双击打开) |
| | | const handleSameDataOperation = async ({projectNumber, thickness, glassType}) => { |
| | | const targetRoute = { |
| | | name: 'optimizeInfo', |
| | | params: { |
| | | projectNo: projectNumber, |
| | | thickNess: String(thickness), |
| | | model: glassType |
| | | } |
| | | }; |
| | | const currentRoute = router.currentRoute.value; |
| | | const isRoutesEqual = currentRoute.name === targetRoute.name && |
| | | currentRoute.params.projectNo === targetRoute.params.projectNo && |
| | | currentRoute.params.thickNess === targetRoute.params.thickNess && |
| | | currentRoute.params.model === targetRoute.params.model; |
| | | if (isRoutesEqual) { |
| | | handleConfirm(); |
| | | } |
| | | return {isRoutesEqual}; |
| | | }; |
| | | //选中相同数据时弹窗提示 |
| | | const handleConfirm = () => { |
| | | const currentRoute = router.currentRoute.value; |
| | | const projectNumber = currentRoute.params.projectNo; |
| | | ElMessageBox.confirm(`当前工程(工程号:${projectNumber})已打开,是否重新打开?`, '确认操作', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | }) |
| | | .then(() => { |
| | | emit('close-detail-page'); |
| | | ElMessage.success('已打开!'); |
| | | }) |
| | | .catch(() => { |
| | | ElMessage.info('已取消操作'); |
| | | }); |
| | | }; |
| | | |
| | | |
| | | // 封装发送右键菜单请求、处理响应以及错误回滚等逻辑的函数 |
| | | function rollbackStateAndReloadGrid(row, targetState) { |
| | | row.state = getOriginalState(targetState); |
| | | const rollbackIndex = produceList.value.findIndex(item => item === row); |
| | | if (rollbackIndex !== -1) { |
| | | produceList.value.splice(rollbackIndex, 1, {...row}); |
| | | xGrid.value.reloadData(produceList.value); |
| | | } |
| | | } |
| | | |
| | | function updateProjectStateAndHandleResponse(row, projectNumber, targetState,code, successMsg) { |
| | | const updateParams = { |
| | | projectNumber: projectNumber, |
| | | stateToUpdate: targetState |
| | | }; |
| | | request.post(`/glassOptimize/updateProjectState/${projectNumber}/${targetState}/${code}`, updateParams, { |
| | | headers: { |
| | | 'Content-Type': 'application/json' |
| | | } |
| | | }).then((res) => { |
| | | if (Number(res.code) === 200 && (res.msg === "" || res.msg === null)) { |
| | | ElMessage.success(successMsg); |
| | | } else { |
| | | console.log('res.code 的值:', res.code, ', 类型:', typeof res.code); |
| | | console.log('res.msg 的值:', res.msg, ', 类型:', typeof res.msg); |
| | | const errorMsg = res.data ? res.data.errorMessage : '操作失败,未获取到具体原因,请联系管理员'; |
| | | ElMessage.error(`操作失败,原因: ${errorMsg}`); |
| | | rollbackStateAndReloadGrid(row, targetState); // 调用回滚函数 |
| | | } |
| | | }).catch((error) => { |
| | | console.error('请求出错,操作未完成,详细错误信息:', error); |
| | | const errorMsg = (res.data && res.data.errorMessage) ? res.data.errorMessage : '操作失败,未获取到具体原因,请联系管理员'; |
| | | ElMessage.error(`请求出错,操作未完成,原因: ${errorMsg}`); |
| | | rollbackStateAndReloadGrid(row, targetState); // 调用回滚函数 |
| | | }); |
| | | } |
| | | |
| | | //删除工程 |
| | | function deleteProject(projectNumber, config) { |
| | | request.post(`/glassOptimize/deleteProjectThirdParty/${projectNumber}`, { |
| | | headers: { |
| | | 'Content-Type': 'application/json' |
| | | } |
| | | }).then((res) => { |
| | | if (res.code==200 && res.data===true) { |
| | | ElMessage.success(config.successMsg); |
| | | // 从列表中移除已删除的工程数据 |
| | | const index = produceList.value.findIndex(item => item.projectNumber === projectNumber); |
| | | if (index !== -1) { |
| | | produceList.value.splice(index, 1); |
| | | xGrid.value.reloadData(produceList.value); |
| | | } |
| | | } else { |
| | | console.log('res.code 的值:', res.code, ', 类型:', typeof res.code); |
| | | console.log('res.msg 的值:', res.msg, ', 类型:', typeof res.msg); |
| | | const errorMsg = res.data ? res.data.errorMessage : config.failureMsg; |
| | | ElMessage.error(`操作失败,原因: ${errorMsg}`); |
| | | } |
| | | }).catch((error) => { |
| | | console.error('请求出错,工程删除未完成,详细错误信息:', error); |
| | | const errorMsg = (res.data && res.data.errorMessage) ? res.data.errorMessage : config.failureMsg; |
| | | ElMessage.error(`请求出错,工程删除未完成,原因: ${errorMsg}`); |
| | | }); |
| | | } |
| | | |
| | | //定义切换模拟计算弹窗 |
| | | const emit = defineEmits(['switch-dialog', 'close-detail-page']); |
| | | |
| | | //定义工程状态 |
| | | const optionVal = ref('all') |
| | | const options = [ |
| | | { |
| | | value: 'all', |
| | | label: '全部', |
| | | }, |
| | | { |
| | | value: '2', |
| | | label: '优化完成,2', |
| | | }, |
| | | { |
| | | value: '10', |
| | | label: '模拟计算,10', |
| | | }, |
| | | { |
| | | value: '100', |
| | | label: '生产可见,100', |
| | | }, |
| | | { |
| | | value: '200', |
| | | label: '生产领取,200', |
| | | }, |
| | | ] |
| | | |
| | | //工程状态函数 |
| | | function handleOptionChange() { |
| | | if (optionVal.value === 'all') { |
| | | // 清空工程状态相关的已有筛选条件 |
| | | delete filterData.value['state']; |
| | | } else { |
| | | filterData.value['state'] = optionVal.value; // 明确添加选择的工程状态值到筛选条件中 |
| | | } |
| | | getWorkOrder(); |
| | | } |
| | | |
| | | //公共函数 |
| | | function buildRequestParams() { |
| | | let startSelectTime = orderInfo.workOrderDate[0]; |
| | | let endSelectTime = orderInfo.workOrderDate[1]; |
| | | |
| | | const params = { |
| | | startSelectTime, |
| | | endSelectTime, |
| | | }; |
| | | |
| | | return params; |
| | | } |
| | | |
| | | //筛选条件 |
| | | let filterData = ref({ |
| | | projectNumber: '', |
| | | }) |
| | | // 定义数据返回结果,使用ref创建响应式数据,初始化为空数组 |
| | | let produceList = ref([]) |
| | | |
| | | // 获取15天前到当前时间 |
| | | function getNowTime() { |
| | | const start = new Date(new Date().getTime() - 3600 * 1000 * 24 * 70) |
| | | .toISOString() |
| | | .replace('T', ' ') |
| | | .slice(0, 10) //默认开始时间15天前 |
| | | const end = new Date(new Date().getTime()) |
| | | .toISOString() |
| | | .replace('T', ' ') |
| | | .slice(0, 10)//默认结束时间当前时间 |
| | | return [start, end] |
| | | } |
| | | |
| | | // 初始化时间范围,若为空则设为近15天时间 |
| | | if (orderInfo.workOrderDate[0] === "" && orderInfo.workOrderDate[1] === "") { |
| | | orderInfo.workOrderDate = getNowTime(); |
| | | } |
| | | //获取选中时间 |
| | | let startSelectTime = orderInfo.workOrderDate[0] |
| | | let endSelectTime = orderInfo.workOrderDate[1] |
| | | // 首次加载数据 |
| | | request.post(`/glassOptimize/optimizeProjectMange/${startSelectTime}/${endSelectTime}`, filterData.value).then((res) => { |
| | | if (res.code == 200) { |
| | | produceList.value = produceList.value.concat(deepClone(res.data.data)) |
| | | xGrid.value.reloadData(produceList.value); |
| | | gridOptions.loading = false; |
| | | } else { |
| | | ElMessage.warning(res.msg); |
| | | } |
| | | }) |
| | | |
| | | // 点击查询按钮触发的函数,根据当前筛选条件获取数据并加载到表格 |
| | | const getWorkOrder = () => { |
| | | const params = buildRequestParams(); |
| | | request.post(`/glassOptimize/optimizeProjectMange/${params.startSelectTime}/${params.endSelectTime}`, filterData.value).then((res) => { |
| | | if (res.code == 200) { |
| | | produceList.value = deepClone(res.data.data); |
| | | gridOptions.data = produceList.value; |
| | | xGrid.value.loadData(produceList.value); |
| | | gridOptions.loading = false; |
| | | } else { |
| | | ElMessage.warning(res.msg); |
| | | } |
| | | }); |
| | | }; |
| | | // 计算小片数量 |
| | | const smallPieceQuantityInput = computed(() => { |
| | | return produceList.value.reduce((acc, item) => acc + item.quantity, 0); |
| | | }); |
| | | // 计算小片面积 |
| | | const smallPieceAreaInput = computed(() => { |
| | | const sum = produceList.value.reduce((acc, item) => acc + item.area, 0); |
| | | return Number(sum.toFixed(2)); // 先使用toFixed保留两位小数,再转换回数值类型(因为toFixed返回字符串) |
| | | }); |
| | | |
| | | // 计算原片数量 |
| | | const originalPieceQuantityInput = computed(() => { |
| | | return produceList.value.reduce((acc, item) => acc + item.usingQuantity, 0); |
| | | }); |
| | | |
| | | // 原片面积固定为0,直接返回0 |
| | | const originalPieceAreaInput = 0; |
| | | |
| | | //日期左侧栏快捷选项 |
| | | const shortcuts = [ |
| | | { |
| | | text: '当日', |
| | | value: () => { |
| | | const end = new Date() |
| | | const start = new Date(end) |
| | | start.setHours(0, 0, 0, 0) // 设置为当天的凌晨 00:00:00 |
| | | return [start, end] |
| | | } |
| | | }, |
| | | { |
| | | text: '近1周', |
| | | value: () => { |
| | | const end = new Date() |
| | | const start = new Date() |
| | | start.setDate(start.getDate() - 7) |
| | | return [start, end] |
| | | } |
| | | }, |
| | | |
| | | { |
| | | text: '近1个月', |
| | | value: () => { |
| | | const end = new Date() |
| | | const start = new Date() |
| | | start.setMonth(start.getMonth() - 1) |
| | | return [start, end] |
| | | } |
| | | }, |
| | | { |
| | | text: '近2个月', |
| | | value: () => { |
| | | const end = new Date() |
| | | const start = new Date() |
| | | start.setMonth(start.getMonth() - 2) |
| | | return [start, end] |
| | | } |
| | | }, |
| | | { |
| | | text: '近3个月', |
| | | value: () => { |
| | | const end = new Date() |
| | | const start = new Date() |
| | | start.setMonth(start.getMonth() - 3) |
| | | return [start, end] |
| | | } |
| | | } |
| | | ] |
| | | |
| | | </script> |
| | | |
| | | <template> |
| | | <div id="mange"> |
| | | <div id="select" style=""> |
| | | <span>优化日期</span> |
| | | <el-date-picker |
| | | style="margin-left:10px; margin-top: -10px; " |
| | | v-model="orderInfo.workOrderDate" |
| | | :default-time="defaultTime" |
| | | :start-placeholder="$t('basicData.startDate')" |
| | | :end-placeholder="$t('basicData.endDate')" |
| | | type="daterange" |
| | | :shortcuts="shortcuts" |
| | | showToday |
| | | format="YYYY/MM/DD" |
| | | value-format="YYYY-MM-DD" |
| | | /> |
| | | <span class="input">工程状态</span> |
| | | <el-select |
| | | style="margin-left:10px; margin-top: -5px; width: 150px" |
| | | :default-first-option="true" |
| | | ref="getSelect" |
| | | v-model="optionVal" |
| | | clearable |
| | | class="m-2" |
| | | @change="handleOptionChange" |
| | | > |
| | | <el-option |
| | | v-for="item in options" |
| | | :key="item.value" |
| | | :label="item.label" |
| | | :value="item.value" |
| | | /> |
| | | </el-select> |
| | | <!--查询--> |
| | | <el-button |
| | | style="margin-left:10px;margin-top: -5px" |
| | | :icon="Search" |
| | | type="primary" |
| | | @click="getWorkOrder">{{ $t('basicData.search') }} |
| | | </el-button> |
| | | </div> |
| | | |
| | | <vxe-grid |
| | | height="100%" |
| | | class="mytable-scrollbar" |
| | | ref="xGrid" |
| | | v-bind="gridOptions" |
| | | v-on="gridEvents" |
| | | > |
| | | <template #select_filter="{ column, $panel }"> |
| | | <div> |
| | | <div v-for="(option, index) in column.filters" :key="index"> |
| | | <vxe-select v-model="option.data" @change="changeFilterEvent($event, option, $panel)"> |
| | | <vxe-option value="0" :label="$t('basicData.unchecked')"></vxe-option> |
| | | <vxe-option value="1" :label="$t('basicData.selected')"></vxe-option> |
| | | </vxe-select> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <template #num1_filter="{ column, $panel }"> |
| | | <div> |
| | | <div v-for="(option, index) in column.filters" :key="index"> |
| | | <input type="type" v-model="option.data" @keyup.enter.native="$panel.confirmFilter()" @input="changeFilterEvent($event, option, $panel)"/> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | </vxe-grid> |
| | | <div id="last"> |
| | | 小片数量 |
| | | <el-input class="input" disabled v-model="smallPieceQuantityInput"></el-input> |
| | | 小片面积 |
| | | <el-input class="input" disabled v-model="smallPieceAreaInput"></el-input> |
| | | 原片数量 |
| | | <el-input class="input" disabled v-model="originalPieceQuantityInput"></el-input> |
| | | 原片面积 |
| | | <el-input class="input" disabled v-model="originalPieceAreaInput"></el-input> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <style scoped> |
| | | #mange { |
| | | width: 100%; |
| | | height: 85%; |
| | | } |
| | | |
| | | :deep(.vxe-button.type--button.is--circle) { |
| | | margin-top: -20px; |
| | | } |
| | | |
| | | #last { |
| | | margin-top: 10px; |
| | | } |
| | | |
| | | .input { |
| | | width: 80px; |
| | | margin-left: 10px; |
| | | } |
| | | </style> |
New file |
| | |
| | | <script setup> |
| | | import {defineEmits, ref} from "vue"; |
| | | |
| | | |
| | | const value1 = ref('') |
| | | const value2 = ref('') |
| | | |
| | | const options1 = [ |
| | | { |
| | | value: 'Option1', |
| | | label: '5', |
| | | }, |
| | | { |
| | | value: 'Option2', |
| | | label: '6', |
| | | }, |
| | | { |
| | | value: 'Option3', |
| | | label: '8', |
| | | }, |
| | | { |
| | | value: 'Option4', |
| | | label: '10', |
| | | }, |
| | | { |
| | | value: 'Option5', |
| | | label: '12', |
| | | }, |
| | | ] |
| | | const options2 = [ |
| | | { |
| | | value: 'Option1', |
| | | label: '白玻', |
| | | }, |
| | | { |
| | | value: 'Option2', |
| | | label: '灰镜', |
| | | }, |
| | | { |
| | | value: 'Option3', |
| | | label: 'Low-e', |
| | | }, |
| | | ] |
| | | |
| | | const emit = defineEmits(['send-data-inventory',]); |
| | | const props = defineProps({ |
| | | closeDialog: Function, |
| | | thickNess:null, |
| | | model:null |
| | | }); |
| | | |
| | | value1.value=props.thickNess |
| | | value2.value=props.model |
| | | |
| | | const CheckInventory = () => { |
| | | const selectedLabel1 = value1.value; |
| | | const selectedLabel2 = value2.value; |
| | | // 判断两个值是否都被选择了,如果有一个为空字符串,则提示并返回,不执行后续操作 |
| | | if (selectedLabel1==="" || selectedLabel2==="") { |
| | | window.alert('请输入'); |
| | | return; |
| | | } |
| | | props.closeDialog(1); |
| | | emit('send-data-inventory', selectedLabel1,selectedLabel2); |
| | | }; |
| | | |
| | | </script> |
| | | |
| | | <template> |
| | | <div id="box"> |
| | | <div> |
| | | <span>厚度(mm):</span> |
| | | <el-input v-model="value1" style="width: 140px"></el-input> |
| | | </div> |
| | | |
| | | <div style="margin-top: 30px"> |
| | | <span>玻璃类型 :</span> |
| | | <el-input v-model="value2" style="width: 140px"> |
| | | |
| | | </el-input> |
| | | </div> |
| | | |
| | | <div style="float: right; margin:-55px 35px 0 0;"> |
| | | <el-button type="primary" @click="CheckInventory">查询</el-button> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <style scoped> |
| | | #box { |
| | | width: 100%; |
| | | height: 100%; |
| | | padding: 10px; |
| | | margin-top: -20px; |
| | | border-radius: 5px; |
| | | } |
| | | </style> |
New file |
| | |
| | | <script setup> |
| | | import {onMounted, reactive, ref, watch} from "vue"; |
| | | import {useI18n} from "vue-i18n"; |
| | | import {Platform, Search, SuccessFilled} from "@element-plus/icons-vue"; |
| | | import useUserInfoStore from "@/stores/userInfo"; |
| | | import request from "@/utils/request"; |
| | | import {ElMessage} from "element-plus"; |
| | | |
| | | |
| | | const userStore = useUserInfoStore() |
| | | const username = userStore.user.userName |
| | | |
| | | const {t} = useI18n() |
| | | |
| | | const xGrid = ref() |
| | | //获取工程号 |
| | | const props = defineProps({ |
| | | projectNo : String, |
| | | project: null, |
| | | data: { |
| | | type: Array, |
| | | default: () => [] |
| | | } |
| | | }); |
| | | |
| | | |
| | | const selectOptions = [ |
| | | { |
| | | value: '0', |
| | | label: '默认' |
| | | }, |
| | | { |
| | | value: '1', |
| | | label: '优先竖排' |
| | | }, |
| | | { |
| | | value: '2', |
| | | label: '优先横排' |
| | | } |
| | | ] |
| | | |
| | | |
| | | // 定义混排等级 |
| | | const optionVal = ref(50) |
| | | |
| | | // 定义装载率 |
| | | const percentage1 = ref(80) |
| | | const percentage2 = ref(50) |
| | | |
| | | // 定义其他表单数据 |
| | | const furnaceWidth = ref('') // 炉宽 |
| | | const furnaceLength = ref('') // 炉长 |
| | | const heatingTime = ref('') // 钢化加热时间 |
| | | const rotateMode = ref({value: '0', label: '默认'}) // 钢化旋转模式 |
| | | const spacingLong = ref('') // 长轴间隔 |
| | | const spacingWidth = ref('') // 宽轴间隔 |
| | | const quantity = ref('') // 工程片数 |
| | | |
| | | // 定义响应式数据,用于绑定工程号输入框的值 |
| | | let inputValue=ref(null) |
| | | if(props.project!==undefined){ |
| | | inputValue= ref(props.project.projectNumber); |
| | | quantity.value=props.project.quantity |
| | | } |
| | | |
| | | const fetchSettings = async (username) => { |
| | | try { |
| | | const response = await request.post(`/glassOptimize/selectOptimizeParms/${username}`); |
| | | if (response.code == 200) { |
| | | if (!response.data) { |
| | | console.error('响应数据为空'); |
| | | return; |
| | | } |
| | | const parsedData = JSON.parse(response.data); |
| | | if (parsedData.tempering) { |
| | | furnaceLength.value = parsedData.tempering.furnaceLength; |
| | | furnaceWidth.value = parsedData.tempering.furnaceWidth; |
| | | spacingWidth.value=parsedData.tempering.xAxisInterval; |
| | | spacingLong.value=parsedData.tempering.yAxisInterval; |
| | | heatingTime.value=parsedData.tempering.temperingTime; |
| | | } |
| | | |
| | | |
| | | } else { |
| | | console.error('请求失败,状态码:', response.code); |
| | | } |
| | | } catch (error) { |
| | | console.error('请求发生错误:', error); |
| | | } |
| | | }; |
| | | |
| | | fetchSettings(username); |
| | | |
| | | const gridOptions = reactive({ |
| | | height: '100%', |
| | | loading: false, |
| | | border: "full",//表格加边框 |
| | | keepSource: true,//保持源数据 |
| | | align: 'center',//文字居中 |
| | | stripe: true,//斑马纹 |
| | | rowConfig: {isCurrent: true, isHover: true, height: 30, useKey: true},//鼠标移动或选择高亮 |
| | | id: 'Compute', |
| | | scrollX: {enabled: true}, |
| | | scrollY: {enabled: true, gt: 0},//开启虚拟滚动 |
| | | showOverflow: true, |
| | | columnConfig: { |
| | | resizable: true, |
| | | useKey: true |
| | | }, |
| | | filterConfig: { //筛选配置项 |
| | | remote: true |
| | | }, |
| | | customConfig: { |
| | | storage: true |
| | | }, |
| | | editConfig: { |
| | | trigger: 'click', |
| | | mode: 'row', |
| | | showStatus: true |
| | | }, |
| | | |
| | | columns: [ |
| | | {field: 'id', width: 70, title: '序号'}, |
| | | {field: 'layoutsNumber', width: 100, title: '版图数'}, |
| | | {field: 'loadingRate', width: 100, title: '装载率'}, |
| | | {field: 'processCards', width: 100, title: '流程卡数'}, |
| | | {field: 'simulatedPieces', width: 100, title: '模拟片数'}, |
| | | { |
| | | field: 'processId', |
| | | width: 350, |
| | | title: t('processCard.processId'), |
| | | filters: [{data: ''}], |
| | | slots: {filter: 'num1_filter'}, |
| | | sortable: true |
| | | }, |
| | | ],//表头参数 |
| | | data: null,//表格数据 |
| | | toolbarConfig: { |
| | | |
| | | }, |
| | | }) |
| | | |
| | | onMounted(async() => { |
| | | await firstLoading() |
| | | }) |
| | | |
| | | const firstLoading = async() => { |
| | | request.post(`/glassOptimize/selectOptimizeParms/${username}`).then((res) => { |
| | | if (res.code == "200") { |
| | | const parsedData = JSON.parse(res.data); |
| | | furnaceWidth.value=parsedData.tempering.furnaceWidth |
| | | furnaceLength.value=parsedData.tempering.furnaceLength |
| | | spacingLong.value=parsedData.tempering.xAxisInterval |
| | | spacingWidth.value=parsedData.tempering.yAxisInterval |
| | | } else { |
| | | ElMessage.warning(res.msg) |
| | | } |
| | | }) |
| | | |
| | | } |
| | | |
| | | // 监听父组件传递的数据变化 |
| | | watch(() => props.data, (newValue) => { |
| | | if (newValue) { |
| | | // 处理数据并更新表格 |
| | | const processData = newValue.data[0]; |
| | | const processedData = [ |
| | | { |
| | | id: "1", |
| | | layoutsNumber: processData.resultSum[0], |
| | | loadingRate: processData.resultSum[1], |
| | | processCards: processData.rackinfos.length, |
| | | simulatedPieces: processData.glass_details.length, |
| | | processId:processData.rackinfos |
| | | } |
| | | ]; |
| | | gridOptions.data = processedData; |
| | | } else { |
| | | console.error("数据格式不正确或为空"); |
| | | gridOptions.data = []; |
| | | } |
| | | }) |
| | | |
| | | const inputValues = { |
| | | project_no:inputValue.value, |
| | | glass_thickness:"", |
| | | glass_type:"", |
| | | chaos_pct: optionVal.value*0.01,//混排等级 |
| | | cage_free:percentage2.value*0.01,//理片龙空闲度 |
| | | tempering_time:heatingTime.value,//钢化加热时间 |
| | | heat_mode: 0,//模式选择 |
| | | max_load_pct: percentage1.value*0.01,//最大装载率 |
| | | max_area: 0,//最大面积 |
| | | max_qty:0,//最大片数 |
| | | load_width:null, |
| | | load_length: null, |
| | | x_space: null, |
| | | y_space: null, |
| | | load_rate: null, |
| | | furnaces_qty:"", |
| | | rotate_mode:0, |
| | | polys_allow_rotate: 0, |
| | | process_cards:[] |
| | | }; |
| | | |
| | | |
| | | // let emit = defineEmits(['fetch-data']); |
| | | let emit = defineEmits(['fetch-data', 'sendData']) |
| | | |
| | | const handleSearchClick = () => { |
| | | // 通过 $emit 触发自定义事件,将工程号传递给父组件,由父组件去调用接口获取数据 |
| | | emit('fetch-data', inputValue.value); |
| | | |
| | | |
| | | }; |
| | | const handleSimulationClick = () => { |
| | | |
| | | inputValues.load_width = furnaceWidth.value |
| | | inputValues.load_length = furnaceLength.value |
| | | inputValues.x_space = spacingWidth.value |
| | | inputValues.y_space = spacingLong.value |
| | | inputValues.polys_allow_rotate = parseInt(rotateMode.value.value) |
| | | |
| | | emit('sendData', { |
| | | ...inputValues |
| | | }) |
| | | // 触发父组件的 simulate-click 事件 |
| | | emit('simulate-click'); |
| | | |
| | | |
| | | }; |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | const handleSave = () => { |
| | | |
| | | if (props.data) { |
| | | console.log(props.data) |
| | | |
| | | let projectData = ref({ |
| | | projectdetail: props.data.data[0].glass_details, |
| | | ratioResult: props.data.data[0].ratioResult, |
| | | rackinfos: props.data.data[0].rackinfos, |
| | | resultSum: props.data.data[0].resultSum, |
| | | guidance: props.data.data[0].guidance, |
| | | userName : username, |
| | | inputValues:inputValues |
| | | }) |
| | | |
| | | request.post(`/glassOptimize/simulationSave`, projectData.value).then((res) => { |
| | | if (res.code == 200 && res.data === true) { |
| | | ElMessage.success(t('basicData.msg.saveSuccess')) |
| | | |
| | | } else { |
| | | ElMessage.warning(res.msg) |
| | | } |
| | | }) |
| | | |
| | | } |
| | | } |
| | | |
| | | </script> |
| | | |
| | | <template> |
| | | <div style="width: 100%;height: 100%"> |
| | | <!--模拟计算表头--> |
| | | <div id="title" style="margin-top: -10px;height: 50%"> |
| | | <div style="display: flex"> |
| | | <div style="width: 850px"> |
| | | 工程编号 |
| | | <el-input style="width:150px;margin-left: 30px" clearable v-model="inputValue" placeholder="请输入工程号"></el-input> |
| | | <el-button |
| | | type="primary" |
| | | :icon="Search" |
| | | style="margin-left: 20px" |
| | | @click="handleSearchClick" |
| | | >{{ $t('basicData.search') }} |
| | | </el-button> |
| | | </div> |
| | | <div style="display: flex ; width: 700px;align-items: center;"> |
| | | <span>工程混排等级</span> |
| | | <el-slider |
| | | style="max-width: 200px; flex: 1; margin-left: 10px" |
| | | v-model="optionVal" |
| | | :min="0" |
| | | :max="100" |
| | | :step="1"/> |
| | | <span style="margin-left: 20px ; width: 35px;">{{ optionVal }}%</span> |
| | | |
| | | <el-button type="primary" style="margin-left: 10px" :icon="Platform" @click="handleSimulationClick">模拟计算</el-button> |
| | | <el-button type="primary" style="margin-left: 20px" :icon="SuccessFilled" @click="handleSave">保存</el-button> |
| | | </div> |
| | | </div><br> |
| | | <div style="display:flex"> |
| | | <div class="demo-progress" style="margin-top: 5px;width: 50%"> |
| | | <div style="display: flex; align-items: center"> |
| | | <span>钢化最大装载</span> |
| | | <!-- 进度条设置 --> |
| | | <el-slider |
| | | style="max-width: 400px; flex: 1; margin-left: 10px" |
| | | v-model="percentage1" |
| | | :min="0" |
| | | :max="100" |
| | | :step="1"/> |
| | | <span style="margin-left: 20px ; width: 35px;">{{ percentage1 }}%</span> |
| | | <!-- <span style="float: right ; margin-left: 150px;"> |
| | | 工程片数 <vxe-input size="small" disabled class="input" v-model="quantity"></vxe-input> |
| | | 宽轴间隔 <vxe-input size="small" class="input" clearable v-model="spacingWidth"></vxe-input> |
| | | 炉宽(mm) <vxe-input size="small" class="input" clearable v-model="furnaceWidth"></vxe-input> |
| | | </span>--> |
| | | </div> |
| | | <div style="display: flex; align-items: center"> |
| | | <span>理片笼空闲度</span> |
| | | <!-- 进度条设置 --> |
| | | <el-slider |
| | | style="max-width: 400px; flex: 1; margin-left: 10px" |
| | | v-model="percentage2" |
| | | :min="0" |
| | | :max="100" |
| | | :step="1"/> |
| | | <span style="margin-left: 20px ; width: 35px;">{{ percentage2 }}%</span> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="demo-progress" style="display: flex;font-size: 15px"> |
| | | <div style="height: 35px;line-height: 35px"> |
| | | <div>工程片数 <el-input size="small" disabled class="input" v-model="quantity"></el-input></div> |
| | | <div>钢化旋转方式 |
| | | <el-select style="width: 100px;height: 30px" v-model="rotateMode"> |
| | | <el-option |
| | | v-for="item in selectOptions" |
| | | :key="item.value" |
| | | :label="item.label" |
| | | :value="item.value" |
| | | /> |
| | | </el-select> |
| | | </div> |
| | | </div> |
| | | <div style="margin-left: 15px;height: 35px;line-height: 35px"> |
| | | <div>宽轴间隔 <el-input size="small" class="input" clearable v-model="spacingWidth"></el-input></div> |
| | | <div>长轴间隔 <vxe-input size="small" class="input" clearable v-model="spacingLong"></vxe-input></div> |
| | | |
| | | |
| | | </div> |
| | | <div style="margin-left: 15px;height: 35px;line-height: 35px"> |
| | | <div>炉宽(mm) <el-input size="small" class="input" clearable v-model="furnaceWidth"></el-input></div> |
| | | <div>炉长(mm) <vxe-input size="small" class="input" clearable v-model="furnaceLength"></vxe-input></div> |
| | | </div> |
| | | |
| | | </div> |
| | | </div> |
| | | <!-- <div class="demo-progress" style="margin-top: -10px"> |
| | | <div style="display: flex; align-items: center"> |
| | | <span>理片笼空闲度</span> |
| | | <!– 进度条设置 –> |
| | | <el-slider |
| | | style="max-width: 400px; flex: 1; margin-left: 10px" |
| | | v-model="percentage2" |
| | | :min="0" |
| | | :max="100" |
| | | :step="1"/> |
| | | <span style="margin-left: 20px ; width: 35px;">{{ percentage2 }}%</span> |
| | | <span style="float: right ; margin-left: 150px;"> |
| | | 加热时间(秒)<el-select style="width: 50px;height: 30px" v-model="rotateMode"> |
| | | <el-option |
| | | v-for="item in selectOptions" |
| | | :key="item.value" |
| | | :label="item.label" |
| | | :value="item.value" |
| | | /> |
| | | </el-select> |
| | | 长轴间隔 <vxe-input size="small" class="input" clearable v-model="spacingLong"></vxe-input> |
| | | 炉长(mm) <vxe-input size="small" class="input" clearable v-model="furnaceLength"></vxe-input> |
| | | </span> |
| | | </div> |
| | | </div>--> |
| | | </div> |
| | | <div style="height: 50%"> |
| | | <vxe-grid |
| | | size="small" |
| | | @filter-change="filterChanged" |
| | | height="50%" |
| | | class="mytable-scrollbar" |
| | | ref="xGrid" |
| | | v-bind="gridOptions" |
| | | v-on="gridEvents" |
| | | > |
| | | <template #num2_filter="{ column, $panel }"> |
| | | <div> |
| | | <div v-for="(option, index) in column.filters" :key="index"> |
| | | <vxe-select v-model="option.data" :placeholder="$t('processCard.pleaseSelect')" |
| | | @change="changeFilterEvent($event, option, $panel)"> |
| | | <vxe-option value="0" :label="$t('basicData.unchecked')"></vxe-option> |
| | | <vxe-option value="1" :label="$t('basicData.selected')"></vxe-option> |
| | | </vxe-select> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <template #num1_filter="{ column, $panel }"> |
| | | <div> |
| | | <div v-for="(option, index) in column.filters" :key="index"> |
| | | <input |
| | | type="type" |
| | | v-model="option.data" |
| | | @keyup.enter.native="$panel.confirmFilter()" |
| | | @input="changeFilterEvent($event, option, $panel)"/> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | </vxe-grid> |
| | | </div> |
| | | |
| | | </div> |
| | | </template> |
| | | |
| | | <style scoped> |
| | | |
| | | .input { |
| | | width: 80px; |
| | | height: 30px; |
| | | } |
| | | |
| | | </style> |
New file |
| | |
| | | <script setup> |
| | | import {nextTick, onMounted, reactive, ref, watch} from "vue"; |
| | | import request from "@/utils/request"; |
| | | import {useI18n} from "vue-i18n"; |
| | | import {ElMessage} from "element-plus"; |
| | | import deepClone from "@/utils/deepClone"; |
| | | const { t } = useI18n() |
| | | |
| | | const xGrid = ref() |
| | | |
| | | const checkboxCellRender = reactive({ |
| | | name: 'VxeCheckboxGroup', |
| | | options: [ |
| | | { label: '幕墙模式', value: '1' }, |
| | | { label: '允许横排', value: '2' }, |
| | | { label: '钢化', value: '3' }, |
| | | |
| | | ] |
| | | }) |
| | | |
| | | const gridOptions = reactive({ |
| | | |
| | | height:'100%', |
| | | loading: false, |
| | | border: "full",//表格加边框 |
| | | keepSource: true,//保持源数据 |
| | | align: 'center',//文字居中 |
| | | stripe:true,//斑马纹 |
| | | rowConfig: {isCurrent: true, isHover: true,height: 30, useKey: true},//鼠标移动或选择高亮 |
| | | id: 'ComputeCard', |
| | | scrollX:{enabled: true}, |
| | | scrollY:{ enabled: true ,gt:0},//开启虚拟滚动 |
| | | showOverflow:true, |
| | | columnConfig: { |
| | | resizable: true, |
| | | useKey: true |
| | | }, |
| | | filterConfig: { //筛选配置项 |
| | | remote: true |
| | | }, |
| | | customConfig: { |
| | | storage: true |
| | | }, |
| | | editConfig: { |
| | | trigger: 'click', |
| | | mode: 'row', |
| | | showStatus: true |
| | | }, |
| | | |
| | | columns:[ |
| | | {type: 'seq', title: t('basicData.Number'), width: 80}, |
| | | {field: 'curtain_wall',title: '幕墙模式',width: 80, slots: { default: 'state1' }}, |
| | | {field: "allow_rotate", title: '允许横排', width: 80, slots: { default: 'state2' }}, |
| | | {field: "tempering", title: '钢化', width: 80, slots: { default: 'state3' }}, |
| | | |
| | | {field: 'processId',width: 150, title: t('processCard.processId'), sortable: true}, |
| | | {field: 'technologyNumber',width: 70, title: '层', sortable: true}, |
| | | {field: 'total_layers',width: 150, title: '总层数', sortable: true}, |
| | | {field: 'TotalNumber',width: 150, title: '规格', sortable: true}, |
| | | {field: 'total_num',width: 150, title: t('order.quantity'), sortable: true}, |
| | | {field: 'glass_child',width: 150, title: t('order.product'), sortable: true}, |
| | | {field: 'project', width:150, title: t('order.project'), showOverflow: "ellipsis"}, |
| | | {field: 'total_area',width: 150, title: t('order.area'), sortable: true}, |
| | | |
| | | |
| | | ],//表头参数 |
| | | data:null,//表格数据 |
| | | |
| | | toolbarConfig: { |
| | | buttons: [], |
| | | zoom: true, |
| | | custom: true |
| | | }, |
| | | |
| | | }) |
| | | |
| | | let emit = defineEmits([ |
| | | 'changeDialog','upProcessId', 'sendData' |
| | | ]); |
| | | |
| | | |
| | | const selectFullData = () => { |
| | | return xGrid.value.getTableData().fullData |
| | | } |
| | | |
| | | |
| | | const props = defineProps({ |
| | | tableData: Array, |
| | | processId: null, |
| | | technologyNumber: null, |
| | | patchState: null, |
| | | }); |
| | | |
| | | |
| | | |
| | | |
| | | watch(() => props.tableData, async (newData) => { |
| | | if (Array.isArray(newData)) { |
| | | console.log(newData) |
| | | xGrid.value.loadData(deepClone(newData)) |
| | | const data = xGrid.value.getTableData().fullData |
| | | data.forEach(item => { |
| | | item.tempering = item.tempering === 1; |
| | | item.allow_rotate = item.allow_rotate === 1; |
| | | item.curtain_wall = item.curtain_wall === 1; |
| | | }) |
| | | } |
| | | }); |
| | | |
| | | |
| | | |
| | | |
| | | let process_id = ref() |
| | | //获取流程卡号详情 |
| | | let rowClickIndex = ref(null) |
| | | const gridEvents = { |
| | | cellClick({row}) { |
| | | rowClickIndex.value = row |
| | | // Emit 事件将更新后的值传递给父组件 |
| | | emit('upProcessId', rowClickIndex.value.processId,rowClickIndex.value.technologyNumber); |
| | | emit('updateTechnologyNumber', rowClickIndex.value.processId,rowClickIndex.value.technologyNumber); |
| | | } |
| | | } |
| | | |
| | | defineExpose({selectFullData}) |
| | | </script> |
| | | |
| | | <template> |
| | | <div style="width: 100%;height: 100%"> |
| | | <span>流程卡</span> |
| | | <vxe-grid |
| | | size="small" |
| | | height="100%" |
| | | class="mytable-scrollbar" |
| | | ref="xGrid" |
| | | v-bind="gridOptions" |
| | | v-on="gridEvents" |
| | | > |
| | | <template #num2_filter="{ column, $panel }"> |
| | | <div> |
| | | <div v-for="(option, index) in column.filters" :key="index"> |
| | | <vxe-select v-model="option.data" :placeholder="$t('processCard.pleaseSelect')" @change="changeFilterEvent($event, option, $panel)"> |
| | | <vxe-option value="0" :label="$t('basicData.unchecked')"></vxe-option> |
| | | <vxe-option value="1" :label="$t('basicData.selected')"></vxe-option> |
| | | </vxe-select> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <template #num1_filter="{ column, $panel }"> |
| | | <div> |
| | | <div v-for="(option, index) in column.filters" :key="index"> |
| | | <input |
| | | |
| | | type="type" |
| | | v-model="option.data" |
| | | @keyup.enter.native="$panel.confirmFilter()" |
| | | @input="changeFilterEvent($event, option, $panel)"/> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <template #state1="{ row,column}"> |
| | | <el-checkbox |
| | | v-model="row.curtain_wall"/> |
| | | </template> |
| | | <template #state2="{ row,column}"> |
| | | <el-checkbox |
| | | v-model="row.allow_rotate"/> |
| | | </template> |
| | | <template #state3="{ row,column}"> |
| | | <el-checkbox |
| | | v-model="row.tempering"/> |
| | | </template> |
| | | </vxe-grid> |
| | | </div> |
| | | </template> |
| | | |
| | | <style scoped> |
| | | :deep(.vxe-tools--operate){ |
| | | height: 20px; |
| | | margin-top: -20px; |
| | | } |
| | | |
| | | </style> |
New file |
| | |
| | | <script setup> |
| | | import {onMounted, reactive, ref, watch} from "vue"; |
| | | import {useI18n} from "vue-i18n"; |
| | | import request from "@/utils/request"; |
| | | import {ElMessage} from "element-plus"; |
| | | import {Minus, Plus, Search} from "@element-plus/icons-vue"; |
| | | import {changeFilterEvent, filterChanged} from "@/hook"; |
| | | const { t } = useI18n() |
| | | |
| | | // 接收父组件传递过来的流程卡号(processId) |
| | | let props = defineProps({ |
| | | processId:null, |
| | | patchState:null, |
| | | technologyNumber:null |
| | | }); |
| | | |
| | | const xGrid = ref() |
| | | |
| | | const gridOptions = reactive({ |
| | | height:'100%', |
| | | loading: false, |
| | | border: "full",//表格加边框 |
| | | keepSource: true,//保持源数据 |
| | | align: 'center',//文字居中 |
| | | stripe:true,//斑马纹 |
| | | rowConfig: {isCurrent: true, isHover: true,height: 30, useKey: true},//鼠标移动或选择高亮 |
| | | id: 'ComputeDetail', |
| | | scrollX:{enabled: true}, |
| | | scrollY:{ enabled: true ,gt:0},//开启虚拟滚动 |
| | | showOverflow:true, |
| | | columnConfig: { |
| | | resizable: true, |
| | | useKey: true |
| | | }, |
| | | filterConfig: { //筛选配置项 |
| | | //remote: true |
| | | }, |
| | | customConfig: { |
| | | storage: true |
| | | }, |
| | | editConfig: { |
| | | trigger: 'click', |
| | | mode: 'row', |
| | | showStatus: true |
| | | }, |
| | | |
| | | columns:[ |
| | | {type: 'seq',width: 70, title: '序号',filters:[{ data: '' }],}, |
| | | {field: 'width',width: 150,title: t('order.width'),filters:[{ data: '' }],slots: { filter: 'num1_filter' },filterMethod:filterChanged}, |
| | | {field: 'height',width: 150,title: t('order.height'),filters:[{ data: '' }],slots: { filter: 'num1_filter' },filterMethod:filterChanged}, |
| | | {field: 'quantity',width: 150, title: t('order.quantity'),filters:[{ data: '' }],slots: { filter: 'num1_filter' },filterMethod:filterChanged}, |
| | | {field: 'building_number',width: 150, title: t('order.buildingNumber'),filters:[{ data: '' }],slots: { filter: 'num1_filter' },filterMethod:filterChanged}, |
| | | {field: 'shape',width: 150, title: t('order.shape'),filters:[{ data: '' }],slots: { filter: 'num1_filter' },filterMethod:filterChanged}, |
| | | {field: 'area',width: 150, title: t('order.grossArea'),filters:[{ data: '' }],slots: { filter: 'num1_filter' },filterMethod:filterChanged}, |
| | | {field: 'icon',width: 150, title: '印标',filters:[{ data: '' }],slots: { filter: 'num1_filter' },filterMethod:filterChanged}, |
| | | ],//表头参数 |
| | | data:null,//表格数据 |
| | | toolbarConfig: { |
| | | buttons: [], |
| | | zoom: true, |
| | | custom: true |
| | | }, |
| | | }) |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | watch( |
| | | ()=> [props.processId,props.technologyNumber], |
| | | ([newValue, oldValue])=> { |
| | | if (props.processId!=null){ |
| | | selectComputeDetail() |
| | | |
| | | } |
| | | }); |
| | | |
| | | |
| | | |
| | | const selectComputeDetail = () => { |
| | | if (props.processId!=null || props.processId!=""){ |
| | | request.post(`/glassOptimize/selectComputeDetailThirdParty/${props.processId}/${props.technologyNumber}`).then((res) => { |
| | | if(Number(res.code) === 200){ |
| | | xGrid.value.loadData(res.data.data) |
| | | }else{ |
| | | ElMessage.warning(res.msg) |
| | | } |
| | | }) |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <template> |
| | | <div style="width: 100%;height: 100%"> |
| | | <span>流程卡详情</span> |
| | | <vxe-grid |
| | | size="small" |
| | | @filter-change="filterChanged" |
| | | height="100%" |
| | | class="mytable-scrollbar" |
| | | ref="xGrid" |
| | | v-bind="gridOptions" |
| | | v-on="gridEvents" |
| | | > |
| | | <template #num2_filter="{ column, $panel }"> |
| | | <div> |
| | | <div v-for="(option, index) in column.filters" :key="index"> |
| | | <vxe-select v-model="option.data" :placeholder="$t('processCard.pleaseSelect')" @change="changeFilterEvent($event, option, $panel)"> |
| | | <vxe-option value="0" :label="$t('basicData.unchecked')"></vxe-option> |
| | | <vxe-option value="1" :label="$t('basicData.selected')"></vxe-option> |
| | | </vxe-select> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <template #num1_filter="{ column, $panel }"> |
| | | <div> |
| | | <div v-for="(option, index) in column.filters" :key="index"> |
| | | <input type="type" v-model="option.data" @keyup.enter.native="$panel.confirmFilter()" @input="changeFilterEvent($event, option, $panel)"/> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | |
| | | </vxe-grid> |
| | | </div> |
| | | </template> |
| | | |
| | | <style scoped> |
| | | :deep(.vxe-tools--operate){ |
| | | height: 20px; |
| | | margin-top: -20px; |
| | | } |
| | | </style> |
New file |
| | |
| | | <script setup> |
| | | import {nextTick, onMounted, reactive, ref, watch} from "vue"; |
| | | import {useI18n} from "vue-i18n"; |
| | | import request from "@/utils/request"; |
| | | import {ElMessage, ElMessageBox} from "element-plus"; |
| | | import {useRoute} from "vue-router"; |
| | | import {addListener, toolbarButtonClickEvent} from "@/hook/mouseMove"; |
| | | const { t } = useI18n() |
| | | |
| | | const xGrid = ref() |
| | | const gridOptions = reactive({ |
| | | height:'100%', |
| | | loading: false, |
| | | border: "full",//表格加边框 |
| | | keepSource: true,//保持源数据 |
| | | align: 'center',//文字居中 |
| | | stripe:true,//斑马纹 |
| | | rowConfig: {isCurrent: true, isHover: true,height: 30, useKey: true},//鼠标移动或选择高亮 |
| | | id: 'GlassInventory', |
| | | scrollX:{enabled: true}, |
| | | scrollY:{ enabled: true ,gt:0},//开启虚拟滚动 |
| | | showOverflow:true, |
| | | columnConfig: { |
| | | resizable: true, |
| | | useKey: true |
| | | }, |
| | | filterConfig: { //筛选配置项 |
| | | remote: true |
| | | }, |
| | | customConfig: { |
| | | storage: true |
| | | }, |
| | | editConfig: { |
| | | trigger: 'dblclick', |
| | | mode: 'cell', |
| | | showStatus: true |
| | | }, |
| | | |
| | | columns:[ |
| | | {type:'seq',fixed:"left", title:' ', width: 50}, |
| | | {type: 'checkbox', fixed: "left", title: t('basicData.check'), width: 80}, |
| | | {field: 'id', title: '物料编码',}, |
| | | {field:'width',editRender: { name: 'input',attrs: {disabled:true}}, title: t('order.width'),}, |
| | | {field: 'height',editRender: { name: 'input',attrs: {disabled:true}},title: t('order.height')}, |
| | | {field: 'thickness', title: t('order.totalThickness'),}, |
| | | {field: 'model', title: t('warehouseBasicData.type'),}, |
| | | {field: 'leftTrim',editRender: { name: 'input',attrs: {disabled:false}}, title: '左修边',}, |
| | | {field: 'downTrim',editRender: { name: 'input',attrs: {disabled:false}}, title: '下修边',}, |
| | | {field: 'rightTrim',editRender: { name: 'input',attrs: {disabled:false}}, title: '右修边',}, |
| | | {field: 'upTrim',editRender: { name: 'input',attrs: {disabled:false}}, title: '上修边',}, |
| | | {field: 'available_quantity',editRender: { name: 'input',attrs: {disabled:false}}, title: '库存数量',}, |
| | | {field: 'processingQuantity', title: '加工数量',}, |
| | | {field: 'name', title: '名称',}, |
| | | {field: 'producer', title: '供应商',} |
| | | ],//表头参数 |
| | | data:null,//表格数据 |
| | | //右键菜单 |
| | | menuConfig: { |
| | | body: { |
| | | options: [ |
| | | [ |
| | | {code: 'selectTrimming', name: '设置统一修边',}, |
| | | {code: 'Exports', name: '数据导出', prefixIcon: 'vxe-icon-download', visible: true, disabled: false}, |
| | | {code: 'addRow', name: t('basicData.add'), prefixIcon: 'vxe-icon-square-plus', visible: true, disabled: true }, |
| | | ], |
| | | ] |
| | | }, |
| | | }, |
| | | toolbarConfig: { |
| | | buttons: [], |
| | | }, |
| | | }) |
| | | |
| | | const emit = defineEmits(['select-trimming']); |
| | | // 右键菜单 |
| | | const operationConfigs = [ |
| | | { |
| | | code: 'selectTrimming', // 设置统一修边 |
| | | successMsg: '已打开!', |
| | | gridRef: xGrid, |
| | | requiresRow: false, |
| | | openTrimming: async () => { |
| | | emit ( 'select-trimming', true) |
| | | } |
| | | }, |
| | | { |
| | | code: 'Exports', // 导出文件操作的配置 |
| | | successMsg: '文件导出成功!', |
| | | gridRef: xGrid, |
| | | requiresRow: false, |
| | | }, |
| | | { |
| | | code: 'addRow', // 导出文件操作的配置 |
| | | successMsg: '添加成功!', |
| | | gridRef: xGrid, |
| | | requiresRow: false, |
| | | }, |
| | | ] |
| | | |
| | | // 右键菜单点击逻辑 |
| | | const gridEvents = { |
| | | menuClick({menu}) { |
| | | const $grid = xGrid.value; |
| | | if ($grid) { |
| | | const config = operationConfigs.find(c => c.code === menu.code); |
| | | if (config) { |
| | | if (config.code === 'Exports') { |
| | | config.gridRef.value.exportData(); |
| | | ElMessage.success(config.successMsg); |
| | | return; |
| | | } |
| | | if (config.code === 'addRow') { |
| | | if ($grid.getTableData().tableData.length >=100){ |
| | | ElMessage.error(t('order.msg.tableLengthMax')) |
| | | return |
| | | } |
| | | if ($grid.getCheckedFilters().length!==0){ |
| | | ElMessage.error(t('order.msg.pleaseCancelTheFilteringFirst')) |
| | | return |
| | | } |
| | | console.log($grid.getTableData().visibleData) |
| | | let result = toolbarButtonClickEvent() |
| | | let lengths = 0 |
| | | if (result!=null){ |
| | | lengths=result.start |
| | | } |
| | | if($grid.getTableData().fullData.length>lengths+1){ |
| | | $grid.insertAt({}, lengths+1) |
| | | }else{ |
| | | $grid.insertAt({}, -1) |
| | | } |
| | | return; |
| | | } |
| | | // 添加确认提示弹窗,询问用户是否进行当前操作 |
| | | ElMessageBox.confirm('是否进行当前操作?', '确认操作', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | }).then(() => { |
| | | if (config.code === 'selectTrimming') { |
| | | config.openTrimming(); |
| | | ElMessage.success(config.successMsg); |
| | | } |
| | | }).catch(() => { |
| | | // 用户点击取消后执行的逻辑 |
| | | ElMessage.info('已取消操作'); |
| | | }); |
| | | } else { |
| | | console.error(`未找到操作选项 ${menu.code} 对应的配置,请检查配置项`); |
| | | } |
| | | } |
| | | }, |
| | | }; |
| | | |
| | | |
| | | const route = useRoute(); |
| | | |
| | | const thickness = ref(route.params.thickNess); |
| | | const model = ref(route.params.model); |
| | | let projectNo = ref(null); |
| | | let type = ref(1); |
| | | |
| | | const selectMaterialStore = async () =>{ |
| | | request.get(`/glassOptimize/materialStoreSvThirdParty/${projectNo}`).then((res) => { |
| | | if (Number(res.code) === 200) { |
| | | const rawData = res.data.data; |
| | | const edgeTrimming = res.data.edgeTrimming; |
| | | let state = res.data.state; |
| | | if (Array.isArray(rawData) && rawData.length > 0) { |
| | | const formattedData = rawData.map(item => { |
| | | const formattedItem = {}; |
| | | for (const key in item) { |
| | | if (typeof item[key] === 'string') { |
| | | //去除字符串属性值开头和结尾的双引号 |
| | | formattedItem[key] = item[key].replace(/^\"|\"$/g, ''); |
| | | } else { |
| | | formattedItem[key] = item[key]; |
| | | } |
| | | } |
| | | return formattedItem; |
| | | }); |
| | | |
| | | gridOptions.columns[7].editRender.attrs.disabled=true |
| | | gridOptions.columns[8].editRender.attrs.disabled=true |
| | | gridOptions.columns[9].editRender.attrs.disabled=true |
| | | gridOptions.columns[10].editRender.attrs.disabled=true |
| | | gridOptions.columns[11].editRender.attrs.disabled=true |
| | | |
| | | xGrid.value.loadData(formattedData); |
| | | gridOptions.data = formattedData; |
| | | } else { |
| | | } |
| | | } else { |
| | | ElMessage.warning(res.msg); |
| | | console.error('请求获取库存失败,状态码:', res.code, ',错误信息:', res.msg); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | addListener(xGrid.value,gridOptions) |
| | | if(route.params.projectNo!=null){ |
| | | projectNo=route.params.projectNo |
| | | selectMaterialStore(); |
| | | } |
| | | |
| | | }); |
| | | |
| | | watch(() => props.receivedData, (newData) => { |
| | | if (newData) { |
| | | Trimming(newData); |
| | | } |
| | | }, { immediate: true }); |
| | | |
| | | watch(() => props.InventoryData, (newInventoryData) => { |
| | | if (newInventoryData) { |
| | | thickness.value = newInventoryData.selectedLabel1; |
| | | model.value = newInventoryData.selectedLabel2; |
| | | type.value=newInventoryData.type |
| | | // 由于 thickness 和 model 的值改变了,更新表格,调用 selectMaterialStore 重新获取数据 |
| | | selectMaterialStore(); |
| | | } |
| | | }); |
| | | |
| | | const props = defineProps({ |
| | | receivedData : { |
| | | type: Object, |
| | | required: false, |
| | | properties: { |
| | | quicksetLeft: { type: Number }, |
| | | quicksetBottom: { type: Number }, |
| | | quicksetRight: { type: Number }, |
| | | quicksetTop: { type: Number } |
| | | } |
| | | }, |
| | | InventoryData : { |
| | | type: Object, |
| | | required: false, |
| | | properties: { |
| | | selectedLabel1: { type: String }, |
| | | selectedLabel2: { type: String }, |
| | | type: { type: String } |
| | | } |
| | | } |
| | | }); |
| | | |
| | | //接受SetTrimming的值 (修边) |
| | | const Trimming = (receivedData) => { |
| | | nextTick(() => { |
| | | const data = gridOptions.data; |
| | | if (data) { |
| | | try { |
| | | const updatedData = []; |
| | | for (let i = 0; i < data.length; i++) { |
| | | const item = data[i]; |
| | | const updatedItem = { |
| | | ...item, |
| | | // 从 receivedData 中获取对应的值来更新表格数据项 |
| | | leftTrim: Number(receivedData.quicksetLeft), |
| | | downTrim: Number(receivedData.quicksetBottom), |
| | | rightTrim: Number(receivedData.quicksetRight), |
| | | upTrim: Number(receivedData.quicksetTop), |
| | | }; |
| | | updatedData.push(updatedItem); |
| | | } |
| | | gridOptions.data = updatedData; |
| | | xGrid.value.loadData(updatedData); |
| | | } catch (error) { |
| | | console.error('更新表格数据时出错:', error); |
| | | ElMessage.error('更新表格数据时出现错误,请检查输入或联系管理员'); |
| | | } |
| | | } else { |
| | | console.warn('表格数据为空,无法更新磨量值'); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | |
| | | </script> |
| | | |
| | | <template> |
| | | <div style="width: 100%;height: 100%"> |
| | | <vxe-grid |
| | | height="100%" |
| | | class="mytable-scrollbar" |
| | | ref="xGrid" |
| | | v-bind="gridOptions" |
| | | v-on="gridEvents" |
| | | > |
| | | |
| | | |
| | | <template #num2_filter="{ column, $panel }"> |
| | | <div> |
| | | <div v-for="(option, index) in column.filters" :key="index"> |
| | | <vxe-select v-model="option.data" :placeholder="$t('processCard.pleaseSelect')" @change="changeFilterEvent($event, option, $panel)"> |
| | | <vxe-option value="0" :label="$t('basicData.unchecked')"></vxe-option> |
| | | <vxe-option value="1" :label="$t('basicData.selected')"></vxe-option> |
| | | </vxe-select> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <template #num1_filter="{ column, $panel }"> |
| | | <div> |
| | | <div v-for="(option, index) in column.filters" :key="index"> |
| | | <input |
| | | type="type" |
| | | v-model="option.data" |
| | | @keyup.enter.native="$panel.confirmFilter()" |
| | | @input="changeFilterEvent($event, option, $panel)"/> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | </vxe-grid> |
| | | </div> |
| | | </template> |
| | | |
| | | <style scoped> |
| | | |
| | | |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <div style="display: flex; height: 100vh;"> |
| | | <!-- Sidebar --> |
| | | <div class="sidebar" style="width: 200px; background: #f4f4f4; padding: 10px;"> |
| | | <div |
| | | v-for="(layout, layoutIndex) in layouts" |
| | | :key="layoutIndex" |
| | | class="sidebar-item" |
| | | @click="selectLayout(layoutIndex)" |
| | | :class="{ 'selected': selectedLayoutIndex === layoutIndex }" |
| | | > |
| | | {{ layout.width }} × {{ layout.height }} × {{ layout.SameCount }} |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- Main Layout Panel --> |
| | | <div ref="layoutPanel" :class="panelClass" :style="panelStyle"> |
| | | <div |
| | | v-for="(layout, layoutIndex) in layouts" |
| | | :key="layoutIndex" |
| | | class="layout-wrapper" |
| | | :style="{ display: selectedLayoutIndex === layoutIndex ? 'block' : 'none', top: '-150px' }" |
| | | > |
| | | <!-- Layout Info Label --> |
| | | <div class="layout-info" :style="layoutInfoStyle(layoutIndex)"> |
| | | {{ getCurrentRectInfo(layoutIndex) }} |
| | | </div> |
| | | |
| | | <!-- Layout Container --> |
| | | <div class="layout-container" :style="layoutContainerStyle(layoutIndex)"> |
| | | <!-- 灰色矩形 --> |
| | | <div |
| | | v-for="(rect, rectIndex) in layout.rects.filter(r => r.isRemain)" |
| | | :key="`gray-${rectIndex}`" |
| | | :ref="(el) => { if (el) rectsElements[layoutIndex + '-' + rectIndex] = el }" |
| | | class="layout-rect" |
| | | :style="rectStyle(rect, layoutIndex)" |
| | | @contextmenu.prevent="handleGrayRectRightClick(layoutIndex, rectIndex)" |
| | | /> |
| | | |
| | | <!-- 蓝色矩形 --> |
| | | <div |
| | | v-for="(rect, rectIndex) in layout.rects.filter(r => !r.isRemain)" |
| | | :key="`blue-${rectIndex}`" |
| | | :ref="(el) => { if (el) rectsElements[layoutIndex + '-' + rectIndex] = el }" |
| | | class="layout-rect" |
| | | :style="rectStyle(rect, layoutIndex)" |
| | | @contextmenu.prevent="handleRectRightClick(layoutIndex, rectIndex)" |
| | | @mousedown="handleRectDragStart(layoutIndex, rectIndex)" |
| | | @mousemove="handleRectDragging" |
| | | @mouseup="handleRectDragEnd" |
| | | @mouseleave="handleRectDragEnd" |
| | | @click="handleRectClick(layoutIndex, rectIndex)" |
| | | > |
| | | <div class="rect-content"> |
| | | <div class="size">{{ rect.w }}×{{ rect.h }}</div> |
| | | <div v-if="showJiaHao" class="jia-hao">{{ rect.JiaHao }}</div> |
| | | <div v-if="showProcessId" class="liuchengka">{{ rect.liuchengka }}</div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 提交按钮 --> |
| | | <button @click="submitLayouts" style="position: fixed; bottom: 20px; right: 20px; padding: 10px; background: #4CAF50; color: white; border: none; border-radius: 5px; cursor: pointer;"> |
| | | 保存调整 |
| | | </button> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, onUnmounted } from 'vue'; |
| | | import request from "@/utils/request"; |
| | | import { useI18n } from "vue-i18n"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import useUserInfoStore from "@/stores/userInfo"; |
| | | |
| | | const { t } = useI18n(); |
| | | const userStore = useUserInfoStore() |
| | | const username = userStore.user.userName |
| | | let clickEventListener = null; |
| | | const props = defineProps({ |
| | | layoutData: { type: Object, required: true }, |
| | | gw: { type: Number, default: 1000 }, |
| | | gh: { type: Number, default: 1000 }, |
| | | style: { type: String, default: 'width:1000px;height:600px;display:block;background:gray' } |
| | | }); |
| | | |
| | | const emit = defineEmits(['rectClicked']); |
| | | const layoutPanel = ref(null); |
| | | const rectsElements = ref({}); |
| | | const focusIndex = ref(null); |
| | | const layouts = ref([]); |
| | | const panelClass = ref(''); |
| | | const panelStyle = ref(props.style); |
| | | const rectClass = ref('layout-rect'); |
| | | const selectedLayoutIndex = ref(0); |
| | | const currentRect = ref(null); |
| | | const dragging = ref(false); |
| | | const dragStartPos = ref({ x: 0, y: 0 }); |
| | | const dragRect = ref(null); |
| | | const showJiaHao = ref(false); |
| | | const showProcessId = ref(false); |
| | | const themeColor = ref(null); |
| | | |
| | | const submitLayouts = async () => { |
| | | layouts.value.forEach(layout => { |
| | | layout.rects.forEach(rect => { |
| | | rect.x = Math.round(rect.x); |
| | | rect.y = Math.round(rect.y); |
| | | rect.w = Math.round(rect.w); |
| | | rect.h = Math.round(rect.h); |
| | | }); |
| | | }); |
| | | const savedProjectNo = localStorage.getItem('projectNo'); |
| | | const processId = savedProjectNo; |
| | | const Layouts = layouts.value; |
| | | request.post(`/glassOptimize/updateOptimizeResult/${processId}`, JSON.stringify({ Layouts }), { |
| | | headers: { |
| | | 'Content-Type': 'application/json' |
| | | } |
| | | }).then((res) => { |
| | | if (res.code == 200 && res.data === true) { |
| | | ElMessage.success(t('basicData.msg.saveSuccess')); |
| | | } else { |
| | | ElMessage.warning(res.msg); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const fetchSettings = async (username) => { |
| | | try { |
| | | const response = await request.post(`/glassOptimize/selectOptimizeParms/${username}`); |
| | | if (response.code == 200) { |
| | | if (!response.data) { |
| | | console.error('响应数据为空'); |
| | | return; |
| | | } |
| | | const parsedData = JSON.parse(response.data); |
| | | if (parsedData.display && parsedData.frameNumber) { |
| | | showJiaHao.value = parsedData.display.frameNumber; |
| | | } |
| | | if (parsedData.display && parsedData.orderNumber) { |
| | | showProcessId.value = parsedData.display.orderNumber; |
| | | } |
| | | if (parsedData.display) { |
| | | themeColor.value = parsedData.display.themeColor; |
| | | } |
| | | } else { |
| | | console.error('请求失败,状态码:', response.code); |
| | | } |
| | | } catch (error) { |
| | | console.error('请求发生错误:', error); |
| | | } |
| | | }; |
| | | |
| | | const showAddDialog = (layoutIndex, rectIndex) => { |
| | | ElMessageBox.prompt('请输入成品的宽度和高度', '添加成品', { |
| | | inputType: 'text', |
| | | inputValidator: (value) => { |
| | | const values = value.split(',').map(v => v.trim()); |
| | | if (values.length !== 2) { |
| | | return '请输入两个数字,用逗号分隔'; |
| | | } |
| | | const [width, height] = values; |
| | | if (isNaN(width) || isNaN(height)) { |
| | | return '请输入有效的数字'; |
| | | } |
| | | if (width <= 0 || height <= 0) { |
| | | return '宽度和高度必须大于0'; |
| | | } |
| | | return true; |
| | | }, |
| | | inputErrorMessage: '输入格式不正确' |
| | | }) |
| | | .then(({ value }) => { |
| | | const values = value.split(',').map(v => parseFloat(v.trim())); |
| | | const newRect = { |
| | | x: 0, |
| | | y: 0, |
| | | w: values[0], |
| | | h: values[1], |
| | | isRemain: false |
| | | }; |
| | | addNewRect(layoutIndex, newRect); |
| | | }) |
| | | .catch(() => { |
| | | // 用户取消 |
| | | }); |
| | | }; |
| | | |
| | | const addNewRect = (layoutIndex, newRect) => { |
| | | const layout = layouts.value[layoutIndex]; |
| | | const bestFitPosition = findBestFitPosition(layoutIndex, newRect); |
| | | if (bestFitPosition) { |
| | | newRect.x = bestFitPosition.x; |
| | | newRect.y = bestFitPosition.y; |
| | | layout.rects.push(newRect); |
| | | adjustGrayRectangles(layoutIndex); |
| | | } else { |
| | | ElMessage.warning('无法放置,没有足够的空间'); |
| | | } |
| | | }; |
| | | |
| | | const findBestFitPosition = (layoutIndex, newRect) => { |
| | | const layout = layouts.value[layoutIndex]; |
| | | const obstacles = layout.rects.filter(r => !r.isRemain); |
| | | let bestFit = null; |
| | | let minAreaDifference = Infinity; |
| | | |
| | | const remainingAreas = calculateRemainingAreas(layout.width, layout.height, obstacles); |
| | | remainingAreas.forEach(area => { |
| | | if (newRect.w <= area.w && newRect.h <= area.h) { |
| | | const areaDifference = Math.abs(area.w * area.h - newRect.w * newRect.h); |
| | | if (areaDifference < minAreaDifference) { |
| | | minAreaDifference = areaDifference; |
| | | bestFit = { |
| | | x: area.x, |
| | | y: area.y, |
| | | w: newRect.w, |
| | | h: newRect.h |
| | | }; |
| | | } |
| | | } |
| | | }); |
| | | |
| | | return bestFit; |
| | | }; |
| | | |
| | | const layoutContainerStyle = (layoutIndex) => { |
| | | const layout = layouts.value[layoutIndex]; |
| | | const scale = Math.min( |
| | | (props.gw - 100) / layout.width, |
| | | (props.gh - 100) / layout.height |
| | | ); |
| | | return { |
| | | position: 'absolute', |
| | | left: `${(props.gw - layout.width * scale) / 2}px`, |
| | | top: `${(props.gh - layout.height * scale) / 2}px`, |
| | | width: `${layout.width * scale}px`, |
| | | height: `${layout.height * scale}px`, |
| | | overflow: 'visible', |
| | | border: '1px solid #ccc', |
| | | background: '#fff' |
| | | }; |
| | | }; |
| | | |
| | | const layoutInfoStyle = (layoutIndex) => { |
| | | const layout = layouts.value[layoutIndex]; |
| | | const scale = Math.min( |
| | | (props.gw - 100) / layout.width, |
| | | (props.gh - 100) / layout.height |
| | | ); |
| | | return { |
| | | position: 'absolute', |
| | | left: `${(props.gw - layout.width * scale) / 2}px`, |
| | | top: `${(props.gh - layout.height * scale) / 2 - 45}px`, |
| | | background: 'none', |
| | | textAlign: 'center', |
| | | zIndex: 1000 |
| | | }; |
| | | }; |
| | | |
| | | const rectStyle = (rect, layoutIndex) => { |
| | | const layout = layouts.value[layoutIndex]; |
| | | const scale = Math.min( |
| | | (props.gw - 100) / layout.width, |
| | | (props.gh - 100) / layout.height |
| | | ); |
| | | return { |
| | | position: 'absolute', |
| | | left: `${rect.x * scale}px`, |
| | | top: `${rect.y * scale}px`, |
| | | width: `${rect.w * scale}px`, |
| | | height: `${rect.h * scale}px`, |
| | | backgroundColor: rect.isRemain ? '#f0f0f0' : themeColor.value, |
| | | border: '1px solid #000', |
| | | cursor: 'pointer', |
| | | draggable: !rect.isRemain, |
| | | zIndex: rect.isRemain ? 1 : 2 |
| | | }; |
| | | }; |
| | | |
| | | const handleRectClick = (layoutIndex, rectIndex) => { |
| | | focusIndex.value = { layoutIndex, rectIndex }; |
| | | emit('rectClicked', layoutIndex, rectIndex); |
| | | }; |
| | | |
| | | const handleRectRightClick = (layoutIndex, rectIndex) => { |
| | | const rect = layouts.value[layoutIndex].rects[rectIndex]; |
| | | if (rect.isRemain) return; |
| | | |
| | | const contextMenu = document.createElement('div'); |
| | | contextMenu.className = 'context-menu'; |
| | | contextMenu.style.position = 'absolute'; |
| | | contextMenu.style.left = `${event.clientX}px`; |
| | | contextMenu.style.top = `${event.clientY}px`; |
| | | contextMenu.style.backgroundColor = '#fff'; |
| | | contextMenu.style.border = '1px solid #ccc'; |
| | | contextMenu.style.padding = '5px'; |
| | | contextMenu.style.zIndex = 1001; |
| | | |
| | | const rotateItem = document.createElement('div'); |
| | | rotateItem.textContent = '旋转'; |
| | | rotateItem.style.cursor = 'pointer'; |
| | | rotateItem.addEventListener('click', () => { |
| | | rotateRect(layoutIndex, rectIndex); |
| | | document.body.removeChild(contextMenu); |
| | | }); |
| | | |
| | | const moveUpAndRotateItem = document.createElement('div'); |
| | | moveUpAndRotateItem.textContent = '向上移动并旋转'; |
| | | moveUpAndRotateItem.style.cursor = 'pointer'; |
| | | moveUpAndRotateItem.addEventListener('click', () => { |
| | | moveRectAndRotate(layoutIndex, rectIndex, 'up'); |
| | | document.body.removeChild(contextMenu); |
| | | }); |
| | | |
| | | const moveDownAndRotateItem = document.createElement('div'); |
| | | moveDownAndRotateItem.textContent = '向下移动并旋转'; |
| | | moveDownAndRotateItem.style.cursor = 'pointer'; |
| | | moveDownAndRotateItem.addEventListener('click', () => { |
| | | moveRectAndRotate(layoutIndex, rectIndex, 'down'); |
| | | document.body.removeChild(contextMenu); |
| | | }); |
| | | |
| | | const moveLeftAndRotateItem = document.createElement('div'); |
| | | moveLeftAndRotateItem.textContent = '向左移动并旋转'; |
| | | moveLeftAndRotateItem.style.cursor = 'pointer'; |
| | | moveLeftAndRotateItem.addEventListener('click', () => { |
| | | moveRectAndRotate(layoutIndex, rectIndex, 'left'); |
| | | document.body.removeChild(contextMenu); |
| | | }); |
| | | |
| | | const moveRightAndRotateItem = document.createElement('div'); |
| | | moveRightAndRotateItem.textContent = '向右移动并旋转'; |
| | | moveRightAndRotateItem.style.cursor = 'pointer'; |
| | | moveRightAndRotateItem.addEventListener('click', () => { |
| | | moveRectAndRotate(layoutIndex, rectIndex, 'right'); |
| | | document.body.removeChild(contextMenu); |
| | | }); |
| | | |
| | | const moveUpItem = document.createElement('div'); |
| | | moveUpItem.textContent = '向上移动'; |
| | | moveUpItem.style.cursor = 'pointer'; |
| | | moveUpItem.addEventListener('click', () => { |
| | | moveRect(layoutIndex, rectIndex, 'up'); |
| | | document.body.removeChild(contextMenu); |
| | | }); |
| | | |
| | | const moveDownItem = document.createElement('div'); |
| | | moveDownItem.textContent = '向下移动'; |
| | | moveDownItem.style.cursor = 'pointer'; |
| | | moveDownItem.addEventListener('click', () => { |
| | | moveRect(layoutIndex, rectIndex, 'down'); |
| | | document.body.removeChild(contextMenu); |
| | | }); |
| | | |
| | | const moveLeftItem = document.createElement('div'); |
| | | moveLeftItem.textContent = '向左移动'; |
| | | moveLeftItem.style.cursor = 'pointer'; |
| | | moveLeftItem.addEventListener('click', () => { |
| | | moveRect(layoutIndex, rectIndex, 'left'); |
| | | document.body.removeChild(contextMenu); |
| | | }); |
| | | |
| | | const moveRightItem = document.createElement('div'); |
| | | moveRightItem.textContent = '向右移动'; |
| | | moveRightItem.style.cursor = 'pointer'; |
| | | moveRightItem.addEventListener('click', () => { |
| | | moveRect(layoutIndex, rectIndex, 'right'); |
| | | document.body.removeChild(contextMenu); |
| | | }); |
| | | |
| | | const deleteItem = document.createElement('div'); |
| | | deleteItem.textContent = '删除'; |
| | | deleteItem.style.cursor = 'pointer'; |
| | | deleteItem.addEventListener('click', () => { |
| | | deleteRect(layoutIndex, rectIndex); |
| | | document.body.removeChild(contextMenu); |
| | | }); |
| | | |
| | | const addItem = document.createElement('div'); |
| | | addItem.textContent = '添加成品'; |
| | | addItem.style.cursor = 'pointer'; |
| | | addItem.addEventListener('click', () => { |
| | | showAddDialog(layoutIndex, rectIndex); |
| | | document.body.removeChild(contextMenu); |
| | | }); |
| | | |
| | | const mirrorXItem = document.createElement('div'); |
| | | mirrorXItem.textContent = 'X镜像'; |
| | | mirrorXItem.style.cursor = 'pointer'; |
| | | mirrorXItem.addEventListener('click', () => { |
| | | mirrorLayoutX(layoutIndex); |
| | | document.body.removeChild(contextMenu); |
| | | }); |
| | | |
| | | const mirrorYItem = document.createElement('div'); |
| | | mirrorYItem.textContent = 'Y镜像'; |
| | | mirrorYItem.style.cursor = 'pointer'; |
| | | mirrorYItem.addEventListener('click', () => { |
| | | mirrorLayoutY(layoutIndex); |
| | | document.body.removeChild(contextMenu); |
| | | }); |
| | | |
| | | contextMenu.appendChild(rotateItem); |
| | | contextMenu.appendChild(moveUpAndRotateItem); |
| | | contextMenu.appendChild(moveDownAndRotateItem); |
| | | contextMenu.appendChild(moveLeftAndRotateItem); |
| | | contextMenu.appendChild(moveRightAndRotateItem); |
| | | contextMenu.appendChild(moveUpItem); |
| | | contextMenu.appendChild(moveDownItem); |
| | | contextMenu.appendChild(moveLeftItem); |
| | | contextMenu.appendChild(moveRightItem); |
| | | contextMenu.appendChild(deleteItem); |
| | | contextMenu.appendChild(addItem); |
| | | contextMenu.appendChild(mirrorXItem); |
| | | contextMenu.appendChild(mirrorYItem); |
| | | |
| | | document.body.appendChild(contextMenu); |
| | | }; |
| | | |
| | | const handleGrayRectRightClick = (layoutIndex, rectIndex) => { |
| | | const rect = layouts.value[layoutIndex].rects[rectIndex]; |
| | | if (!rect.isRemain) return; |
| | | |
| | | const contextMenu = document.createElement('div'); |
| | | contextMenu.className = 'context-menu'; |
| | | contextMenu.style.position = 'absolute'; |
| | | contextMenu.style.left = `${event.clientX}px`; |
| | | contextMenu.style.top = `${event.clientY}px`; |
| | | contextMenu.style.backgroundColor = '#fff'; |
| | | contextMenu.style.border = '1px solid #ccc'; |
| | | contextMenu.style.padding = '5px'; |
| | | contextMenu.style.zIndex = 1001; |
| | | |
| | | const addItem = document.createElement('div'); |
| | | addItem.textContent = '添加成品'; |
| | | addItem.style.cursor = 'pointer'; |
| | | addItem.addEventListener('click', () => { |
| | | showAddDialog(layoutIndex, rectIndex); |
| | | document.body.removeChild(contextMenu); |
| | | }); |
| | | |
| | | const mirrorXItem = document.createElement('div'); |
| | | mirrorXItem.textContent = 'X镜像'; |
| | | mirrorXItem.style.cursor = 'pointer'; |
| | | mirrorXItem.addEventListener('click', () => { |
| | | mirrorLayoutX(layoutIndex); |
| | | document.body.removeChild(contextMenu); |
| | | }); |
| | | |
| | | const mirrorYItem = document.createElement('div'); |
| | | mirrorYItem.textContent = 'Y镜像'; |
| | | mirrorYItem.style.cursor = 'pointer'; |
| | | mirrorYItem.addEventListener('click', () => { |
| | | mirrorLayoutY(layoutIndex); |
| | | document.body.removeChild(contextMenu); |
| | | }); |
| | | |
| | | contextMenu.appendChild(addItem); |
| | | contextMenu.appendChild(mirrorXItem); |
| | | contextMenu.appendChild(mirrorYItem); |
| | | |
| | | document.body.appendChild(contextMenu); |
| | | }; |
| | | |
| | | const handleRectDragStart = (layoutIndex, rectIndex) => { |
| | | const layout = layouts.value[layoutIndex]; |
| | | const rect = layout.rects[rectIndex]; |
| | | if (rect.isRemain) return; |
| | | |
| | | dragging.value = true; |
| | | dragRect.value = { layoutIndex, rectIndex }; |
| | | dragStartPos.value = { |
| | | x: event.clientX, |
| | | y: event.clientY |
| | | }; |
| | | }; |
| | | |
| | | const handleRectDragging = (event) => { |
| | | if (!dragging.value || !dragRect.value) return; |
| | | |
| | | const layoutIndex = dragRect.value.layoutIndex; |
| | | const rectIndex = dragRect.value.rectIndex; |
| | | const layout = layouts.value[layoutIndex]; |
| | | const rect = layout.rects[rectIndex]; |
| | | const scale = Math.min( |
| | | (props.gw - 100) / layout.width, |
| | | (props.gh - 100) / layout.height |
| | | ); |
| | | |
| | | const deltaX = event.clientX - dragStartPos.value.x; |
| | | const deltaY = event.clientY - dragStartPos.value.y; |
| | | |
| | | const newRect = { ...rect }; |
| | | newRect.x += deltaX / scale; |
| | | newRect.y += deltaY / scale; |
| | | |
| | | const otherRects = layout.rects.filter(r => !r.isRemain && r !== rect); |
| | | let isValidMove = true; |
| | | |
| | | otherRects.forEach(otherRect => { |
| | | if (checkOverlap(newRect, otherRect)) { |
| | | isValidMove = false; |
| | | } |
| | | }); |
| | | |
| | | if (newRect.x < 0 || newRect.y < 0 || |
| | | newRect.x + newRect.w > layout.width || |
| | | newRect.y + newRect.h > layout.height) { |
| | | isValidMove = false; |
| | | } |
| | | |
| | | if (isValidMove) { |
| | | rect.x = newRect.x; |
| | | rect.y = newRect.y; |
| | | dragStartPos.value = { |
| | | x: event.clientX, |
| | | y: event.clientY |
| | | }; |
| | | adjustGrayRectangles(layoutIndex); |
| | | } |
| | | }; |
| | | |
| | | const handleRectDragEnd = () => { |
| | | if (dragRect.value) { |
| | | const layoutIndex = dragRect.value.layoutIndex; |
| | | const rectIndex = dragRect.value.rectIndex; |
| | | const rect = layouts.value[layoutIndex].rects[rectIndex]; |
| | | const layout = layouts.value[layoutIndex]; |
| | | const scale = Math.min( |
| | | (props.gw - 100) / layout.width, |
| | | (props.gh - 100) / layout.height |
| | | ); |
| | | |
| | | rect.x = Math.round(rect.x); |
| | | rect.y = Math.round(rect.y); |
| | | adjustAlignmentPosition(layoutIndex, rectIndex); |
| | | adjustGrayRectangles(layoutIndex); |
| | | } |
| | | |
| | | dragging.value = false; |
| | | dragRect.value = null; |
| | | }; |
| | | |
| | | const adjustAlignmentPosition = (layoutIndex, rectIndex) => { |
| | | const layout = layouts.value[layoutIndex]; |
| | | const rect = layout.rects[rectIndex]; |
| | | const otherRects = layout.rects.filter((r, i) => i !== rectIndex); |
| | | |
| | | const threshold = Math.max(rect.w, rect.h) * 0.1; |
| | | |
| | | otherRects.forEach(otherRect => { |
| | | // 水平对齐 |
| | | if (Math.abs(rect.x - otherRect.x) < threshold) { |
| | | rect.x = Math.round((rect.x + otherRect.x) / 2); |
| | | } |
| | | // 水平对齐右侧边缘 |
| | | if (Math.abs((rect.x + rect.w) - (otherRect.x + otherRect.w)) < threshold) { |
| | | rect.x = Math.round((otherRect.x + otherRect.w - rect.w)); |
| | | } |
| | | // 垂直对齐 |
| | | if (Math.abs(rect.y - otherRect.y) < threshold) { |
| | | rect.y = Math.round((rect.y + otherRect.y) / 2); |
| | | } |
| | | // 垂直对齐下边缘 |
| | | if (Math.abs((rect.y + rect.h) - (otherRect.y + otherRect.h)) < threshold) { |
| | | rect.y = Math.round((otherRect.y + otherRect.h - rect.h)); |
| | | } |
| | | }); |
| | | |
| | | // 确保矩形不会超出布局边界 |
| | | rect.x = Math.max(0, rect.x); |
| | | rect.y = Math.max(0, rect.y); |
| | | rect.x = Math.min(rect.x, layout.width - rect.w); |
| | | rect.y = Math.min(rect.y, layout.height - rect.h); |
| | | |
| | | // 调整后重新计算灰色余料 |
| | | adjustGrayRectangles(layoutIndex); |
| | | }; |
| | | |
| | | const mergeAdjacentGrayRects = (rects) => { |
| | | const grayRects = rects.filter(r => r.isRemain); |
| | | let merged = []; |
| | | |
| | | grayRects.sort((a, b) => { |
| | | if (a.x !== b.x) return a.x - b.x; |
| | | return a.y - b.y; |
| | | }); |
| | | |
| | | if (grayRects.length === 0) return; |
| | | |
| | | merged.push({ ...grayRects[0] }); |
| | | |
| | | for (let i = 1; i < grayRects.length; i++) { |
| | | const last = merged[merged.length - 1]; |
| | | const current = grayRects[i]; |
| | | |
| | | if (current.x === last.x + last.w && |
| | | current.y === last.y && |
| | | current.h === last.h) { |
| | | last.w += current.w; |
| | | last.x = Math.round(last.x); |
| | | last.y = Math.round(last.y); |
| | | last.w = Math.round(last.w); |
| | | last.h = Math.round(last.h); |
| | | } else if (current.y === last.y + last.h && |
| | | current.x === last.x && |
| | | current.w === last.w) { |
| | | last.h += current.h; |
| | | last.x = Math.round(last.x); |
| | | last.y = Math.round(last.y); |
| | | last.w = Math.round(last.w); |
| | | last.h = Math.round(last.h); |
| | | } else { |
| | | merged.push({ |
| | | x: Math.round(current.x), |
| | | y: Math.round(current.y), |
| | | w: Math.round(current.w), |
| | | h: Math.round(current.h), |
| | | isRemain: true |
| | | }); |
| | | } |
| | | } |
| | | |
| | | const nonGray = rects.filter(r => !r.isRemain); |
| | | rects.splice(0, rects.length, ...nonGray, ...merged); |
| | | }; |
| | | |
| | | const adjustGrayRectangles = (layoutIndex) => { |
| | | const layout = layouts.value[layoutIndex]; |
| | | const rects = layout.rects; |
| | | const nonGrayRects = rects.filter(rect => !rect.isRemain); |
| | | |
| | | const remainingAreas = calculateRemainingAreas(layout.width, layout.height, nonGrayRects); |
| | | |
| | | const currentGrayRects = rects.filter(r => r.isRemain); |
| | | currentGrayRects.forEach((_, index) => { |
| | | if (index >= remainingAreas.length) { |
| | | rects.splice(index, 1); |
| | | } |
| | | }); |
| | | |
| | | remainingAreas.forEach((area, index) => { |
| | | if (index < currentGrayRects.length) { |
| | | currentGrayRects[index].x = Math.round(area.x); |
| | | currentGrayRects[index].y = Math.round(area.y); |
| | | currentGrayRects[index].w = Math.round(area.w); |
| | | currentGrayRects[index].h = Math.round(area.h); |
| | | } else { |
| | | rects.push({ |
| | | x: Math.round(area.x), |
| | | y: Math.round(area.y), |
| | | w: Math.round(area.w), |
| | | h: Math.round(area.h), |
| | | isRemain: true |
| | | }); |
| | | } |
| | | }); |
| | | |
| | | mergeAdjacentGrayRects(rects); |
| | | }; |
| | | |
| | | const rotateRect = (layoutIndex, rectIndex) => { |
| | | const layout = layouts.value[layoutIndex]; |
| | | const rect = layout.rects[rectIndex]; |
| | | const originalState = { ...rect }; |
| | | |
| | | const temp = rect.w; |
| | | rect.w = rect.h; |
| | | rect.h = temp; |
| | | |
| | | const otherRects = layout.rects.filter(r => !r.isRemain && r !== rect); |
| | | let isValidRotation = true; |
| | | |
| | | otherRects.forEach(otherRect => { |
| | | if (checkOverlap(rect, otherRect)) { |
| | | isValidRotation = false; |
| | | } |
| | | }); |
| | | |
| | | if (rect.x + rect.w > layout.width || rect.y + rect.h > layout.height) { |
| | | isValidRotation = false; |
| | | } |
| | | |
| | | if (isValidRotation) { |
| | | adjustGrayRectangles(layoutIndex); |
| | | } else { |
| | | rect.w = originalState.w; |
| | | rect.h = originalState.h; |
| | | ElMessage.warning('无法旋转,存在重叠或超出边界'); |
| | | } |
| | | }; |
| | | |
| | | const moveRectAndRotate = (layoutIndex, rectIndex, direction) => { |
| | | const layout = layouts.value[layoutIndex]; |
| | | const rect = layout.rects[rectIndex]; |
| | | const grayRects = layout.rects.filter(r => r.isRemain); |
| | | |
| | | const temp = rect.w; |
| | | rect.w = rect.h; |
| | | rect.h = temp; |
| | | |
| | | const canPlace = grayRects.some(grayRect => { |
| | | return grayRect.w >= rect.w && grayRect.h >= rect.h; |
| | | }); |
| | | |
| | | if (!canPlace) { |
| | | const temp = rect.w; |
| | | rect.w = rect.h; |
| | | rect.h = temp; |
| | | ElMessage.warning('无法旋转,没有足够的空间'); |
| | | return; |
| | | } |
| | | |
| | | adjustGrayRectangles(layoutIndex); |
| | | moveRect(layoutIndex, rectIndex, direction); |
| | | }; |
| | | |
| | | const moveRect = (layoutIndex, rectIndex, direction) => { |
| | | const layout = layouts.value[layoutIndex]; |
| | | const rect = layout.rects[rectIndex]; |
| | | const originalState = { ...rect }; |
| | | |
| | | let maxStep = 0; |
| | | const obstacles = layout.rects.filter(r => r.isRemain || r !== rect); |
| | | |
| | | switch (direction) { |
| | | case 'up': |
| | | maxStep = getAvailableSpaceUp(rect, layout, obstacles); |
| | | break; |
| | | case 'down': |
| | | maxStep = getAvailableSpaceDown(rect, layout, obstacles); |
| | | break; |
| | | case 'left': |
| | | maxStep = getAvailableSpaceLeft(rect, layout, obstacles); |
| | | break; |
| | | case 'right': |
| | | maxStep = getAvailableSpaceRight(rect, layout, obstacles); |
| | | break; |
| | | } |
| | | |
| | | if (maxStep <= 0) { |
| | | ElMessage.warning('无法移动,没有足够的空间'); |
| | | return; |
| | | } |
| | | |
| | | switch (direction) { |
| | | case 'up': |
| | | rect.y -= maxStep; |
| | | break; |
| | | case 'down': |
| | | rect.y += maxStep; |
| | | break; |
| | | case 'left': |
| | | rect.x -= maxStep; |
| | | break; |
| | | case 'right': |
| | | rect.x += maxStep; |
| | | break; |
| | | } |
| | | |
| | | const otherRects = layout.rects.filter(r => !r.isRemain && r !== rect); |
| | | let isValidMove = true; |
| | | |
| | | otherRects.forEach(otherRect => { |
| | | if (checkOverlap(rect, otherRect)) { |
| | | isValidMove = false; |
| | | } |
| | | }); |
| | | |
| | | if (rect.x < 0 || rect.y < 0 || |
| | | rect.x + rect.w > layout.width || |
| | | rect.y + rect.h > layout.height) { |
| | | isValidMove = false; |
| | | } |
| | | |
| | | if (isValidMove) { |
| | | adjustGrayRectangles(layoutIndex); |
| | | } else { |
| | | rect.x = originalState.x; |
| | | rect.y = originalState.y; |
| | | ElMessage.warning('无法移动,存在重叠或超出边界'); |
| | | } |
| | | }; |
| | | |
| | | const deleteRect = (layoutIndex, rectIndex) => { |
| | | const layout = layouts.value[layoutIndex]; |
| | | layout.rects.splice(rectIndex, 1); |
| | | adjustGrayRectangles(layoutIndex); |
| | | }; |
| | | |
| | | const checkOverlap = (rect1, rect2) => { |
| | | return !(rect1.x + rect1.w <= rect2.x || |
| | | rect1.x >= rect2.x + rect2.w || |
| | | rect1.y + rect1.h <= rect2.y || |
| | | rect1.y >= rect2.y + rect2.h); |
| | | }; |
| | | |
| | | const calculateRemainingAreas = (totalWidth, totalHeight, obstacles) => { |
| | | let remaining = [{ x: 0, y: 0, w: totalWidth, h: totalHeight }]; |
| | | obstacles.forEach(rect => { |
| | | remaining = cutRemainingAreas(remaining, rect); |
| | | }); |
| | | return remaining; |
| | | }; |
| | | |
| | | const cutRemainingAreas = (remainingAreas, obstacle) => { |
| | | const newRemaining = []; |
| | | remainingAreas.forEach(area => { |
| | | if (checkOverlap(area, obstacle)) { |
| | | if (obstacle.x > area.x) { |
| | | newRemaining.push({ |
| | | x: area.x, |
| | | y: area.y, |
| | | w: obstacle.x - area.x, |
| | | h: area.h |
| | | }); |
| | | } |
| | | if (obstacle.x + obstacle.w < area.x + area.w) { |
| | | newRemaining.push({ |
| | | x: obstacle.x + obstacle.w, |
| | | y: area.y, |
| | | w: area.w - (obstacle.x + obstacle.w - area.x), |
| | | h: area.h |
| | | }); |
| | | } |
| | | if (obstacle.y > area.y) { |
| | | newRemaining.push({ |
| | | x: area.x, |
| | | y: area.y, |
| | | w: area.w, |
| | | h: obstacle.y - area.y |
| | | }); |
| | | } |
| | | if (obstacle.y + obstacle.h < area.y + area.h) { |
| | | newRemaining.push({ |
| | | x: area.x, |
| | | y: obstacle.y + obstacle.h, |
| | | w: area.w, |
| | | h: area.h - (obstacle.y + obstacle.h - area.y) |
| | | }); |
| | | } |
| | | } else { |
| | | newRemaining.push(area); |
| | | } |
| | | }); |
| | | return newRemaining; |
| | | }; |
| | | |
| | | const getCurrentRectInfo = (layoutIndex) => { |
| | | const layout = layouts.value[layoutIndex]; |
| | | const rect = layout.rects[focusIndex.value?.rectIndex || 0]; |
| | | if (!rect) return ''; |
| | | const totalRects = layouts.value.length; |
| | | const currentRectIndex = layoutIndex + 1; |
| | | const width = layout.width; |
| | | const height = layout.height; |
| | | const percentage = ((rect.w / layout.width) * 100).toFixed(1) + '%'; |
| | | return `${currentRectIndex}/${totalRects} ${width}×${height} ×1 ${percentage}`; |
| | | }; |
| | | |
| | | const selectLayout = (layoutIndex) => { |
| | | selectedLayoutIndex.value = layoutIndex; |
| | | }; |
| | | |
| | | const updateLayout = () => { |
| | | if (!layoutPanel.value) return; |
| | | layouts.value = props.layoutData.Layouts; |
| | | }; |
| | | |
| | | const getAvailableSpaceUp = (rect, layout, obstacles) => { |
| | | let maxSpace = rect.y; |
| | | obstacles.forEach(obstacle => { |
| | | if (obstacle.y + obstacle.h < rect.y && |
| | | obstacle.x <= rect.x + rect.w && |
| | | obstacle.x + obstacle.w >= rect.x) { |
| | | maxSpace = Math.min(maxSpace, rect.y - (obstacle.y + obstacle.h)); |
| | | } |
| | | }); |
| | | return maxSpace; |
| | | }; |
| | | |
| | | const getAvailableSpaceDown = (rect, layout, obstacles) => { |
| | | let maxSpace = layout.height - (rect.y + rect.h); |
| | | obstacles.forEach(obstacle => { |
| | | if (obstacle.y > rect.y + rect.h && |
| | | obstacle.x <= rect.x + rect.w && |
| | | obstacle.x + obstacle.w >= rect.x) { |
| | | maxSpace = Math.min(maxSpace, obstacle.y - (rect.y + rect.h)); |
| | | } |
| | | }); |
| | | return maxSpace; |
| | | }; |
| | | |
| | | const getAvailableSpaceLeft = (rect, layout, obstacles) => { |
| | | let maxSpace = rect.x; |
| | | obstacles.forEach(obstacle => { |
| | | if (obstacle.x + obstacle.w < rect.x && |
| | | obstacle.y <= rect.y + rect.h && |
| | | obstacle.y + obstacle.h >= rect.y) { |
| | | maxSpace = Math.min(maxSpace, rect.x - (obstacle.x + obstacle.w)); |
| | | } |
| | | }); |
| | | return maxSpace; |
| | | }; |
| | | |
| | | const getAvailableSpaceRight = (rect, layout, obstacles) => { |
| | | let maxSpace = layout.width - (rect.x + rect.w); |
| | | obstacles.forEach(obstacle => { |
| | | if (obstacle.x > rect.x + rect.w && |
| | | obstacle.y <= rect.y + rect.h && |
| | | obstacle.y + obstacle.h >= rect.y) { |
| | | maxSpace = Math.min(maxSpace, obstacle.x - (rect.x + rect.w)); |
| | | } |
| | | }); |
| | | return maxSpace; |
| | | }; |
| | | |
| | | let moveInterval = null; |
| | | |
| | | const handleKeyDown = (event) => { |
| | | if (!focusIndex.value) return; |
| | | |
| | | const { layoutIndex, rectIndex } = focusIndex.value; |
| | | const layout = layouts.value[layoutIndex]; |
| | | const rect = layout.rects[rectIndex]; |
| | | const obstacles = layout.rects.filter(r => r.isRemain || r !== rect); |
| | | |
| | | switch (event.key) { |
| | | case 'ArrowUp': |
| | | event.preventDefault(); |
| | | if (!moveInterval) { |
| | | moveInterval = setInterval(() => { |
| | | moveRect(layoutIndex, rectIndex, 'up'); |
| | | }, 50); |
| | | } |
| | | break; |
| | | case 'ArrowDown': |
| | | event.preventDefault(); |
| | | if (!moveInterval) { |
| | | moveInterval = setInterval(() => { |
| | | moveRect(layoutIndex, rectIndex, 'down'); |
| | | }, 50); |
| | | } |
| | | break; |
| | | case 'ArrowLeft': |
| | | event.preventDefault(); |
| | | if (!moveInterval) { |
| | | moveInterval = setInterval(() => { |
| | | moveRect(layoutIndex, rectIndex, 'left'); |
| | | }, 50); |
| | | } |
| | | break; |
| | | case 'ArrowRight': |
| | | event.preventDefault(); |
| | | if (!moveInterval) { |
| | | moveInterval = setInterval(() => { |
| | | moveRect(layoutIndex, rectIndex, 'right'); |
| | | }, 50); |
| | | } |
| | | break; |
| | | } |
| | | }; |
| | | |
| | | const handleKeyUp = (event) => { |
| | | if (event.key === 'ArrowUp' || event.key === 'ArrowDown' || |
| | | event.key === 'ArrowLeft' || event.key === 'ArrowRight') { |
| | | if (moveInterval) { |
| | | clearInterval(moveInterval); |
| | | moveInterval = null; |
| | | } |
| | | } |
| | | }; |
| | | |
| | | const mirrorLayoutX = (layoutIndex) => { |
| | | const layout = layouts.value[layoutIndex]; |
| | | const width = layout.width; |
| | | const rects = [...layout.rects]; // 创建副本避免直接修改 |
| | | |
| | | rects.forEach(rect => { |
| | | // 计算X镜像后的坐标 |
| | | const newX = width - rect.x - rect.w; |
| | | const newY = rect.y; |
| | | |
| | | // 更新矩形位置 |
| | | rect.x = newX; |
| | | rect.y = newY; |
| | | }); |
| | | |
| | | // 更新布局 |
| | | layout.rects = rects; |
| | | adjustGrayRectangles(layoutIndex); |
| | | }; |
| | | |
| | | const mirrorLayoutY = (layoutIndex) => { |
| | | const layout = layouts.value[layoutIndex]; |
| | | const height = layout.height; |
| | | const rects = [...layout.rects]; // 创建副本避免直接修改 |
| | | |
| | | rects.forEach(rect => { |
| | | // 计算Y镜像后的坐标 |
| | | const newX = rect.x; |
| | | const newY = height - rect.y - rect.h; |
| | | |
| | | // 更新矩形位置 |
| | | rect.y = newY; |
| | | }); |
| | | |
| | | // 更新布局 |
| | | layout.rects = rects; |
| | | adjustGrayRectangles(layoutIndex); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | fetchSettings(username); |
| | | updateLayout(); |
| | | |
| | | selectedLayoutIndex.value = 0; |
| | | |
| | | clickEventListener = (event) => { |
| | | const contextMenus = document.querySelectorAll('.context-menu'); |
| | | if (contextMenus.length > 0) { |
| | | contextMenus.forEach(menu => menu.remove()); |
| | | } |
| | | }; |
| | | document.addEventListener('click', clickEventListener); |
| | | |
| | | document.addEventListener('keydown', handleKeyDown); |
| | | document.addEventListener('keyup', handleKeyUp); |
| | | }); |
| | | |
| | | onUnmounted(() => { |
| | | rectsElements.value = {}; |
| | | if (clickEventListener) { |
| | | document.removeEventListener('click', clickEventListener); |
| | | clickEventListener = null; |
| | | } |
| | | document.removeEventListener('keydown', handleKeyDown); |
| | | document.removeEventListener('keyup', handleKeyUp); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .layout-wrapper { |
| | | position: relative; |
| | | margin-top: 50px; |
| | | } |
| | | |
| | | .layout-rect { |
| | | user-select: none; |
| | | } |
| | | |
| | | .layout-container { |
| | | position: relative; |
| | | overflow: visible; |
| | | } |
| | | |
| | | .layout-info { |
| | | color: #444; |
| | | font-size: 12px; |
| | | background-color: #ffffff; |
| | | padding: 5px 10px; |
| | | border-radius: 3px; |
| | | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .rect-content { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: flex-start; |
| | | padding: 5px; |
| | | min-width: 60px; |
| | | min-height: 20px; |
| | | white-space: normal; |
| | | overflow-wrap: break-word; |
| | | } |
| | | |
| | | .size { |
| | | font-size: 12px; |
| | | color: #444; |
| | | white-space: normal; |
| | | word-wrap: break-word; |
| | | } |
| | | |
| | | .jia-hao, .liuchengka { |
| | | font-size: 14px; |
| | | font-weight: bold; |
| | | white-space: normal; |
| | | word-wrap: break-word; |
| | | } |
| | | |
| | | .sidebar-item { |
| | | padding: 10px; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .sidebar-item.selected { |
| | | background: #ddd; |
| | | } |
| | | |
| | | .context-menu { |
| | | position: absolute; |
| | | background-color: #fff; |
| | | border: 1px solid #ccc; |
| | | padding: 5px; |
| | | z-index: 1001; |
| | | } |
| | | |
| | | .context-menu div { |
| | | padding: 5px; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .context-menu div:hover { |
| | | background-color: #f0f0f0; |
| | | } |
| | | </style> |
New file |
| | |
| | | <script setup> |
| | | import {ref, computed} from "vue"; |
| | | import {Connection} from "@element-plus/icons-vue"; |
| | | |
| | | //计时器 |
| | | let seconds = ref(0); |
| | | let isRunning = ref(false); |
| | | let intervalId; |
| | | |
| | | //优化进度条 |
| | | let progress = ref(0); |
| | | |
| | | //优化次数;时长;效率 |
| | | let progressValue1 = ref(10); |
| | | let progressValue2 = ref(1000); |
| | | let progressValue3 = ref(50); |
| | | |
| | | const formattedTime = computed(() => { |
| | | const minutes = Math.floor(seconds.value / 60); |
| | | const remainingSeconds = seconds.value % 60; |
| | | return `${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`; |
| | | }); |
| | | const startTimer = () => { |
| | | if (!isRunning.value) { |
| | | intervalId = setInterval(() => { |
| | | seconds.value++; |
| | | progress.value += 10; |
| | | if (progress.value >= 100) { |
| | | progress.value = 100; |
| | | clearInterval(intervalId); |
| | | } |
| | | }, 1000); |
| | | isRunning.value = true; |
| | | } |
| | | }; |
| | | const pauseTimer = () => { |
| | | if (isRunning.value) { |
| | | clearInterval(intervalId); |
| | | isRunning.value = false; |
| | | } |
| | | }; |
| | | const resetTimer = () => { |
| | | clearInterval(intervalId); |
| | | isRunning.value = false; |
| | | seconds.value = 0; |
| | | progress.value = 0; |
| | | }; |
| | | |
| | | </script> |
| | | |
| | | <template> |
| | | <div class="header"> |
| | | <el-header> |
| | | <div class="header-content"> |
| | | <span style="margin-left: -20px;">需切成品</span><br> |
| | | <div> |
| | | <span>总面积(m2):</span> |
| | | <vxe-input class="input" disabled placeholder=""></vxe-input> |
| | | <span>总数量(片):</span> |
| | | <vxe-input class="input" disabled placeholder=""></vxe-input> |
| | | <span>优化时长(秒):</span> |
| | | <span class="time-display">{{ formattedTime }}</span> |
| | | <el-button type="primary" style="margin-left: 20px" @click="startTimer">开始优化</el-button> |
| | | <el-button class="buttons" type="primary" @click="pauseTimer">暂停</el-button> |
| | | <el-button class="buttons" type="primary" @click="resetTimer">完成</el-button> |
| | | </div> |
| | | <br> |
| | | <div style="display: flex;margin-top: -10px"> |
| | | <span>方案池 优化次数:</span> |
| | | <el-slider v-model="progressValue1" :min="2" :max="20" size="small" style="max-width: 200px;"></el-slider> |
| | | <span>单次优化时长:</span> |
| | | <el-slider v-model="progressValue2" :min="50" :max="2000" size="small" style="max-width: 200px;"></el-slider> |
| | | <span>钢化效率优先:</span> |
| | | <el-slider v-model="progressValue3" :show-tooltip="false" size="small" style="max-width: 200px;"></el-slider> |
| | | <span>切裁率优先</span> |
| | | </div> |
| | | </div> |
| | | </el-header> |
| | | </div> |
| | | |
| | | <div id="box"> |
| | | <span style="font-size: 20px">优化进度</span> |
| | | <div class="progress-bar"> |
| | | <el-progress :percentage="progress" |
| | | :text-inside="true" |
| | | :stroke-width="20" |
| | | status="success" |
| | | striped |
| | | striped-flow |
| | | duration="45"/> |
| | | </div> |
| | | <div style="height: 100px; margin-top: 20px; border: 2px solid #e6e6e6; padding: 10px; border-radius: 5px;"> |
| | | <span style="font-weight: bold">耗用原片</span> |
| | | </div> |
| | | <div style="height: 130px; margin-top: 20px; display: flex; border: 2px solid #e6e6e6; padding: 10px; border-radius: 5px;"> |
| | | <span style="font-weight: bold">当前结果</span> |
| | | <div style="margin-top: 5%"> |
| | | <span>产生时间:{{ formattedTime }}</span> |
| | | <span style="margin-left: 20px">总利用率:0.0%</span> |
| | | <span style="margin-left: 20px">尾片:</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <style scoped> |
| | | |
| | | .header { |
| | | height: 120px; |
| | | background-color: #d5eaff; |
| | | margin-top: -30px; |
| | | padding: 10px; |
| | | border-radius: 5px; |
| | | } |
| | | |
| | | div header span { |
| | | margin-left: 10px; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .input { |
| | | border: none !important; |
| | | height: 25px; |
| | | width: 80px; |
| | | } |
| | | |
| | | .time-display { |
| | | font-size: 18px; |
| | | color: #409eff; |
| | | } |
| | | |
| | | .buttons { |
| | | float: right; |
| | | } |
| | | |
| | | #box { |
| | | padding: 10px; |
| | | margin-top: 10px; |
| | | width: 100%; |
| | | height: 80%; |
| | | } |
| | | |
| | | |
| | | </style> |
New file |
| | |
| | | <script setup> |
| | | import {onMounted, reactive, ref} from "vue"; |
| | | import {useI18n} from "vue-i18n"; |
| | | import {Search} from "@element-plus/icons-vue"; |
| | | import request from "@/utils/request"; |
| | | import deepClone from "@/utils/deepClone"; |
| | | import {ElMessage} from "element-plus"; |
| | | import useUserInfoStore from "@/stores/userInfo"; |
| | | import {changeFilterEvent, filterChanged} from "@/hook"; |
| | | |
| | | const {t} = useI18n() |
| | | const userStore = useUserInfoStore() |
| | | const username = userStore.user.userName |
| | | let rowClickIndex = ref(null) |
| | | |
| | | let props = defineProps({ |
| | | processId: null, |
| | | technologyNumber: null |
| | | }) |
| | | |
| | | //膜系 |
| | | const optionVal = ref() |
| | | //膜系 |
| | | const projectNmae = ref() |
| | | //工程编号个数 |
| | | let projectId = ref() |
| | | //工程编号 |
| | | const Id = ref() |
| | | let oddNumbers = ref() |
| | | |
| | | //定义接收加载表头下拉数据 |
| | | const titleSelectJson = ref({ |
| | | processType: [], |
| | | }) |
| | | |
| | | const selectGlassType = () => { |
| | | //查询膜系 |
| | | request.post(`/glassOptimize/selectGlassType`).then((res) => { |
| | | if (res.code == 200) { |
| | | titleSelectJson.value.processType = res.data.data |
| | | } else { |
| | | ElMessage.warning(res.msg) |
| | | } |
| | | }) |
| | | } |
| | | //查询最新工程号 |
| | | const getProjectId = () => { |
| | | request.post(`/glassOptimize/getProjectId`).then((res) => { |
| | | if (res.code == 200) { |
| | | projectId = res.data.data[0].project_no |
| | | // 获取字符串的最后两位数字 |
| | | let maximum = projectId.slice(-2); |
| | | let lastTwoInteger = parseInt(maximum, 10); |
| | | // 设置两位不够补0 |
| | | let formattedNumber = (lastTwoInteger + 1).toString().padStart(2, '0'); |
| | | // 格式化当前日期为 "yyMMdd" |
| | | let currentDate = new Date(); |
| | | let formattedDate = currentDate.getFullYear().toString().slice(-2) + |
| | | (currentDate.getMonth() + 1).toString().padStart(2, '0') + |
| | | currentDate.getDate().toString().padStart(2, '0'); |
| | | |
| | | // 拼接成最终的字符串 |
| | | oddNumbers.value = 'P' + formattedDate + formattedNumber; |
| | | |
| | | } else { |
| | | ElMessage.warning(res.msg) |
| | | } |
| | | }) |
| | | } |
| | | |
| | | |
| | | onMounted(() => { |
| | | getProjectId(); |
| | | selectGlassType(); |
| | | |
| | | }) |
| | | |
| | | const xGrid = ref() |
| | | const gridOptions = reactive({ |
| | | |
| | | height: '100%', |
| | | loading: false, |
| | | border: "full",//表格加边框 |
| | | keepSource: true,//保持源数据 |
| | | align: 'center',//文字居中 |
| | | stripe: true,//斑马纹 |
| | | rowConfig: {isCurrent: true, isHover: true, height: 30, useKey: true},//鼠标移动或选择高亮 |
| | | id: 'ProcessCard', |
| | | scrollX: {enabled: true}, |
| | | scrollY: {enabled: true, gt: 0},//开启虚拟滚动 |
| | | showOverflow: true, |
| | | columnConfig: { |
| | | resizable: true, |
| | | useKey: true |
| | | }, |
| | | filterConfig: { //筛选配置项 |
| | | //remote: true |
| | | }, |
| | | customConfig: { |
| | | storage: true |
| | | }, |
| | | editConfig: { |
| | | trigger: 'click', |
| | | mode: 'row', |
| | | showStatus: true |
| | | }, |
| | | columns: [ |
| | | |
| | | {field: 'select',type:'checkbox',title: t('basicData.check'), width: 80,fixed:"left"}, |
| | | { |
| | | field: 'process_id', |
| | | width: 150, |
| | | title: t('processCard.processId'), |
| | | filters: [{data: ''}], |
| | | slots: {filter: 'num1_filter'}, |
| | | sortable: true, |
| | | filterMethod:filterChanged |
| | | }, |
| | | { |
| | | field: 'technology_number', |
| | | width: 70, |
| | | title: '层', |
| | | filters: [{data: ''}], |
| | | slots: {filter: 'num1_filter'}, |
| | | sortable: true, |
| | | filterMethod:filterChanged |
| | | }, |
| | | { |
| | | field: 'TotalFloors', |
| | | width: 150, |
| | | title: '总层数', |
| | | filters: [{data: ''}], |
| | | slots: {filter: 'num1_filter'}, |
| | | sortable: true, |
| | | filterMethod:filterChanged |
| | | }, |
| | | { |
| | | field: 'TotalNumber', |
| | | width: 150, |
| | | title: '规格', |
| | | filters: [{data: ''}], |
| | | slots: {filter: 'num1_filter'}, |
| | | sortable: true, |
| | | filterMethod:filterChanged |
| | | }, |
| | | { |
| | | field: 'quantity', |
| | | width: 150, |
| | | title: t('order.quantity'), |
| | | filters: [{data: ''}], |
| | | slots: {filter: 'num1_filter'}, |
| | | sortable: true, |
| | | filterMethod:filterChanged |
| | | }, |
| | | { |
| | | field: 'shape', |
| | | width: 150, |
| | | title: t('order.shape'), |
| | | filters: [{data: ''}], |
| | | slots: {filter: 'num1_filter'}, |
| | | sortable: true, |
| | | filterMethod:filterChanged |
| | | }, |
| | | { |
| | | field: 'glass_child', |
| | | width: 150, |
| | | title: t('order.product'), |
| | | filters: [{data: ''}], |
| | | slots: {filter: 'num1_filter'}, |
| | | sortable: true, |
| | | filterMethod:filterChanged |
| | | }, |
| | | {field: 'project', width: 150, title: t('order.project'), showOverflow: "ellipsis",filterMethod:filterChanged}, |
| | | { |
| | | field: 'area', |
| | | width: 150, |
| | | title: t('order.area'), |
| | | filters: [{data: ''}], |
| | | slots: {filter: 'num1_filter'}, |
| | | sortable: true, |
| | | filterMethod:filterChanged |
| | | },{ |
| | | field: 'patch_state', |
| | | width: 100, |
| | | title: '补片状态' |
| | | }, |
| | | |
| | | |
| | | ],//表头参数 |
| | | data: null,//表格数据 |
| | | |
| | | toolbarConfig: { |
| | | buttons: [], |
| | | slots: { |
| | | buttons: "toolbar_buttons" |
| | | }, |
| | | }, |
| | | |
| | | |
| | | }) |
| | | |
| | | const gridEvents = { |
| | | cellClick({row}) { |
| | | rowClickIndex.value = row |
| | | // Emit 事件将更新后的值传递给父组件 |
| | | emit('updateProcessId', rowClickIndex.value.process_id); |
| | | emit('updateTechnologyNumber', rowClickIndex.value.technology_number); |
| | | } |
| | | } |
| | | const emit = defineEmits(['updateProcessId', 'updateTechnologyNumber', 'updateState']); |
| | | |
| | | |
| | | //小圆点单选框 |
| | | let radio = ref(1); |
| | | let isButtonDisabledAdd = ref(false); |
| | | let isButtonDisabledUpdate = ref(true); |
| | | |
| | | const selectFlowCardList = async () => { |
| | | request.post(`/glassOptimize/getFlowCardList/${optionVal.value}/${radio.value}`).then((res) => { |
| | | if (res.code == 200) { |
| | | getProjectId() |
| | | isButtonDisabledAdd.value=false |
| | | isButtonDisabledUpdate.value = true |
| | | xGrid.value.loadData(res.data.data) |
| | | } else { |
| | | ElMessage.warning(res.msg) |
| | | } |
| | | }) |
| | | } |
| | | |
| | | const getUpdateFlowCardList = async (projectNumber,type,thickness) => { |
| | | radio.value=1 |
| | | request.post(`/glassOptimize/getUpdateFlowCardList/${type}/${thickness}/${radio.value}/${projectNumber}`).then((res) => { |
| | | if (res.code == 200) { |
| | | oddNumbers.value=projectNumber |
| | | optionVal.value=thickness+"mm"+type |
| | | isButtonDisabledAdd.value=true |
| | | isButtonDisabledUpdate.value=false |
| | | xGrid.value.loadData(deepClone(res.data.data)) |
| | | xGrid.value.getTableData().fullData.forEach(item => { |
| | | if(item.occupyState===0){ |
| | | xGrid.value.setCheckboxRow(item, true); |
| | | } |
| | | }) |
| | | } else { |
| | | ElMessage.warning(res.msg) |
| | | } |
| | | }) |
| | | } |
| | | |
| | | //创建工程 |
| | | const addProject = (type) => { |
| | | const $table = xGrid.value |
| | | if ($table) { |
| | | const selectRecords = $table.getCheckboxRecords() |
| | | if($table.getCheckedFilters().length!==0){ |
| | | ElMessage.error(t('order.msg.pleaseCancelTheFilteringFirst')) |
| | | return |
| | | } |
| | | if (selectRecords.length == 0) { |
| | | ElMessage.warning(t('reportingWorks.selectProcessCardData')) |
| | | return; |
| | | } |
| | | |
| | | let projectData = ref({ |
| | | projectdetail: selectRecords, |
| | | userName : username, |
| | | projectType: type |
| | | }) |
| | | let inputProject = projectNmae.value |
| | | if (inputProject == undefined) { |
| | | inputProject = null |
| | | } |
| | | request.post(`/glassOptimize/addProject/${optionVal.value}/${oddNumbers.value}/${inputProject}`, projectData.value).then((res) => { |
| | | if (res.code == 200 && res.data === "true") { |
| | | emit('updateState', 1); |
| | | ElMessage.success(t('basicData.msg.saveSuccess')) |
| | | selectFlowCardList() |
| | | getProjectId(); |
| | | }else if(res.data === "false1") { |
| | | ElMessage.warning("工程号已存在请重新刷新界面") |
| | | } else { |
| | | ElMessage.warning(res.msg) |
| | | } |
| | | }) |
| | | |
| | | } |
| | | } |
| | | const handleRowClassName = ({ row, rowIndex }) => { |
| | | if (row.patch_state === 1) { |
| | | return 'high-score'; |
| | | } |
| | | if (row.occupyState === 0) { |
| | | return 'high-score2'; |
| | | } |
| | | } |
| | | |
| | | //抛出方法到父界面 |
| | | defineExpose({getProjectId,selectFlowCardList,selectGlassType,getUpdateFlowCardList}) |
| | | </script> |
| | | |
| | | <template> |
| | | <div style="width: 100%;height: 110%; margin-top: -36px"> |
| | | <div> |
| | | 流程卡列表 |
| | | <span style="margin-left: 140px;font-size: 14px">工程编号:</span> |
| | | <!-- <span>{{oddNumbers}}</span>--> |
| | | <el-input disabled v-model="oddNumbers" style="width: 100px"></el-input> |
| | | <!-- <vxe-input :disabled="isDisabled" placeholder="" v-model="oddNumbers" size="small" style="color:black;"></vxe-input> --> |
| | | <span style="font-size: 14px">工程名称:</span> |
| | | <el-input style="width: 150px" placeholder="" v-model="projectNmae" size="small"></el-input> |
| | | <el-button style="margin-left: 20px" type="primary" :disabled="isButtonDisabledAdd" @click="addProject(1)">创建</el-button> |
| | | <el-button style="margin-left: 20px;" type="primary" :disabled="isButtonDisabledUpdate" @click="addProject(2)">修改</el-button> |
| | | </div> |
| | | <vxe-grid |
| | | ref="xGrid" |
| | | class="mytable-scrollbar" |
| | | height="100%" |
| | | size="small" |
| | | v-bind="gridOptions" |
| | | v-on="gridEvents" |
| | | :row-class-name="handleRowClassName" |
| | | > |
| | | <template #num2_filter="{ column, $panel }"> |
| | | <div> |
| | | <div v-for="(option, index) in column.filters" :key="index"> |
| | | <vxe-select v-model="option.data" :placeholder="$t('processCard.pleaseSelect')" |
| | | @change="changeFilterEvent($event, option, $panel)"> |
| | | <vxe-option :label="$t('basicData.unchecked')" value="0"></vxe-option> |
| | | <vxe-option :label="$t('basicData.selected')" value="1"></vxe-option> |
| | | </vxe-select> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <template #num1_filter="{ column, $panel }"> |
| | | <div> |
| | | <div v-for="(option, index) in column.filters" :key="index"> |
| | | <input type="type" v-model="option.data" @keyup.enter.native="$panel.confirmFilter()" @input="changeFilterEvent($event, option, $panel)"/> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <template #toolbar_buttons> |
| | | <h1>膜系筛选:</h1> |
| | | <el-select v-model="optionVal" clearable default-value="default_city" placeholder="选择膜系" |
| | | style="width: 120px"> |
| | | <el-option |
| | | v-for="item in titleSelectJson['processType']" |
| | | :key="item.id" |
| | | :label="item.glassType" |
| | | :value="item.glassType" |
| | | /> |
| | | </el-select> |
| | | <el-button :icon="Search" style="margin-left: 20px" type="primary" @click="selectFlowCardList">查询</el-button> |
| | | <vxe-radio-group v-model="radio" style="margin-left: 20px"> |
| | | <vxe-radio content="全部" label="1"></vxe-radio> |
| | | <vxe-radio content="正单" label="2"></vxe-radio> |
| | | <vxe-radio content="补单" label="3"></vxe-radio> |
| | | </vxe-radio-group> |
| | | </template> |
| | | |
| | | </vxe-grid> |
| | | </div> |
| | | </template> |
| | | |
| | | <style lang="css"> |
| | | .high-score { |
| | | background-color: #d4baba !important; |
| | | } |
| | | .high-score2 { |
| | | background-color: #a5aec9 !important; |
| | | } |
| | | </style> |
New file |
| | |
| | | <script setup> |
| | | import {onMounted, reactive, ref, watch} from "vue"; |
| | | import {useI18n} from "vue-i18n"; |
| | | import request from "@/utils/request"; |
| | | import {ElMessage} from "element-plus"; |
| | | import {changeFilterEvent, filterChanged} from "@/hook"; |
| | | const { t } = useI18n() |
| | | |
| | | let props = defineProps({ |
| | | processId:null, |
| | | technologyNumber:null |
| | | }) |
| | | const xGrid = ref() |
| | | |
| | | const gridOptions = reactive({ |
| | | height:'100%', |
| | | loading: false, |
| | | border: "full",//表格加边框 |
| | | keepSource: true,//保持源数据 |
| | | align: 'center',//文字居中 |
| | | stripe:true,//斑马纹 |
| | | rowConfig: {isCurrent: true, isHover: true,height: 30, useKey: true},//鼠标移动或选择高亮 |
| | | id: 'ProcessCardDetail', |
| | | scrollX:{enabled: true}, |
| | | scrollY:{ enabled: true ,gt:0},//开启虚拟滚动 |
| | | showOverflow:true, |
| | | columnConfig: { |
| | | resizable: true, |
| | | useKey: true |
| | | }, |
| | | filterConfig: { //筛选配置项 |
| | | //remote: true |
| | | }, |
| | | customConfig: { |
| | | storage: true |
| | | }, |
| | | editConfig: { |
| | | trigger: 'click', |
| | | mode: 'row', |
| | | showStatus: true |
| | | }, |
| | | /*formConfig: { |
| | | data: { |
| | | width: '', |
| | | height: '', |
| | | quantity: '' |
| | | }, |
| | | items: [ |
| | | { field: 'width', title: t('order.width')+':', itemRender: { name: 'VxeInput' } }, |
| | | { field: 'height', title: t('order.height')+':', itemRender: { name: 'VxeInput' } }, |
| | | { field: 'quantity', title: t('order.quantity')+':', itemRender: { name: 'VxeInput' } }, |
| | | { |
| | | itemRender: { |
| | | name: 'VxeButtonGroup', |
| | | options: [ |
| | | { type: 'submit', content: t('craft.sure'), status: 'primary' }, |
| | | { type: 'reset', content: t('product.msg.reset') } |
| | | ] |
| | | } |
| | | } |
| | | ] |
| | | },*/ |
| | | columns:[ |
| | | {field: 'order_number',width: 70, title: '序号',filters:[{ data: '' }],slots: { filter: 'num1_filter' }, sortable: true,filterMethod:filterChanged}, |
| | | {field:'child_width',width: 150,title: t('order.width'),filters:[{ data: '' }],slots: { filter: 'num1_filter' }, sortable: true,filterMethod:filterChanged}, |
| | | {field: 'child_height',width: 150,title: t('order.height'),filters:[{ data: '' }],slots: { filter: 'num1_filter' }, sortable: true,filterMethod:filterChanged}, |
| | | {field: 'quantity',width: 150, title: t('order.quantity'),filters:[{ data: '' }],slots: { filter: 'num1_filter' }, sortable: true,filterMethod:filterChanged}, |
| | | {field: 'building_number',width: 150, title: t('order.buildingNumber'),filters:[{ data: '' }],slots: { filter: 'num1_filter' }, sortable: true,filterMethod:filterChanged}, |
| | | {field: 'shape',width: 150, title: t('order.shape'),filters:[{ data: '' }],slots: { filter: 'num1_filter' }, sortable: true,filterMethod:filterChanged}, |
| | | {field: 'grossArea',width: 150, title: t('order.grossArea'),filters:[{ data: '' }],slots: { filter: 'num1_filter' }, sortable: true,filterMethod:filterChanged}, |
| | | {field: 'separation',width: 150, title: t('craft.TrademarkAttribute'),filters:[{ data: '' }],slots: { filter: 'num1_filter' }, sortable: true,filterMethod:filterChanged}, |
| | | |
| | | ],//表头参数 |
| | | data:null,//表格数据 |
| | | toolbarConfig: { |
| | | buttons: [], |
| | | slots:{ |
| | | }, |
| | | }, |
| | | |
| | | }) |
| | | |
| | | |
| | | onMounted(()=>{ |
| | | |
| | | }) |
| | | |
| | | watch( |
| | | |
| | | () => props.processId, |
| | | (newValue, oldValue) => { |
| | | if (props.processId!=null){ |
| | | getWorkOrder() |
| | | } |
| | | |
| | | } |
| | | ); |
| | | |
| | | const getWorkOrder = () => { |
| | | if (props.processId!=null || props.processId!=""){ |
| | | request.post(`/glassOptimize/getProcessCardDetail/${props.processId}/${props.technologyNumber}`).then((res) => { |
| | | if(res.code==200){ |
| | | xGrid.value.loadData(res.data.data) |
| | | }else{ |
| | | ElMessage.warning(res.msg) |
| | | } |
| | | }) |
| | | } |
| | | |
| | | } |
| | | </script> |
| | | |
| | | <template> |
| | | <div style="width: 100%;height: 100%"> |
| | | <h1>流程卡详情</h1> |
| | | <vxe-grid |
| | | size="small" |
| | | height="100%" |
| | | class="mytable-scrollbar" |
| | | ref="xGrid" |
| | | v-bind="gridOptions" |
| | | v-on="gridEvents" |
| | | > |
| | | <template #num2_filter="{ column, $panel }"> |
| | | <div> |
| | | <div v-for="(option, index) in column.filters" :key="index"> |
| | | <vxe-select v-model="option.data" :placeholder="$t('processCard.pleaseSelect')" @change="changeFilterEvent($event, option, $panel)"> |
| | | <vxe-option value="0" :label="$t('basicData.unchecked')"></vxe-option> |
| | | <vxe-option value="1" :label="$t('basicData.selected')"></vxe-option> |
| | | </vxe-select> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <template #num1_filter="{ column, $panel }"> |
| | | <div> |
| | | <div v-for="(option, index) in column.filters" :key="index"> |
| | | <input type="type" v-model="option.data" @keyup.enter.native="$panel.confirmFilter()" @input="changeFilterEvent($event, option, $panel)"/> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | </vxe-grid> |
| | | </div> |
| | | </template> |
| | | |
| | | <style scoped> |
| | | |
| | | </style> |
New file |
| | |
| | | <script setup> |
| | | import {nextTick, onMounted, reactive, ref, watch,onBeforeUnmount } from "vue"; |
| | | import {useI18n} from "vue-i18n"; |
| | | import {Folder, Plus, Setting, Operation,} from "@element-plus/icons-vue"; |
| | | import OptimizeCompute from "@/views/pp/glassOptimize/page/OptimizeCompute.vue"; |
| | | import SetAmount from "@/views/pp/glassOptimize/page/SetAmount.vue"; |
| | | import SetTrimming from "@/views/pp/glassOptimize/page/SetTrimming.vue"; |
| | | import CheckInventory from "@/views/pp/glassOptimize/page/CheckInventory.vue"; |
| | | import request from "@/utils/request"; |
| | | import {ElMessage, ElMessageBox} from "element-plus"; |
| | | import {useRoute} from 'vue-router'; |
| | | |
| | | |
| | | const {t} = useI18n() |
| | | |
| | | const xGrid = ref() |
| | | const gridOptions = reactive({ |
| | | height: '100%', |
| | | loading: false, |
| | | border: "full",//表格加边框 |
| | | keepSource: true,//保持源数据 |
| | | align: 'center',//文字居中 |
| | | stripe: true,//斑马纹 |
| | | rowConfig: {isCurrent: true, isHover: true, height: 30, useKey: true},//鼠标移动或选择高亮 |
| | | id: 'ProjectDetail', |
| | | scrollX: {enabled: true}, |
| | | scrollY: {enabled: true, gt: 0},//开启虚拟滚动 |
| | | showOverflow: true, |
| | | columnConfig: { |
| | | resizable: true, |
| | | useKey: true |
| | | }, |
| | | filterConfig: { //筛选配置项 |
| | | remote: true |
| | | }, |
| | | customConfig: { |
| | | storage: true |
| | | }, |
| | | editConfig: { |
| | | trigger: 'click', |
| | | mode: 'row', |
| | | showStatus: true |
| | | }, |
| | | |
| | | columns: [ |
| | | {type: 'seq', title: t('basicData.Number'), width: 80}, |
| | | {field: 'order_number', title: '订序', width: 70}, |
| | | {field: 'width', |
| | | width: 100, |
| | | title: t('order.width'), |
| | | sortable: true |
| | | }, |
| | | { |
| | | field: 'height', |
| | | width: 100, |
| | | title: t('order.height'), |
| | | sortable: true |
| | | }, |
| | | { |
| | | field: 'quantity', |
| | | width: 150, |
| | | title: t('order.quantity'), |
| | | filters: [{data: ''}], |
| | | slots: {filter: 'num1_filter'}, |
| | | sortable: true |
| | | }, |
| | | { |
| | | field: 'longGrind1', |
| | | width: 150, |
| | | title: '长磨1', |
| | | filters: [{data: ''}], |
| | | slots: {filter: 'num1_filter'}, |
| | | sortable: true |
| | | }, |
| | | { |
| | | field: 'longGrind2', |
| | | width: 150, |
| | | title: '长磨2', |
| | | filters: [{data: ''}], |
| | | slots: {filter: 'num1_filter'}, |
| | | sortable: true |
| | | }, |
| | | { |
| | | field: 'shortGrind1', |
| | | width: 150, |
| | | title: '短磨1', |
| | | filters: [{data: ''}], |
| | | slots: {filter: 'num1_filter'}, |
| | | sortable: true |
| | | }, |
| | | { |
| | | field: 'shortGrind2', |
| | | width: 150, |
| | | title: '短磨2', |
| | | filters: [{data: ''}], |
| | | slots: {filter: 'num1_filter'}, |
| | | sortable: true |
| | | }, |
| | | { |
| | | field: 'shape', |
| | | width: 150, |
| | | title: t('order.shape'), |
| | | filters: [{data: ''}], |
| | | slots: {filter: 'num1_filter'}, |
| | | sortable: true |
| | | }, |
| | | { |
| | | field: 'process_id', |
| | | width: 150, |
| | | title: '流程卡号', |
| | | filters: [{data: ''}], |
| | | slots: {filter: 'num1_filter'}, |
| | | sortable: true |
| | | }, |
| | | { |
| | | field: 'product_name', |
| | | width: 150, |
| | | title: t('order.product'), |
| | | filters: [{data: ''}], |
| | | slots: {filter: 'num1_filter'}, |
| | | sortable: true |
| | | }, |
| | | { |
| | | field: 'price', |
| | | width: 150, |
| | | title: t('单价'), |
| | | filters: [{data: ''}], |
| | | slots: {filter: 'num1_filter'}, |
| | | sortable: true |
| | | }, |
| | | { |
| | | field: 'remark', |
| | | width: 150, |
| | | title: t('basicData.remarks'), |
| | | filters: [{data: ''}], |
| | | slots: {filter: 'num1_filter'}, |
| | | sortable: true |
| | | }, |
| | | { |
| | | field: 'building_number', |
| | | width: 150, |
| | | title: '楼层号', |
| | | filters: [{data: ''}], |
| | | slots: {filter: 'num1_filter'}, |
| | | sortable: true |
| | | }, |
| | | { |
| | | field: 'perimeter', |
| | | width: 150, |
| | | title: t('order.perimeter'), |
| | | filters: [{data: ''}], |
| | | slots: {filter: 'num1_filter'}, |
| | | sortable: true |
| | | }, |
| | | { |
| | | field: 'area', |
| | | width: 150, |
| | | title: t('order.grossArea'), |
| | | filters: [{data: ''}], |
| | | slots: {filter: 'num1_filter'}, |
| | | sortable: true |
| | | }, |
| | | |
| | | { |
| | | field: 'layout_id', |
| | | width: 150, |
| | | title: '架号', |
| | | filters: [{data: ''}], |
| | | slots: {filter: 'num1_filter'}, |
| | | sortable: true |
| | | }, |
| | | { |
| | | field: 'technology_number', |
| | | width: 150, |
| | | title: '层', |
| | | filters: [{data: ''}], |
| | | slots: {filter: 'num1_filter'}, |
| | | sortable: true |
| | | }, |
| | | { |
| | | field: 'glass_child', |
| | | width: 150, |
| | | title: '单片名称', |
| | | filters: [{data: ''}], |
| | | slots: {filter: 'num1_filter'}, |
| | | sortable: true |
| | | }, |
| | | { |
| | | field: 'icon', |
| | | width: 150, |
| | | title: '印标类型', |
| | | filters: [{data: ''}], |
| | | slots: {filter: 'num1_filter'}, |
| | | sortable: true |
| | | }, |
| | | ],//表头参数 |
| | | data: null,//表格数据 |
| | | toolbarConfig: { |
| | | buttons: [ |
| | | ], |
| | | import: false, |
| | | // export: true, |
| | | // print: true, |
| | | zoom: true, |
| | | custom: true |
| | | }, |
| | | //右键菜单 |
| | | menuConfig: { |
| | | body: { |
| | | options: [ |
| | | [ |
| | | {code: 'displayProcessCard', name: '显示流程卡',}, |
| | | {code: 'hideProcessCard', name: '隐藏流程卡',}, |
| | | {code: 'setShape', name: '设置图形',}, |
| | | {code: 'Export', name: '数据导出', prefixIcon: 'vxe-icon-download', visible: true, disabled: false}, |
| | | {code: 'safeDXF', name: '图形另存为DXF',}, |
| | | ], |
| | | [] |
| | | ] |
| | | }, |
| | | }, |
| | | }) |
| | | const processCardColumns = reactive({ |
| | | columns:[ |
| | | {field: 'process_id', title: '流程卡', width: 200, align: 'center'}, |
| | | {field: 'project', title: '项目名', width: 150, align: 'center'}, |
| | | {field: 'order_number', title: '订序', width: 100, align: 'center'}, |
| | | {field: 'sizes', title: '尺寸', width: 200, align: 'center'}, |
| | | {field: 'layer', title: '层', width: 100, align: 'center'}, |
| | | {field: 'quantity', title: '数量', width: 100, align: 'center'} |
| | | ], |
| | | toolbarConfig: { |
| | | buttons: [ |
| | | ], |
| | | import: false, |
| | | // export: true, |
| | | // print: true, |
| | | |
| | | }, |
| | | }); |
| | | |
| | | // 右键菜单 |
| | | const operationConfigs = [ |
| | | { |
| | | code: 'setAmount', // 设置统一磨量 |
| | | successMsg: '已打开!', |
| | | gridRef: xGrid, |
| | | requiresRow: false, |
| | | openAmount: async () => { |
| | | dialogVisible.value[2] = true; |
| | | } |
| | | }, |
| | | { |
| | | code: 'displayProcessCard', |
| | | successMsg: '操作成功!', |
| | | gridRef: xGrid, |
| | | requiresRow: false, |
| | | displayProcess: () => { |
| | | getProcessCard(); |
| | | // 显示流程卡时,将 left-table 宽度改为 50% |
| | | leftTableWidth.value = 60; |
| | | showProcessCardTable.value = true; |
| | | } |
| | | }, |
| | | { |
| | | code: 'hideProcessCard', |
| | | successMsg: '操作成功!', |
| | | gridRef: xGrid, |
| | | requiresRow: false, |
| | | hideProcess: () => { |
| | | leftTableWidth.value = 100; |
| | | showProcessCardTable.value = false; |
| | | } |
| | | }, |
| | | { |
| | | code: 'setShape', |
| | | successMsg: '操作成功!', |
| | | gridRef: xGrid, |
| | | requiresRow: false, |
| | | showMessage: () => { |
| | | ElMessage.info('此功能暂未完善,暂时无法执行设置图形操作。'); |
| | | } |
| | | }, |
| | | { |
| | | code: 'Export', // 导出文件操作的配置 |
| | | successMsg: '文件导出成功!', |
| | | gridRef: xGrid, |
| | | requiresRow: false, |
| | | }, |
| | | { |
| | | code: 'safeDXF', |
| | | successMsg: '操作成功!', |
| | | gridRef: xGrid, |
| | | requiresRow: false, |
| | | showMessage: () => { |
| | | ElMessage.info('此功能暂未完善,暂时无法执行图形另存为DXF操作。'); |
| | | } |
| | | }, |
| | | { |
| | | code: 'exportOPTIMA', |
| | | successMsg: '操作成功!', |
| | | gridRef: xGrid, |
| | | requiresRow: false, |
| | | showMessage: () => { |
| | | ElMessage.info('此功能暂未完善,暂时无法执行导出数据到OPTIMA操作。'); |
| | | } |
| | | }, |
| | | ] |
| | | |
| | | // 右键菜单点击逻辑 |
| | | const gridEvents = { |
| | | menuClick({menu}) { |
| | | const $grid = xGrid.value; |
| | | if ($grid) { |
| | | const config = operationConfigs.find(c => c.code === menu.code); |
| | | if (config) { |
| | | if (config.code === 'Export') { |
| | | config.gridRef.value.exportData(); |
| | | ElMessage.success(config.successMsg); |
| | | return; |
| | | } |
| | | // 添加确认提示弹窗,询问用户是否进行当前操作 |
| | | ElMessageBox.confirm('是否进行当前操作?', '确认操作', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | }).then(() => { |
| | | if (config.code === 'setAmount') { |
| | | config.openAmount(); |
| | | ElMessage.success(config.successMsg); |
| | | } else if (config.code === 'displayProcessCard') { |
| | | config.displayProcess(); |
| | | ElMessage.success(config.successMsg); |
| | | } else if (config.code === 'hideProcessCard') { |
| | | config.hideProcess(); |
| | | ElMessage.success(config.successMsg); |
| | | } else if (['setShape', 'safeDXF', 'exportOPTIMA'].includes(config.code)) { |
| | | config.showMessage(); |
| | | } |
| | | }).catch(() => { |
| | | // 用户点击取消后执行的逻辑 |
| | | ElMessage.info('已取消操作'); |
| | | }); |
| | | } else { |
| | | console.error(`未找到操作选项 ${menu.code} 对应的配置,请检查配置项`); |
| | | } |
| | | } |
| | | }, |
| | | }; |
| | | |
| | | const handleCommand = async (command) => { |
| | | await emit('changeDialog', command) |
| | | } |
| | | |
| | | let originalFilm=ref(true) |
| | | let surplusMaterial=ref(false) |
| | | |
| | | //优化计算 |
| | | const dialogVisible = ref({}); |
| | | const openDialog = (index) => { |
| | | dialogVisible.value[index] = true; |
| | | }; |
| | | //关闭弹窗 |
| | | const closeDialog = (index) => { |
| | | dialogVisible.value[index] = false; |
| | | }; |
| | | |
| | | //右键菜单统一修边 |
| | | const props = defineProps({ |
| | | TrimmingDialogVisible: Boolean |
| | | }); |
| | | watch(() => props.TrimmingDialogVisible, (newValue) => { |
| | | if (newValue === true) { |
| | | dialogVisible.value[3] = newValue; |
| | | } |
| | | }); |
| | | |
| | | const route = useRoute(); |
| | | //工程号 |
| | | const projectNo = ref(route.params.projectNo); |
| | | const projectName = ref(''); |
| | | const thickNess = ref(route.params.thickNess); |
| | | const model = ref(route.params.model); |
| | | onBeforeUnmount(() => { |
| | | localStorage.setItem('projectNo', projectNo.value); |
| | | }); |
| | | |
| | | const fetchData = () => { |
| | | request.post(`/glassOptimize/projectInfoThirdParty/${projectNo.value}`).then((res) => { |
| | | if ((Number(res.code) === 200)) { |
| | | const data = res.data.data; |
| | | const grindingTrimming = res.data.grindingTrimming; |
| | | if(grindingTrimming!==null){ |
| | | const formattedData = grindingTrimming.map(item => { |
| | | const formattedItem = {}; |
| | | for (const key in item) { |
| | | if (typeof item[key] === 'string') { |
| | | //去除字符串属性值开头和结尾的双引号 |
| | | formattedItem[key] = item[key].replace(/^\"|\"$/g, ''); |
| | | } else { |
| | | formattedItem[key] = item[key]; |
| | | } |
| | | } |
| | | return formattedItem; |
| | | }); |
| | | |
| | | } |
| | | xGrid.value.loadData(data); |
| | | gridOptions.data = data; |
| | | projectName.value = data[0].project_name; |
| | | } else { |
| | | ElMessage.warning(res.msg); |
| | | } |
| | | }).catch((error) => { |
| | | console.error("获取数据出错:", error); |
| | | }); |
| | | }; |
| | | onMounted(() => { |
| | | if (projectNo.value) { |
| | | fetchData(); |
| | | } |
| | | }); |
| | | |
| | | // 流程卡 宽度 |
| | | const leftTableWidth = ref(100); |
| | | const showProcessCardTable = ref(false); |
| | | // 用于存储流程卡数据 |
| | | const processCardData = ref(null); |
| | | //流程卡 |
| | | const getProcessCard = () => { |
| | | request.post(`/glassOptimize/getProcessCardMpThirdParty/${projectNo.value}`).then((res) => { |
| | | if (Number(res.code) === 200) { |
| | | processCardData.value = res.data.data; |
| | | } else { |
| | | ElMessage.warning(res.msg); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | |
| | | // 从子组件SetAmount获取磨量值,并更新表格数据 |
| | | const Amount = (amountData) => { |
| | | fetchData() |
| | | /*nextTick(() => { |
| | | const data = gridOptions.data; |
| | | if (data) { |
| | | try { |
| | | const updatedData = []; |
| | | for (let i = 0; i < data.length; i++) { |
| | | const item = data[i]; |
| | | const updatedItem = { |
| | | ...item, |
| | | longGrind1: Number(amountData.quicksetTop), |
| | | longGrind2: Number(amountData.quicksetRight), |
| | | shortGrind1: Number(amountData.quicksetBottom), |
| | | shortGrind2: Number(amountData.quicksetLeft) |
| | | }; |
| | | updatedData.push(updatedItem); |
| | | } |
| | | gridOptions.data = updatedData; |
| | | xGrid.value.loadData(updatedData); |
| | | } catch (error) { |
| | | console.error('更新表格数据时出错:', error); |
| | | // 这里可以根据实际需求添加一些回滚操作或者提示用户的逻辑,比如显示一个错误提示框等 |
| | | ElMessage.error('更新磨量数据时出现错误,请检查输入或联系管理员'); |
| | | } |
| | | } else { |
| | | console.warn('表格数据为空,无法更新磨量值'); |
| | | } |
| | | });*/ |
| | | }; |
| | | |
| | | //中转站接受SetTrimming的值(设置修边) |
| | | const emit = defineEmits([ |
| | | 'changeDialog', |
| | | 'forward-data-to-grandparent', |
| | | 'send-inventory-to-op' |
| | | ]); |
| | | |
| | | const handleTrimmingData = (data) => { |
| | | emit('forward-data-to-grandparent', data); |
| | | }; |
| | | |
| | | //中转站接受CheckInventory的值(查询库存) |
| | | const handleInventory = (selectedLabel1, selectedLabel2) => { |
| | | let type=0; |
| | | if(originalFilm.value===true&&surplusMaterial.value===true){ |
| | | type=3 |
| | | }else if(originalFilm.value===true&&surplusMaterial.value===false){ |
| | | type=1 |
| | | }else if(originalFilm.value===false&&surplusMaterial.value===true){ |
| | | type=2 |
| | | }else{ |
| | | type=1 |
| | | } |
| | | emit('send-inventory-to-op', selectedLabel1, selectedLabel2,type); |
| | | } |
| | | |
| | | </script> |
| | | |
| | | <template> |
| | | <div style="width: 100%;height: 85%;"> |
| | | <!-- 头部 --> |
| | | <div id="header" > |
| | | <!--工程文件菜单--> |
| | | <el-dropdown @command="handleCommand"> |
| | | <el-button type="primary" :icon="Folder" style="margin-top: 8px; margin-left: 5px"> |
| | | 工程文件 |
| | | </el-button> |
| | | <template #dropdown> |
| | | <el-dropdown-menu> |
| | | <el-dropdown-item :command="2" :icon="Setting">工程管理</el-dropdown-item> |
| | | <el-dropdown-item :command="3" :icon="Operation">模拟计算</el-dropdown-item> |
| | | </el-dropdown-menu> |
| | | </template> |
| | | </el-dropdown> |
| | | |
| | | <div id="title"> |
| | | <span>工程编号:</span> |
| | | <el-input readonly placeholder="" style="width: 150px" v-model="projectNo"></el-input> |
| | | <span>工程名称:</span> |
| | | <el-input readonly placeholder="" style="width: 150px; margin-right: 140px;" v-model="projectName" ></el-input> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 表格容器 --> |
| | | <div class="table-container"> |
| | | <vxe-grid |
| | | class="left-table" |
| | | @filter-change="filterChanged" |
| | | height="100%" |
| | | ref="xGrid" |
| | | v-bind="gridOptions" |
| | | v-on="gridEvents" |
| | | v-bind:style="{ width: leftTableWidth + '%' }" |
| | | > |
| | | <template #num2_filter="{ column, $panel }"> |
| | | <div> |
| | | <div v-for="(option, index) in column.filters" :key="index"> |
| | | <vxe-select v-model="option.data" :placeholder="$t('processCard.pleaseSelect')" |
| | | @change="changeFilterEvent($event, option, $panel)"> |
| | | <vxe-option value="0" :label="$t('basicData.unchecked')"></vxe-option> |
| | | <vxe-option value="1" :label="$t('basicData.selected')"></vxe-option> |
| | | </vxe-select> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | <template #num1_filter="{ column, $panel }"> |
| | | <div> |
| | | <div v-for="(option, index) in column.filters" :key="index"> |
| | | <input |
| | | type="type" |
| | | v-model="option.data" |
| | | @keyup.enter.native="$panel.confirmFilter()" |
| | | @input="changeFilterEvent($event, option, $panel)"/> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | </vxe-grid> |
| | | |
| | | <!-- 流程卡表格 --> |
| | | <vxe-grid |
| | | height="100%" |
| | | class="right-table" |
| | | :data="processCardData" |
| | | v-bind="processCardColumns" |
| | | v-if="showProcessCardTable" |
| | | :header-cell-style="{'height': '51.9px'}" |
| | | > |
| | | </vxe-grid> |
| | | |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <style scoped> |
| | | .table-container { |
| | | width: 100%; |
| | | height: 100%; |
| | | flex: 1; |
| | | display: flex; |
| | | } |
| | | |
| | | .left-table { |
| | | float: left; |
| | | } |
| | | |
| | | .right-table { |
| | | width: 40%; |
| | | } |
| | | |
| | | :deep(.vxe-toolbar){ |
| | | height: 40px; |
| | | } |
| | | |
| | | #header { |
| | | height: 50px; |
| | | display: flex; |
| | | |
| | | } |
| | | |
| | | #title { |
| | | margin: 8px 5px; |
| | | width: 1240px; |
| | | } |
| | | |
| | | #button { |
| | | margin-left: 10px; |
| | | } |
| | | |
| | | </style> |
New file |
| | |
| | | <script setup> |
| | | import {nextTick, onMounted, reactive, ref, watch} from "vue"; |
| | | import {useI18n} from "vue-i18n"; |
| | | import request from "@/utils/request"; |
| | | import {ElMessage, ElMessageBox} from "element-plus"; |
| | | import {defineEmits} from 'vue'; |
| | | const { t } = useI18n() |
| | | |
| | | const xGrid = ref() |
| | | |
| | | |
| | | const gridOptions = reactive({ |
| | | height:'100%', |
| | | loading: false, |
| | | border: "full",//表格加边框 |
| | | keepSource: true,//保持源数据 |
| | | align: 'center',//文字居中 |
| | | stripe:true,//斑马纹 |
| | | rowConfig: {isCurrent: true, isHover: true,height: 30, useKey: true},//鼠标移动或选择高亮 |
| | | id: 'ProjectList', |
| | | scrollX:{enabled: true}, |
| | | scrollY:{ enabled: true ,gt:0},//开启虚拟滚动 |
| | | showOverflow:true, |
| | | columnConfig: { |
| | | resizable: true, |
| | | useKey: true |
| | | }, |
| | | filterConfig: { //筛选配置项 |
| | | remote: true |
| | | }, |
| | | customConfig: { |
| | | storage: true |
| | | }, |
| | | editConfig: { |
| | | trigger: 'click', |
| | | mode: 'row', |
| | | showStatus: true |
| | | }, |
| | | columns:[ |
| | | {field: 'id',width: 150, title: 'ID',filters:[{ data: '' }],slots: { filter: 'num1_filter' }, sortable: true}, |
| | | {field: 'project_no',width: 150, title: '工程号',filters:[{ data: '' }],slots: { filter: 'num1_filter' }, sortable: true}, |
| | | {field: 'project_name',width: 150, title: '项目名称',filters:[{ data: '' }],slots: { filter: 'num1_filter' }, sortable: true}, |
| | | {field: 'glass_type',width: 150, title: '膜系',filters:[{ data: '' }],slots: { filter: 'num1_filter' }, sortable: true}, |
| | | {field: 'glass_thickness',width: 150, title: '厚度',filters:[{ data: '' }],slots: { filter: 'num1_filter' }, sortable: true}, |
| | | {field: 'type',width: 150, title: '类型',filters:[{ data: '' }],slots: { filter: 'num1_filter' }, sortable: true}, |
| | | {field: 'state',width: 150, title: '状态',filters:[{ data: '' }],slots: { filter: 'num1_filter' }, sortable: true}, |
| | | {field: 'glass_total',width: 150, title: t('order.quantity'),filters:[{ data: '' }],slots: { filter: 'num1_filter' }, sortable: true}, |
| | | {field: 'glass_total_area',width: 150, title: t('order.grossArea'),filters:[{ data: '' }],slots: { filter: 'num1_filter' }, sortable: true}, |
| | | {field: 'process_qty',width: 150, title: '流程数量',filters:[{ data: '' }],slots: { filter: 'num1_filter' }, sortable: true}, |
| | | {field: 'process_cards',width: 150, title: '流程卡号',filters:[{ data: '' }],slots: { filter: 'num1_filter' }, sortable: true}, |
| | | |
| | | ],//表头参数 |
| | | data:null,//表格数据 |
| | | toolbarConfig: { |
| | | buttons: [ |
| | | ], |
| | | import: false, |
| | | // export: true, |
| | | // print: true, |
| | | zoom: true, |
| | | custom: true |
| | | }, |
| | | //右键菜单选项 |
| | | menuConfig: { |
| | | body: { |
| | | options: [ |
| | | [ |
| | | {code: 'openProject', name: '打开工程', prefixIcon: 'vxe-icon-folder-open'}, |
| | | {code: 'compute', name: '模拟计算', prefixIcon: 'vxe-icon-subtable'}, |
| | | {code: 'delProject', name: '删除工程', prefixIcon: 'vxe-icon-delete'}, |
| | | ], |
| | | [] |
| | | ] |
| | | } |
| | | } |
| | | |
| | | }) |
| | | |
| | | // 定义操作配置对象数组,集中管理不同操作选项对应的参数 |
| | | const operationConfigs = [ |
| | | { |
| | | code: 'openProject', // 打开工程 |
| | | initialState: ['10', '20', '100', '200'], // |
| | | targetState: null, |
| | | successMsg: '已打开!', |
| | | checkMessage: '当前工程状态不符合条件,请确认工程状态后再操作!', |
| | | requiresRow: true, |
| | | openFile: async ({row}) => { |
| | | const projectNumber = row.projectNumber; |
| | | const thickness = row.thickness; |
| | | const glassType = row.glassType; |
| | | await router.push({ |
| | | name: 'optimizeInfo', |
| | | params: { |
| | | projectNo: projectNumber, |
| | | thickNess: thickness, |
| | | model: glassType |
| | | } |
| | | }); |
| | | } |
| | | }, |
| | | { |
| | | code: 'compute', // 打开模拟计算操作 |
| | | initialState: ['1', '2'], // |
| | | targetState: null, |
| | | successMsg: '模拟计算已启动!', |
| | | checkMessage: '当前工程状态不符合模拟计算条件,请确认工程状态后再操作!', |
| | | requiresRow: true, |
| | | actionFunction: async ({row}) => { |
| | | const projectNo = row.projectNumber; |
| | | emit('switch-dialog', row); |
| | | } |
| | | }, |
| | | { |
| | | code: 'delProject', |
| | | initialState: ['1', '2', '10', '20', '100'], |
| | | targetState: null, |
| | | successMsg: '工程删除成功!', |
| | | checkMessage: '当前工程状态不符合删除条件,请确认工程状态后再操作!', |
| | | } |
| | | |
| | | ]; |
| | | |
| | | //定义切换模拟计算弹窗 |
| | | const emit = defineEmits(['switch-dialog']); |
| | | |
| | | onMounted(async () => { |
| | | getProject(); |
| | | }) |
| | | |
| | | |
| | | |
| | | const getProject = ()=>{ |
| | | request.post(`/glassOptimize/getProjectList`).then((res) => { |
| | | if(res.code==200){ |
| | | xGrid.value.loadData(res.data.data) |
| | | }else{ |
| | | ElMessage.warning(res.msg) |
| | | } |
| | | }) |
| | | } |
| | | |
| | | const gridEvents = { |
| | | menuClick({menu, row}) { |
| | | const $grid = xGrid.value; |
| | | if ($grid) { |
| | | const config = operationConfigs.find(c => c.code === menu.code); |
| | | if (config) { |
| | | if (config.requiresRow && !row) { |
| | | ElMessage.warning('未选中工程,请选中工程后再进行当前操作!'); |
| | | return; |
| | | } |
| | | if (config.code === 'compute') { |
| | | config.actionFunction({row}); |
| | | return; |
| | | } |
| | | // 添加确认提示弹窗,询问用户是否进行当前操作 |
| | | ElMessageBox.confirm('是否进行当前操作?', '确认操作', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | }).then(() => { |
| | | if (config.code === 'viewTempered') { |
| | | ElMessageBox.alert('当前点击的是查看钢化版图功能,目前暂时仅做提示,暂无实际查看操作!', '功能提示', { |
| | | confirmButtonText: '我知道了' |
| | | }); |
| | | return; |
| | | } |
| | | if (config.code === 'viewOptimize') { |
| | | ElMessageBox.alert('当前点击的是查看钢化版图功能,目前暂时仅做提示,暂无实际查看操作!', '功能提示', { |
| | | confirmButtonText: '我知道了' |
| | | }); |
| | | return; |
| | | } |
| | | if (config.code === 'delProject') { |
| | | if (!row) { |
| | | ElMessage.warning(config.checkMessage); |
| | | return; |
| | | } |
| | | const isInitialStateMatched = config.initialState.includes(String(row.state)); |
| | | if (!isInitialStateMatched) { |
| | | ElMessage.warning(config.checkMessage); |
| | | return; |
| | | } |
| | | deleteProject(row.projectNumber, config); |
| | | } else if (!checkOperationCondition(config, row)) { |
| | | ElMessage.warning(config.checkMessage); |
| | | return; |
| | | } else { |
| | | if (config.code === 'openProject') { |
| | | handleSameDataOperation(row).then(({isRoutesEqual}) => { |
| | | if (!isRoutesEqual) { |
| | | config.openFile({row}); |
| | | ElMessage.success(config.successMsg); |
| | | } |
| | | }); |
| | | } else if (config.code === 'compute') { |
| | | config.actionFunction({row}); |
| | | } else if (config.code === 'optimizeTypography') { |
| | | handleSameDataOperation(row).then(({isRoutesEqual}) => { |
| | | if (!isRoutesEqual) { |
| | | config.Typography({row}); |
| | | ElMessage.success(config.successMsg); |
| | | } |
| | | }); |
| | | } |
| | | else { |
| | | row.state = config.targetState; |
| | | const index = produceList.value.findIndex(item => item === row); |
| | | if (index !== -1) { |
| | | produceList.value.splice(index, 1, {...row}); |
| | | xGrid.value.reloadData(produceList.value); |
| | | } |
| | | updateProjectStateAndHandleResponse(row, row.projectNumber, config.targetState, config.successMsg); |
| | | } |
| | | } |
| | | }).catch(() => { |
| | | // 用户点击取消后执行的逻辑 |
| | | ElMessage.info('已取消操作'); |
| | | }); |
| | | } else { |
| | | console.error(`未找到操作选项 ${menu.code} 对应的配置,请检查配置项`); |
| | | } |
| | | } |
| | | }, |
| | | cellDblclick: ({row}) => { |
| | | const menu = {code: 'openProject'}; |
| | | nextTick(() => { |
| | | handleSameDataOperation(row).then(({isRoutesEqual}) => { |
| | | if (!isRoutesEqual) { |
| | | gridEvents.menuClick({menu, row}); |
| | | } |
| | | }); |
| | | }); |
| | | }, |
| | | }; |
| | | </script> |
| | | |
| | | <template> |
| | | <div style="width: 100%;height: 100%"> |
| | | <h1>工程列表</h1> |
| | | <vxe-grid |
| | | size="small" |
| | | @filter-change="filterChanged" |
| | | height="100%" |
| | | class="mytable-scrollbar" |
| | | ref="xGrid" |
| | | v-bind="gridOptions" |
| | | v-on="gridEvents" |
| | | > |
| | | |
| | | <template #num2_filter="{ column, $panel }"> |
| | | <div> |
| | | <div v-for="(option, index) in column.filters" :key="index"> |
| | | <vxe-select v-model="option.data" :placeholder="$t('processCard.pleaseSelect')" @change="changeFilterEvent($event, option, $panel)"> |
| | | <vxe-option value="0" :label="$t('basicData.unchecked')"></vxe-option> |
| | | <vxe-option value="1" :label="$t('basicData.selected')"></vxe-option> |
| | | </vxe-select> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <template #num1_filter="{ column, $panel }"> |
| | | <div> |
| | | <div v-for="(option, index) in column.filters" :key="index"> |
| | | <input |
| | | |
| | | type="type" |
| | | v-model="option.data" |
| | | @keyup.enter.native="$panel.confirmFilter()" |
| | | @input="changeFilterEvent($event, option, $panel)"/> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | </vxe-grid> |
| | | </div> |
| | | </template> |
| | | |
| | | <style scoped> |
| | | |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <div ref="layoutPanel" :class="panelClass" :style="panelStyle"> |
| | | <div id="printFlowCard"> |
| | | <div v-for="(layout, layoutIndex) in layouts" :key="layoutIndex" class="layout-wrapper"> |
| | | <div class="header" :style="headerStyle(layoutIndex)"> |
| | | 工程号{{ processId }} |
| | | {{ getCurrentRectInfo(layoutIndex) }} |
| | | </div> |
| | | <div class="layout-container" :style="layoutContainerStyle(layoutIndex)"> |
| | | <div class="grid-container" :class="`cols-${printColumns}`"> |
| | | <div |
| | | v-for="(rect, rectIndex) in layout.rects" |
| | | :key="rectIndex" |
| | | :ref="(el) => { if (el) rectsElements[layoutIndex + '-' + rectIndex] = el }" |
| | | :class="rectClass" |
| | | :style="rectStyle(rect, layoutIndex)" |
| | | @click="handleRectClick(layoutIndex, rectIndex)" |
| | | > |
| | | <div v-if="!rect.isRemain" class="rect-content"> |
| | | <div class="size">{{ rect.w }}×{{ rect.h }}</div> |
| | | <div class="jia-hao">{{ rect.JiaHao }}</div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, onUnmounted, watch, nextTick } from 'vue'; |
| | | import request from "@/utils/request"; |
| | | |
| | | const props = defineProps({ |
| | | layoutData: { type: Object, required: true }, |
| | | gw: { type: Number, default: 1400 }, |
| | | gh: { type: Number, default: 1100 }, |
| | | style: { type: String, default: 'width:100%;height:800px;display:block;background:gray' }, |
| | | printLayout: { type: String, default: '2rows-2cols' }, // 可选值:4rows-2cols, 3rows-2cols, 3rows-1col, 2rows-2cols |
| | | fixedPageHeight: { type: Number, default: 1100 } // 固定页面高度 |
| | | }); |
| | | |
| | | const emit = defineEmits(['rectClicked']); |
| | | const layoutPanel = ref(null); |
| | | const rectsElements = ref({}); |
| | | const focusIndex = ref(null); |
| | | const layouts = ref([]); |
| | | const panelClass = ref(''); |
| | | const panelStyle = ref(props.style); |
| | | const rectClass = ref('layout-rect'); |
| | | const processId = localStorage.getItem('projectNo'); |
| | | const printColumns = ref(2); // 初始化为2列 |
| | | const layoutsPerPage = ref(4); // 默认每页显示4个布局(2行×2列) |
| | | |
| | | // 定义不同布局的放大比例 |
| | | const layoutScales = { |
| | | '4rows-2cols': 0.8, // 四行两列,较小的放大比例 |
| | | '3rows-2cols': 0.9, // 三行两列,适中的放大比例 |
| | | '3rows-1col': 1.0, // 三行一列,较大的放大比例 |
| | | '2rows-2cols': 1 // 两行两列,较大的放大比例 |
| | | }; |
| | | |
| | | // 监听printLayout变化 |
| | | watch(() => props.printLayout, (newVal) => { |
| | | adjustPrintLayout(); |
| | | updateLayout(); |
| | | }); |
| | | |
| | | const layoutContainerStyle = (layoutIndex) => { |
| | | const containerWidth = (props.gw - 20) / printColumns.value; // 减少边距 |
| | | const containerHeight = (props.gh - 20) / Math.ceil(layoutsPerPage.value / printColumns.value); |
| | | const x = (layoutIndex % printColumns.value) * containerWidth; |
| | | const y = Math.floor(layoutIndex / printColumns.value) * containerHeight; |
| | | return { |
| | | position: 'absolute', |
| | | left: `${x}px`, |
| | | top: `${y}px`, |
| | | width: `${containerWidth}px`, |
| | | height: `${containerHeight}px`, |
| | | overflow: 'visible', |
| | | padding: '10px' // 添加内边距 |
| | | }; |
| | | }; |
| | | |
| | | const headerStyle = (layoutIndex) => { |
| | | const containerWidth = (props.gw - 20) / printColumns.value; |
| | | const containerHeight = (props.gh - 20) / Math.ceil(layoutsPerPage.value / printColumns.value); |
| | | const x = (layoutIndex % printColumns.value) * containerWidth; |
| | | const y = Math.floor(layoutIndex / printColumns.value) * containerHeight; |
| | | const scale = Math.min( |
| | | containerWidth, |
| | | containerHeight |
| | | ) * 1.2; // 放大1.2倍 |
| | | return { |
| | | position: 'absolute', |
| | | left: `${x}px`, |
| | | top: `${y - 45}px`, |
| | | width: `${scale}px`, |
| | | textAlign: 'center', |
| | | zIndex: 1000, |
| | | background: '#ffffff', |
| | | padding: '5px', |
| | | fontSize: '12px' |
| | | }; |
| | | }; |
| | | |
| | | const rectStyle = (rect, layoutIndex) => { |
| | | const layout = layouts.value[layoutIndex]; |
| | | const containerWidth = (props.gw - 100) / printColumns.value; |
| | | const containerHeight = (props.gh - 100) / Math.ceil(layoutsPerPage.value / printColumns.value); |
| | | |
| | | // 根据当前打印布局获取放大比例 |
| | | const currentScale = layoutScales[props.printLayout] || 1.0; |
| | | |
| | | const scale = Math.min( |
| | | containerWidth / layout.width, |
| | | containerHeight / layout.height |
| | | ) * currentScale; // 应用当前布局的放大比例 |
| | | |
| | | return { |
| | | position: 'absolute', |
| | | left: `${rect.x * scale}px`, |
| | | top: `${rect.y * scale}px`, |
| | | width: `${rect.w * scale}px`, |
| | | height: `${rect.h * scale}px`, |
| | | backgroundColor: rect.isRemain ? '#f0f0f0' : '#a0d8ef', |
| | | border: '1px solid #000', |
| | | cursor: 'pointer' |
| | | }; |
| | | }; |
| | | |
| | | const handleRectClick = (layoutIndex, rectIndex) => { |
| | | focusIndex.value = { layoutIndex, rectIndex }; |
| | | emit('rectClicked', layoutIndex, rectIndex); |
| | | }; |
| | | |
| | | const getCurrentRectInfo = (layoutIndex) => { |
| | | const layout = layouts.value[layoutIndex]; |
| | | const rect = layout.rects[focusIndex.value?.rectIndex || 0]; |
| | | if (!rect) return ''; |
| | | const totalRects = layouts.value.length; |
| | | const currentRectIndex = layoutIndex + 1; |
| | | const width = layout.width; |
| | | const height = layout.height; |
| | | const sum = layout.rects.reduce((sum, r) => sum + (r.w * r.h), 0); |
| | | const areaUtilization = ((sum / (width * height)) * 100).toFixed(2); |
| | | return `${currentRectIndex}/${totalRects} ${height}X${width}X1 ${areaUtilization}%`; |
| | | }; |
| | | |
| | | const adjustPrintLayout = () => { |
| | | switch (props.printLayout) { |
| | | case '4rows-2cols': |
| | | printColumns.value = 2; |
| | | layoutsPerPage.value = 8; // 4行×2列 |
| | | break; |
| | | case '3rows-2cols': |
| | | printColumns.value = 2; |
| | | layoutsPerPage.value = 6; // 3行×2列 |
| | | break; |
| | | case '3rows-1col': |
| | | printColumns.value = 1; |
| | | layoutsPerPage.value = 3; // 3行×1列 |
| | | break; |
| | | case '2rows-2cols': |
| | | printColumns.value = 2; |
| | | layoutsPerPage.value = 4; // 2行×2列 |
| | | break; |
| | | default: |
| | | printColumns.value = 2; |
| | | layoutsPerPage.value = 4; |
| | | } |
| | | }; |
| | | |
| | | const updateLayout = () => { |
| | | if (!layoutPanel.value) return; |
| | | layouts.value = props.layoutData.Layouts; |
| | | adjustPrintLayout(); |
| | | // 强制重新渲染 |
| | | layoutPanel.value.offsetHeight; // 触发布局更新 |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | updateLayout(); |
| | | }); |
| | | |
| | | onUnmounted(() => { |
| | | rectsElements.value = {}; |
| | | }); |
| | | |
| | | const print = () => { |
| | | const el = document.getElementById('printFlowCard'); |
| | | const doc = document; |
| | | const body = doc.body || doc.getElementsByTagName("body")[0]; |
| | | const printId = "print-" + Date.now(); |
| | | |
| | | // 创建一个克隆的节点 |
| | | const content = document.createElement("div"); |
| | | content.id = printId; |
| | | content.appendChild(el.cloneNode(true)); // 克隆节点并保留所有属性和子节点 |
| | | |
| | | const style = document.createElement("style"); |
| | | style.innerHTML = |
| | | "body>#" + |
| | | printId + |
| | | "{display:none}@media print{" + |
| | | "@page {" + |
| | | " size: auto; " + |
| | | " margin: 13mm 4mm 0mm 4mm; " + |
| | | " }body>:not(#" + |
| | | printId + |
| | | "){display:none !important}body>#" + |
| | | printId + |
| | | "{display:block;padding-top:1px}}"; |
| | | |
| | | body.appendChild(style); |
| | | body.appendChild(content); |
| | | |
| | | // 优化分页逻辑 |
| | | const layoutWrappers = content.querySelectorAll('.layout-wrapper'); |
| | | let currentPageHeight = 0; |
| | | let currentWrapperIndex = 0; |
| | | |
| | | layoutWrappers.forEach((wrapper, index) => { |
| | | const wrapperHeight = wrapper.offsetHeight; |
| | | if (currentPageHeight + wrapperHeight > props.fixedPageHeight) { |
| | | const pageBreak = document.createElement('div'); |
| | | pageBreak.className = 'element-to-break-after'; |
| | | layoutWrappers[currentWrapperIndex - 1].appendChild(pageBreak); |
| | | currentPageHeight = wrapperHeight; |
| | | } else { |
| | | currentPageHeight += wrapperHeight; |
| | | } |
| | | currentWrapperIndex = index + 1; |
| | | }); |
| | | |
| | | setTimeout(() => { |
| | | window.print(); |
| | | body.removeChild(content); |
| | | body.removeChild(style); |
| | | }, 200); |
| | | }; |
| | | |
| | | defineExpose({ |
| | | print, |
| | | updateLayout |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | @media print { |
| | | .layout-wrapper { |
| | | page-break-inside: avoid; |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .element-to-break-after { |
| | | page-break-after: always; |
| | | } |
| | | |
| | | .header { |
| | | position: static; |
| | | width: 100%; |
| | | } |
| | | |
| | | .layout-container { |
| | | position: static; |
| | | width: 100%; |
| | | height: auto; |
| | | } |
| | | |
| | | .grid-container { |
| | | display: grid; |
| | | gap: 10px; /* 减少打印时的网格间距 */ |
| | | } |
| | | |
| | | .cols-1 { |
| | | grid-template-columns: 1fr; |
| | | } |
| | | |
| | | .cols-2 { |
| | | grid-template-columns: repeat(2, 1fr); |
| | | } |
| | | |
| | | .cols-3 { |
| | | grid-template-columns: repeat(3, 1fr); |
| | | } |
| | | |
| | | .cols-4 { |
| | | grid-template-columns: repeat(4, 1fr); |
| | | } |
| | | } |
| | | |
| | | .element-to-break-after { |
| | | page-break-after: always; |
| | | } |
| | | |
| | | .layout-wrapper { |
| | | position: relative; |
| | | margin-top: 50px; |
| | | } |
| | | |
| | | .header { |
| | | position: absolute; |
| | | top: -45px; |
| | | left: 0; |
| | | width: 100%; |
| | | text-align: center; |
| | | z-index: 1000; |
| | | background-color: #ffffff; |
| | | padding: 5px; |
| | | font-size: 12px; |
| | | } |
| | | |
| | | .layout-container { |
| | | position: relative; |
| | | overflow: visible; |
| | | } |
| | | |
| | | .rect-content { |
| | | display: grid; |
| | | grid-template-columns: 1fr; |
| | | grid-template-rows: 1fr; |
| | | padding: 5px; |
| | | } |
| | | |
| | | .size { |
| | | grid-row: 1; |
| | | grid-column: 1; |
| | | color: #444; |
| | | font-size: 12px; |
| | | } |
| | | |
| | | .jia-hao { |
| | | grid-row: 2; |
| | | grid-column: 1; |
| | | margin: auto; |
| | | font-size: 14px; |
| | | font-weight: bold; |
| | | } |
| | | </style> |
New file |
| | |
| | | <script setup> |
| | | import {onMounted, ref} from "vue"; |
| | | import {defineEmits} from 'vue'; |
| | | import request from "@/utils/request"; |
| | | import {ElMessage} from "element-plus"; |
| | | |
| | | onMounted(async() => { |
| | | await firstLoading() |
| | | }) |
| | | |
| | | const firstLoading = async() => { |
| | | request.post(`/glassOptimize/getConfiguration/磨量`).then((res) => { |
| | | if (res.code == "200") { |
| | | const rawData = res.data.data; |
| | | if (Array.isArray(rawData) && rawData.length > 0) { |
| | | const formattedData = rawData.map(item => { |
| | | const formattedItem = {}; |
| | | for (const key in item) { |
| | | if (typeof item[key] === 'string') { |
| | | //去除字符串属性值开头和结尾的双引号 |
| | | formattedItem[key] = item[key].replace(/^\"|\"$/g, ''); |
| | | } else { |
| | | formattedItem[key] = item[key]; |
| | | } |
| | | } |
| | | return formattedItem; |
| | | }); |
| | | quicksetLeft.value=formattedData[0].leftEdge |
| | | quicksetTop.value=formattedData[0].upEdge |
| | | quicksetRight.value=formattedData[0].rightEdge |
| | | quicksetBottom.value=formattedData[0].downEdge |
| | | controlValue.value=formattedData[0].quickEdge |
| | | if(formattedData[0].autoFillEdge=="true"){ |
| | | check.value=true |
| | | }else{ |
| | | check.value=false |
| | | } |
| | | minAutoLenght.value=formattedData[0].minAutoLenght |
| | | |
| | | } |
| | | } else { |
| | | ElMessage.warning(res.msg) |
| | | } |
| | | }); |
| | | }; |
| | | function setupComponent() { |
| | | // 各个输入框绑定的值,初始化为0 |
| | | const quicksetTop = ref('0'); |
| | | const quicksetRight = ref('0'); |
| | | const quicksetBottom = ref('0'); |
| | | const quicksetLeft = ref('0'); |
| | | // 用于控制的输入值,初始化为1 |
| | | const controlValue = ref('1'); |
| | | const minAutoLenght = ref('0'); |
| | | |
| | | const syncValues = () => { |
| | | // 将控制值赋给其他四个组件 |
| | | quicksetTop.value = controlValue.value; |
| | | quicksetRight.value = controlValue.value; |
| | | quicksetBottom.value = controlValue.value; |
| | | quicksetLeft.value = controlValue.value; |
| | | }; |
| | | return { |
| | | quicksetTop, |
| | | quicksetRight, |
| | | quicksetBottom, |
| | | quicksetLeft, |
| | | controlValue, |
| | | minAutoLenght, |
| | | syncValues |
| | | }; |
| | | } |
| | | const { |
| | | quicksetTop, |
| | | quicksetRight, |
| | | quicksetBottom, |
| | | quicksetLeft, |
| | | controlValue, |
| | | minAutoLenght, |
| | | syncValues |
| | | } = setupComponent(); |
| | | |
| | | const check = ref() |
| | | |
| | | const emit = defineEmits(['set-amount']); |
| | | const props = defineProps({ |
| | | closeDialog: Function |
| | | }); |
| | | |
| | | const setAmount = () => { |
| | | let json = "{" |
| | | json+='"left_edge":"'+quicksetLeft.value+'",' |
| | | json+='"up_edge":"'+quicksetTop.value+'",' |
| | | json+='"right_edge":"'+quicksetRight.value+'",' |
| | | json+='"down_edge":"'+quicksetBottom.value+'",' |
| | | json+='"auto_fill_edge":"'+check.value+'",' |
| | | json+='"quick_edge":"'+controlValue.value+'",' |
| | | json+='"min_auto_lenght":"'+minAutoLenght.value+'"' |
| | | json+="}" |
| | | |
| | | let configuration={ |
| | | json:json, |
| | | } |
| | | |
| | | request.post(`/glassOptimize/saveConfiguration/磨量`,configuration).then((res) => { |
| | | if(res.code==200 && res.data===true){ |
| | | const amountData = { |
| | | quicksetTop: quicksetTop.value, |
| | | quicksetRight: quicksetRight.value, |
| | | quicksetBottom: quicksetBottom.value, |
| | | quicksetLeft: quicksetLeft.value, |
| | | controlValue:controlValue.value, |
| | | minAutoLenght:minAutoLenght.value, |
| | | }; |
| | | emit('set-amount', amountData); |
| | | props.closeDialog(2); |
| | | } else { |
| | | ElMessage.warning(res.msg) |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | </script> |
| | | |
| | | <template> |
| | | <div id="box1"> |
| | | <div style="display: flex; justify-content: center"> |
| | | <div class="square-container"> |
| | | <div class="square" @click="syncValues"></div> |
| | | <el-input-number v-model="quicksetTop" class="top" placeholder="0" |
| | | controls-position="right" :step="0.1" :min="0"></el-input-number> |
| | | <el-input-number v-model="quicksetRight" class="right" placeholder="0" |
| | | controls-position="right" :step="0.1" :min="0"></el-input-number> |
| | | <el-input-number v-model="quicksetBottom" class="bottom" placeholder="0" |
| | | controls-position="right" :step="0.1" :min="0"></el-input-number> |
| | | <el-input-number v-model="quicksetLeft" class="left" placeholder="0" |
| | | controls-position="right" :step="0.1" :min="0"></el-input-number> |
| | | </div> |
| | | <el-button type="primary" style="float: right; margin: -75px 0;" @click="setAmount">应用</el-button> |
| | | </div> |
| | | |
| | | <div style="margin-top: 20px"> |
| | | <span>鼠标点击蓝色图形区域可快速设置磨量</span> |
| | | <el-input-number v-model="controlValue" class="quickset" placeholder="0" |
| | | controls-position="right" :step="0.1" :min="0"></el-input-number> |
| | | <br> |
| | | <div style="margin-top: 30px"> |
| | | <span>成品边长大于等于多少时自动填充(单位: mm)</span> |
| | | <el-checkbox v-model="check" style="margin: 5px 0 0 12px;"/> |
| | | <vxe-input style="width: 100px; height: 40px; margin-left: 5px" v-model="minAutoLenght" placeholder=""/> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <style scoped> |
| | | #box1 { |
| | | width: 100%; |
| | | height: 100%; |
| | | padding: 10px; |
| | | border-radius: 5px; |
| | | } |
| | | |
| | | .square-container { |
| | | width: 300px; |
| | | height: 250px; |
| | | display: flex; |
| | | position: relative; |
| | | justify-content: center; |
| | | align-items: center; |
| | | } |
| | | |
| | | .square { |
| | | width: 100px; |
| | | height: 100px; |
| | | background-color: #5cadfe; |
| | | } |
| | | |
| | | .top, |
| | | .right, |
| | | .bottom, |
| | | .left { |
| | | width: 100px; |
| | | height: 50px; |
| | | position: absolute; |
| | | } |
| | | |
| | | .top { |
| | | top: 20px; |
| | | left: 50%; |
| | | transform: translateX(-50%); |
| | | } |
| | | |
| | | .right { |
| | | top: 50%; |
| | | right: -5px; |
| | | transform: translateY(-50%); |
| | | } |
| | | |
| | | .bottom { |
| | | bottom: 20px; |
| | | left: 50%; |
| | | transform: translateX(-50%); |
| | | } |
| | | |
| | | .left { |
| | | top: 50%; |
| | | left: -5px; |
| | | transform: translateY(-50%); |
| | | } |
| | | |
| | | .quickset { |
| | | margin-left: 60px; |
| | | width: 100px; |
| | | height: 40px; |
| | | } |
| | | </style> |
New file |
| | |
| | | <script setup> |
| | | import {onMounted, ref} from "vue"; |
| | | import {defineEmits} from 'vue'; |
| | | import request from "@/utils/request"; |
| | | import {ElMessage} from "element-plus"; |
| | | |
| | | |
| | | onMounted(async() => { |
| | | await firstLoading() |
| | | }) |
| | | |
| | | const firstLoading = async() => { |
| | | request.post(`/glassOptimize/getConfiguration/修边`).then((res) => { |
| | | if (res.code == "200") { |
| | | const rawData = res.data.data; |
| | | if (Array.isArray(rawData) && rawData.length > 0) { |
| | | const formattedData = rawData.map(item => { |
| | | const formattedItem = {}; |
| | | for (const key in item) { |
| | | if (typeof item[key] === 'string') { |
| | | //去除字符串属性值开头和结尾的双引号 |
| | | formattedItem[key] = item[key].replace(/^\"|\"$/g, ''); |
| | | } else { |
| | | formattedItem[key] = item[key]; |
| | | } |
| | | } |
| | | return formattedItem; |
| | | }); |
| | | quicksetLeft.value=formattedData[0].leftTrim |
| | | quicksetTop.value=formattedData[0].upTrim |
| | | quicksetRight.value=formattedData[0].rightTrim |
| | | quicksetBottom.value=formattedData[0].downTrim |
| | | |
| | | } |
| | | } else { |
| | | ElMessage.warning(res.msg) |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | function setupComponent() { |
| | | // 各个输入框绑定的值,初始化为0 |
| | | const quicksetTop = ref('0'); |
| | | const quicksetRight = ref('0'); |
| | | const quicksetBottom = ref('0'); |
| | | const quicksetLeft = ref('0'); |
| | | // 用于控制的输入值,初始化为1 |
| | | const controlValue = ref('1'); |
| | | |
| | | const syncValues = () => { |
| | | // 将控制值赋给其他四个组件 |
| | | quicksetTop.value = controlValue.value; |
| | | quicksetRight.value = controlValue.value; |
| | | quicksetBottom.value = controlValue.value; |
| | | quicksetLeft.value = controlValue.value; |
| | | }; |
| | | return { |
| | | quicksetTop, |
| | | quicksetRight, |
| | | quicksetBottom, |
| | | quicksetLeft, |
| | | controlValue, |
| | | syncValues |
| | | }; |
| | | } |
| | | const { |
| | | quicksetTop, |
| | | quicksetRight, |
| | | quicksetBottom, |
| | | quicksetLeft, |
| | | controlValue, |
| | | syncValues |
| | | } = setupComponent(); |
| | | |
| | | const check = ref(true) |
| | | |
| | | |
| | | const emit = defineEmits(['send-data-event',]); |
| | | const props = defineProps({ |
| | | closeDialog: Function |
| | | }); |
| | | |
| | | const setTrimming = () => { |
| | | let json = "{" |
| | | json+='"left_trim":"'+quicksetLeft.value+'",' |
| | | json+='"up_trim":"'+quicksetTop.value+'",' |
| | | json+='"right_trim":"'+quicksetRight.value+'",' |
| | | json+='"down_trim":"'+quicksetBottom.value+'",' |
| | | json+='"auto_fill_trim":"'+true+'",' |
| | | json+='"quick_trim":"'+15+'"' |
| | | json+="}" |
| | | |
| | | let configuration={ |
| | | json:json, |
| | | } |
| | | |
| | | request.post(`/glassOptimize/saveConfiguration/修边`,configuration).then((res) => { |
| | | if(res.code==200 && res.data===true){ |
| | | const dataToSend = { |
| | | quicksetTop: quicksetTop.value, |
| | | quicksetRight: quicksetRight.value, |
| | | quicksetBottom: quicksetBottom.value, |
| | | quicksetLeft: quicksetLeft.value |
| | | }; |
| | | emit('send-data-event', dataToSend); |
| | | props.closeDialog(3); |
| | | } else { |
| | | ElMessage.warning(res.msg) |
| | | } |
| | | }); |
| | | |
| | | }; |
| | | |
| | | </script> |
| | | |
| | | <template> |
| | | <div id="box1"> |
| | | <div style="display: flex; margin-top: -20px; justify-content: center"> |
| | | <div class="square-container"> |
| | | <div class="square" @click="syncValues"></div> |
| | | <el-input-number v-model="quicksetTop" class="top" placeholder="0" |
| | | controls-position="right" :step="0.1" :min="0"></el-input-number> |
| | | <el-input-number v-model="quicksetRight" class="right" placeholder="0" |
| | | controls-position="right" :step="0.1" :min="0"></el-input-number> |
| | | <el-input-number v-model="quicksetBottom" class="bottom" placeholder="0" |
| | | controls-position="right" :step="0.1" :min="0"></el-input-number> |
| | | <el-input-number v-model="quicksetLeft" class="left" placeholder="0" |
| | | controls-position="right" :step="0.1" :min="0"></el-input-number> |
| | | </div> |
| | | <el-button type="primary" style="float: right; margin: -55px 0;" @click="setTrimming">应用</el-button> |
| | | </div> |
| | | |
| | | <div> |
| | | <span>鼠标点击蓝色图形区域可快速设置修边量</span> |
| | | <el-input-number v-model="controlValue" class="quickset" placeholder="0" |
| | | controls-position="right" :step="0.1" :min="0"></el-input-number> |
| | | <br> |
| | | <span>查询原片时自动填充(单位: mm)</span> |
| | | <el-checkbox v-model="check" style="margin: 20px 0 0 117px;"/> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <style scoped> |
| | | #box1 { |
| | | width: 100%; |
| | | height: 100%; |
| | | padding: 10px; |
| | | border-radius: 5px; |
| | | } |
| | | |
| | | .square-container { |
| | | width: 300px; |
| | | height: 300px; |
| | | display: flex; |
| | | position: relative; |
| | | justify-content: center; |
| | | align-items: center; |
| | | } |
| | | |
| | | .square { |
| | | width: 100px; |
| | | height: 100px; |
| | | background-color: #5cadfe; |
| | | } |
| | | |
| | | .top, |
| | | .right, |
| | | .bottom, |
| | | .left { |
| | | width: 100px; |
| | | height: 50px; |
| | | position: absolute; |
| | | } |
| | | |
| | | .top { |
| | | top: 45px; |
| | | left: 50%; |
| | | transform: translateX(-50%); |
| | | } |
| | | |
| | | .right { |
| | | top: 50%; |
| | | right: -5px; |
| | | transform: translateY(-50%); |
| | | } |
| | | |
| | | .bottom { |
| | | bottom: 45px; |
| | | left: 50%; |
| | | transform: translateX(-50%); |
| | | } |
| | | |
| | | .left { |
| | | top: 50%; |
| | | left: -5px; |
| | | transform: translateY(-50%); |
| | | } |
| | | |
| | | .quickset { |
| | | margin-left: 60px; |
| | | width: 100px; |
| | | height: 45px; |
| | | } |
| | | </style> |
| | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | //模拟计算工程号查询第三方 |
| | | @ApiOperation("模拟计算工程号查询") |
| | | @PostMapping ("/selectProjectComputeMpThirdParty/{projectNumber}") |
| | | public Result selectProjectComputeMpThirdParty( @PathVariable String projectNumber){ |
| | | return Result.seccess(glassOptimizeService.selectProjectComputeMpThirdParty(projectNumber)); |
| | | } |
| | | |
| | | //模拟计算流程卡详情第三方 |
| | | @ApiOperation("模拟计算流程卡详情") |
| | | @PostMapping ("/selectComputeDetailThirdParty/{processId}/{technologyNumber}") |
| | | public Result selectComputeDetailThirdParty( |
| | | @PathVariable String processId, @PathVariable Integer technologyNumber){ |
| | | return Result.seccess(glassOptimizeService.selectComputeDetailThirdParty(processId,technologyNumber)); |
| | | } |
| | | |
| | | @ApiOperation("模拟计算保存") |
| | | @PostMapping("/simulationSaveThirdParty") |
| | | public Result simulationSaveThirdParty(@RequestBody Map<String, Object> object) { |
| | | try { |
| | | return Result.seccess(glassOptimizeService.simulationSaveThirdParty(object)); |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | return Result.error(); |
| | | } |
| | | } |
| | | |
| | | //删除工程 |
| | | @ApiOperation("工程管理删除接口") |
| | | @PostMapping("/deleteProjectThirdParty/{projectNumber}") |
| | | public Result deleteProjectThirdParty( |
| | | @PathVariable String projectNumber){ |
| | | return Result.seccess(glassOptimizeService.deleteProjectThirdParty(projectNumber)); |
| | | } |
| | | |
| | | @ApiOperation("工程信息接口") |
| | | @PostMapping ("/projectInfoThirdParty/{projectNo}") |
| | | public Result projectInfoThirdParty( |
| | | @PathVariable String projectNo){ |
| | | return Result.seccess(glassOptimizeService.projectInfoThirdParty(projectNo)); |
| | | } |
| | | |
| | | //库存信息 |
| | | @ApiOperation("库存信息接口") |
| | | @GetMapping ("/materialStoreSvThirdParty/{projectNumber}") |
| | | public Result materialStoreSvThirdParty(@PathVariable String projectNumber ){ |
| | | return Result.seccess(glassOptimizeService.materialStoreSvThirdParty(projectNumber)); |
| | | } |
| | | |
| | | //工程信息流程卡 |
| | | @ApiOperation("工程信息流程卡接口") |
| | | @PostMapping ("/getProcessCardMpThirdParty/{projectNo}") |
| | | public Result getProcessCardMpThirdParty( |
| | | @PathVariable String projectNo){ |
| | | return Result.seccess(glassOptimizeService.getProcessCardMpThirdParty(projectNo)); |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | } |
New file |
| | |
| | | package com.example.erp.entity.pp; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.Data; |
| | | |
| | | import java.time.LocalDate; |
| | | |
| | | @Data |
| | | @TableName("pp.`optimize_detail`") |
| | | public class OptimizeDetail { |
| | | @TableId(type = IdType.AUTO) |
| | | private Long id; |
| | | private String projectNo; |
| | | private String processId; |
| | | private Integer total_layer; |
| | | private Integer layer; |
| | | private Integer orderSort; |
| | | private Integer stock_id; |
| | | private Integer stock_number; |
| | | private String glass_id; |
| | | private Double pWidth; |
| | | private Double pHeight; |
| | | private Integer heatLayoutId; |
| | | private Integer heatLayoutSort; |
| | | |
| | | |
| | | } |
New file |
| | |
| | | package com.example.erp.entity.pp; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.Data; |
| | | |
| | | import java.time.LocalDate; |
| | | |
| | | @Data |
| | | @TableName("pp.`optimize_heat_detail`") |
| | | public class OptimizeHeatDetail { |
| | | @TableId(type = IdType.AUTO) |
| | | private Long id; |
| | | private String projectNo; |
| | | private String processId; |
| | | private Integer patchState; |
| | | private Integer layer; |
| | | private Integer orderSort; |
| | | private Integer layoutId; |
| | | private Integer sort; |
| | | private Integer glass_id; |
| | | private Double width; |
| | | private Double height; |
| | | private LocalDate createTime; |
| | | |
| | | |
| | | } |
| | |
| | | package com.example.erp.mapper.pp; |
| | | |
| | | |
| | | import com.example.erp.entity.pp.FlowCard; |
| | | import com.example.erp.entity.pp.OptimizeProjectMange; |
| | | import com.example.erp.entity.pp.OptimizeUse; |
| | | import com.example.erp.entity.pp.PatchLog; |
| | | import com.example.erp.entity.pp.*; |
| | | import com.example.erp.entity.sd.OrderGlassDetail; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | import org.apache.ibatis.annotations.Param; |
| | |
| | | |
| | | int deleteOptimizeDetail(String projectNumber); |
| | | |
| | | List<OptimizeHeatDetail> selectOptimizeHeatDetail(String projectNumber); |
| | | |
| | | OptimizeDetail selectOptimizeDetailById(String projectNumber,String processId,Integer orderSort,Integer layer); |
| | | |
| | | //模拟计算工程号查询 |
| | | List<Map<String, Object>> selectProjectComputeMp(@Param("projectNo")String projectNumber); |
| | | |
| | |
| | | List<Map<String, Object>> selectOptimizeResult(String processId); |
| | | //优化结果调整 |
| | | void updateOptimizeResult(String jsonString,String processId); |
| | | |
| | | void updateOptimizeDetail(Long id,Integer layoutId,Integer sort); |
| | | |
| | | List<Map<String, Object>> getProcessCardDetailmMp(String processId, Integer technologyNumber); |
| | | |
| | |
| | | List<Map<String, Object>> getEdgeTrimming(); |
| | | |
| | | Boolean updateOptimizeConfig(String json,Integer type); |
| | | |
| | | |
| | | |
| | | |
| | | //模拟计算工程号查询 |
| | | List<Map<String, Object>> selectProjectComputeMpThirdParty(@Param("projectNo")String projectNumber); |
| | | |
| | | //模拟计算流程卡详情 |
| | | List<Map<String, Object>> selectComputeDetailMpThirdParty(String processId,Integer technologyNumber); |
| | | |
| | | int deleteOptimizeDetailThirdParty(String projectNumber); |
| | | |
| | | List<Map<String, Object>> firstOptimizationThirdParty(String projectNo); |
| | | |
| | | List<Map<String, Object>> analogComputationOptimizationThirdParty(String projectNo); |
| | | |
| | | //工程信息流程卡 |
| | | List<Map<String, Object>> getProcessCardMpThirdParty(String projectNo); |
| | | |
| | | } |
| | |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import com.baomidou.dynamic.datasource.annotation.DS; |
| | | import com.example.erp.common.RabbitMQUtil; |
| | | import com.example.erp.entity.pp.FlowCard; |
| | | import com.example.erp.entity.pp.OptimizeProjectMange; |
| | | import com.example.erp.entity.pp.PatchLog; |
| | | import com.example.erp.entity.pp.*; |
| | | import com.example.erp.entity.sd.Delivery; |
| | | import com.example.erp.entity.sd.OrderDetail; |
| | | import com.example.erp.entity.userInfo.Log; |
| | | import com.example.erp.entity.userInfo.SysError; |
| | | import com.example.erp.mapper.pp.GlassOptimizeMapper; |
| | |
| | | //模拟计算保存 |
| | | public Boolean addSimulation(Map<String, Object> object) { |
| | | try { |
| | | Map<String, Object> objectMap = (Map<String, Object>) object.get("inputValues"); |
| | | String projectNo = objectMap.get("project_no").toString(); |
| | | Map<String, Object> optimizeProjectMap = glassOptimizeMapper.selectProjectCount(projectNo); |
| | | glassOptimizeMapper.addSimulation(object); |
| | | glassOptimizeMapper.addratioResult(object); |
| | | glassOptimizeMapper.addratioProjectResult(object); |
| | |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | //模拟计算工程号查询 |
| | | public Map<String, Object>selectProjectComputeMpThirdParty(String projectNumber) { |
| | | Map<String, Object> map = new HashMap<>(); |
| | | map.put("data", glassOptimizeMapper.selectProjectComputeMpThirdParty(projectNumber)); |
| | | return map; |
| | | } |
| | | |
| | | //模拟计算流程卡详情 |
| | | public Map<String, Object>selectComputeDetailThirdParty(String processId,Integer technologyNumber) { |
| | | Map<String, Object> map = new HashMap<>(); |
| | | map.put("data", glassOptimizeMapper.selectComputeDetailMpThirdParty(processId,technologyNumber)); |
| | | return map; |
| | | } |
| | | |
| | | public Boolean simulationSaveThirdParty(Map<String, Object> object) { |
| | | try { |
| | | Map<String, Object> objectMap = (Map<String, Object>) object.get("inputValues"); |
| | | String projectNo = objectMap.get("project_no").toString(); |
| | | Map<String, Object> optimizeProjectMap = glassOptimizeMapper.selectProjectCount(projectNo); |
| | | glassOptimizeMapper.addSimulation(object); |
| | | glassOptimizeMapper.addratioResult(object); |
| | | glassOptimizeMapper.addratioProjectResult(object); |
| | | |
| | | List<OptimizeHeatDetail> optimizeHeatDetail = glassOptimizeMapper.selectOptimizeHeatDetail(projectNo); |
| | | for (OptimizeHeatDetail projectdetail:optimizeHeatDetail){ |
| | | OptimizeDetail optimizeDetail=glassOptimizeMapper.selectOptimizeDetailById(projectNo,projectdetail.getProcessId(), |
| | | projectdetail.getOrderSort(),projectdetail.getLayer()); |
| | | glassOptimizeMapper.updateOptimizeDetail(optimizeDetail.getId(),projectdetail.getLayoutId(),projectdetail.getSort()); |
| | | } |
| | | |
| | | |
| | | return true; |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | //删除工程 |
| | | public boolean deleteProjectThirdParty(String projectNumber) { |
| | | Map<String, Object> stringObjectMap = glassOptimizeMapper.selectProjectCount(projectNumber); |
| | | if(stringObjectMap.get("state").equals(2)){ |
| | | glassOptimizeMapper.deleteOptimizeDetailThirdParty(projectNumber); |
| | | glassOptimizeMapper.deleteOptimizeDetail(projectNumber); |
| | | }else if(stringObjectMap.get("state").equals(10)){ |
| | | glassOptimizeMapper.deleteOptimizeDetailThirdParty(projectNumber); |
| | | glassOptimizeMapper.deleteOptimizeHeatDetail(projectNumber); |
| | | glassOptimizeMapper.deleteOptimizeHeatLayout(projectNumber); |
| | | } |
| | | glassOptimizeMapper.deleteProjectMp(projectNumber); |
| | | |
| | | return true; |
| | | } |
| | | |
| | | //工程信息 |
| | | public Map<String, Object> projectInfoThirdParty(String projectNo) { |
| | | Map<String, Object> stringObjectMap = glassOptimizeMapper.selectProjectCount(projectNo); |
| | | Map<String, Object> map = new HashMap<>(); |
| | | if(stringObjectMap.get("state").equals(2)){ |
| | | map.put("data", glassOptimizeMapper.firstOptimizationThirdParty(projectNo)); |
| | | map.put("grindingTrimming", null); |
| | | }else{ |
| | | map.put("data", glassOptimizeMapper.firstOptimizationThirdParty(projectNo)); |
| | | map.put("grindingTrimming", null); |
| | | } |
| | | return map; |
| | | } |
| | | |
| | | //库存信息 |
| | | public Map<String, Object> materialStoreSvThirdParty(String projectNumber) { |
| | | Map<String, Object> stringObjectMap = glassOptimizeMapper.selectProjectCount(projectNumber); |
| | | Map<String, Object> map = new HashMap<>(); |
| | | map.put("data", glassOptimizeMapper.materialStoreOptimizeUse(projectNumber)); |
| | | map.put("edgeTrimming", null); |
| | | map.put("state", Integer.parseInt(stringObjectMap.get("state").toString())); |
| | | return map; |
| | | } |
| | | |
| | | //工程信息流程卡 |
| | | public Map<String, Object> getProcessCardMpThirdParty(String projectNo) { |
| | | Map<String, Object> map = new HashMap<>(); |
| | | map.put("data", glassOptimizeMapper.getProcessCardMpThirdParty(projectNo)); |
| | | return map; |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | } |
| | |
| | | fc.process_id, |
| | | fc.order_number, |
| | | fc.quantity, |
| | | CONCAT(fc.technology_number, '/', COUNT(fc.technology_number)) AS layer, |
| | | CONCAT(fc.technology_number, '/', COUNT(fc.layers_number)) AS layer, |
| | | so.project, |
| | | CONCAT(sd.child_width, ' × ', sd.child_height) AS sizes |
| | | FROM |
| | |
| | | where project_no = #{projectNumber} |
| | | </delete> |
| | | |
| | | <select id="selectOptimizeDetailById"> |
| | | select * from pp.optimize_detail |
| | | where project_no = #{projectNumber} and process_id= #{processId} and layer= #{layer} and order_sort= #{orderSort} |
| | | and heat_layout_id is null limit 0,1; |
| | | </select> |
| | | |
| | | <select id="selectOptimizeHeatDetail"> |
| | | select * from pp.optimize_heat_detail |
| | | where project_no = #{projectNumber} |
| | | </select> |
| | | |
| | | <select id="getProjectListMp"> |
| | | SELECT |
| | | p.id, |
| | |
| | | |
| | | |
| | | <insert id="addSimulation" parameterType="map"> |
| | | <foreach collection="projectdetail.data[0].glass_details" item="glass"> |
| | | <foreach collection="projectdetail" item="glass"> |
| | | INSERT INTO pp.optimize_heat_detail ( |
| | | project_no, |
| | | process_id, |
| | |
| | | </insert> |
| | | <!--模拟计算结果保存--> |
| | | <insert id="addratioResult" parameterType="map"> |
| | | <foreach collection="projectdetail.data[0].ratioResult" item="glass"> |
| | | <foreach collection="ratioResult" item="glass"> |
| | | INSERT INTO pp.optimize_heat_layout ( |
| | | project_no, |
| | | layout_id, |
| | |
| | | UPDATE pp.optimize_project |
| | | SET |
| | | state = 10, |
| | | furnaces_qty=#{resultSum[0]}, |
| | | load_rate=#{resultSum[1]}, |
| | | chaos_pct = #{inputValues.chaos_pct}, |
| | | max_load_pct = #{inputValues.max_load_pct}, |
| | | max_area=#{inputValues.max_area}, |
| | |
| | | load_width=#{inputValues.load_width}, |
| | | load_length=#{inputValues.load_length}, |
| | | x_space=#{inputValues.x_space}, |
| | | y_space=#{inputValues.y_space}, |
| | | load_rate=#{inputValues.load_rate} |
| | | y_space=#{inputValues.y_space} |
| | | WHERE |
| | | project_no = #{inputValues.project_no} |
| | | |
| | |
| | | update pp.optimize_project_file as u |
| | | set u.content = #{jsonString} |
| | | where u.project_no=#{processId} and type='优化结果' |
| | | </update> |
| | | |
| | | <update id="updateOptimizeDetail"> |
| | | update pp.optimize_detail |
| | | set heat_layout_id=#{layoutId},heat_layout_sort=#{sort},glass_id=concat(process_id,'|',#{layoutId},'|',#{sort}) |
| | | where id=#{id}; |
| | | </update> |
| | | |
| | | |
| | |
| | | c.process_id, |
| | | c.order_number |
| | | </select> |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | <!--模拟计算查询流程卡第三方--> |
| | | <select id="selectProjectComputeMpThirdParty"> |
| | | select |
| | | process_id as processId, |
| | | layer as technologyNumber, |
| | | total_layer as total_layers, |
| | | filmsid as TotalNumber, |
| | | sum(glass_total) as total_num, |
| | | product_name as glass_child, |
| | | 0 as tempering, |
| | | 0 as allow_rotate, |
| | | 0 as curtain_wall, |
| | | 0 as priority_level, |
| | | 1 as is_must |
| | | from pp.other_flow_card where project_no = #{projectNo} group by process_id,layer |
| | | </select> |
| | | |
| | | <!--模拟计算流程卡详情第三方--> |
| | | <select id="selectComputeDetailMpThirdParty"> |
| | | select |
| | | process_id as process_id, |
| | | width as width, |
| | | height as height, |
| | | width as maxwidth, |
| | | height as maxheight, |
| | | order_number as order_number, |
| | | glass_total as quantity, |
| | | layer as technology_number, |
| | | 0 as patch_state |
| | | from pp.other_flow_card where process_id = #{processId} and layer=#{technologyNumber} |
| | | </select> |
| | | |
| | | <!--第一次优化查询--> |
| | | <select id="firstOptimizationThirdParty"> |
| | | select |
| | | project_no, |
| | | concat( process_id, '-', layer ) AS process_id, |
| | | width, |
| | | height, |
| | | layer, |
| | | glass_total as quantity, |
| | | product_name, |
| | | order_number |
| | | from pp.other_flow_card where project_no = #{projectNo} |
| | | </select> |
| | | |
| | | <select id="analogComputationOptimizationThirdParty"> |
| | | SELECT |
| | | h.project_no, |
| | | h.layout_id, |
| | | h.width AS width, |
| | | h.height AS height, |
| | | count( 1 ) AS quantity, |
| | | concat( h.process_id, '-', h.layer ) AS 'process_id', |
| | | h.layer, |
| | | c.order_number |
| | | from |
| | | pp.`optimize_heat_detail` h |
| | | LEFT JOIN pp.other_flow_card c ON h.process_id = c.process_id |
| | | AND h.layer = c.technology_number |
| | | AND h.order_sort = c.order_number |
| | | WHERE |
| | | h.project_no = #{projectNo} |
| | | GROUP BY |
| | | h.project_no, |
| | | h.layout_id, |
| | | h.width, |
| | | h.height, |
| | | h.process_id, |
| | | h.layer, |
| | | c.quantity, |
| | | c.order_number |
| | | ORDER BY |
| | | LENGTH( h.layout_id ), |
| | | h.layout_id; |
| | | </select> |
| | | |
| | | <!--工程信息流程卡--> |
| | | <select id="getProcessCardMpThirdParty"> |
| | | SELECT |
| | | fc.process_id, |
| | | fc.order_number, |
| | | fc.glass_total as quantity, |
| | | CONCAT(fc.layer, '/', COUNT(fc.total_layer)) AS layer, |
| | | CONCAT(fc.width, ' × ', fc.height) AS sizes |
| | | FROM |
| | | pp.other_flow_card AS fc |
| | | WHERE |
| | | fc.project_no = #{projectNo} |
| | | GROUP BY |
| | | fc.layer, |
| | | fc.order_number |
| | | </select> |
| | | |
| | | <delete id="deleteOptimizeDetailThirdParty"> |
| | | delete from pp.other_flow_card |
| | | where project_no = #{projectNumber} |
| | | </delete> |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | </mapper> |