快乐的昕的电脑
2025-11-05 dc34231c52f55ef844d6c1f93859c4b0e81283d5
components/WorkOrderPrint.vue
@@ -1,46 +1,97 @@
<template>
   <view class="page">
   <view class="page wide-layout" :class="{'has-overlay': (isShowUserSelect || isShow || barcodeIsShow)}">
      <view class="status-section">
         <!-- 报工记录表 -->
         <!-- 报工记录表部分,在标题行添加刷新按钮 -->
         <view class="report-table-wrapper">
            <view class="report-title">报工记录表</view>
            <table class="report-table">
               <thead>
                  <tr>
                     <th>时间</th>
                     <th>报工人</th>
                     <th>工单号</th>
                     <th>产品名称</th>
                     <th>计划生产数</th>
                     <th>机台号</th>
                     <th>初始采集数</th>
                     <th>报工时采集数</th>
                     <th>报工数(计算)</th>
                     <th>不良数</th>
                     <th>良品数(计算)</th>
                  </tr>
               </thead>
               <tbody>
                  <tr>
                     <td>{{ nowTime }}</td>
                     <td>{{ staffNo || '-' }}</td>
                     <td>{{ orderNo || '-' }}</td>
                     <td>{{ order.daa003 || '-' }}</td>
                     <td>{{ planQtyDisplay }}</td>
                     <td>{{ machineNo || '-' }}</td>
                     <td>{{ initialValue }}</td>
                     <td>{{ productionCount }}</td>
                     <td>{{ calculatedTotalProduction }}</td>
                     <td>{{ calculatedDefectiveCount }}</td>
                     <td>{{ sQuantity }}</td>
                  </tr>
               </tbody>
            </table>
            <view class="report-header">
               <view class="report-title">报工记录表</view>
               <view class="header-buttons">
                  <button class="refresh-btn-header" @click="refresh">刷新</button>
                  <button v-if="isGeneratingBarcode" class="reset-btn-header" @click="resetGenerateState">重置</button>
               </view>
            </view>
            <!-- 数采总产量 -->
            <view class="section-title">数采产量</view>
            <div class="table-scroll">
               <table class="report-table">
                  <thead>
                     <tr>
                        <th>时间</th>
                        <th>报工人</th>
                        <th>工单号</th>
                        <th>产品名称</th>
                        <th>计划生产数</th>
                        <th>机台号</th>
                        <th>初始采集数</th>
                        <th>报工时采集数</th>
                        <th>报工数(计算)</th>
                        <th>不良数</th>
                        <th>良品数(计算)</th>
                     </tr>
                  </thead>
                  <tbody>
                     <tr class="summary-row highlight-row">
                        <td>{{ nowTime }}</td>
                        <td>{{ staffDisplay || '-' }}</td>
                        <td>{{ orderNo || '-' }}</td>
                        <td>{{ order.daa003 || '-' }}</td>
                        <td>{{ planQtyDisplay }}</td>
                        <td>{{ machineNo || '-' }}</td>
                        <td>{{ order.initCjNum }}</td>
                        <td>{{ order.currentCjNum }}</td>
                        <td>{{ order.currentCjNum - order.initCjNum }}</td>
                        <td>{{ realTimeDefectiveCount }}</td>
                        <td>{{ realTimeOkCount }}</td>
                     </tr>
                  </tbody>
               </table>
            </div>
            <!-- 历史报工记录 -->
            <view class="section-title history-title">历史报工记录</view>
            <div class="table-scroll">
               <table class="report-table">
                  <thead>
                     <tr>
                        <th>时间</th>
                        <th>报工人</th>
                        <th>工单号</th>
                        <th>产品名称</th>
                        <th>计划生产数</th>
                        <th>机台号</th>
                        <th>初始采集数</th>
                        <th>报工时采集数</th>
                        <th>报工数(计算)</th>
                        <th>不良数</th>
                        <th>良品数(计算)</th>
                     </tr>
                  </thead>
                  <tbody>
                     <tr v-for="(r, idx) in reportingHistory" :key="idx">
                        <td>{{ r.bgDate }}</td>
                        <td>{{ r.staff || '-' }}</td>
                        <td>{{ r.orderNo || '-' }}</td>
                        <td>{{ order.daa003 || '-' }}</td>
                        <td>{{ planQtyDisplay }}</td>
                        <td>{{ r.machineNo || '-' }}</td>
                        <td>{{ r.initialValue }}</td>
                        <td>{{ r.productionCount }}</td>
                        <td>{{ r.totalProduction }}</td>
                        <td>{{ r.BfQty }}</td>
                        <td>{{ r.OkQty }}</td>
                     </tr>
                     <tr v-if="!reportingHistory.length">
                        <td colspan="11" class="no-data">暂无历史报工记录</td>
                     </tr>
                  </tbody>
               </table>
            </div>
         </view>
         <!-- 统计行(刷新按钮移到这里右侧) -->
         <!-- 移除了原来的状态行中的按钮组 -->
         <view class="status-row">
            <view class="status-box">
            <!--<view class="status-box">
               <text>机台面板数:</text>
               <input v-model="productionCount" class="highlight" disabled />
            </view>
@@ -61,66 +112,72 @@
            <view class="status-box result-box">
               <text>已生产数:</text>
               <input v-model="calculatedTotalProduction" class="highlight" disabled />
            </view>-->
         </view>
         <view class="flex-row gap-lg">
            <!-- 当前报工人部分 -->
            <view class="current-user-section">
               <text>当前报工人:</text>
               <text class="current-user-name">{{ staffDisplay || '未选择' }}</text>
               <button class="select-user-btn" @click="isShowUserSelect = true">选人</button>
            </view>
            <view class="btn-group">
               <button class="refresh-btn-inline" @click="refresh">刷新</button>
               <button v-if="isGeneratingBarcode" class="reset-btn-inline" @click="resetGenerateState">重置</button>
            <!-- 不良数量部分 -->
            <view class="defective-section">
               <text>不良数量:</text>
               <input v-model="customAmount" class="inp bad-input" placeholder="请输入数量" />
            </view>
            <!-- 确认提交按钮 -->
            <view class="submit-section">
               <button class="details-btn" @click="confirmCustomAmount">确认提交</button>
            </view>
         </view>
         <!-- 基础信息(已去掉 图号 / 材质 / 颜色) -->
         <view class="basic-info">
            <view class="form-row">
               <view class="form-item">
                  <label>产品编码:</label>
                  <input class="inp" type="text" v-model="order.daa002" disabled />
         <!-- 选人弹窗 -->
         <view v-if="isShowUserSelect" class="overlay">
            <view class="popup user-select-popup">
               <!-- 搜索栏 -->
               <view class="user-search-bar">
                  <input v-model.trim="userSearch"
                        type="text"
                        class="user-search-input"
                        placeholder="输入工号或姓名搜索"
                        @keydown.enter.prevent />
                  <button v-if="userSearch" class="user-search-clear" @click="userSearch=''">清空</button>
                  <view class="user-search-info">
                     匹配:{{ filteredUsers.length }} / {{ users.length }}
         </view>
               </view>
               <view class="form-item">
                  <label>产品名称:</label>
                  <superwei-combox :candidates="DAA003List" v-model="order.daa003"
                               @select="onDaa003Change" class="inp"></superwei-combox>
               <view class="user-list-scroll">
                  <template v-if="filteredUsers.length">
                     <view class="user-list-grid">
                        <button v-for="(u, index) in filteredUsers"
                              :key="index"
                              :class="['user-list-btn', {'selected': u===staffNo}]"
                              @click="selectUser(u)">
                           <span class="user-code">{{ u.split(':')[0] }}</span>
                           <span class="user-name">{{ u.split(':')[1] }}</span>
                        </button>
                     </view>
                  </template>
                  <view v-else class="no-user-result">
                     未找到匹配人员
                  </view>
               </view>
               <view class="form-item">
                  <label>产品规格:</label>
                  <input class="inp" type="text" v-model="order.daa004" disabled />
               <view class="user-popup-footer">
                  <button class="clean-btn wide-btn" @click="isShowUserSelect = false">关闭</button>
               </view>
            </view>
         </view>
         <!-- 不良数量 -->
         <view class="print-section" style="margin-top:10px;">
            <view class="barcode-info">
               <view class="user-select">
                  <text style="display:inline-block;float:left;">不良数量:</text>
                  <input v-model="customAmount" class="inp"
                        style="width:55%;height:60px;border:3px solid #808080;font-size:32px;text-align:center;margin-top:5px;"
                        placeholder="请输入数量" />
               </view>
               <view class="user-select" style="margin-left:30px;">
                  <button class="details-btn" @click="confirmCustomAmount">确认提交</button>
               </view>
            </view>
         </view>
         <!-- 报工人选择 -->
         <view>
            <view class="reason-section" style="margin-bottom:-10px">
               <text>报工人:</text>
               <view class="reason-buttons" style="font-size:20px;">
                  <button v-for="(u,index) in users" :key="index"
                        :class="{'reason-btn':true,'selected': staffNo===u}"
                        @click="toggleUser(u)" v-text="u"></button>
               </view>
            </view>
         </view>
         <!-- 底部按钮 -->
         <view class="bottom-section">
         <!-- 禁用按钮:‘保存并生效'、‘取消’ -->
         <!--<view class="bottom-section">
            <button class="save-btn" @click="save">保存并生效</button>
            <button class="cancel-btn" @click="cancel">取消</button>
         </view>
         <!-- 保留弹窗 -->
         </view>-->
         <!-- 保留旧弹窗 -->
         <view v-if="isShow" class="overlay">
            <view class="popup">
               <view class="bottom-section1">
@@ -131,7 +188,9 @@
                  <view class="reason-buttons">
                     <button v-for="(u,index) in users" :key="index"
                           :class="{'reason-btn':true,'selected': user===u}"
                           @click="toggleUser(u)" v-text="u"></button>
                           @click="toggleUser(u)">
                        {{ formatUser(u) }}
                     </button>
                  </view>
               </view>
            </view>
@@ -153,9 +212,7 @@
                     <uni-td align="center"><input v-model="item.okQty" /></uni-td>
                  </uni-tr>
               </uni-table>
               <view>
                  <button class="clean-btn" type="warn" @click="barcodeIsShow=false">关闭</button>
               </view>
               <view><button class="clean-btn" type="warn" @click="barcodeIsShow=false">关闭</button></view>
            </view>
         </view>
      </view>
@@ -169,61 +226,49 @@
      props: { orderNo: String, orderId: Number, machineNo: String },
      data() {
         return {
            isShowUserSelect: false,
            currentUser: '',
            barcodeAmount: '',
            users: [],
            userForm: [],
            staff: [],
            user: {},
            productionCount: 0,
            printedCount: 0,
            defectiveCount: 0,
            order: {},
            icount: 1,
            bqty: 0,
            sQuantity: 0,
            kgQty: 0,
            initialValue: 0,
            qqty: 0,
            ngStaid: 0,
            bufferData: '',
            dataToPrint: [],
            isLoading: false,
            but: false,
            DAA003List: [],
            lineList: [],
            isShow: false,
            barcodeIsShow: false,
            barcodeList: [],
            staffNo: '',
            printStr: '',
            printMac: '',
            bluetoothSocket: {},
            device: '',
            uuid: '',
            printNum: 1,
            reportingList: [],
            printLoading: false,
            customAmount: '',
            isGeneratingBarcode: false,
            lastGenerateTime: 0,
            generateRequestId: null,
            nowTimeTimer: null,
            nowTime: ''
         };
            users: [], userForm: [], staff: [], user: {},
            productionCount: 0, printedCount: 0, defectiveCount: 0, order: {},
            icount: 1, bqty: 0, sQuantity: 0, kgQty: 0, initialValue: 0, qqty: 0,
            ngStaid: 0, bufferData: '', dataToPrint: [], isLoading: false, but: false,
            DAA003List: [], lineList: [], isShow: false, barcodeIsShow: false, barcodeList: [],
            staffNo: '', printStr: '', printMac: '', bluetoothSocket: {}, device: '', uuid: '',
            printNum: 1, reportingList: [], printLoading: false, customAmount: '',
            isGeneratingBarcode: false, lastGenerateTime: 0, generateRequestId: null,
            nowTimeTimer: null, nowTime: '',
            userSearch: '',
            /* 新增:历史报工记录数组 */
            reportingHistory: []
         }
      },
      computed: {
         calculatedCurrentCount() {
            return (this.productionCount || 0) - (this.initialValue || 0);
         //良品数实时计算
         realTimeDefectiveCount() {
            // 优先用输入框的值,否则用接口数据
            const val = Number(this.customAmount);
            if (!isNaN(val) && this.customAmount !== '') return val;
            return this.calculatedDefectiveCount;
         },
         calculatedTotalProduction() {
            return (this.kgQty || 0) + this.calculatedCurrentCount;
         realTimeOkCount() {
            // 良品数 = 报工数(计算) - 不良数
            const total = (this.order.currentCjNum || 0) - (this.order.initCjNum || 0);
            return total - this.realTimeDefectiveCount;
         },
         calculatedDefectiveCount() {
            return this.calculatedTotalProduction - (this.sQuantity || 0);
         calculatedCurrentCount() { return (this.productionCount || 0) - (this.initialValue || 0); },
         calculatedTotalProduction() { return (this.kgQty || 0); }, // 若需恢复旧逻辑可用 (this.kgQty||0)+this.calculatedCurrentCount
         calculatedDefectiveCount() { return this.calculatedTotalProduction - (this.sQuantity || 0); },
         planQtyDisplay() { return this.order.planQty || this.order.planQuantity || this.order.daa008 || 0; },
         staffDisplay() {
            if (!this.staffNo) return '';
            const segs = this.staffNo.split(':');
            return segs.length > 1 ? `${segs[0]} ${segs[1]}` : this.staffNo;
         },
         planQtyDisplay() {
            return this.order.planQty || this.order.planQuantity || this.order.daa007 || this.order.daa010 || 0;
         filteredUsers() {
            if (!this.userSearch) return this.users;
            const kw = this.userSearch.trim().toLowerCase();
            return this.users.filter(u => u.toLowerCase().includes(kw));
         }
      },
      created() {
@@ -232,15 +277,21 @@
         this.init();
         this.getXS0101();
         this.updateNowTime();
         this.nowTimeTimer = setInterval(this.updateNowTime, 60000);
         // 秒级刷新;如不需动态跳秒可改为 60000
         this.nowTimeTimer = setInterval(this.updateNowTime, 1000);
      },
      beforeDestroy() {
         if (this.nowTimeTimer) clearInterval(this.nowTimeTimer);
      },
      beforeDestroy() { if (this.nowTimeTimer) clearInterval(this.nowTimeTimer); },
      methods: {
         formatUser(u) {
            if (!u) return '';
            const segs = u.split(':');
            return segs.length > 1 ? `${segs[0]} ${segs[1]}` : u;
         },
         selectUser(u) { this.staffNo = u; this.isShowUserSelect = false; this.userSearch = ''; },
         /* 修改:增加秒 */
         updateNowTime() {
            const d = new Date(), p = n => n.toString().padStart(2, '0');
            this.nowTime = `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}`;
            this.nowTime = `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}`;
         },
         initializeData() {
            this.productionCount = this.printedCount = this.defectiveCount = 0;
@@ -249,19 +300,15 @@
            this.isGeneratingBarcode = false; this.lastGenerateTime = 0; this.generateRequestId = null;
         },
         resetGenerateState() {
            this.isGeneratingBarcode = false;
            this.generateRequestId = null;
            this.lastGenerateTime = 0;
            this.isGeneratingBarcode = false; this.generateRequestId = null; this.lastGenerateTime = 0;
            this.$showMessage("已重置条码生成状态");
         },
         refresh() {
            this.$sendPostRequest({
               url: "http://192.168.0.94:9095/Numerical/RefreshDev",
               url: "http://192.168.0.94:9095/Numerical/RefreshDevBycl",
               data: { machineNo: this.order.machineNo },
               contentType: "application/json"
            }).then(r => {
               if (r.code == 200) this.fetchData(true); else this.$showMessage("同步失败");
            });
            }).then(r => { r.code == 200 ? this.fetchData(true) : this.$showMessage("同步失败"); });
         },
         onDaa003Change(v) {
            let o = this.lineList[this.DAA003List.indexOf(v)];
@@ -275,6 +322,7 @@
            if (!this.orderId && !this.orderNo) return;
            this.getOrderById();
            this.getWomdaaPrintById();
            this.getReportingHistory(); // 新增:每次刷新同步历史
            if (flag) {
               this.$post({ url: "/Womdaa/GetWomdaasByShow", data: { machineNo: this.machineNo } })
                  .then(res => {
@@ -283,29 +331,71 @@
                  });
            }
         },
         /* 新增:获取历史报工记录 */
         /* 修改:规范历史时间到秒 */
         getReportingHistory() {
            if (!this.orderNo) { this.reportingHistory = []; return; }
            const fmtSec = v => {
               if (!v) return '';
               // 兼容后端可能返回的不同格式
               const d = new Date(typeof v === 'string' ? v.replace(/-/g, '/') : v);
               if (isNaN(d.getTime())) return v; // 无法解析则原样返回
               const p = n => n.toString().padStart(2, '0');
               return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}`;
            };
            this.$post({
               url: "/Womdaa/GetByBillNoBG",
               data: { billNo: this.orderNo, machineNo: this.machineNo || null }
            }).then(res => {
               const list = res?.data?.tbBillList || res?.data || [];
               this.reportingHistory = list.map(r => {
                  // 依据你数据库字段做映射(下面字段名按常见命名举例,需要按实际改)
                  return {
                     bgDate: r.bgDate || '', // 报工时间
                     staff: (r.staffNo ? (r.staffNo + ' ' + (r.staffName || '')) : (r.staffName || '')),//报工人
                     orderNo: r.billNo,//工单号
                     machineNo: r.machineNo,//机台号
                     initialValue: r.csQty ?? 0,//初始采集数
                     productionCount: r.cjQty ?? 0,//报工时采集数
                     totalProduction: (r.cjQty - r.csQty) ?? 0,//报工数(计算)
                     BfQty: r.bfQty,//不良数
                     OkQty: r.okQty//良品数(计算)
                  }
               });
            }).catch(() => { this.reportingHistory = []; });
         },
         toggleUser(u) {
            if (!u) return;
            this.user = this.user === u ? null : u;
            this.staffNo = this.user;
         },
         //选择报工人
         confirmCustomAmount() {
            if (!this.customAmount || isNaN(Number(this.customAmount))) {
               this.$showMessage('请输入有效的数量'); return;
            }
            let amount = Number(this.customAmount);
            if (!this.customAmount || isNaN(Number(this.customAmount))) { this.$showMessage('请输入有效的数量'); return; }
            if (!this.staffNo) { this.$showMessage('请选择报工人'); return; }
            const staffNo = this.staffNo.split(':')[0];
            const amount = Number(this.customAmount);
            this.$post({
               url: "/MesInvItemBarcodes/AddBFToBarcodes",
               data: { orderNo: this.orderNo, orderId: this.orderId, bf: amount }
               data: {
                  orderNo: this.orderNo,
                  orderId: this.orderId,
                  bf: amount,
                  staffNo: staffNo,
                  initCjNum: this.order.initCjNum,        // 初始采集数
                  currentCjNum: this.order.currentCjNum   // 报工时采集数
               }
            }).then(res => {
               if (res.status == 1) { this.$showMessage(res.message); return; }
               this.$showMessage('报废数量填写成功');
               this.fetchData(true);
               this.fetchData(true); // 自动刷新历史
               this.customAmount = '';
            }).catch(() => this.$showMessage('报废数量填写失败,请重试'));
         },
         save() {
            if (!this.staffNo) { this.$showMessage('请选择报工人'); return; }
            uni.showToast({ title: '保存成功', icon: 'success' });
            this.getReportingHistory(); // 保存后也可刷新
         },
         cancel() { uni.showToast({ title: '取消操作', icon: 'none' }); },
         getOrderById() {
@@ -318,10 +408,11 @@
               });
         },
         getXS0101() {
            this.$post({ url: "/MesStaff/GetAllXS0101" }).then(res => {
               this.staff = res.data.tbBillList;
               this.users = this.staff.map(s => s.staffNo + ":" + s.staffName);
            });
            this.$post({ url: "/MesStaff/GetAllXS0101" })
               .then(res => {
                  this.staff = res.data.tbBillList;
                  this.users = this.staff.map(s => s.staffNo + ":" + s.staffName);
               });
         },
         getWomdaaPrintById() {
            this.$post({ url: "/Womdaa/GetWomdaaPrintById", data: { orderId: this.orderId } })
@@ -343,7 +434,7 @@
         init() {
            try {
               const v = this.getAndroidVersion();
               if (v >= 12) this.initForAndroid12Plus(); else this.initForAndroidLegacy();
               v >= 12 ? this.initForAndroid12Plus() : this.initForAndroidLegacy();
            } catch (e) { console.error(e); }
         },
         getAndroidVersion() {
@@ -389,47 +480,132 @@
            this.isShow = false;
            this.isGeneratingBarcode = false;
            this.generateRequestId = null;
            this.bufferData = '';
            this.dataToPrint = [];
            this.staffNo = null;
            this.user = '';
            this.barcodeAmount = '';
            this.icount = 1;
            this.staff = null;
            this.bufferData = ''; this.dataToPrint = [];
            this.staffNo = null; this.user = ''; this.barcodeAmount = ''; this.icount = 1; this.staff = null;
         }
      }
   };
   }
</script>
<style scoped>
   .section-title {
      font-size: 20px;
      font-weight: bold;
      margin: 18px 0 8px 0;
      color: #fff;
      background: #007aff;
      padding: 8px 18px;
      border-radius: 8px 8px 0 0;
      display: inline-block;
   }
   .history-title {
      background: #555;
   }
   .highlight-row {
      background: #ffe9b3 !important;
      font-weight: bold;
   }
   .report-table .summary-row {
      background: #f0f8ff;
      font-weight: 600;
   }
   .report-table .no-data {
      text-align: center;
      color: #777;
      font-size: 14px;
   }
   .report-header {
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-bottom: 8px;
   }
   .header-buttons {
      display: flex;
      align-items: center;
      gap: 12px;
   }
   .refresh-btn-header {
      background: #00A2E9;
      color: #fff;
      border: none;
      font-size: 16px;
      border-radius: 8px;
      padding: 8px 20px;
      transition: background 0.15s;
   }
      .refresh-btn-header:hover {
         background: #0086c0;
      }
   .reset-btn-header {
      background: #ff6b6b;
      color: #fff;
      border: none;
      font-size: 14px;
      border-radius: 8px;
      padding: 8px 16px;
      transition: background 0.15s;
   }
      .reset-btn-header:hover {
         background: #e94d4d;
      }
   .page {
      padding: 1.2vh 1.5vw;
      padding: 1.2vh 2vw;
      display: flex;
      flex-direction: column;
      box-sizing: border-box;
   }
   .wide-layout {
      max-width: 1600px;
      margin: 0 auto;
   }
   .page.has-overlay .status-section > :not(.overlay) {
      pointer-events: none;
   }
   .page.has-overlay .status-section > .overlay {
      pointer-events: auto;
   }
   .report-table-wrapper {
      margin-bottom: 8px;
      margin-bottom: 12px;
   }
   .table-scroll {
      overflow-x: auto;
   }
   .report-title {
      font-size: 22px;
      font-size: 24px;
      font-weight: 600;
      text-align: center;
      margin: 0 0 6px;
      margin: 0;
   }
   .report-table {
      min-width: 1080px;
      width: 100%;
      border-collapse: collapse;
      font-size: 13px;
      font-size: 14px;
      background: #fff;
   }
      .report-table th, .report-table td {
         border: 1px solid #555;
         padding: 4px 6px;
         padding: 6px 8px;
         text-align: center;
         white-space: nowrap;
      }
@@ -437,18 +613,18 @@
   .status-section {
      display: flex;
      flex-direction: column;
      gap: 10px;
   }
   .status-row {
      display: flex;
      flex-wrap: wrap;
      align-items: center;
      gap: 4px;
      gap: 10px;
      background: #f9f9f9;
      padding: 8px 10px;
      border-radius: 6px;
      box-shadow: 0 1px 3px rgba(0,0,0,.05);
      margin-bottom: 6px;
      padding: 10px 14px;
      border-radius: 8px;
      box-shadow: 0 1px 4px rgba(0,0,0,.06);
   }
   .status-box {
@@ -458,39 +634,43 @@
   .result-box {
      background: #f0f8ff;
      padding: 2px 10px;
      border-radius: 4px;
      border-left: 4px solid #007aff;
      padding: 4px 12px;
      border-radius: 6px;
      border-left: 5px solid #007aff;
   }
   input.highlight {
      width: 7.5vw;
      min-width: 90px;
      width: 9vw;
      min-width: 110px;
      font-weight: 600;
      border: none;
      background: #fff;
      text-align: center;
      font-size: 16px;
      padding: 4px 0;
      border-radius: 4px;
      box-shadow: inset 0 1px 3px rgba(0,0,0,.1);
      font-size: 18px;
      padding: 6px 0;
      border-radius: 6px;
      box-shadow: inset 0 1px 3px rgba(0,0,0,.12);
   }
   .operator-box {
      display: flex;
      justify-content: center;
      align-items: center;
      font-size: 26px;
      font-size: 28px;
      font-weight: bold;
      color: #007aff;
      margin: 0 4px;
      margin: 0 6px;
   }
   .btn-group {
      margin-left: auto;
      display: flex;
      align-items: center;
      gap: 10px;
      gap: 12px;
   }
   .refresh-btn-inline, .reset-btn-inline {
      transition: .15s;
   }
   .refresh-btn-inline {
@@ -498,76 +678,294 @@
      color: #fff;
      border: none;
      font-size: 18px;
      border-radius: 8px;
      padding: 6px 26px;
      border-radius: 10px;
      padding: 8px 30px;
   }
      .refresh-btn-inline:hover {
         background: #0086c0;
      }
   .reset-btn-inline {
      background: #ff6b6b;
      color: #fff;
      border: none;
      font-size: 16px;
      border-radius: 8px;
      padding: 6px 14px;
      border-radius: 10px;
      padding: 8px 18px;
   }
   .basic-info .form-row {
      .reset-btn-inline:hover {
         background: #e94d4d;
      }
   .flex-row {
      display: flex;
      justify-content: space-between;
      margin: 4px 0 10px;
      flex-wrap: wrap;
      gap: 20px;
      align-items: stretch;
   }
   .form-item {
      width: 32%;
   .flex-grow {
      flex: 1 1 540px;
   }
   .gap-lg {
      gap: 30px;
   }
   .defective-section {
      display: flex;
      align-items: center;
      gap: 14px;
      font-size: 18px;
   }
   .submit-section {
      display: flex;
      align-items: center;
   }
   .inp {
      width: 100%;
      padding: 6px;
      font-size: 14px;
      padding: 8px;
      font-size: 16px;
      border: 1px solid #808080;
      border-radius: 6px;
      border-radius: 8px;
      box-sizing: border-box;
   }
   .print-section {
      padding: 4px 0 10px;
      margin-bottom: 8px;
   }
   .barcode-info {
      display: flex;
      align-items: flex-start;
      gap: 20px;
   .bad-input {
      width: 320px;
      max-width: 100%;
      height: 66px;
      border: 3px solid #808080;
      font-size: 34px;
      text-align: center;
   }
   .details-btn {
      padding: 10px 26px;
      padding: 12px 34px;
      background: #00a2e9;
      color: #fff;
      font-size: 18px;
      font-size: 20px;
      border: none;
      cursor: pointer;
      border-radius: 10px;
      border-radius: 12px;
   }
      .details-btn:hover {
         background: #008ac2;
      }
   .current-user-section {
      display: flex;
      align-items: center;
      font-size: 18px;
      border: 1.5px solid #f00;
      border-radius: 10px;
      padding: 14px 22px;
      background: #fff;
      gap: 14px;
      flex: 0 0 auto;
   }
   .current-user-name {
      font-weight: bold;
      font-size: 22px;
   }
   .select-user-btn {
      padding: 6px 22px;
      background: #eee;
      border: 1px solid #aaa;
      border-radius: 8px;
      font-size: 16px;
   }
      .select-user-btn:hover {
         background: #ddd;
      }
   .overlay {
      position: fixed;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      background: rgba(0,0,0,.45);
      display: flex;
      justify-content: center;
      align-items: center;
      z-index: 1000;
   }
   .popup {
      background: #fff;
      padding: 2vh;
      border: 1px solid #ccc;
      box-shadow: 0 0 14px rgba(0,0,0,.12);
      width: 72vw;
      max-width: 1400px;
      height: 70vh;
      font-size: 1.4vw;
      max-height: 80vh;
      overflow-y: auto;
      border-radius: 12px;
      z-index: 1001;
   }
   .user-select-popup {
      width: 860px;
      max-width: 80vw;
      height: auto;
      min-height: 480px;
      padding: 0;
      display: flex;
      flex-direction: column;
   }
   .user-search-bar {
      display: flex;
      align-items: center;
      gap: 12px;
      padding: 18px 32px 0 32px;
      background: #fff;
      flex-wrap: wrap;
   }
   .user-search-input {
      flex: 1 1 260px;
      padding: 10px 14px;
      font-size: 16px;
      border: 1px solid #bbb;
      border-radius: 8px;
      outline: none;
   }
      .user-search-input:focus {
         border-color: #007aff;
         box-shadow: 0 0 0 2px rgba(0,122,255,.15);
      }
   .user-search-clear {
      padding: 10px 18px;
      background: #ff9f43;
      color: #fff;
      border: none;
      border-radius: 8px;
      font-size: 14px;
      cursor: pointer;
   }
      .user-search-clear:hover {
         background: #ff8920;
      }
   .user-search-info {
      font-size: 14px;
      color: #555;
   }
   .user-list-scroll {
      flex: 1 1 auto;
      overflow-y: auto;
      padding: 24px 32px 0 32px;
   }
   .user-list-grid {
      display: grid;
      grid-template-columns: repeat(auto-fill,minmax(110px,1fr));
      gap: 12px 12px;
   }
   .user-list-btn {
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      gap: 4px;
      padding: 10px 8px;
      height: 75px;
      font-size: 14px;
      background: #00a2e9;
      color: #fff;
      border: none;
      border-radius: 8px;
      cursor: pointer;
      box-sizing: border-box;
      word-break: break-word;
   }
      .user-list-btn .user-code {
         font-weight: 600;
         font-size: 14px;
      }
      .user-list-btn .user-name {
         font-size: 13px;
      }
      .user-list-btn.selected {
         background: #0072c9;
         box-shadow: 0 0 0 3px rgba(255,255,255,.6) inset;
      }
      .user-list-btn:hover {
         background: #008ed0;
      }
   .no-user-result {
      padding: 40px 0;
      text-align: center;
      font-size: 18px;
      color: #666;
   }
   .user-popup-footer {
      flex-shrink: 0;
      padding: 22px 32px 32px 32px;
      background: #fff;
      text-align: center;
   }
   .clean-btn {
      width: 48%;
      padding: 1.6vh;
      color: #fff;
      font-size: 1.4vw;
      border: none;
      text-align: center;
      cursor: pointer;
      border-radius: .6vw;
      background: #007aff;
   }
      .clean-btn.wide-btn {
         width: 60%;
         font-size: 20px;
      }
      .clean-btn:hover {
         background: #0062c9;
      }
   .reason-section {
      margin: 10px 0 12px;
      margin: 14px 0 18px;
   }
   .reason-buttons {
      display: grid;
      grid-template-columns: repeat(5,1fr);
      gap: 10px;
      gap: 12px;
   }
   .reason-btn {
      padding: 8px 4px;
      padding: 10px 6px;
      background: #808080;
      color: #fff;
      font-size: 14px;
      border: none;
      border-radius: 6px;
      border-radius: 8px;
      cursor: pointer;
   }
@@ -579,55 +977,23 @@
   .bottom-section {
      display: flex;
      justify-content: space-between;
      margin-top: 12px;
      margin-top: 16px;
      gap: 16px;
   }
   .save-btn, .cancel-btn {
      width: 48%;
      padding: 14px 0;
      flex: 1;
      padding: 16px 0;
      background: #00A2E9;
      color: #fff;
      font-size: 18px;
      font-size: 20px;
      border: none;
      border-radius: 8px;
      border-radius: 10px;
   }
   .overlay {
      position: fixed;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      background: rgba(0,0,0,.5);
      display: flex;
      justify-content: center;
      align-items: center;
   }
   .popup {
      background: #fff;
      padding: 2vh;
      border: 1px solid #ccc;
      box-shadow: 0 0 10px rgba(0,0,0,.1);
      width: 70vw;
      height: 70vh;
      font-size: 1.6vw;
      max-height: 80vh;
      overflow-y: auto;
      border-radius: 8px;
   }
   .clean-btn {
      width: 48%;
      padding: 1.5vh;
      color: #fff;
      font-size: 1.5vw;
      border: none;
      text-align: center;
      cursor: pointer;
      border-radius: .5vw;
      background: #007aff;
   }
      .save-btn:hover, .cancel-btn:hover {
         background: #0086c0;
      }
   .table1 {
      width: 100%;
@@ -636,11 +1002,34 @@
   @media (max-width:1400px) {
      input.highlight {
         font-size: 14px;
         font-size: 16px;
      }
      .user-list-btn {
         height: 70px;
         font-size: 13px;
      }
         .user-list-btn .user-code {
            font-size: 13px;
         }
         .user-list-btn .user-name {
            font-size: 12px;
         }
      .reason-btn {
         font-size: 12px;
      }
      .refresh-btn-header {
         font-size: 14px;
         padding: 6px 16px;
      }
      .reset-btn-header {
         font-size: 12px;
         padding: 6px 12px;
      }
   }
</style>