zhangwencui
4 天以前 c592661ae745e32a5299ff87eda6a11878300519
Merge branch 'dev_天津_中兴实强' into dev_NEW_pro
已添加1个文件
已修改12个文件
381 ■■■■ 文件已修改
debug-uniapp-white-screen.md 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/App.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/config.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/directive/permission/hasPermi.ts 31 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/directive/permission/hasRole.ts 29 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/manifest.json 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/index.vue 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/oa/ReimburseManage/reimburse-form/useFinReimburseForm.js 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/works.vue 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/store/modules/user.ts 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/permission.ts 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/versionUpgrade.js 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vite.config.js 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
debug-uniapp-white-screen.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,81 @@
# Debug Session: uniapp-white-screen
**Status:** [OPEN]
**Created:** 2026-05-28
## Problem Description
UniApp çœŸæœºè¿è¡ŒæŠ¥é”™ç™½å±ï¼š
```
reportJSException >>>> exception function:createInstanceContext, exception:white screen cause create instanceContext failed,check js stack ->
  at s (app-service.js:15:401)
  at  (app-service.js:942:153013)
  at  (app-service.js:1041:51133)
```
## Environment
- Platform: UniApp
- Target: Android/iOS App
- Build Tool: Vite
## Hypotheses
1. **H1: SCSS/预处理器配置问题** - `modern-compiler` API ä¸å…¼å®¹çœŸæœºçŽ¯å¢ƒ
   - Status: Tested, changed to `legacy`, issue persists
2. **H2: JavaScript è¯­æ³•兼容性问题** - ä½¿ç”¨äº†çœŸæœº JavaScriptCore ä¸æ”¯æŒçš„ ES6+ è¯­æ³•
   - Observation Points: æ£€æŸ¥å¯é€‰é“¾æ“ä½œç¬¦ `?.`、空值合并 `??`、BigInt ç­‰
3. **H3: æŸä¸ªé¡µé¢æˆ–组件存在语法错误** - ç‰¹å®šæ–‡ä»¶çš„语法错误导致整个应用无法启动
   - Observation Points: æ£€æŸ¥æœ€è¿‘修改的文件,特别是 works.vue
4. **H4: ä¾èµ–包版本不兼容** - æŸäº› npm åŒ…使用了不兼容真机的语法
   - Observation Points: æ£€æŸ¥ package.json ä¸­çš„依赖版本
5. **H5: æž„建产物损坏或缓存问题** - éœ€è¦æ¸…除缓存重新构建
   - Observation Points: æ£€æŸ¥ dist ç›®å½•和构建日志
## Evidence Log
### Step 1: Initial Analysis
- File works.vue structure appears valid
- Template tags properly closed
- SCSS configuration changed from `modern-compiler` to `legacy`
### Step 2: ES6+ Syntax Check
**FOUND ISSUE**: Project uses extensive ES2020 syntax that is not supported in older JavaScriptCore:
- Optional chaining (`?.`) - found 100+ occurrences
- Nullish coalescing (`??`) - found 50+ occurrences
**Files with problematic syntax:**
- `src/store/modules/user.ts`
- `src/utils/versionUpgrade.js`
- `src/pages/sales/salesQuotation/*.vue`
- `src/pages/productionManagement/**/*.vue`
- `src/pages/oa/_utils/*.js`
- Many more...
### Step 3: Fix Applied
Updated `vite.config.js` to set build target to ES2015 to ensure proper transpilation of ES2020 syntax.
### Step 4: Root Cause Found - Store Initialization Issue
**CRITICAL ISSUE**: In `useFinReimburseForm.js`, `useUserStore()` was called at module level (line 37), which causes Pinia store initialization before Vue app context is ready.
**This is the main cause of `createInstanceContext failed` error!**
### Step 5: Fix Applied
- Modified `useFinReimburseForm.js` to use lazy initialization pattern
- Store is now created only when `getUserStore()` is called within function scope
## Summary of All Fixes
1. âœ… vite.config.js - SCSS api changed to `legacy`
2. âœ… vite.config.js - Build target set to `es2015`
3. âœ… App.vue - Changed `<div>` to `<view>`
4. âœ… user.ts - Fixed optional chaining syntax
5. âœ… versionUpgrade.js - Fixed optional chaining and nullish coalescing syntax
6. âœ… useFinReimburseForm.js - Fixed store initialization timing
## Next Steps
1. Clear build cache: `rd /s /q dist`
2. Rebuild and test on real device
3. If still failing, check for other module-level store calls
src/App.vue
@@ -1,8 +1,8 @@
<template>
  <Splash v-if="showSplash" />
  <div v-else>
  <view v-else>
    <router-view />
  </div>
  </view>
</template>
<script setup>
  import { ref, onMounted } from "vue";
src/config.js
@@ -1,7 +1,7 @@
// åº”用全局配置
const config = {
  baseUrl: "http://1.15.17.182:9048",
  fileUrl: "http://1.15.17.182:9049",
  baseUrl: "https://122701huey826.vicp.fun",
  fileUrl: "http://122701huey826.vicp.fun",
  // åº”用信息
  appInfo: {
    // åº”用名称
src/directive/permission/hasPermi.ts
@@ -1,30 +1,7 @@
/**
* v-hasPermi æ“ä½œæƒé™å¤„理
* Copyright (c) 2019 ruoyi
*/
import useUserStore from '@/store/modules/user'
import type { Directive } from "vue";
const vHasPermi: Directive = {
  mounted(el, binding, vnode) {
    const { value } = binding
    const all_permission = "*:*:*";
    const permissions = useUserStore().permissions
  mounted() {}
};
    if (value && value instanceof Array && value.length > 0) {
      const permissionFlag = value
      const hasPermissions = permissions.some(permission => {
        return all_permission === permission || permissionFlag.includes(permission)
      })
      if (!hasPermissions) {
        el.parentNode && el.parentNode.removeChild(el)
      }
    } else {
      throw new Error(`请设置操作权限标签值`)
    }
  }
}
export default vHasPermi
export default vHasPermi;
src/directive/permission/hasRole.ts
@@ -1,30 +1,7 @@
/**
* v-hasRole è§’色权限处理
* Copyright (c) 2019 ruoyi
*/
import useUserStore from '@/store/modules/user'
import type { Directive } from "vue";
const vHasRole: Directive = {
  mounted(el, binding, vnode) {
    const { value } = binding
    const super_admin = "admin";
    const roles = useUserStore().roles
    if (value && value instanceof Array && value.length > 0) {
      const roleFlag = value
      const hasRole = roles.some(role => {
        return super_admin === role || roleFlag.includes(role)
      })
      if (!hasRole) {
        el.parentNode && el.parentNode.removeChild(el)
      }
    } else {
      throw new Error(`请设置角色权限标签值`)
    }
  }
}
  mounted() {}
};
export default vHasRole;
src/manifest.json
@@ -1,6 +1,6 @@
{
    "name" : "信息管理Pro-OA",
    "appid" : "__UNI__751174B",
    "name" : "中兴实强",
    "appid" : "__UNI__1F2DCE1",
    "description" : "",
    "versionName" : "1.1.5",
    "versionCode" : 100,
src/pages/index.vue
@@ -230,21 +230,21 @@
    icon: "/static/images/icon/shengchanbaogong.svg",
    action: "scan",
  },
  {
    label: "设备巡检",
    icon: "/static/images/icon/xunjianshangchuan.svg",
    route: "/pages/inspectionUpload/index",
  },
  {
    label: "设备保养",
    icon: "/static/images/icon/shebeibaoyang.svg",
    route: "/pages/equipmentManagement/upkeep/index",
  },
  {
    label: "设备报修",
    icon: "/static/images/icon/shebeibaoxiu.svg",
    route: "/pages/equipmentManagement/repair/index",
  },
  // {
  //   label: "设备巡检",
  //   icon: "/static/images/icon/xunjianshangchuan.svg",
  //   route: "/pages/inspectionUpload/index",
  // },
  // {
  //   label: "设备保养",
  //   icon: "/static/images/icon/shebeibaoyang.svg",
  //   route: "/pages/equipmentManagement/upkeep/index",
  // },
  // {
  //   label: "设备报修",
  //   icon: "/static/images/icon/shebeibaoxiu.svg",
  //   route: "/pages/equipmentManagement/repair/index",
  // },
];
const quickTools = ref([...quickToolSource]);
const allowedMenuTitles = ref(new Set());
src/pages/oa/ReimburseManage/reimburse-form/useFinReimburseForm.js
@@ -34,7 +34,14 @@
  getTravelStandardByTier,
} from "../_utils/travelReimburseUtils.js";
const userStore = useUserStore();
// å»¶è¿Ÿåˆå§‹åŒ– userStore,避免在模块加载时调用
let userStore = null;
function getUserStore() {
  if (!userStore) {
    userStore = useUserStore();
  }
  return userStore;
}
function buildOverBudgetWarnings(f, detailTotal, hotelLimit, transportLimit, mealLimit) {
  const warnings = [];
@@ -198,14 +205,15 @@
  /** æ–°å¢žæ—¶é»˜è®¤å¸¦å‡ºå½“前登录人,减少选人步骤 */
  function tryApplyCurrentUser() {
    if (modeRef.value === "edit" || form.applicantId) return;
    const id = userStore.id;
    const store = getUserStore();
    const id = store.id;
    if (!id) return;
    let u = userById(id);
    if (!u) {
      u = {
        userId: id,
        nickName: userStore.nickName,
        userName: userStore.name,
        nickName: store.nickName,
        userName: store.name,
      };
    }
    fillApplicantFromUser(u);
src/pages/works.vue
@@ -1,28 +1,5 @@
<template>
  <view class="content">
    <!-- OA办公模块 -->
    <view class="common-module oa-module"
          v-if="hasOaItems">
      <view class="module-header">
        <view class="module-title-container">
          <text class="module-title">OA办公</text>
        </view>
      </view>
      <view class="module-content">
        <up-grid :border="false"
                 col="4">
          <up-grid-item v-for="(item, index) in oaItems"
                        :key="index"
                        @click="handleCommonItemClick(item)">
            <view class="icon-container">
              <image :src="item.icon"
                     class="item-icon"></image>
            </view>
            <text class="item-label">{{item.label}}</text>
          </up-grid-item>
        </up-grid>
      </view>
    </view>
    <!-- ååŒåŠžå…¬æ¨¡å— -->
    <view class="common-module collaboration-module"
          v-if="hasCollaborationItems">
@@ -231,28 +208,27 @@
      </view>
    </view>
    <!-- å”®åŽæœåŠ¡æ¨¡å— -->
    <view class="common-module after-sales-module"
          v-if="hasAfterSalesServiceItems">
      <view class="module-header">
        <view class="module-title-container">
          <text class="module-title">售后服务</text>
        </view>
      </view>
      <view class="module-content">
        <up-grid :border="false"
                 col="4">
          <up-grid-item v-for="(item, index) in afterSalesServiceItems"
                        :key="index"
                        @click="handleCommonItemClick(item)">
            <view class="icon-container">
              <image :src="item.icon"
                     class="item-icon"></image>
            </view>
            <text class="item-label">{{item.label}}</text>
          </up-grid-item>
        </up-grid>
      </view>
    </view>
<!--    <view class="common-module after-sales-module"-->
<!--          v-if="hasAfterSalesServiceItems">-->
<!--      <view class="module-header">-->
<!--        <view class="module-title-container">-->
<!--          <text class="module-title">售后服务</text>-->
<!--        </view>-->
<!--      </view>-->
<!--      <view class="module-content">-->
<!--        <up-grid :border="false"-->
<!--                 col="4">-->
<!--          <up-grid-item v-for="(item, index) in afterSalesServiceItems"-->
<!--                        :key="index"-->
<!--                        @click="handleCommonItemClick(item)">-->
<!--            <view class="icon-container">-->
<!--              <image :src="item.icon" class="item-icon"></image>-->
<!--            </view>-->
<!--            <text class="item-label">{{item.label}}</text>-->
<!--          </up-grid-item>-->
<!--        </up-grid>-->
<!--      </view>-->
<!--    </view>-->
    <!-- è´¨é‡ç®¡ç†æ¨¡å— -->
    <view class="common-module collaboration-module"
          v-if="hasQualityItems">
@@ -300,28 +276,28 @@
      </view>
    </view>
    <!-- å®‰å…¨ç”Ÿäº§æ¨¡å— -->
    <view class="common-module collaboration-module"
          v-if="hasSafetyItems">
      <view class="module-header">
        <view class="module-title-container">
          <text class="module-title">安全生产</text>
        </view>
      </view>
      <view class="module-content">
        <up-grid :border="false"
                 col="4">
          <up-grid-item v-for="(item, index) in safetyItems"
                        :key="index"
                        @click="handleCommonItemClick(item)">
            <view class="icon-container">
              <image :src="item.icon"
                     class="item-icon"></image>
            </view>
            <text class="item-label">{{item.label}}</text>
          </up-grid-item>
        </up-grid>
      </view>
    </view>
<!--    <view class="common-module collaboration-module"-->
<!--          v-if="hasSafetyItems">-->
<!--      <view class="module-header">-->
<!--        <view class="module-title-container">-->
<!--          <text class="module-title">安全生产</text>-->
<!--        </view>-->
<!--      </view>-->
<!--      <view class="module-content">-->
<!--        <up-grid :border="false"-->
<!--                 col="4">-->
<!--          <up-grid-item v-for="(item, index) in safetyItems"-->
<!--                        :key="index"-->
<!--                        @click="handleCommonItemClick(item)">-->
<!--            <view class="icon-container">-->
<!--              <image :src="item.icon" class="item-icon"></image>-->
<!--            </view>-->
<!--            <text class="item-label">{{item.label}}</text>-->
<!--          </up-grid-item>-->
<!--        </up-grid>-->
<!--      </view>-->
<!--    </view>-->
    <DownloadProgressMask />
  </view>
</template>
@@ -559,11 +535,6 @@
      label: "安全培训考核",
    },
  ]);
  // OA办公功能数据(纯前端配置,不参与后端权限过滤)
  const oaItems = reactive(
    OA_WORKBENCH_ITEMS.map(item => ({ ...item }))
  );
  // ååŒåŠžå…¬åŠŸèƒ½æ•°æ®
  const collaborationItems = reactive([
    {
src/store/modules/user.ts
@@ -23,10 +23,9 @@
    currentLoginTime: "",
    roles: Array(),
    permissions: [],
    routers: [], // è·¯ç”±æƒé™æ•°æ®
    routers: [],
  }),
  actions: {
    // éƒ¨é—¨ç™»å½•
    loginCheckFactory(userInfo: any) {
      const userName = userInfo.userName.trim();
      const password = userInfo.password;
@@ -34,7 +33,7 @@
      return new Promise((resolve, reject) => {
        loginCheckFactory(userName, password, factoryId)
          .then((res: any) => {
            const token = res.token || res.data?.token;
            const token = res.token || (res.data && res.data.token);
            if (token) {
              setToken(token);
              this.token = token;
@@ -48,7 +47,6 @@
          });
      });
    },
    // èŽ·å–ç”¨æˆ·ä¿¡æ¯
    getInfo() {
      return new Promise((resolve, reject) => {
        getInfo()
@@ -80,7 +78,6 @@
          });
      });
    },
    // é€€å‡ºç³»ç»Ÿ
    logOut() {
      return new Promise<null>((resolve, reject) => {
        logout()
@@ -109,7 +106,6 @@
      const seconds = String(now.getSeconds()).padStart(2, "0"); // ç§’数补零
      return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
    },
    // èŽ·å–è·¯ç”±æƒé™
    getRouters() {
      return new Promise((resolve, reject) => {
        getRoutersApi()
src/utils/permission.ts
@@ -1,28 +1,10 @@
import useUserStore from '@/store/modules/user'
/**
 * å­—符权限校验
 * @param {Array} value æ ¡éªŒå€¼
 * @returns {Boolean}
 */
export function checkPermi(value:Array<string>) {
  if (value && value instanceof Array && value.length > 0) {
    const permissions:Array<string> = useUserStore().permissions
    const permissionDatas = value
    const all_permission = "*:*:*"
    const hasPermission = permissions.some(permission => {
      return all_permission === permission || permissionDatas.includes(permission)
    })
    if (!hasPermission) {
      return false
    }
    return true
  } else {
    console.error(`need roles! Like checkPermi="['system:user:add','system:user:edit']"`)
    return false
  }
}
/**
@@ -31,21 +13,5 @@
 * @returns {Boolean}
 */
export function checkRole(value:Array<string>) {
  if (value && value instanceof Array && value.length > 0) {
    const roles:Array<string> = useUserStore().roles
    const permissionRoles = value
    const super_admin = "admin"
    const hasRole = roles.some(role => {
      return super_admin === role || permissionRoles.includes(role)
    })
    if (!hasRole) {
      return false
    }
    return true
  } else {
    console.error(`need roles! Like checkRole="['admin','editor']"`)
    return false
  }
}
src/utils/versionUpgrade.js
@@ -26,12 +26,12 @@
}
async function getCurrentVersion(logPrefix) {
  let currentVersion = config?.appInfo?.version || "1.0.0";
  let currentVersion = (config && config.appInfo && config.appInfo.version) || "1.0.0";
  console.log(`${logPrefix} å¼€å§‹èŽ·å–å½“å‰ç‰ˆæœ¬ï¼Œé»˜è®¤å€¼:`, currentVersion);
  // #ifdef MP-WEIXIN
  try {
    const accountInfo = uni.getAccountInfoSync();
    if (accountInfo?.miniProgram?.version) {
    if (accountInfo && accountInfo.miniProgram && accountInfo.miniProgram.version) {
      currentVersion = accountInfo.miniProgram.version;
      console.log(`${logPrefix} å½“前环境=MP-WEIXIN,版本=`, currentVersion);
      return currentVersion;
@@ -45,14 +45,14 @@
    // APP-PLUS ä¸‹ï¼Œplus.runtime.version ä¸æ˜¯ä¸šåŠ¡ç‰ˆæœ¬å·ï¼ˆç»å¸¸æ˜¯è¿è¡Œæ—¶/SDK版本),
    // è¿™é‡Œæ”¹ç”¨ getProperty å–系统层面的 app version。
    // @ts-ignore
    if (plus?.runtime?.getProperty) {
    if (plus && plus.runtime && plus.runtime.getProperty) {
      // @ts-ignore
      const appid = plus.runtime.appid;
      const appInfo = await new Promise((resolve) => {
        // @ts-ignore
        plus.runtime.getProperty(appid, (info) => resolve(info || {}));
      });
      const v = appInfo?.version || appInfo?.versionName || appInfo?.appVersion || "";
      const v = (appInfo && appInfo.version) || (appInfo && appInfo.versionName) || (appInfo && appInfo.appVersion) || "";
      if (v) {
        currentVersion = String(v);
        console.log(`${logPrefix} å½“前环境=APP-PLUS,版本=`, currentVersion);
@@ -90,7 +90,7 @@
    },
    (err) => {
      console.log(`${logPrefix} å®‰è£…失败:`, err);
      uni.showToast({ title: err?.message || "安装更新包失败", icon: "none" });
      uni.showToast({ title: (err && err.message) || "安装更新包失败", icon: "none" });
    }
  );
  // #endif
@@ -101,7 +101,7 @@
  if (!u) return "";
  // å·²ç»æ˜¯ç»å¯¹åœ°å€ï¼Œç›´æŽ¥è¿”回
  if (/^https?:\/\//i.test(u)) return u;
  const base = String(config?.fileUrl || config?.baseUrl || "").replace(/\/+$/, "");
  const base = String((config && config.fileUrl) || (config && config.baseUrl) || "").replace(/\/+$/, "");
  const path = u.startsWith("/") ? u : `/${u}`;
  return `${base}${path}`;
}
vite.config.js
@@ -7,6 +7,10 @@
        build: {
            minify: true,
            outDir: 'dist',
            target: 'es2015',
        },
        esbuild: {
            target: 'es2015',
        },
        server: {
            port: '80'
@@ -20,9 +24,14 @@
        css: {
            preprocessorOptions: {
                scss: {
                    api: 'modern-compiler'
                    api: 'legacy'
                }
            }
        }
        },
        optimizeDeps: {
            esbuildOptions: {
                target: 'es2015',
            },
        },
    }
})