wbc
6 天以前 3897a86d1a714cca8087d589f55b00446679e48e
通用界面补充
已添加2个文件
1927 ■■■■■ 文件已修改
pages/Repair/DefectRegistration.vue 361 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/Repair/input.vue 1566 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/Repair/DefectRegistration.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,361 @@
<template>
    <view class="content">
        <!-- å¤´éƒ¨æ ‡é¢˜ -->
        <!-- è¡¨å•区域 -->
        <view class="form-container">
            <!-- æŸ¥è¯¢å·¥å• -->
            <view class="form-item">
                <text class="label">查询工单</text>
                <input class="input" type="text" v-model="formData.queryWorkOrder" placeholder="请输入查询工单" @input="onQueryWorkOrderChange" />
            </view>
            <!-- ç”Ÿäº§å·¥å• -->
            <view class="form-item">
                <text class="label">生产工单</text>
                <picker class="picker" :range="productionWorkOrderOptions" range-key="text" :value="productionWorkOrderIndex" @change="onProductionWorkOrderChange">
                    <view class="picker-content">
                        {{ productionWorkOrderOptions[productionWorkOrderIndex] ? productionWorkOrderOptions[productionWorkOrderIndex].text : '请选择/扫描生产工单' }}
                    </view>
                </picker>
                <view class="scan-icon" @click="scanProductionWorkOrder">
                    <uni-icons type="scan" size="24" color="#28a745"></uni-icons>
                </view>
            </view>
            <!-- äº§å“ç¼–码 -->
            <view class="form-item">
                <text class="label">产品编码</text>
                <view class="value">{{ formData.productCode }}</view>
            </view>
            <!-- äº§å“åç§° -->
            <view class="form-item">
                <text class="label">产品名称</text>
                <view class="value">{{ formData.productName }}</view>
            </view>
            <!-- å·¥åº -->
            <view class="form-item">
                <text class="label">工序</text>
                <picker class="picker" :range="processOptions" range-key="text" :value="processIndex" @change="onProcessChange">
                    <view class="picker-content">
                        {{ processOptions[processIndex] ? processOptions[processIndex].text : '请选择/扫描工序' }}
                    </view>
                </picker>
                <view class="scan-icon" @click="scanProcess">
                    <uni-icons type="scan" size="24" color="#28a745"></uni-icons>
                </view>
            </view>
            <!-- ä¸è‰¯ç±»åž‹ -->
            <view class="form-item">
                <text class="label">不良类型</text>
                <picker class="picker" :range="defectTypeOptions" range-key="text" :value="defectTypeIndex" @change="onDefectTypeChange">
                    <view class="picker-content">
                        {{ defectTypeOptions[defectTypeIndex] ? defectTypeOptions[defectTypeIndex].text : '请选择/扫描不良类型' }}
                    </view>
                </picker>
                <view class="scan-icon" @click="scanDefectType">
                    <uni-icons type="scan" size="24" color="#28a745"></uni-icons>
                </view>
            </view>
            <!-- ä¸è‰¯é¡¹ç›® -->
            <view class="form-item">
                <text class="label">不良项目</text>
                <picker class="picker" :range="defectItemOptions" range-key="text" :value="defectItemIndex" @change="onDefectItemChange">
                    <view class="picker-content">
                        {{ defectItemOptions[defectItemIndex] ? defectItemOptions[defectItemIndex].text : '请选择/扫描不良项目' }}
                    </view>
                </picker>
                 <view class="scan-icon" @click="scanDefectItem">
                    <uni-icons type="scan" size="24" color="#28a745"></uni-icons>
                </view>
            </view>
            <!-- ä¸è‰¯æ¡ç  -->
            <view class="form-item">
                <text class="label">不良条码</text>
                <input class="input" type="text" v-model="formData.defectBarcode" placeholder="请输入不良条码" />
            </view>
            <!-- æ¡ç æ•°é‡ -->
            <view class="form-item">
                <text class="label">条码数量</text>
                <input class="input" type="number" v-model="formData.barcodeQuantity" placeholder="请输入条码数量" />
            </view>
            <!-- æç¤ºä¿¡æ¯ -->
            <view class="form-item">
                <text class="label">提示信息</text>
                <view class="value hint">{{ formData.hintInfo }}</view>
            </view>
        </view>
        <!-- æäº¤æŒ‰é’® -->
        <view class="footer">
            <button class="submit-btn" @click="submitForm">登记提交</button>
        </view>
    </view>
</template>
<script>
import uniIcons from "@/components/uni-icons/uni-icons.vue"
export default {
    components: {uniIcons},
    data() {
        return {
            formData: {
                queryWorkOrder: '',
                productionWorkOrder: '',
                productCode: '',
                productName: '',
                process: '',
                defectType: '',
                defectItem: '',
                defectBarcode: '',
                barcodeQuantity: '',
                hintInfo: ''
            },
            productionWorkOrderOptions: [],
            productionWorkOrderIndex: -1,
            processOptions: [],
            processIndex: -1,
            defectTypeOptions: [],
            defectTypeIndex: -1,
            defectItemOptions: [],
            defectItemIndex: -1
        };
    },
    onLoad() {
        this.initData();
    },
    methods: {
        goBack() {
            uni.navigateBack();
        },
        initData() {
            // é¢„ç•™API调用位置
            this.loadProductionWorkOrders();
            this.loadProcesses();
            this.loadDefectTypes();
            this.loadDefectItems();
        },
        loadProductionWorkOrders() {
            // æ¨¡æ‹ŸAPI获取生产工单列表
            // this.$post({url: '/api/getWorkOrders'}).then(...)
            this.productionWorkOrderOptions = [
                {text: 'MO-20231201001', value: 'WO001'},
                {text: 'MO-20231201002', value: 'WO002'}
            ];
        },
        loadProcesses() {
            // æ¨¡æ‹ŸAPI获取工序列表
             this.processOptions = [
                 {text: 'SMT贴片', value: 'P001'},
                 {text: 'DIP插件', value: 'P002'},
                 {text: '组装', value: 'P003'}
             ];
        },
        loadDefectTypes() {
             // æ¨¡æ‹ŸAPI获取不良类型列表
             this.defectTypeOptions = [
                 {text: '物料不良', value: 'DT001'},
                 {text: '制程不良', value: 'DT002'}
             ];
        },
        loadDefectItems() {
             // æ¨¡æ‹ŸAPI获取不良项目列表
             this.defectItemOptions = [
                 {text: '虚焊', value: 'DI001'},
                 {text: '短路', value: 'DI002'},
                 {text: '缺件', value: 'DI003'}
             ];
        },
        onQueryWorkOrderChange(e) {
            // å®žæ—¶æŸ¥è¯¢å·¥å•对应下拉框发生变化
            const query = this.formData.queryWorkOrder;
            console.log('Querying work orders with:', query);
            // è¿™é‡Œåº”该调用接口根据输入内容筛选生产工单
            // this.$post({url: '/api/searchWorkOrders', data: {keyword: query}}).then(...)
            // æ¨¡æ‹Ÿç­›é€‰
            if(query) {
                this.productionWorkOrderOptions = this.productionWorkOrderOptions.filter(item => item.text.includes(query));
            } else {
                this.loadProductionWorkOrders(); // æ¢å¤é»˜è®¤åˆ—表
            }
        },
        onProductionWorkOrderChange(e) {
            this.productionWorkOrderIndex = e.target.value;
            const selected = this.productionWorkOrderOptions[this.productionWorkOrderIndex];
            if (selected) {
                this.formData.productionWorkOrder = selected.value;
                // æ¨¡æ‹Ÿæ ¹æ®å·¥å•带出产品信息
                this.formData.productCode = 'PROD-' + selected.value;
                this.formData.productName = '产品 ' + selected.text;
            }
        },
        onProcessChange(e) {
            this.processIndex = e.target.value;
            if (this.processOptions[this.processIndex]) {
                this.formData.process = this.processOptions[this.processIndex].value;
            }
        },
        onDefectTypeChange(e) {
            this.defectTypeIndex = e.target.value;
            if (this.defectTypeOptions[this.defectTypeIndex]) {
                this.formData.defectType = this.defectTypeOptions[this.defectTypeIndex].value;
            }
        },
        onDefectItemChange(e) {
            this.defectItemIndex = e.target.value;
            if (this.defectItemOptions[this.defectItemIndex]) {
                this.formData.defectItem = this.defectItemOptions[this.defectItemIndex].value;
            }
        },
        scanProductionWorkOrder() {
            uni.scanCode({
                success: (res) => {
                    console.log('Scanned:', res.result);
                    // å¤„理扫描结果,自动选中对应工单
                    uni.showToast({title: '扫描成功: ' + res.result, icon: 'none'});
                }
            });
        },
        scanProcess() {
             uni.scanCode({
                 success: (res) => {
                     console.log('Scanned Process:', res.result);
                     uni.showToast({title: '扫描成功: ' + res.result, icon: 'none'});
                 }
             });
        },
        scanDefectType() {
             uni.scanCode({
                 success: (res) => {
                     console.log('Scanned DefectType:', res.result);
                     uni.showToast({title: '扫描成功: ' + res.result, icon: 'none'});
                 }
             });
        },
        scanDefectItem() {
             uni.scanCode({
                 success: (res) => {
                     console.log('Scanned DefectItem:', res.result);
                     uni.showToast({title: '扫描成功: ' + res.result, icon: 'none'});
                 }
             });
        },
        submitForm() {
            console.log('Submitting:', this.formData);
            if (!this.formData.productionWorkOrder) {
                uni.showToast({title: '请选择生产工单', icon: 'none'});
                return;
            }
            // æäº¤é€»è¾‘
            // this.$post({url: '/api/submitDefect', data: this.formData}).then(...)
            uni.showToast({
                title: '登记提交成功',
                icon: 'success'
            });
        }
    }
};
</script>
<style>
.content {
    background-color: #f5f5f5;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
}
.page-header {
    background-color: #90CAF9; /* Light Blue */
    padding: 44px 15px 10px 15px; /* Status bar padding */
}
.header-content {
    display: flex;
    justify-content: space-between;
    align-items: center;
}
.page-title {
    font-size: 18px;
    font-weight: bold;
    color: #333;
}
.header-right {
    color: #fff;
    font-size: 14px;
}
.form-container {
    background-color: #fff;
    margin-top: 0;
    flex: 1;
}
.form-item {
    display: flex;
    align-items: center;
    padding: 12px 15px;
    border-bottom: 1px solid #eee;
}
.label {
    width: 90px;
    font-size: 15px;
    color: #333;
}
.input {
    flex: 1;
    font-size: 15px;
}
.value {
    flex: 1;
    font-size: 15px;
    color: #666;
}
.picker {
    flex: 1;
}
.picker-content {
    font-size: 15px;
    color: #333;
}
.scan-icon {
    width: 30px;
    height: 30px;
    display: flex;
    justify-content: center;
    align-items: center;
    margin-left: 10px;
    background-color: #4CD964; /* Green background for icon */
    border-radius: 4px;
}
.scan-icon uni-icons {
    /* Make icon white if background is green, but uni-icons color prop handles it */
}
/* Override uni-icons color to white since background is green */
.scan-icon ::v-deep span {
    color: #fff !important;
}
.footer {
    padding: 20px;
}
.submit-btn {
    background-color: #7B68EE; /* Purple */
    color: #fff;
    border-radius: 5px;
    font-size: 16px;
    height: 44px;
    line-height: 44px;
}
.hint {
    color: #999;
}
</style>
pages/Repair/input.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,1566 @@
<template>
  <view class="container">
    <view class="content">
      <!-- åŠ¨æ€è¡¨å•åŒºåŸŸ -->
      <view id="divid" class="form-container">
        <!-- åŠ¨æ€ç”Ÿæˆçš„è¡¨å•å†…å®¹ -->
        <view class="uni-form" v-if="formData.length > 0">
          <block v-for="(item, index) in formData" :key="index">
            <!-- æ–‡æœ¬è¾“入框 -->
          <view class="form-item" v-if="(item[2] === 'VARCHAR2' || item[2] === 'VARCHAR') && item[4] === '3'">
            <text class="form-label">{{ item[1] }}</text>
            <view class="scan-input-container">
              <input
                :id="item[0]"
                :disabled="item[3] === '1'"
                :readonly="item[3] === '1'"
                type="text"
                class="form-input"
                :class="{'readonly': item[3] === '1'}"
                v-model="formValues[item[0]]"
                :placeholder="'请输入'+item[1]"
                @keyup="(e) => handleKeyUp(e, item[7], item[8], item[0], item[9], item[10])"
                @confirm="() => enterSearch({keyCode: 13}, item[7], item[8], item[0], item[9], item[10])"
               :focus="autoFocusId === item[0]"
              />
              <!-- æ·»åŠ æ‰«ç å›¾æ ‡ï¼Œæ ¹æ®item[13]决定是否显示 -->
              <view
               v-if="item[3] === '0'"
                class="scan-icon-btn"
                @tap="() => scanQRCode(item[0])"
              >
                <uni-icons type="scan" size="20" color="#5677fc"></uni-icons>
              </view>
            </view>
          </view>
            <!-- ä¸‹æ‹‰é€‰æ‹©æ¡† -->
            <view class="form-item" v-else-if="(item[2] === 'VARCHAR2' || item[2] === 'VARCHAR') && item[4] === '2'">
              <text class="form-label">{{ item[1] }}</text>
              <view class="select-wrapper">
                <input
                  type="text"
                  :id="'selectList-'+item[0]+'-'+item[1]"
                  class="form-input select-input"
                  readonly
                  placeholder="请点击选择"
                  @tap="() => openList(item[0], item[1])"
                />
                <view class="select-icon">
                  <uni-icons type="right" size="16" color="#999"></uni-icons>
                </view>
              </view>
            </view>
            <!-- æ‰«æé€‰æ‹©æ¡† -->
            <view class="form-item" v-else-if="(item[2] === 'VARCHAR2' || item[2] === 'VARCHAR') && item[4] === '4'">
              <text class="form-label">{{ item[1] }}</text>
              <!-- ç§»é™¤è¿™ä¸ªéšè—çš„input,不再需要 -->
              <!-- <input :id="item[0]" v-model="formValues[item[0]]" type="hidden"> -->
              <view class="scan-select-container">
                <input
                  type="text"
                  class="form-input scan-input"
                  :id="item[0]"
                  v-model="formValues[item[0]]"
                  @keyup="(e) => handleScanKeyUp(e, item[8], item[0], item[10], item[7])"
                  @confirm="() => enterScanSelect({keyCode: 13}, item[8], item[0], item[10], item[7])"
                  :placeholder="'请选择/扫描'+item[1]"
                />
                <picker
                  :id="'select-'+item[0]"
                  :range="selectOptions[item[0]] || []"
                  @change="(e) => getCheckItem(item[8], item[0], item[10], item[7], e)"
                >
                  <view class="select-btn">
                    <uni-icons type="down" size="18" color="#fff"></uni-icons>
                  </view>
                </picker>
              </view>
            </view>
            <!-- ç…§ç‰‡æŒ‰é’® -->
            <view class="form-item button-item" v-else-if="(item[2] === 'VARCHAR2' || item[2] === 'VARCHAR') && item[4] === '9'">
              <view class="action-button photo-btn" @tap="() => photoButton(item)">
                <uni-icons type="camera" size="18" color="#fff" style="margin-right: 5px;"></uni-icons>
                <text>{{ item[1] }}</text>
              </view>
            </view>
            <!-- åˆ—表按钮 -->
            <view class="form-item button-item" v-else-if="(item[2] === 'VARCHAR2' || item[2] === 'VARCHAR') && item[4] === '11'">
              <view class="action-button list-btn" @tap="() => listButton(item)">
                <uni-icons type="list" size="18" color="#fff" style="margin-right: 5px;"></uni-icons>
                <text>{{ item[1] }}</text>
              </view>
            </view>
            <!-- æ‰“印按钮 -->
            <view class="form-item button-item" v-else-if="(item[2] === 'VARCHAR2' || item[2] === 'VARCHAR') && item[4] === '13'">
              <view class="action-button print-btn" @tap="() => clickPrintBtn(item)">
                <uni-icons type="paperplane" size="18" color="#fff" style="margin-right: 5px;"></uni-icons>
                <text>{{ item[1] }}</text>
              </view>
            </view>
            <!-- å¼€å…³ -->
            <view class="form-item switch-item" v-else-if="(item[2] === 'VARCHAR2' || item[2] === 'VARCHAR')">
              <text class="form-label">{{ item[1] }}</text>
              <input :id="item[0]" v-model="formValues[item[0]]" type="hidden">
              <switch
                :id="item[0]+'-switch'"
                :data-href="item[0]"
                @change="(e) => switchChange(e, item[0])"
                color="#5677fc"
                class="custom-switch"
              />
            </view>
            <!-- æ–‡æœ¬åŸŸ -->
            <view class="form-item" v-else-if="item[2] === 'MEMO'">
              <text class="form-label">{{ item[1] }}</text>
              <textarea
                :id="item[0]"
                v-model="formValues[item[0]]"
                class="form-textarea"
                :disabled="item[3] === '1'"
                :readonly="item[3] === '1'"
                :class="{'readonly': item[3] === '1'}"
                :style="{'height': item[14]+'px'}"
              ></textarea>
            </view>
            <!-- æŒ‰é’® -->
            <view class="form-item button-item" v-else-if="item[2] === 'BUTTON'">
              <view class="action-button normal-btn" @tap="() => clickButton(item)">
                <text>{{ item[1] }}</text>
              </view>
            </view>
            <!-- å¤šé€‰ç¼–辑 -->
            <view class="form-item button-item" v-else-if="item[2] === 'EDIT' && item[4] === '11'">
              <view class="action-button list-btn" @tap="() => listButton(item)">
                <uni-icons type="list" size="18" color="#fff" style="margin-right: 5px;"></uni-icons>
                <text>{{ item[1] }}</text>
              </view>
            </view>
            <!-- å¤šé€‰æ‰«æ -->
            <view class="form-item" v-else-if="item[2] === 'EDIT' && item[4] === '4'">
              <text class="form-label">{{ item[1] }}</text>
              <input :id="item[0]" v-model="formValues[item[0]]" type="hidden">
              <view class="scan-select-container">
                <input
                  type="text"
                  class="form-input scan-input"
                  :id="'scanSelect-mul'+item[0]"
                  v-model="scanValues['mul'+item[0]]"
                  @keyup="(e) => enterScanSelect(e, item[8], item[0], item[10], item[7])"
                  :placeholder="'请选择/扫描'+item[1]"
                />
                <picker
                  mode="multiSelector"
                  :id="'select-mul'+item[0]"
                  :range="selectOptions['mul'+item[0]] || []"
                  @change="(e) => getCheckMultiItem(item[8], item[0], item[10], item[7], e)"
                >
                  <view class="select-btn">
                    <uni-icons type="down" size="18" color="#fff"></uni-icons>
                  </view>
                </picker>
              </view>
            </view>
          </block>
        </view>
      </view>
      <!-- è¡¨æ ¼åŒºåŸŸ -->
      <view id="tableDiv" class="table-container" v-if="showTable">
        <view class="table-header">
          <text class="table-title">查询结果</text>
        </view>
        <scroll-view scroll-y class="table-scroll">
          <view class="table">
            <!-- è¡¨å¤´ -->
            <view class="thead">
              <view class="tr">
                <view
                  class="th"
                  v-for="(col, colIndex) in tableColumns"
                  :key="colIndex"
                  :style="getColumnStyle(col)"
                >
                  {{ getColumnName(col) }}
                </view>
              </view>
              <!-- æœç´¢è¡Œ -->
              <view class="tr search-row">
                <view
                  class="td search-cell"
                  v-for="(col, colIndex) in tableColumns"
                  :key="colIndex"
                  :style="getColumnStyle(col)"
                >
                  <input
                    type="text"
                    class="search-input"
                    v-model="searchValues[colIndex]"
                    :placeholder="'搜索...'"
                    @input="filterTableData"
                  />
                </view>
              </view>
            </view>
            <!-- è¡¨æ ¼å†…容 -->
            <view class="tbody">
              <view
                class="tr"
                v-for="(row, rowIndex) in filteredTableData"
                :key="rowIndex"
                :class="{'tr-alter': rowIndex % 2 === 1}"
                @tap="() => handleRowTap(row)"
              >
                <view
                  class="td"
                  v-for="(col, colIndex) in tableColumns"
                  :key="colIndex"
                  :style="getColumnStyle(col)"
                  @longpress="() => showFullContent(row[colIndex])"
                >
                  <rich-text :nodes="formatCellContent(row[colIndex])"></rich-text>
                </view>
              </view>
            </view>
          </view>
        </scroll-view>
      </view>
    </view>
  </view>
</template>
<script>
export default {
  data() {
    return {
      title: 'MES移动终端',
      username: '',
      formData: [],
      formValues: {},
      scanValues: {},
      selectOptions: {},
      tableColumns: [],
      tableData: [],
      searchValues: {}, // å­˜å‚¨æ¯åˆ—的搜索值
      filteredTableData: [], // è¿‡æ»¤åŽçš„表格数据
      showTable: false,
      isCommitted: false, // è¯·æ±‚是否已提交标识
      bluetoothSocket: null,
      device: null,
      autoFocusId: '' // ç”¨äºŽè‡ªåŠ¨èšç„¦çš„ID
    };
  },
  onReady(){
      this.username = this.setUsrCode();
      this.title = uni.getStorageSync("functionName") || 'MES移动终端';
      uni.setNavigationBarTitle({
        title: this.title
      });
  },
  onLoad() {
     this.getData();
     // åˆå§‹åŒ–蓝牙打印机
     if (this.$store.state.serverInfo.Bluetooth === 'true') {
       this.initBluetooth();
     }
   },
  methods: {
    // è¿”回上一页
    goBack() {
      uni.navigateBack();
    },
    // èŽ·å–ç”¨æˆ·ä»£ç 
    setUsrCode() {
      return uni.getStorageSync("code") || '';
    },
    // èŽ·å–è¡¨å•æ•°æ®
    getData() {
      uni.showLoading({
        title: '加载中...'
      });
      uni.request({
        url: this.$store.state.serverInfo.serverAPI + '/modules/getRfSetup',
        data: {
          functionName: uni.getStorageSync("functionName")
        },
        method: 'POST',
        timeout: 60000,
        header: {
          'Content-Type': 'application/json'
        },
        success: (res) => {
          if (res.data.status === 0 && res.data.data && res.data.data.setupData) {
            let fData = res.data.data.setupData;
            console.log("原始数据:", fData);
            // å¤„理回车符号
            if (typeof fData === 'string') {
              while (fData.indexOf('\\r\\n') !== -1) {
                fData = fData.replace("\\r\\n", " ");
              }
              // è§£æžå­—符串为表单数据数组
              try {
                // æŒ‰æ–¹æ‹¬å·åˆ†å‰²å­—段
                const fieldsStr = fData.split('[');
                const formFields = [];
                // ä»Žç´¢å¼•1开始,因为第一个元素是空的或前缀
                for (let i = 1; i < fieldsStr.length; i++) {
                  // åŽ»æŽ‰ç»“å°¾çš„æ–¹æ‹¬å·å¹¶æŒ‰#分割属性
                  const fieldStr = fieldsStr[i].replace(']', '');
                  const fieldProps = fieldStr.split('#');
                  formFields.push(fieldProps);
                }
                this.formData = formFields;
                console.log("解析后的表单数据:", this.formData);
                // åˆå§‹åŒ–表单值
                this.formData.forEach(item => {
                  if (item[12]) {
                    this.$set(this.formValues, item[0], item[12]);
                  } else {
                    this.$set(this.formValues, item[0], '');
                  }
                  // åˆå§‹åŒ–扫描值
                  if (item[4] === '4') {
                    this.$set(this.scanValues, item[0], '');
                  }
                });
                // èšç„¦ç¬¬ä¸€ä¸ªè¾“入框
               this.$nextTick(() => {
                 // æ‰¾åˆ°ç¬¬ä¸€ä¸ªè¾“入框并聚焦
                 const firstInput = this.formData.find(item => item[4] === '3' || item[4] === '4');
                 if (firstInput) {
                   setTimeout(() => {
                     this.focusInput(firstInput[0]);
                   }, 300);
                 }
               });
                // åˆå§‹åŒ–下拉框数据
                this.getSelectData();
                // æ‰§è¡ŒSQL语句的默认值
                this.getExcuteSql();
              } catch (error) {
                console.error("解析表单数据出错:", error);
                uni.showToast({
                  title: '解析表单数据出错',
                  content: "解析表单数据出错: " + err.errMsg,
                  icon: 'none'
                });
              }
            } else {
              console.error("setupData不是字符串类型");
              uni.showToast({
                title: '数据格式错误',
                icon: 'none'
              });
            }
          } else {
            uni.showToast({
              title: res.data.message || '获取数据失败',
              content: res.data.message || '获取数据失败',
              icon: 'none'
            });
          }
        },
        fail: (err) => {
          console.error("请求失败:", err);
          uni.showToast({
            title: "服务器断开",
            icon: 'none'
          });
        },
        complete: () => {
          uni.hideLoading();
        }
      });
    },
     // æ‰«æäºŒç»´ç 
      scanQRCode(fieldId) {
        // #ifdef APP-PLUS
        // è°ƒç”¨åŽŸç”Ÿæ‰«ç åŠŸèƒ½
        uni.scanCode({
          scanType: ['qrCode', 'barCode'], // åŒæ—¶æ”¯æŒäºŒç»´ç å’Œæ¡å½¢ç 
          success: (res) => {
            console.log('扫码成功:', res);
            // å°†æ‰«ç ç»“果设置到对应的输入框
            this.formValues[fieldId] = res.result;
            // æŸ¥æ‰¾å½“前字段的配置
            const fieldConfig = this.formData.find(item => item[0] === fieldId);
            if (fieldConfig) {
              // å¦‚果有配置的回车事件,则触发
              if (fieldConfig[7] !== 'N') {
                this.doProByFile8(fieldConfig[8], fieldId);
              }
              // å¦‚果有配置的跳转字段,则聚焦到下一个字段
              if (fieldConfig[10] !== '') {
                this.formValues[fieldConfig[10]] = '';
                this.focusInput(fieldConfig[10]);
              }
            }
          },
          fail: (err) => {
            console.error('扫码失败:', err);
            uni.showToast({
              title: '扫码失败',
              icon: 'none'
            });
          }
        });
        // #endif
        // #ifdef H5
        uni.showToast({
          title: 'H5环境不支持扫码功能',
          icon: 'none'
        });
        // #endif
      },
    formatCellContent(content) {
      if (content === undefined || content === null) {
        return '';
      }
      // å°†å­—符串转换为字符串
      const strContent = String(content);
      // æ£€æŸ¥æ˜¯å¦åŒ…含<br>标签
      if (strContent.includes('<br>')) {
        return strContent;
      } else {
        return strContent;
      }
    },
    // æ˜¾ç¤ºå®Œæ•´å†…容
    showFullContent(content) {
      if (!content) return;
      uni.showModal({
        title: '完整内容',
        content: String(content).replace(/<br>/g, '\n'),
        showCancel: false,
        confirmText: '关闭'
      });
    },
    // èšç„¦è¾“入框
//     focusInput(id) {
//   setTimeout(() => {
//     // ä½¿ç”¨uni-app的原生API
//     uni.createSelectorQuery()
//       .select('#' + id)
//       .boundingClientRect(data => {
//         if (data) {
//           // å…ƒç´ å­˜åœ¨ï¼Œå°è¯•聚焦
//           uni.createSelectorQuery()
//             .select('#' + id)
//             .fields({
//               context: true
//             }, res => {
//               if (res && res.context) {
//                 res.context.focus();
//               } else {
//                 console.log('无法获取元素上下文:', id);
//               }
//             })
//             .exec();
//         } else {
//           console.log('未找到元素:', id);
//         }
//       })
//       .exec();
//   }, 500); // å¢žåŠ å»¶è¿Ÿæ—¶é—´
// },
    // èšç„¦è¾“入框 - ç®€åŒ–版
    focusInput(id) {
      console.log('尝试聚焦元素:', id);
      // è®¾ç½®ä¸€ä¸ªä¸´æ—¶å˜é‡ï¼Œç”¨äºŽè‡ªåŠ¨èšç„¦
      this.autoFocusId = id;
      // ä½¿ç”¨nextTick确保DOM已更新
      this.$nextTick(() => {
        setTimeout(() => {
          // é‡ç½®è‡ªåŠ¨èšç„¦ID,触发视图更新
          this.autoFocusId = '';
          // å†æ¬¡è®¾ç½®ï¼Œè§¦å‘自动聚焦
          setTimeout(() => {
            this.autoFocusId = id;
          }, 100);
        }, 200);
      });
    },
    // å¤„理键盘按键事件
    handleKeyUp(e, file7, str, fileId, file9, file10) {
      // åªæœ‰åœ¨æŒ‰ä¸‹å›žè½¦é”®(13)或Tab键(9)时才触发
      if (e.keyCode === 13 || e.keyCode === 9) {
        // æ·»åŠ å»¶è¿Ÿï¼Œç¡®ä¿è¾“å…¥å®Œæˆ
        setTimeout(() => {
          this.enterSearch(e, file7, str, fileId, file9, file10);
        }, 100);
      }
    },
    // å¤„理扫描输入键盘事件
    handleScanKeyUp(e, file8, fileId, file10, file7) {
      // åªæœ‰åœ¨æŒ‰ä¸‹å›žè½¦é”®(13)或Tab键(9)时才触发
      if (e.keyCode === 13 || e.keyCode === 9) {
        // æ·»åŠ å»¶è¿Ÿï¼Œç¡®ä¿è¾“å…¥å®Œæˆ
        setTimeout(() => {
          this.enterScanSelect(e, file8, fileId, file10, file7);
        }, 100);
      }
    },
    // å›žè½¦æœç´¢
    enterSearch(e, file7, str, fileId, file9, file10) {
      // æ£€æŸ¥æ˜¯å¦æœ‰ keyCode,如果没有则默认为回车键
      const keyCode = e.keyCode || e.which || 13;
      if (keyCode == 13 || keyCode == 9) {
        console.log('触发回车搜索:', fileId, file7, str);
        // åˆ¤æ–­æ˜¯å¦éœ€è¦è°ƒç”¨å­˜å‚¨è¿‡ç¨‹
        if (file7 != 'N') {
          this.doProByFile8(str, fileId);
        }
        // åˆ¤æ–­è·³è½¬å­—段
        if (file9 == '1') {
          // å›žè½¦è‡ªåŠ¨è·³è‡³ä¸‹ä¸€éžåªè¯»å­—æ®µ
          this.focusNextInput(fileId);
        }
        if (file9 == '0' && file10 == '') {
          // å›žè½¦åŽå°±åœç•™åœ¨æœ¬å­—段,本字段内容先清空
          this.formValues[fileId] = '';
          this.focusInput(fileId);
        }
        if (file10 != '') {
          // è·³è‡³æŒ‡å®šå­—段,当要求跳到指定字段时,这个指定字段及后面顺序的字段框内容均需要清空
          this.formValues[file10] = '';
          this.focusInput(file10);
        }
      }
    },
    focusNextInput(currentId) {
      // æ‰¾åˆ°å½“前字段在 formData ä¸­çš„索引
      const currentIndex = this.formData.findIndex(item => item[0] === currentId);
      if (currentIndex === -1) return;
      // ä»Žå½“前字段之后查找第一个非只读的输入字段
      for (let i = currentIndex + 1; i < this.formData.length; i++) {
        const item = this.formData[i];
        // æ£€æŸ¥æ˜¯å¦æ˜¯å¯è¾“入字段且非只读
        if ((item[4] === '3' || item[4] === '4') && item[3] !== '1') {
          this.focusInput(item[0]);
          return;
        }
      }
    },
    // æ‰«æé€‰æ‹©æ¡†å›žè½¦äº‹ä»¶
    enterScanSelect(e, file8, fileId, file10, file7) {
      const keyCode = e.keyCode || e.which || 13;
      if (keyCode == 13 || keyCode == 9) {
        console.log('触发扫描选择回车:', fileId, file7, file8);
        const value = this.formValues[fileId]; // ç›´æŽ¥ä½¿ç”¨formValues
        if (value == '') {
          uni.showToast({
            title: '请扫描',
            icon: 'none'
          });
          return false;
        }
        // ä¸å†éœ€è¦è¿™ä¸€è¡Œï¼Œå› ä¸ºå·²ç»ç›´æŽ¥ç»‘定到formValues
        // this.formValues[fileId] = value;
        if (file7 != 'N') {
          this.doProByFile8(file8, fileId);
        }
        if (file10 != '') {
          this.focusInput(file10);
        }
      }
    },
    // ä¸‹æ‹‰æ¡†é€‰æ‹©äº‹ä»¶
    getCheckItem(file8, fileId, file10, file7, e) {
      const index = e.detail.value;
      const value = this.selectOptions[fileId][index];
      if (!value) {
        uni.showToast({
          title: '请选择',
          icon: 'none'
        });
        this.formValues[fileId] = '';
        return false;
      }
      // ä¸å†éœ€è¦scanValues
      // this.scanValues[fileId] = value;
      this.formValues[fileId] = value;
      if (file7 != 'N') {
        this.doProByFile8(file8, fileId);
      }
      if (file10 != '') {
        this.formValues[file10] = '';
        this.focusInput(file10);
      }
    },
    // å¤šé€‰æ¡†é€‰æ‹©äº‹ä»¶
    getCheckMultiItem(file8, fileId, file10, file7, e) {
      const indexes = e.detail.value;
      const values = indexes.map(index => this.selectOptions['mul' + fileId][index]);
      if (values.length === 0) {
        uni.showToast({
          title: '请选择',
          icon: 'none'
        });
        this.scanValues['mul' + fileId] = '';
        this.formValues[fileId] = '';
        return false;
      }
      const str = values.join('@');
      this.scanValues['mul' + fileId] = str;
      this.formValues[fileId] = str;
      if (file7 != 'N') {
        this.doProByFile8(file8, fileId);
      }
      if (file10 != '') {
        this.formValues[file10] = '';
        this.focusInput(file10);
      }
    },
    // è¿‡æ»¤è¡¨æ ¼æ•°æ®
    filterTableData() {
      // å¦‚果没有搜索条件,显示全部数据
      if (Object.keys(this.searchValues).length === 0 ||
          Object.values(this.searchValues).every(v => !v)) {
        this.filteredTableData = this.tableData;
        return;
      }
      // æ ¹æ®æœç´¢æ¡ä»¶è¿‡æ»¤æ•°æ®
      this.filteredTableData = this.tableData.filter(row => {
        // æ£€æŸ¥æ¯ä¸€åˆ—是否符合搜索条件
        return Object.keys(this.searchValues).every(colIndex => {
          const searchValue = this.searchValues[colIndex];
          // å¦‚果该列没有搜索值,则视为符合条件
          if (!searchValue) return true;
          const cellValue = String(row[colIndex] || '').toLowerCase();
          return cellValue.includes(searchValue.toLowerCase());
        });
      });
    },
    // å¤„理表格数据变化时更新过滤结果
    updateFilteredData() {
      this.searchValues = {}; // é‡ç½®æœç´¢æ¡ä»¶
      this.filteredTableData = this.tableData; // é‡ç½®è¿‡æ»¤ç»“æžœ
    },
    // å¼€å…³åˆ‡æ¢äº‹ä»¶
    switchChange(e, fileId) {
      this.formValues[fileId] = e.detail.value ? "1" : "0";
    },
    // èŽ·å–ä¸‹æ‹‰æ¡†æ•°æ®
    getSelectData() {
      this.formData.forEach(item => {
        if ((item[2] === 'VARCHAR2' || item[2] === 'VARCHAR') && item[4] === '4') {
          this.getExcProc(item[0], uni.getStorageSync("code") + '[_N', '', 'select');
        } else if (item[2] === 'EDIT' && item[4] === '4') {
          this.getExcProc(item[0], uni.getStorageSync("code") + '[_N', '', 'select');
        }
      });
    },
    // æ‰§è¡ŒSQL语句的默认值
    getExcuteSql() {
      this.formData.forEach(item => {
        if (item[3] === '1' && (item[2] === 'VARCHAR2' || item[2] === 'VARCHAR') && item[4] === '3') {
          let upperValue = item[12] ? item[12].toUpperCase() : '';
          // è¿‡æ»¤æ•°æ®åº“关键词
          const reg = new RegExp(/INSERT|DELETE|UPDATE|DROP|TRUNCATE/);
          if (reg.test(upperValue)) {
            return;
          }
          upperValue = upperValue.replace('{USERCODE}', uni.getStorageSync("code"));
          if (upperValue.substring(0, 6) === "SELECT") {
            this.getExcuteSqlData(upperValue, item[0]);
          }
        }
      });
    },
    // æ‰§è¡ŒSQL查询语句
    getExcuteSqlData(str, inputId) {
      uni.request({
        url: this.$store.state.serverInfo.serverAPI + '/getExcuteSql',
        data: {
          str: str
        },
        method: 'POST',
        timeout: 30000,
        success: (res) => {
          if (res.data.result) {
            this.formValues[inputId] = res.data.data[0];
          } else {
            uni.showToast({
              title: res.data.message,
              icon: 'none'
            });
          }
        },
        fail: (err) => {
          uni.showToast({
            title: '失败!失败类型是:' + err.errMsg,
            icon: 'none'
          });
        }
      });
    },
    // æ‰“开列表页面
    openList(fileId, fileName) {
      uni.navigateTo({
        url: '/pages/BasePage/select_search_list?file_name=' + fileId + '&title_name=' + fileName
      });
    },
    // æ‰§è¡Œå­˜å‚¨è¿‡ç¨‹
    doProByFile8(file8, fileId) {
      if (this.isCommitted === false) {
        this.isCommitted = true;
        this.doProByFile8Prc(file8, fileId);
      } else {
        return false;
      }
    },
    // æ‰§è¡Œå­˜å‚¨è¿‡ç¨‹å¤„理
    doProByFile8Prc(file8, fileId) {
      try {
        const date = file8.substring(1, file8.length - 1);
        const a = date.split("}{");
        const iput = a[0].split(',');
        let fileValue = uni.getStorageSync("code") + '[';
        for (let i = 0; i < iput.length; i++) {
          let fv = this.formValues[iput[i]] || '';
          fv = fv.trim();
          fileValue += fv + '[';
        }
        fileValue = fileValue.substring(0, fileValue.length - 1);
        console.log(12);
        if (a[1]) {
          const input = a[1].split(',');
          if (input[input.length - 1] === 'FVIEW') {
            this.getExcProc(fileId, fileValue, a[1], 'cursor+proc');
          } else {
            this.getExcProc(fileId, fileValue, a[1], 'proc');
          }
        } else {
          this.getExcProc(fileId, fileValue, a[1], 'proc');
        }
      } catch (e) {
        uni.showModal({
          title: '错误',
          content: "功能发生异常,请联系IT人员",
          showCancel: false
        });
        this.isCommitted = false;
        return false;
      }
    },
    // æ‰§è¡Œå­˜å‚¨è¿‡ç¨‹API
    // æ‰§è¡Œå­˜å‚¨è¿‡ç¨‹API
    getExcProc(fileName, fileValue, outFiles, stype) {
      const fileId = fileName;
      fileName = fileName.replace(/\s+/g, "");
      uni.request({
        url: this.$store.state.serverInfo.serverAPI + '/modules/getExcProc',
        data: {
          functionName: uni.getStorageSync("functionName"),
          fileName: fileName,
          pmachtype: '',
          fileValue: fileValue,
          outFiles: outFiles
        },
        method: 'POST',
        timeout: 60000,
        header: {
          'Content-Type': 'application/json'
        },
        success: (res) => {
          console.log(JSON.stringify(res.data));
          if (res.data.data) {
            if (stype.indexOf('proc') !== -1) {
              const msg_1 = res.data.message;
              if (msg_1 && msg_1.indexOf('999!*') !== -1) {
                uni.showModal({
                  title: '警告提醒',
                  content: res.data.message,
                  showCancel: false
                });
              } else {
                console.log(1);
                this.afterProc(res.data);
              }
            } else if (stype.indexOf('select') !== -1) {
              console.log(2);
              const t = res.data.data.split(',');
              const options = t.filter(item => item.length > 0);
              if (fileName.startsWith('mul')) {
                this.$set(this.selectOptions, fileName, options);
              } else {
                this.$set(this.selectOptions, fileName, options);
              }
            }
            if (stype.indexOf('cursor') !== -1) {
              const valueData = res.data.data.toString();
              const t1 = valueData.substring(valueData.indexOf(',') + 1, valueData.length);
              const tData = t1.split(',');
              const table = tData[tData.length - 1].split('{');
              // å¤„理表头
              const headerRow = table[0].split('@');
              this.tableColumns = headerRow;
              // å¤„理数据行
              const rows = [];
              for (let i = 1; i < table.length; i++) {
                if (table[i].length > 0) {
                  rows.push(table[i].split('@'));
                }
              }
              this.tableData = rows;
              this.filteredTableData = rows; // åˆå§‹åŒ–过滤后的数据
              this.searchValues = {}; // é‡ç½®æœç´¢æ¡ä»¶
              this.showTable = true;
            }
            if (stype.indexOf('print') !== -1) {
              const da = res.data.data[0];
              if (da && da.length > 0) {
                da.splice(0, 1);
                this.printByIds(da.toString());
              }
            }
          } else {
            this.playerNo();
            uni.showModal({
              title: '提示',
              content: res.data.message,
              showCancel: false,
              success: () => {
                this.formValues[fileId] = "";
                this.focusInput(fileId);
              }
            });
          }
          this.isCommitted = false;
        },
        fail: (err) => {
          this.isCommitted = false;
          uni.showToast({
            title: "请求失败: " + err.errMsg,
            content: "请求失败: " + err.errMsg,
            icon: 'none'
          });
        }
      });
    },
    // å¤„理存储过程返回结果
    afterProc(data) {
      this.playerYes();
      console.log('处理返回数据:', JSON.stringify(data));
      // æ£€æŸ¥æ•°æ®æ ¼å¼
      if (!data.data || !Array.isArray(data.data)) {
        console.error('返回数据格式不正确:', data);
        return;
      }
      // éåŽ†æ•°æ®æ•°ç»„
      for (let i = 0; i < data.data.length; i++) {
        const t = data.data[i];
        // æ£€æŸ¥æ•°ç»„元素是否有效
        if (!Array.isArray(t) || t.length < 2) {
          console.warn('数据项格式不正确:', t);
          continue;
        }
        const fieldId = t[0];
        const fieldValue = t[1];
        // æ£€æŸ¥å­—段ID是否存在于表单值中
        if (this.formValues.hasOwnProperty(fieldId)) {
          if (typeof fieldValue === 'string' && fieldValue.indexOf("{") === -1) {
            this.formValues[fieldId] = fieldValue;
          } else if (typeof fieldValue === 'string') {
            const a = fieldValue.split('{');
            this.setSelect(a, fieldId);
          } else {
            console.warn('字段值类型不正确:', fieldValue);
          }
        } else {
          console.warn('表单中不存在字段:', fieldId);
        }
      }
    },
    // è®¾ç½®ä¸‹æ‹‰æ¡†é€‰é¡¹
    setSelect(t, fileName) {
      const options = t.filter(item => item.length > 0);
      this.$set(this.selectOptions, fileName, options);
      this.formValues[fileName] = '';
    },
    // ç‚¹å‡»æŒ‰é’®
    clickButton(item) {
      const file10 = item[10];
      const file8 = item[8];
      this.doProByFile8(file8, item[0]);
      if (file10 !== '') {
        this.formValues[file10] = '';
        this.focusInput(file10);
      }
    },
    // ç‚¹å‡»æ‹ç…§æŒ‰é’®
    photoButton(item) {
      uni.navigateTo({
        url: '/pages/BasePage/ftp/pictrue_add'
      });
    },
    // ç‚¹å‡»åˆ—表按钮
    listButton(item) {
      const file8 = item[8];
      this.doProByFile8Cursor(file8, item[0], item[2]);
    },
    // æ‰§è¡Œåˆ—表查询
    doProByFile8Cursor(file8, fileId, fileType) {
      const date = file8.substring(1, file8.length - 1);
      const a = date.split("}{");
      const iput = a[0].split(',');
      let fileValue = uni.getStorageSync("code") + '[';
      for (let i = 0; i < iput.length; i++) {
        let fv = this.formValues[iput[i]] || '';
        fv = fv.trim();
        fileValue += fv + '[';
      }
      fileValue = fileValue.substring(0, fileValue.length - 1);
      if (fileType === 'EDIT') {
        this.getCursor(fileId, fileValue, a[1], 'cursor');
      } else {
        this.getExcProc(fileId, fileValue, a[1], 'cursor');
      }
    },
    // èŽ·å–æ¸¸æ ‡æ•°æ®
    // èŽ·å–æ¸¸æ ‡æ•°æ®
    getCursor(fileName, fileValue, outFiles, stype) {
      const fileId = fileName;
      fileName = fileName.replace(/\s+/g, "");
      uni.request({
        url: this.$store.state.serverInfo.serverAPI + '/getCursor',
        data: {
          functionName: uni.getStorageSync("functionName"),
          fileName: fileName,
          pmachtype: '',
          fileValue: encodeURIComponent(fileValue),
          outFiles: outFiles
        },
        method: 'POST',
        timeout: 60000,
        header: {
          'Content-Type': 'application/json'
        },
        success: (res) => {
          if (res.data.result) {
            if (res.data.data[0]) {
              uni.showModal({
                title: '错误',
                content: "功能发生异常,请联系IT人员",
                showCancel: false
              });
            } else {
              const tdata = res.data.data[2];
              const col = res.data.data[3];
              this.tableColumns = col;
              this.tableData = tdata.map(item => Object.values(item));
              this.filteredTableData = this.tableData; // åˆå§‹åŒ–过滤后的数据
              this.searchValues = {}; // é‡ç½®æœç´¢æ¡ä»¶
              this.showTable = true;
            }
          } else {
            uni.showToast({
              title: res.data.message,
              icon: 'none'
            });
          }
        },
        fail: (err) => {
          uni.showToast({
            title: "请求失败: " + err.errMsg,
            icon: 'none'
          });
        }
      });
    },
    // ç‚¹å‡»æ‰“印按钮
    clickPrintBtn(item) {
      try {
        const file8 = item[8];
        const file10 = item[10];
        const fileId = item[0];
        const date = file8.substring(1, file8.length - 1);
        const a = date.split("}{");
        const iput = a[0].split(',');
        let fileValue = uni.getStorageSync("code") + '[';
        for (let i = 0; i < iput.length; i++) {
          let fv = this.formValues[iput[i]] || '';
          fileValue += fv + '[';
        }
        fileValue = fileValue.substring(0, fileValue.length - 1);
        // å…ˆæ¸…空字段
        if (file10 !== '') {
          this.formValues[file10] = '';
          this.focusInput(file10);
        }
        setTimeout(() => {
          if (a[1]) {
            const input = a[1].split(',');
         if (input[input.length - 1] === 'FVIEW') {
              this.getExcProc(fileId, fileValue, a[1], 'cursor+print');
            } else {
              this.getExcProc(fileId, fileValue, a[1], 'print');
            }
          }
        }, 100);
      } catch (e) {
        uni.showModal({
          title: '错误',
          content: "功能发生异常,请联系IT人员",
          showCancel: false
        });
      }
    },
    // åˆå§‹åŒ–蓝牙打印机
    initBluetooth() {
      // #ifdef APP-PLUS
      try {
        const main = plus.android.runtimeMainActivity();
        const BluetoothAdapter = plus.android.importClass("android.bluetooth.BluetoothAdapter");
        const UUID = plus.android.importClass("java.util.UUID");
        const uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
        const BAdapter = BluetoothAdapter.getDefaultAdapter();
        BAdapter.cancelDiscovery(); // åœæ­¢æ‰«æ
        const address_mac = uni.getStorageSync("printMac") || "DC:1D:30:3F:D2:50";
        this.device = BAdapter.getRemoteDevice(address_mac);
        plus.android.importClass(this.device);
        this.bluetoothSocket = this.device.createInsecureRfcommSocketToServiceRecord(uuid);
        plus.android.importClass(this.bluetoothSocket);
      } catch (err) {
        console.log(err);
        uni.showToast({
          title: "蓝牙连接异常!",
          icon: 'none'
        });
      }
      // #endif
    },
    // æ‰“印标签
    printByIds(ids) {
      // #ifdef APP-PLUS
      try {
        if (!this.bluetoothSocket) {
          uni.showToast({
            title: "蓝牙未连接",
            icon: 'none'
          });
          return;
        }
        uni.request({
          url: this.$store.state.serverInfo.serverAPI + '/getPrintData',
          data: {
            ids: ids
          },
          method: 'POST',
          timeout: 60000,
          success: (res) => {
            if (res.data.result) {
              const printData = res.data.data;
              this.doPrint(printData);
            } else {
              uni.showToast({
                title: res.data.msg,
                icon: 'none'
              });
            }
          },
          fail: (err) => {
            uni.showToast({
              title: "请求失败: " + err.errMsg,
              content: "请求失败: " + err.errMsg,
              icon: 'none'
            });
          }
        });
      } catch (e) {
        uni.showToast({
          title: "打印异常: " + e.message,
          icon: 'none'
        });
      }
      // #endif
    },
    // æ‰§è¡Œæ‰“印
    doPrint(printData) {
      // #ifdef APP-PLUS
      try {
        this.bluetoothSocket.connect();
        const outputStream = this.bluetoothSocket.getOutputStream();
        plus.android.importClass(outputStream);
        for (let i = 0; i < printData.length; i++) {
          const item = printData[i];
          const template = item.template;
          const data = item.data;
          // è¿™é‡Œå¯ä»¥æ ¹æ®æ¨¡æ¿å’Œæ•°æ®ç”Ÿæˆæ‰“印内容
          const printContent = this.generatePrintContent(template, data);
          outputStream.write(plus.android.newByteArray(printContent));
          outputStream.flush();
        }
        outputStream.close();
        this.bluetoothSocket.close();
        uni.showToast({
          title: "打印成功",
          icon: 'success'
        });
      } catch (e) {
        uni.showToast({
          title: "打印失败: " + e.message,
          icon: 'none'
        });
      }
      // #endif
    },
    // ç”Ÿæˆæ‰“印内容
    generatePrintContent(template, data) {
      // æ ¹æ®æ¨¡æ¿å’Œæ•°æ®ç”Ÿæˆæ‰“印内容
      // è¿™é‡Œéœ€è¦æ ¹æ®å®žé™…打印机和模板格式进行实现
      return new Uint8Array([0x1B, 0x40]); // ç¤ºä¾‹: æ‰“印机初始化命令
    },
    // æ’­æ”¾æˆåŠŸæç¤ºéŸ³
    playerYes() {
      // #ifdef APP-PLUS
      try {
        const context = plus.audio.createPlayer('static/audio/yes.mp3');
        context.play();
      } catch (e) {
        console.log('播放提示音失败:', e);
      }
      // #endif
    },
    // æ’­æ”¾å¤±è´¥æç¤ºéŸ³
    playerNo() {
      // #ifdef APP-PLUS
      try {
        const context = plus.audio.createPlayer('static/audio/no.mp3');
        context.play();
      } catch (e) {
        console.log('播放提示音失败:', e);
      }
      // #endif
    },
    // èŽ·å–åˆ—å®½æ ·å¼
    getColumnStyle(col) {
      if (col.indexOf('_') !== -1) {
        const tArr = col.split('_');
        return { width: tArr[1] + '%' };
      }
      return {};
    },
    // èŽ·å–åˆ—å
    getColumnName(col) {
      if (col.indexOf('_') !== -1) {
        const tArr = col.split('_');
        return tArr[0];
      }
      return col;
    }
  }
};
</script>
<style>
/* é¡µé¢å®¹å™¨ */
.container {
  display: flex;
  flex-direction: column;
  height: 100vh;
  background-color: #f5f7fa;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
}
/* å†…容区域 */
.content {
  flex: 1;
  overflow-y: auto;
  padding: 8px;
}
/* è¡¨å•容器 */
.form-container {
  margin-bottom: 12px;
}
/* è¡¨å•项 */
.form-item {
  display: flex;
  align-items: center;
  margin-bottom: 8px;
  background-color: #fff;
  border-radius: 6px;
  padding: 0 10px;
  min-height: 44px;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}
.form-label {
  width: 80px;
  font-size: 14px;
  color: #333;
  font-weight: 500;
  padding-right: 10px;
  flex-shrink: 0;
}
.form-input {
  flex: 1;
  height: 44px;
  padding: 0 10px;
  font-size: 14px;
  border: none;
  background-color: transparent;
}
.form-input.readonly {
  color: #999;
  background-color: #f5f5f5;
  border-radius: 4px;
}
.form-textarea {
  flex: 1;
  padding: 10px;
  font-size: 14px;
  border: none;
  background-color: transparent;
  min-height: 60px;
}
.form-textarea.readonly {
  color: #999;
  background-color: #f5f5f5;
  border-radius: 4px;
}
/* ä¸‹æ‹‰é€‰æ‹©æ¡† */
.select-wrapper {
  flex: 1;
  position: relative;
}
.select-icon {
  position: absolute;
  right: 10px;
  top: 50%;
  transform: translateY(-50%);
  pointer-events: none;
}
/* æ‰«æé€‰æ‹©æ¡† */
.scan-select-container {
  flex: 1;
  display: flex;
  align-items: center;
}
.scan-input {
  flex: 1;
}
.select-btn {
  width: 36px;
  height: 36px;
  background: linear-gradient(135deg, #5677fc, #8a9ffc);
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 4px;
  margin-left: 8px;
}
.select-btn.readonly-btn {
  background: linear-gradient(135deg, #e0e0e0, #d0d0d0);
  cursor: not-allowed;
}
/* æŒ‰é’®æ ·å¼ */
.button-item {
  justify-content: center;
  padding: 6px 0;
}
.action-button {
  width: 100%;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 6px;
  color: #fff;
  font-size: 14px;
  font-weight: 500;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.normal-btn {
  background: linear-gradient(135deg, #5677fc, #8a9ffc);
}
.photo-btn {
  background: linear-gradient(135deg, #52c41a, #95de64);
}
.list-btn {
  background: linear-gradient(135deg, #fa8c16, #ffc53d);
}
.print-btn {
  background: linear-gradient(135deg, #13c2c2, #5cdbd3);
}
/* å¼€å…³æ ·å¼ */
.switch-item {
  justify-content: space-between;
}
.custom-switch {
  transform: scale(0.8);
}
/* è¡¨æ ¼æ ·å¼ */
.table-container {
  background-color: #fff;
  border-radius: 6px;
  overflow: hidden;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
  margin-top: 8px;
}
.table-header {
  padding: 10px;
  background-color: #f9fafc;
  border-bottom: 1px solid #edf0f7;
}
.table-title {
  font-size: 14px;
  font-weight: 600;
  color: #333;
}
.table-scroll {
  max-height: 300px;
}
.table {
  width: 100%;
  table-layout: fixed;
}
.tr {
  display: flex;
  border-bottom: 1px solid #edf0f7;
  min-height: 44px;
}
.tr-alter {
  background-color: #f9fafc;
}
.th, .td {
  flex: 1;
  padding: 10px 6px;
  text-align: center;
  font-size: 13px;
  word-break: break-word;
  white-space: normal;
  display: flex;
  align-items: center;
  justify-content: center;
}
.td {
  min-height: 44px;
  position: relative;
}
.th {
  background-color: #f5f7fa;
  color: #333;
  font-weight: 600;
  position: sticky;
  top: 0;
  z-index: 2;
}
/* è¡¨æ ¼æœç´¢è¡Œæ ·å¼ */
.search-row {
  background-color: #f0f2f5;
  border-bottom: 1px solid #ddd;
}
.search-cell {
  padding: 5px 3px;
}
.search-input {
  width: 100%;
  height: 30px;
  border: 1px solid #ddd;
  border-radius: 4px;
  padding: 0 5px;
  font-size: 12px;
  background-color: #fff;
}
/* ç¡®ä¿è¡¨å¤´å›ºå®šåœ¨é¡¶éƒ¨ */
.thead {
  position: sticky;
  top: 0;
  z-index: 10;
}
/* é€‚配小屏幕 */
@media screen and (max-width: 320px) {
  .form-label {
    width: 70px;
    font-size: 13px;
  }
  .form-input, .form-textarea {
    font-size: 13px;
  }
  .action-button {
    font-size: 13px;
  }
}
.scan-input-container {
  flex: 1;
  display: flex;
  align-items: center;
  position: relative;
}
/* æ‰«ç å›¾æ ‡æŒ‰é’® */
.scan-icon-btn {
  width: 36px;
  height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
  position: absolute;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  z-index: 1;
}
/* è°ƒæ•´å¸¦æ‰«ç å›¾æ ‡çš„输入框内边距 */
.scan-input-container .form-input {
  padding-right: 36px;
}
</style>