Browse Source

拒绝打野

zhangafei 2 weeks ago
parent
commit
8a2538ea17

+ 4 - 4
.env.development

@@ -7,15 +7,15 @@ VITE_PUBLIC_PATH = /
 # 跨域代理,您可以配置多个 ,请注意,没有换行符
 # VITE_PROXY = [["/jeecgboot","http://192.168.1.53:9999"],["/upload","http://localhost:3300/upload"]]
 # VITE_PROXY = [["/jeecgboot","http://192.168.1.6:9999"],["/upload","http://localhost:3300/upload"]]
-VITE_PROXY = [["/jeecgboot","http://192.168.0.119:9999"],["/upload","http://localhost:3300/upload"]]
-# VITE_PROXY = [["/jeecgboot","http://123.57.213.14:9898"],["/upload","http://localhost:3300/upload"]]
+# VITE_PROXY = [["/jeecgboot","http://192.168.0.119:9999"],["/upload","http://localhost:3300/upload"]]
+VITE_PROXY = [["/jeecgboot","http://123.57.213.14:9898"],["/upload","http://localhost:3300/upload"]]
 
 #后台接口全路径地址(必填)
 # VITE_GLOB_DOMAIN_URL=http://localhost:9999
 # VITE_GLOB_DOMAIN_URL=http://192.168.1.6:9999
 # VITE_GLOB_DOMAIN_URL=http://192.168.1.53:9999
-VITE_GLOB_DOMAIN_URL=http://192.168.0.119:9999
-# VITE_GLOB_DOMAIN_URL=http://123.57.213.14:9898
+# VITE_GLOB_DOMAIN_URL=http://192.168.0.119:9999
+VITE_GLOB_DOMAIN_URL=http://123.57.213.14:9898
 
 
 

+ 12 - 0
src/router/routes/index.ts

@@ -166,6 +166,17 @@ export const operatorPage: AppRouteRecordRaw = {
     ignoreAuth: true,
   },
 };
+
+// 推钢室操作页面
+export const operatorRealPage: AppRouteRecordRaw = {
+  path: '/operatorReal/:ccmNo',
+  name: 'OperatorRealPage',
+  component: () => import('/@/views/billet/operator/indexReal.vue'),
+  meta: {
+    title: t('推钢室实时界面'),
+    ignoreAuth: true,
+  },
+};
 // Basic routing without permission
 export const basicRoutes = [
   LoginRoute,
@@ -185,4 +196,5 @@ export const basicRoutes = [
   carBoard,
   carMonitor,
   operatorPage,
+  operatorRealPage,
 ];

+ 8 - 4
src/views/billet/Dashboard/rollingOne.vue

@@ -39,7 +39,7 @@
                     <div>明细:</div>
                     <div class="dtl flex-1 flex flex-wrap">
                       <div class="flex-dlt-item flex" v-for="ele in item.statisticsDetailsList">
-                        <div class="flex dtl-item bt-line">
+                        <div class="flex dtl-item bt-line" :style="{ color: sizeColor[ele.size] }">
                           <span class="nums">{{ ele.size }}:&nbsp;&nbsp;&nbsp;{{ ele.nums }} 支</span>
                         </div>
                         <div class="flex dtl-item">
@@ -97,7 +97,7 @@
                   <div
                     class="line"
                     :class="[item.sizeValue !== ele.value ? 'noprint' : 'selected']"
-                    style="cursor: pointer"
+                    :style="{ cursor: 'pointer', color: sizeColor[ele.size] }"
                     v-for="ele in item.sizeObj"
                     @click="
                       () => {
@@ -114,7 +114,7 @@
                   <div
                     class="line"
                     :class="[item.sizeValue !== ele.value ? 'noprint' : 'selected']"
-                    style="cursor: pointer"
+                    :style="{ cursor: 'pointer', color: sizeColor[ele.size] }"
                     v-for="ele in item.sizeObj"
                     @click="
                       () => {
@@ -196,6 +196,7 @@
   import { list } from '../ShiftPerformance/ShiftPerformance.api';
   import { getTeamShift } from '../Dashboard/dashboard.api';
   import { useMethods } from '/@/hooks/system/useMethods';
+  import { Colors } from '/@/utils/dict/DictColors';
 
   // 导入导出方法
   const { handleExportXlsx } = useMethods();
@@ -436,6 +437,7 @@
     });
   };
 
+  const sizeColor = ref({});
   // 获取统计信息
   /**
    * ccmNo、changeShiftId、queryDate、queryType(1、2、3、4)代表棒一、棒二、棒三、上若,  就这四个参数,ccmNo、queryType  必输,changeShiftId、queryDate(2025-06-24)非必输,
@@ -458,7 +460,9 @@
         5: { blankOutput: 0, nums: 0 },
       };
       let statisticsDetailsList5: any[] = [];
-      res.billetStatisticList.forEach((element) => {
+
+      res.billetStatisticList.forEach((element, index) => {
+        sizeColor.value[element.size] = Colors[index] ? Colors[index][0] : '#f50';
         statisticsDetailsList5.push({ ...element, nums: element.amountTotal, blankOutput: element.blankOutput.toFixed(4) });
         totalObj[5].blankOutput += element.blankOutput;
         totalObj[5].nums += element.amountTotal;

+ 1 - 1
src/views/billet/operator/components/car - 副本.vue → src/views/billet/operator/components/car-copy.vue

@@ -212,7 +212,7 @@
     }
   };
 
-  const { start, stop } = useTimeoutFn(getInfo, 10000, true);
+  const { start, stop } = useTimeoutFn(getInfo, 10000);
 
   // 获取堆垛机堆垛信息
   // 获取当前堆垛信息

+ 4 - 3
src/views/billet/operator/components/car.vue

@@ -3,8 +3,9 @@
     <div type="card">
       <div :key="item" v-for="item in carPosition[ccmNo]">
         <div class="car-wei" :class="`car-wei-${item}`">
-          <span class="num">{{ '车位' + item }}</span></div
-        >
+          <span class="num">{{ '车位' + item }}</span>
+          <!-- <a-button size="large" type="primary" style="font-size: 18px; margin-left: 6px">创建装运单</a-button> -->
+        </div>
         <div class="car-info-wrapper">
           <div class="licensePlate"
             >装运车辆:{{ vehicleInfo['info' + item] && vehicleInfo['info' + item].licensePlate ? vehicleInfo['info' + item].licensePlate : '' }}</div
@@ -48,7 +49,7 @@
       </div>
       <!-- <a-tab-pane key="2" tab="车位2" force-render>Content of Tab Pane 2</a-tab-pane> -->
     </div>
-    <div class="flex justify-between refresh-wrapper" :style="{ left: carPosition[ccmNo].length * 70 + 20 + 'px' }">
+    <div class="flex justify-between refresh-wrapper" :style="{ left: carPosition[ccmNo].length * 70 + 70 + 'px' }">
       <a-button size="large" type="primary" style="font-size: 18px" @click="refresh(1)"> 刷新 </a-button>
       <RouterLink :to="'/shippingBill/' + ccmNo" target="_blank">
         <a-button size="large" type="primary" style="font-size: 18px"> 装运列表 </a-button>

+ 1 - 1
src/views/billet/operator/components/headTop - 副本.vue → src/views/billet/operator/components/headTop-copy.vue

@@ -281,7 +281,7 @@
     }
   };
 
-  const { start, stop } = useTimeoutFn(getInfo, 5000, true);
+  const { start, stop } = useTimeoutFn(getInfo, 5000);
 
   // 换炉
   const changeHeatLoading = ref(false);

+ 2 - 2
src/views/billet/operator/components/heatList.vue

@@ -824,8 +824,8 @@
 
   const { start, stop } = useTimeoutFn(async () => {
     if (!props.changeShiftId) {
-      // await reload();
-      // start();
+      await reload();
+      start();
     }
   }, 10000);
 

+ 4 - 4
src/views/billet/operator/components/orgData.vue

@@ -1183,13 +1183,13 @@
 
     // 计算补的数量是不是4的倍数
     let stackInfo: any = null;
-    const buCount = lengthGroupCount > -1 ? val - obj[lengthGroupCount].stackingCount : val;
-    if (buCount && buCount > 0) {
-      if (buCount % 4 !== 0) {
+    // const buCount = lengthGroupCount > -1 ? val - obj[lengthGroupCount].stackingCount : val;
+    if (val && val > 0) {
+      if (val % 4 !== 0) {
         createMessage.error('补数量必须是4的倍数');
         return;
       }
-      stackInfo = buCount + '-' + size + '-10';
+      stackInfo = val + '-' + size + '-10';
     } else {
       stackInfo = null;
     }

+ 5 - 4
src/views/billet/operator/components/printOriginalRecords.vue

@@ -1287,13 +1287,13 @@
 
     // 计算补的数量是不是4的倍数
     let stackInfo: any = null;
-    const buCount = lengthGroupCount > -1 ? val - obj[lengthGroupCount].stackingCount : val;
-    if (buCount && buCount > 0) {
-      if (buCount % 4 !== 0) {
+    // const buCount = lengthGroupCount > -1 ? val - obj[lengthGroupCount].stackingCount : val;
+    if (val && val > 0) {
+      if (val % 4 !== 0) {
         createMessage.error('补数量必须是4的倍数');
         return;
       }
-      stackInfo = buCount + '-' + size + '-10';
+      stackInfo = val + '-' + size + '-10';
     } else {
       stackInfo = null;
     }
@@ -1315,6 +1315,7 @@
       stackLength: JSON.stringify(obj),
       stackInfo,
     };
+
     const amount = handleTotalSum(params);
     handleEdit({
       ...params,

+ 189 - 0
src/views/billet/operator/indexReal.vue

@@ -0,0 +1,189 @@
+<template>
+  <div
+    class="operator-app-container"
+    :style="{
+      '--op-border-color': '#d9d9d9',
+      '--op-text-color-fff': '#fff',
+    }"
+  >
+    <a-layout class="operator-large-layout">
+      <a-layout-header class="operator-header cover">
+        <div class="change-shift print-billet-card">
+          <a-button
+            type="primary"
+            style="margin-right: 20px"
+            size="large"
+            @click="() => openPrintOriginalRecordsModal(true, { ccmNo, queryType: '1', shiftText, curShiftInfo })"
+          >
+            原始记录
+          </a-button>
+          <a-button type="primary" size="large" @click="() => openModal(true, { ccmNo, queryType: '1', shiftText, curShiftInfo })"> 送样卡 </a-button>
+        </div>
+        <div class="shift-wrapper"> {{ shiftText }} </div>
+        <div class="change-shift">
+          <a-button type="primary" size="large" danger @click="() => (openJiaobanModal = true)"> 交班 </a-button>
+        </div>
+        <div class="clock-wrapper">
+          <div class="date">{{ clockObj.date }}</div>
+          <div class="week">{{ clockObj.week }}</div>
+          <div class="time">{{ clockObj.time }}</div>
+        </div>
+      </a-layout-header>
+      <a-layout-content class="operator-content">
+        <head-top @onShiftChange="curShiftChange" ref="headTopRef" @lengthChange="(v, auto) => ((lengthList = v), (cuttolength = auto))" />
+        <div class="operator-content-wrapper flex">
+          <div class="operator-content-left">
+            <heat-list
+              ref="heatListRef"
+              v-if="curShiftInfo.shift && curShiftInfo.shiftGroup"
+              :ccmNo="ccmNo"
+              :carRef="carRef"
+              :lengthList="lengthList"
+              :cuttolength="cuttolength"
+              @changeLengthSuccess="() => headTopRef && headTopRef.reload()"
+              :curShiftInfo="curShiftInfo"
+            />
+          </div>
+          <div class="operator-content-right">
+            <car :ccmNo="ccmNo" ref="carRef" />
+          </div>
+        </div>
+      </a-layout-content>
+    </a-layout>
+
+    <!-- 打印钢坯送样卡 -->
+    <print-billet-sample-card @register="registerPrintModal" />
+    <!-- 打印原始数据 -->
+    <printOriginalRecords @register="registerPrintOriginalRecordsModal" />
+    <!-- 交班 -->
+    <a-modal
+      v-model:open="openJiaobanModal"
+      title="切换牌号"
+      centered
+      width="400px"
+      ok-text="确认"
+      :okButtonProps="{ loading: okLoading }"
+      cancel-text="取消"
+      @ok="confirmChangeShift"
+      @cancel="
+        () => {
+          openJiaobanModal = false;
+          newShiftGroup = '';
+        }
+      "
+    >
+      <div class="flex justify-center items-center" style="margin: 20px 0">
+        <div>选择班组:</div>
+        <JSearchSelect type="list" style="width: 277px" v-model:value="newShiftGroup" dict="lg_bz" placeholder="请选择" allowClear />
+      </div>
+    </a-modal>
+  </div>
+</template>
+<script setup lang="ts" name="OperatorRoom">
+  import { onMounted, onUnmounted, ref } from 'vue';
+  import { getDateTimeWeek } from '/@/utils/dateUtil';
+  import { useTimeoutFn } from '/@/hooks/core/useTimeout';
+  import headTop from './components/headTop-copy.vue';
+  import car from './components/car-copy.vue';
+  import heatList from './components/heatList.vue';
+  import { getMachineNum, getMachineDict } from '../hotDelivery/common.data';
+  import { changeShift } from '../hotDelivery/hotDelivery.api';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import { useModal } from '/@/components/Modal';
+  import printBilletSampleCard from './components/printBilletSampleCard.vue';
+  import printOriginalRecords from './components/printOriginalRecords.vue';
+  import JSearchSelect from '/@/components/Form/src/jeecg/components/JSearchSelect.vue';
+
+  const { createConfirm, createMessage } = useMessage();
+
+  // 注册打印送样卡modal
+  const [registerPrintModal, { openModal }] = useModal();
+  // 注册打印原始记录modal
+  const [registerPrintOriginalRecordsModal, { openModal: openPrintOriginalRecordsModal }] = useModal();
+
+  const ccmNo = getMachineNum();
+
+  const carRef = ref();
+  const headTopRef = ref();
+  const heatListRef = ref();
+  // 班次信息
+  const shiftText = ref('');
+  const curShiftInfo = ref({
+    shift: undefined,
+    shiftGroup: undefined,
+  });
+  // 时钟
+  const clockObj = ref({
+    date: '',
+    week: '',
+    time: '',
+  });
+
+  const lengthList = ref('');
+  const cuttolength = ref(2); // 1 手动定尺, 2 自动定尺
+
+  const clock = () => {
+    try {
+      const dateStr = getDateTimeWeek().split(' ');
+      clockObj.value = {
+        date: dateStr[0],
+        time: dateStr[1],
+        week: dateStr[2],
+      };
+
+      start();
+    } catch (error) {}
+  };
+
+  const { start, stop } = useTimeoutFn(clock, 1000);
+
+  // 获取班次信息
+  const curShiftChange = (shiftInfo) => {
+    if (shiftInfo) {
+      const { shift, shiftGroup } = shiftInfo;
+      shiftText.value = `${getMachineDict(shift, 'lg_bb')} - ${getMachineDict(shiftGroup, 'lg_bz')}`;
+    }
+    curShiftInfo.value = shiftInfo;
+  };
+
+  // 交班
+  const newShiftGroup = ref('');
+  const openJiaobanModal = ref(false);
+  const okLoading = ref(false);
+  const confirmChangeShift = () => {
+    if (!newShiftGroup.value) {
+      createMessage.error('请选择班组');
+      return;
+    }
+
+    createConfirm({
+      iconType: 'warning',
+      title: '交班确认',
+      content: '确定要交班吗?',
+      onOk: () => {
+        return changeShift({
+          ccmNo,
+          shiftGroup: newShiftGroup.value,
+        }).then(() => {
+          headTopRef && headTopRef.value.reload();
+          carRef && carRef.value.refreshCarList();
+          heatListRef && heatListRef.value.reload();
+
+          openJiaobanModal.value = false;
+        });
+      },
+    });
+  };
+
+  onMounted(() => {
+    start();
+  });
+
+  onUnmounted(() => {
+    stop();
+  });
+</script>
+<style lang="less" scoped>
+  @import '/@/assets/less/font.less';
+  @import './index.less';
+</style>

+ 635 - 0
src/views/billet/quality/index.vue

@@ -0,0 +1,635 @@
+<template>
+  <div class="quality-wrapper flex flex-col">
+    <div class="search-wrapper">
+      <BasicForm class="search-form" @register="registerForm">
+        <template #ccmNo="{ model, field }">
+          <segmented-select
+            v-model:value="model[field]"
+            @change="
+              (v) => {
+                ccmNo = v;
+                getList();
+              }
+            "
+            dict="lg_zj"
+          />
+        </template>
+        <template #shiftObj>
+          <div class="shift-performance-tags flex-1">
+            <a-tag
+              :color="shiftColor[index]"
+              @click="
+                () => {
+                  currentShift = index;
+                  getList();
+                }
+              "
+              v-for="(item, index) in shiftPerformanceColumns"
+            >
+              {{ item.createTime ? item.createTime.substring(5, 16) : '' }} ~
+              {{ item.changeShiftTime ? item.changeShiftTime.substring(5, 16) : '当前时间' }}
+              【{{ getTeamShift(item.shift, item.shiftGroup) }}】
+              <span class="current-shift" :style="{ background: shiftColor[index] }" v-if="index === currentShift"></span>
+            </a-tag>
+          </div>
+        </template>
+
+        <template #advanceBefore>
+          <div class="flex items-center">
+            <a-button
+              size="large"
+              type="primary"
+              @click="
+                () => {
+                  switchMiopen = true;
+                  switchMiNum = 0;
+                }
+              "
+            >
+              切换米重</a-button
+            >
+            <a-button
+              style="margin-left: 10px"
+              size="large"
+              type="primary"
+              @click="
+                () =>
+                  shiftPerformanceColumns[currentShift] &&
+                  openPrintOriginalRecordsModal(true, {
+                    ccmNo: ccmNo,
+                    queryType: shiftPerformanceColumns[currentShift] && shiftPerformanceColumns[currentShift].changeShiftTime ? '2' : '1',
+                    shiftText: '',
+                    time:
+                      shiftPerformanceColumns[currentShift] && shiftPerformanceColumns[currentShift].changeShiftTime
+                        ? dayjs(shiftPerformanceColumns[currentShift].changeShiftTime).subtract(3, 'hour').format('YYYY-MM-DD HH:mm:ss')
+                        : '',
+                    curShiftInfo: {
+                      shift: shiftPerformanceColumns[currentShift] ? shiftPerformanceColumns[currentShift].shift : '',
+                      shiftGroup: shiftPerformanceColumns[currentShift] ? shiftPerformanceColumns[currentShift].shiftGroup : '',
+                    },
+                    changeShiftId: shiftPerformanceColumns[currentShift] ? shiftPerformanceColumns[currentShift].id : '',
+                  })
+              "
+            >
+              原始记录</a-button
+            >
+            <a-button
+              style="margin-left: 10px"
+              size="large"
+              type="primary"
+              @click="
+                () => {
+                  confirmedWithoutErrorOpen = true;
+                }
+              "
+            >
+              确认无误</a-button
+            >
+          </div>
+        </template>
+      </BasicForm>
+    </div>
+
+    <BasicTable @register="registerTable">
+      <template #tableTitle>
+        <div class="flex table-title-info">
+          <div class="table-title-info-item flex">
+            <span class="line_b">{{ ccmNo }}</span>
+            <span>#机</span>
+          </div>
+          <div class="table-title-info-item flex"><span>质检员</span><span class="line_b"></span></div>
+          <div class="table-title-info-item flex flex-1 align-center justify-center">
+            <span class="line_b">{{ getSelectedDate[0] }}</span>
+            <span>年</span>
+            <span class="line_b">{{ getSelectedDate[1] }}</span>
+            <span>月</span>
+            <span class="line_b">{{ getSelectedDate[2] }}</span>
+            <span>日</span>
+            <span class="line_b">{{
+              shiftPerformanceColumns[currentShift]
+                ? getTeamShift(shiftPerformanceColumns[currentShift].shift, shiftPerformanceColumns[currentShift].shiftGroup)
+                : ''
+            }}</span>
+          </div>
+
+          <div class="table-title-info-item flex"><span>zj/R19</span></div>
+        </div>
+      </template>
+      <template #footer>
+        <div class="flex footer-tj">
+          <a-descriptions
+            size="small"
+            bordered
+            class="remark"
+            :labelStyle="labelStyle"
+            :contentStyle="{
+              padding: 0,
+            }"
+          >
+            <a-descriptions-item label="备注:">
+              <a-textarea :bordered="false" @blur="onTotalNoteBlur" v-model:value="statisticsInfo.remark" style="height: 116px" placeholder="备注" />
+            </a-descriptions-item>
+          </a-descriptions>
+          <a-descriptions size="small" bordered class="produce" :labelStyle="labelStyle">
+            <a-descriptions-item label="本班冶炼:"> {{ statisticsInfo.classHeatNum }} 炉</a-descriptions-item>
+            <a-descriptions-item label="止上班日累计:">{{ statisticsInfo.dayStartHeatCount }} 炉</a-descriptions-item>
+            <a-descriptions-item label="止本班日累计:"> {{ statisticsInfo.dayEndHeatCount }} 炉</a-descriptions-item>
+
+            <a-descriptions-item label="本班冶炼:"> {{ statisticsInfo.classTotalWeight }} 吨</a-descriptions-item>
+            <a-descriptions-item label="止上班日累计:">{{ statisticsInfo.dayStartWeight }} 吨</a-descriptions-item>
+            <a-descriptions-item label="止本班日累计:">{{ statisticsInfo.dayEndWeight }} 吨</a-descriptions-item>
+
+            <a-descriptions-item label="日冶炼:">{{ statisticsInfo.dayHeatCount }} 炉</a-descriptions-item>
+            <a-descriptions-item label="日产:">{{ statisticsInfo.dayEndWeight }} 吨</a-descriptions-item>
+            <a-descriptions-item> </a-descriptions-item>
+          </a-descriptions>
+        </div>
+      </template>
+    </BasicTable>
+  </div>
+  <!-- 打印原始数据 -->
+  <printOriginalRecords @register="registerPrintOriginalRecordsModal" />
+  <!-- 确认无误 -->
+  <a-modal v-model:open="confirmedWithoutErrorOpen" title="确认信息" ok-text="确认" cancel-text="取消" @ok="onTotalNoteBlur(1)">
+    <div style="margin: 20px">
+      <a-textarea v-model:value="statisticsInfo.remark" placeholder="备注" :auto-size="{ minRows: 2, maxRows: 5 }" />
+    </div>
+  </a-modal>
+  <!-- 编辑备注 -->
+  <a-modal v-model:open="editNotesOpen" :title="'备注【' + editNotesRecord.heatNo + '】'" ok-text="确认" cancel-text="取消" @ok="editNotesSubmit">
+    <div style="margin: 20px">
+      <a-textarea v-model:value="editNotesTxt" placeholder="备注" :auto-size="{ minRows: 2, maxRows: 5 }" />
+    </div>
+  </a-modal>
+  <!-- 切换米重 -->
+  <a-modal v-model:open="switchMiopen" title="切换米重" ok-text="确认" cancel-text="取消" @ok="switchMiSubmit">
+    <div style="margin: 20px">
+      <a-input-number v-model:value="switchMiNum" placeholder="米重" :min="0" />
+    </div>
+  </a-modal>
+</template>
+<script setup lang="ts">
+  import { computed, onMounted, ref } from 'vue';
+  import { useForm, BasicForm } from '/@/components/Form';
+  import SegmentedSelect from '/@/components/SegmentedSelect/index.vue';
+  import { BasicTable } from '/@/components/Table';
+  import dayjs, { Dayjs } from 'dayjs';
+  import { useListPage } from '/@/hooks/system/useListPage';
+  import { getFormSchemas, getTableColumns } from './quality.data';
+  import { getTeamShift } from '../Dashboard/dashboard.api';
+  import { list } from '../ShiftPerformance/ShiftPerformance.api';
+  import { getQualityInspection, updateInfo, confirmRecordNote, changeMeterWeight } from './quality.api';
+  import { useModal } from '/@/components/Modal';
+  import printOriginalRecords from '../operator/components/printOriginalRecords.vue';
+  import { useMessage } from '/@/hooks/web/useMessage';
+
+  const { createMessage } = useMessage();
+
+  // 注册打印原始记录modal
+  const [registerPrintOriginalRecordsModal, { openModal: openPrintOriginalRecordsModal }] = useModal();
+
+  const shiftPerformanceColumns = ref<any>([]);
+  const shiftColor = ['#f50', '#2db7f5', '#87d068'];
+  const currentShift = ref(-1);
+  const ccmNo = ref('5');
+  const labelStyle = {
+    width: '100px',
+    paddingRight: 0,
+    paddingLeft: 0,
+    textAlign: 'center',
+  };
+  /**
+   * BasicForm绑定注册;
+   */
+  const [registerForm, { getFieldsValue }] = useForm({
+    //注册表单列
+    schemas: getFormSchemas({
+      onDateChange: (v) => {
+        currentShift.value = -1;
+        getShiftInfo(3, dayjs(v));
+      },
+    }),
+    //是否显示展开收起按钮,默认false
+    showAdvancedButton: false,
+    showResetButton: false,
+    showSubmitButton: false,
+    compact: true,
+    //超过指定行数折叠,默认3行
+    autoAdvancedCol: 3,
+    //折叠时默认显示行数,默认1行
+    alwaysShowLines: 3,
+    //将表单内时间区域的值映射成 2个字段, 'YYYY-MM-DD'日期格式化
+    fieldMapToTime: [
+      ['arrivalTime', ['startTime', 'endTime'], 'YYYY-MM-DD HH:mm:ss'],
+      // ['storageTime', ['storageTime_begin', 'storageTime_end'], 'YYYY-MM-DD HH:mm:ss'],
+    ],
+    //每列占比,默认一行为24
+    baseColProps: { span: 6 },
+  });
+
+  // 获取日期
+  const getSelectedDate = computed(() => {
+    const formVals = getFieldsValue();
+    if (formVals && formVals.queryDate) {
+      return formVals.queryDate.split('-');
+    }
+
+    return dayjs().format('YYYY-MM-DD').split('-');
+  });
+
+  const columns = getTableColumns({
+    onTimeChange(date, record) {
+      editInfo({
+        originalProductRecordId: record.originalProductRecordId,
+        deliveryTime: date.format('YYYY-MM-DD HH:mm:ss'),
+      });
+    },
+    onClickRemark(record) {
+      editNotesOpen.value = true;
+      editNotesTxt.value = record.notes;
+      editNotesRecord.value = record;
+    },
+    onBrandNumChange(v, record) {
+      editInfo({
+        originalProductRecordId: record.originalProductRecordId,
+        brandNum: v,
+      });
+    },
+  });
+
+  //注册table数据
+  const { tableContext } = useListPage({
+    tableProps: {
+      columns,
+      canResize: false,
+      showActionColumn: false,
+      useSearchForm: false,
+      showTableSetting: false,
+      striped: true,
+      pagination: false,
+      size: 'small',
+    },
+  });
+
+  const [registerTable, { setLoading, setTableData, getColumns, setColumns }, {}] = tableContext;
+
+  const getDefaulData = (total = 30) => {
+    let data: any[] = [];
+    for (let i = 0; i < total; i++) {
+      data.push({
+        heatNo: '',
+        brandNum: '',
+        deliveryTime: '',
+        originalProductRecordId: '',
+        lengthCountMap: {},
+      });
+    }
+
+    return data;
+  };
+
+  const statisticsInfo = ref<any>({});
+  const getList = async () => {
+    try {
+      setLoading(true);
+      const res = await getQualityInspection({
+        ccmNo: ccmNo.value,
+        changeShiftId: shiftPerformanceColumns.value[currentShift.value].id,
+      });
+
+      const { records, statistics } = res;
+      statisticsInfo.value = { ...statistics, orgRemark: statistics.remark };
+
+      let sizeArr: string[] = [];
+      records.forEach((item: any) => {
+        if (item.lengthCountMap && typeof item.lengthCountMap === 'object') {
+          sizeArr = sizeArr.concat(Object.keys(item.lengthCountMap));
+        }
+      });
+
+      // 班产统计
+      const workLength = JSON.parse(statistics.classLengthCountWeight ? statistics.classLengthCountWeight : '{}');
+      const workLengthKeys = Object.keys(workLength);
+      const workLengthArr: any[] = [];
+      if (workLengthKeys.length) {
+        workLengthKeys.forEach((item) => {
+          const numW = workLength[item].split('/');
+          workLengthArr.push({
+            size: Number(item) / 1000 + 'm',
+            num: numW[0],
+            weight: numW[1],
+          });
+        });
+      }
+
+      // 日产统计
+      const dayLength = JSON.parse(statistics.dayLengthCountWeight ? statistics.dayLengthCountWeight : '{}');
+      const dayLengthKey: any[] = Object.keys(dayLength);
+      const dayLengthArr: any[] = [];
+      if (dayLengthKey.length) {
+        dayLengthKey.forEach((item: any) => {
+          const numW = dayLength[item].split('/');
+          dayLengthArr.push({
+            size: Number(item) / 1000 + 'm',
+            num: numW[0],
+            weight: numW[1],
+          });
+        });
+      }
+
+      let sizeArrChildren: any[] = [];
+      [...new Set(sizeArr)].forEach((item) => {
+        sizeArrChildren.push({
+          title: item + 'm',
+          dataIndex: ['lengthCountMap', item],
+          width: 60,
+          align: 'center',
+          customRender({ text }) {
+            return text || '';
+          },
+        });
+      });
+
+      const oldColumns = getColumns();
+      const sizeColumnIndex = oldColumns.findIndex((item) => item.dataIndex === 'size');
+      oldColumns[sizeColumnIndex].children = sizeArrChildren;
+
+      // 设计表格
+      const rowNums = records.length < 10 ? 10 : records.length;
+      const datas = getDefaulData(rowNums + 3).map((item, index) => {
+        return {
+          ...item,
+          ...(records[index] || {}),
+          dayLength: dayLengthArr[index] || '',
+          workLength: workLengthArr[index] || '',
+        };
+      });
+      setColumns(oldColumns);
+      setTableData(datas);
+      setLoading(false);
+    } catch (error) {
+      console.log(error);
+      setLoading(false);
+    }
+  };
+
+  // 编辑信息
+  const editInfo = async (params: any) => {
+    try {
+      setLoading(true);
+      await updateInfo(params);
+
+      getList();
+    } catch (error) {
+      console.log(error);
+      setLoading(false);
+    }
+  };
+
+  // 获取班次信息
+  const getShiftInfo = async (pageSize: number = 3, date?: Dayjs) => {
+    const formVals = getFieldsValue();
+    try {
+      if (date && !date.isValid()) {
+        shiftPerformanceColumns.value = [];
+        currentShift.value = -1;
+        return false;
+      }
+
+      setLoading(true);
+      const createTime_begin = date ? date.subtract(1, 'day').format('YYYY-MM-DD 23:55:00') : undefined;
+      const createTime_end = date ? date.add(1, 'day').format('YYYY-MM-DD 00:05:00') : undefined;
+      const res = await list({
+        ccmNo: formVals.ccmNo || '5',
+        pageNo: 1,
+        pageSize: pageSize,
+        column: 'createTime',
+        order: 'asc',
+        ...(date ? { createTime_begin, createTime_end } : {}),
+      });
+      const { records } = res;
+
+      const arr = records || [];
+
+      shiftPerformanceColumns.value = arr
+        .filter((item) => item.createTime) // 过滤掉 createTime 为空的数据
+        .sort((a, b) => {
+          const dateA = new Date(a.createTime).getTime(); // 转换为时间戳
+          const dateB = new Date(b.createTime).getTime(); // 转换为时间戳
+          if (isNaN(dateA) || isNaN(dateB)) {
+            console.warn('Invalid date detected:', a.createTime, b.createTime);
+            return 0; // 如果日期无效,视为相等
+          }
+          return dateA - dateB; // 按时间戳排序
+        });
+      currentShift.value = shiftPerformanceColumns.value.length > 0 ? shiftPerformanceColumns.value.length - 1 : 0;
+
+      // 获取列表
+      getList();
+    } catch (error) {
+      console.error(error);
+      setLoading(false);
+    }
+  };
+
+  // 修改备注
+  const editNotesOpen = ref(false);
+  const editNotesTxt = ref('');
+  const editNotesRecord = ref({
+    heatNo: '',
+    originalProductRecordId: undefined,
+  });
+  const editNotesSubmit = async () => {
+    if (!editNotesTxt.value) {
+      createMessage.error('请填写备注');
+      return;
+    }
+
+    editInfo({
+      originalProductRecordId: editNotesRecord.value.originalProductRecordId,
+      notes: editNotesTxt.value,
+    });
+
+    editNotesOpen.value = false;
+  };
+
+  // 确认无误
+  const confirmedWithoutErrorOpen = ref(false);
+  // 编辑总备注
+  const onTotalNoteBlur = async (t?) => {
+    try {
+      if (t !== 1 && statisticsInfo.value.orgRemark === statisticsInfo.value.remark) {
+        return;
+      }
+      setLoading(true);
+
+      await confirmRecordNote({
+        id: statisticsInfo.value.id,
+        remark: statisticsInfo.value.remark, // 备注
+      });
+
+      getList();
+      confirmedWithoutErrorOpen.value = false;
+    } catch (error) {
+      setLoading(false);
+    }
+  };
+
+  // 切换米重
+  const switchMiopen = ref(false);
+  const switchMiNum = ref(0);
+  const switchMiSubmit = async () => {
+    try {
+      await changeMeterWeight({
+        ccmNo: ccmNo.value,
+        meterWeight: switchMiNum.value,
+      });
+
+      getList();
+      switchMiopen.value = false;
+    } catch (error) {
+      console.log(error);
+    }
+  };
+
+  onMounted(() => {
+    getShiftInfo(3, dayjs('2025-07-07'));
+  });
+</script>
+<style lang="less" scoped>
+  .quality-wrapper {
+    width: 100%;
+    height: 100%;
+    padding: 20px;
+    overflow: hidden;
+    background: var(--bg-s-color);
+
+    .search-wrapper {
+      margin-bottom: 10px;
+      background-color: #005baf;
+      padding: 20px 20px 0;
+      border-radius: 4px;
+
+      .search-form {
+        :deep(.btnArea) {
+          .ant-form-item-row {
+            width: 100%;
+            justify-content: flex-end;
+          }
+        }
+      }
+
+      :deep(.ant-form) {
+        .ant-form-item .ant-form-item-label > label {
+          color: #fff;
+        }
+
+        .ant-col-12 {
+          width: 100%;
+
+          .ant-form-item-row {
+            .ant-form-item-control {
+              flex: 1;
+              max-width: 100%;
+            }
+          }
+        }
+      }
+
+      .shift-performance-tags {
+        margin-left: 10px;
+
+        .ant-tag {
+          padding: 6px 6px;
+          position: relative;
+          cursor: pointer;
+          margin-bottom: 6px;
+          font-size: 14px;
+        }
+
+        .current-shift {
+          position: absolute;
+          display: inline-block;
+          width: 60px;
+          height: 4px;
+          border-radius: 4px;
+          bottom: -8px;
+          left: 50%;
+          transform: translateX(-30px);
+        }
+      }
+    }
+
+    .table-title-info {
+      width: 100%;
+      font-size: 16px;
+      font-weight: 600;
+      line-height: 24px;
+
+      .line_b {
+        display: inline-block;
+        min-width: 40px;
+        height: 24px;
+        text-align: center;
+        border-bottom: 1px solid #ebeef5;
+        padding: 0 10px;
+      }
+
+      .table-title-info-item {
+        margin-right: 20px;
+      }
+    }
+
+    .jeecg-basic-table {
+      :deep(.ant-table-footer) {
+        padding: 0;
+      }
+
+      :deep(.jeecg-basic-table-header__toolbar) {
+        width: 0;
+      }
+
+      :deep(.ant-picker) {
+        color: var(--fn-color);
+        padding: 0;
+
+        input {
+          color: var(--fn-color);
+        }
+      }
+
+      :deep(.ant-select) {
+        .ant-select-selection-item {
+          color: var(--fn-color);
+        }
+      }
+    }
+
+    .footer-tj {
+      background: var(--bg-color);
+      color: var(--fn-color);
+
+      .remark {
+        width: 400px;
+      }
+
+      .produce {
+        flex: 1;
+      }
+
+      :deep(.ant-descriptions-item-label),
+      :deep(.ant-input) {
+        color: var(--fn-color);
+      }
+
+      :deep(.ant-descriptions-item-content),
+      :deep(.ant-descriptions-item-label) {
+        border-inline-end: 1px solid var(--fn-color);
+      }
+
+      :deep(.ant-descriptions-row) {
+        border-bottom: 1px solid var(--fn-color);
+      }
+    }
+  }
+</style>

+ 32 - 0
src/views/billet/quality/quality.api.ts

@@ -0,0 +1,32 @@
+import { defHttp } from '/@/utils/http/axios';
+
+enum Api {
+  // 获取质检菜单
+  qualityInspectionMenu = '/billet/billetOriginalProductRecord/qualityInspectionMenu',
+  // 编辑
+  updateInfo = '/billet/billetOriginalProductRecord/updateInfo ',
+  // 编辑总备注
+  confirmRecordNote = '/qualityInspectionStatistics/qualityInspectionStatistics/confirmRecord',
+  // 切换米重
+  switchMiZhong = '/qualityInspectionStatistics/qualityInspectionStatistics/switch',
+}
+
+// 获取质检菜单
+export const getQualityInspection = (params) => {
+  return defHttp.get({ url: Api.qualityInspectionMenu, params }, { joinParamsToUrl: true });
+};
+
+// 编辑
+export const updateInfo = (params) => {
+  return defHttp.put({ url: Api.updateInfo, params });
+};
+
+// 编辑总备注
+export const confirmRecordNote = (params) => {
+  return defHttp.put({ url: Api.confirmRecordNote, params });
+};
+
+// 切换米重
+export const changeMeterWeight = (params) => {
+  return defHttp.post({ url: Api.switchMiZhong, params });
+};

+ 168 - 0
src/views/billet/quality/quality.data.ts

@@ -0,0 +1,168 @@
+import { BasicColumn } from '/@/components/Table';
+import { FormSchema } from '/@/components/Form';
+import dayjs from 'dayjs';
+import { h } from 'vue';
+import { DatePicker } from 'ant-design-vue';
+import JSearchSelect from '/@/components/Form/src/jeecg/components/JSearchSelect.vue';
+
+// 表格字段
+export const getTableColumns = ({ onTimeChange, onClickRemark, onBrandNumChange }): BasicColumn[] => [
+  {
+    title: '序号',
+    dataIndex: 'sIndex',
+    key: 'sIndex',
+    width: 40,
+    align: 'center',
+    customRender: function ({ index }) {
+      return parseInt(index) + 1;
+    },
+  },
+  {
+    title: '炉号',
+    dataIndex: 'heatNo',
+    key: 'heatNo',
+    width: 100,
+    align: 'center',
+  },
+  {
+    title: '送样时间',
+    dataIndex: 'deliveryTime',
+    key: 'deliveryTime',
+    width: 120,
+    align: 'center',
+    customRender({ text, record }) {
+      if (!record.heatNo) return '';
+      return h(DatePicker, {
+        bordered: false,
+        showTime: true,
+        defaultValue: dayjs(text),
+        onChange(date) {
+          onTimeChange && onTimeChange(date, record);
+        },
+      });
+    },
+  },
+  {
+    title: '牌号',
+    dataIndex: 'brandNum',
+    key: 'brandNum',
+    width: 90,
+    align: 'center',
+    customRender(opt) {
+      if (!opt.record.heatNo) return '';
+      return h(JSearchSelect, {
+        value: opt.text,
+        dict: 'billet_spec',
+        bordered: false,
+        dropdownStyle: {},
+        onChange(value) {
+          onBrandNumChange && onBrandNumChange(value, opt.record);
+        },
+      });
+    },
+  },
+  {
+    title: '班产 / 170 / 定尺',
+    dataIndex: 'size',
+    key: 'size',
+    width: 100,
+    align: 'center',
+  },
+  {
+    title: '班产',
+    dataIndex: 'workLength',
+    key: 'workLength',
+    width: 150,
+    align: 'left',
+    customRender({ text, record }) {
+      if (!record.heatNo) return '';
+      return h('div', {}, [
+        h('span', { style: { display: 'inline-block', width: '60px', color: '#f50' } }, text.size),
+        h('span', { style: { display: 'inline-block', width: '60px', color: '#108ee9' } }, text.num ? text.num + ' 支' : ''),
+        h('span', { style: { display: 'inline-block', color: '#87d068' } }, text.weight ? text.weight + ' 吨' : ''),
+      ]);
+    },
+  },
+  {
+    title: '日产',
+    dataIndex: 'dayLength',
+    key: 'dayLength',
+    width: 150,
+    align: 'left',
+    customRender({ text, record }) {
+      if (!record.heatNo) return '';
+      return h('div', {}, [
+        h('span', { style: { display: 'inline-block', width: '60px', color: '#f50' } }, text.size),
+        h('span', { style: { display: 'inline-block', width: '60px', color: '#108ee9' } }, text.num ? text.num + ' 支' : ''),
+        h('span', { style: { display: 'inline-block', color: '#87d068' } }, text.weight ? text.weight + ' 吨' : ''),
+      ]);
+    },
+  },
+  {
+    title: '备注',
+    dataIndex: 'notes',
+    key: 'notes',
+    width: 100,
+    align: 'center',
+    customRender(opt) {
+      if (!opt.record.heatNo) return '';
+      return h(
+        'div',
+        {
+          style: {
+            cursor: 'pointer',
+            width: '100%',
+            height: '32px',
+          },
+          onClick: () => {
+            onClickRemark && onClickRemark(opt.record, opt.text);
+          },
+        },
+        opt.text
+      );
+    },
+  },
+];
+
+// 自定义表单字段
+export const getFormSchemas = ({ onDateChange }) => {
+  return [
+    {
+      field: 'ccmNo',
+      label: '铸机',
+      component: 'Input',
+      defaultValue: '5',
+      componentProps: {
+        dictCode: 'lg_zj',
+      },
+      colProps: { span: 6 },
+      slot: 'ccmNo',
+    },
+    {
+      label: '',
+      field: 'zhanwei',
+      component: 'Input',
+      slot: 'zhanwei',
+      colProps: { span: 18 },
+    },
+    {
+      label: '班次日期',
+      field: 'queryDate',
+      component: 'DatePicker',
+      defaultValue: dayjs('2025-07-07'),
+      componentProps: {
+        valueFormat: 'YYYY-MM-DD',
+        onChange: (v) => {
+          onDateChange && onDateChange(v);
+        },
+      },
+    },
+    {
+      label: '',
+      field: 'shiftObj',
+      component: 'Input',
+      slot: 'shiftObj',
+      colProps: { span: 14 },
+    },
+  ] as FormSchema[];
+};

+ 1 - 0
src/views/billet/shippingBill/components/printModal.vue

@@ -224,6 +224,7 @@
         newRecord = res.records[0];
       }
 
+      headDtl.value = [];
       info.value = {
         ...newRecord,
         arrivalTimeDay: dayjs(record.arrivalTime),