zjk
2026-07-29 eef69115934235cd9a0392643083a83b9f45904c
refactor(admin-records): 优化访客记录验证样式并重构导出逻辑

新增isCardVerifyMethod工具函数识别刷卡验证方式并添加对应警告样式,重构访客记录本地导出逻辑,将原回调式文件写入代码改为Promise异步实现,新增相关工具函数处理文件转换与写入操作
2个文件已修改
116 ■■■■■ 已修改文件
components/admin/AdminRecordsPanel.vue 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/admin-record-detail/index.vue 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
components/admin/AdminRecordsPanel.vue
@@ -343,6 +343,72 @@
  return `${dateValue} ${isEnd ? '23:59:59' : '00:00:00'}`;
};
const arrayBufferToBase64 = (buffer) => {
  if (!(buffer instanceof ArrayBuffer)) {
    return '';
  }
  const bytes = new Uint8Array(buffer);
  const chunkSize = 0x8000;
  let binary = '';
  for (let index = 0; index < bytes.length; index += chunkSize) {
    const chunk = bytes.subarray(index, index + chunkSize);
    binary += String.fromCharCode.apply(null, chunk);
  }
  return btoa(binary);
};
const saveExportArrayBufferToLocal = (buffer, fileName) => {
  return new Promise((resolve, reject) => {
    // #ifdef APP-PLUS
    try {
      if (typeof plus === 'undefined' || !plus.android || !plus.io) {
        reject(new Error('当前环境不支持原生文件写入'));
        return;
      }
      const base64Content = arrayBufferToBase64(buffer);
      if (!base64Content) {
        reject(new Error('导出内容为空'));
        return;
      }
      const Base64 = plus.android.importClass('android.util.Base64');
      const File = plus.android.importClass('java.io.File');
      const FileOutputStream = plus.android.importClass('java.io.FileOutputStream');
      const exportDirPath = plus.io.convertLocalFileSystemURL('_doc/exports');
      const exportDir = new File(exportDirPath);
      if (!exportDir.exists()) {
        exportDir.mkdirs();
      }
      const absoluteFilePath = `${exportDirPath}/${fileName}`;
      const exportFile = new File(absoluteFilePath);
      const outputStream = new FileOutputStream(exportFile);
      try {
        const fileBytes = Base64.decode(base64Content, Base64.DEFAULT);
        outputStream.write(fileBytes);
        outputStream.flush();
      } finally {
        outputStream.close();
      }
      resolve(absoluteFilePath);
    } catch (error) {
      reject(error);
    }
    // #endif
    // #ifndef APP-PLUS
    reject(new Error('当前环境不支持原生文件写入'));
    // #endif
  });
};
const getSelectedAreaLeafName = (areaValue = '') => {
  if (!areaValue) {
    return '';
@@ -725,36 +791,16 @@
        return;
      }
      // 保存到本地
      plus.io.resolveLocalFileSystemURL('_doc/', (docEntry) => {
        docEntry.getFile(fileName, { create: true }, (fileEntry) => {
          fileEntry.createWriter((writer) => {
            writer.onwrite = () => {
              uni.hideLoading();
              const filePath = fileEntry.toLocalURL();
              console.log('访客记录导出成功,文件路径:', filePath);
              uni.showToast({ title: '导出成功,文件地址:' + filePath, icon: 'none' });
            };
            writer.onerror = (err) => {
              uni.hideLoading();
              uni.showToast({ title: '文件保存失败', icon: 'none' });
              console.error('文件保存失败:', err);
            };
            writer.seek(0);
            writer.truncate(0);
            writer.writeAsBinary(res); // 使用 writeAsBinary 写入 ArrayBuffer
          }, (err) => {
            uni.hideLoading();
            console.error('创建文件写入器失败:', err);
          });
        }, (err) => {
          uni.hideLoading();
          console.error('创建导出文件失败:', err);
        });
      }, (err) => {
      try {
        const savedFilePath = await saveExportArrayBufferToLocal(res, fileName);
        uni.hideLoading();
        console.error('访问_doc目录失败:', err);
      });
        console.log('访客记录导出成功,文件路径:', savedFilePath);
        uni.showToast({ title: '导出成功,文件地址:' + savedFilePath, icon: 'none' });
      } catch (err) {
        uni.hideLoading();
        uni.showToast({ title: '文件保存失败', icon: 'none' });
        console.error('文件保存失败:', err);
      }
      // #endif
      return;
    }
pages/admin-record-detail/index.vue
@@ -76,8 +76,8 @@
            <image :src="hostMethodIcon" class="field-icon" mode="aspectFit"></image>
            <view class="field-copy">
              <text class="field-label host-method-label">接待人验证方式</text>
              <view class="method-badge">
                <text class="method-badge-text">{{ detail.hostVerify }}</text>
              <view class="method-badge" :class="{ 'method-badge-warn': isCardVerifyMethod(detail.hostVerify) }">
                <text class="method-badge-text" :class="{ 'method-badge-text-warn': isCardVerifyMethod(detail.hostVerify) }">{{ detail.hostVerify }}</text>
              </view>
            </view>
          </view>
@@ -147,8 +147,8 @@
              <image :src="signoutMethodIcon" class="field-icon" mode="aspectFit"></image>
              <view class="field-copy">
                <text class="field-label">接待人验证方式</text>
                <view class="method-badge method-badge-warn">
                  <text class="method-badge-text method-badge-text-warn">{{ detail.signoutVerify }}</text>
                <view class="method-badge" :class="{ 'method-badge-warn': isCardVerifyMethod(detail.signoutVerify) }">
                  <text class="method-badge-text" :class="{ 'method-badge-text-warn': isCardVerifyMethod(detail.signoutVerify) }">{{ detail.signoutVerify }}</text>
                </view>
              </view>
            </view>
@@ -244,6 +244,10 @@
  return typeStr || '未知';
};
const isCardVerifyMethod = (verifyMethod = '') => {
  return String(verifyMethod || '').includes('刷卡');
};
const normalizeDetailData = (data = {}) => {
  const statusInfo = getStatusInfo(data.status);
  const areaTags = Array.isArray(data.visitAreaNameList)