Compare commits

...

4 Commits
pro ... expo

Author SHA1 Message Date
kuaifan
2239ea9f58 fix(appbuild): 恢复 electron/build.js 的 'app' 分支做 post-processing
Phase 6 过度简化:把 cmd 的 appbuild 从 electron_operate app "$@" 改成纯
web_build prod,漏掉了 startBuild({id:'app'}) 里做的后处理:

  1. 生成 electron/public/config.js(window.systemInfo.apiUrl 等)
  2. 读 manifest.json 把 index.html 里 <!--style--> / <!--script--> 占位符
     替换为真实资源路径(vite manifest 模式,HTML 不自动注入)
  3. 拷贝 public/language/ 到 electron/public/language/

结果:裸 vite build 跑出来的 public/index.html 还是模板原型,dootask-app
同步过去之后 WebView 加载只能看见占位 HTML 或 404。

恢复方式:
- cmd: appbuild 回到 electron_operate app "$@",但提示词改为指向
  electron/public/ 而不是 resources/mobile(Phase 6 & 7 的 EEUI 清理保留)
- electron/build.js: 重新加回 argv 'app' 的 dispatch,调 startBuild
  (startBuild 内部的 data.id==='app' 分支里 EEUI 特有的逻辑仍然是
  Phase 6 删掉的状态,不会再碰 resources/mobile)

产物位置:electron/public/。用户同步到 dootask-app 的命令已更新到提示词。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 03:33:37 +00:00
kuaifan
4b34932468 chore(mobile): Phase 7 收尾 — 移除 resources/mobile 子模块 + 刷新发布文档
移动端已迁移到 dootask-app 独立仓库(Expo + EAS Build),主仓库不再承担移动端
打包(Phase 6 已断开 CI/构建链路),现在把 git 残留与文档一并清理。

- .gitmodules:删除 submodule "resources/mobile" 条目
- resources/mobile:git rm 子模块引用(目录本身由 .gitmodules 控制,已为空)
- README_PUBLISH.md "编译 App" 章节改写:指向 dootask-app 仓库的 EAS Build 流程,
  本地 appbuild 现在只负责前端资源(对齐 Phase 6 改动)

剩余无 mobile 残留的前端/后端 UA 兼容代码(Base.php / UserDevice.php 等)在
Phase 5 的 84f225f3f 里已经加了 dootask_expo 支持,保持向后兼容旧 EEUI 壳直到所有
用户升级。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 10:03:25 +00:00
kuaifan
f4b623deb6 chore(mobile): 移除 EEUI 构建链路(迁移至独立 dootask-app 仓库)
配合 dootask-app 仓库接入 Expo + EAS Build(见 docs/migration 的 Phase 6),
主仓库不再承担 iOS/Android 打包,保留前端资源构建即可。

- cmd:
  - appbuild/buildapp:从 electron_operate app 改为直接 web_build prod,
    构建完成后打印提示去 dootask-app 仓库跑 EAS Build / 同步 assets/web/
  - 移除 eeui 命令(docker run kuaifan/eeui-cli)
- electron/build.js:
  - 删除 data.id === 'app' 分支(Android local.properties / iOS Version.xcconfig
    回写、eeui build 调用)
  - 删除 argv 'app' 和 'android-upload' 两个命令
- .github/workflows/publish.yml:
  - build-client 矩阵移除 android;同时删除所有 (Android) 条件 steps(Build Js/
    Setup JDK/Build App/Upload File/Upload Release)
  - 保留 Electron 桌面端(mac / windows)分支
- .github/workflows/ios-publish.yml:整体删除(iOS 移动端构建搬去 dootask-app)

子模块 resources/mobile 的 .gitmodules 条目和目录引用留到 Phase 7 一起清理。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 09:56:07 +00:00
kuaifan
84f225f3f3 feat(mobile): 兼容新 Expo 壳(dootask_expo UA)
配合 dootask-app 仓库的 Expo 迁移(见 docs/migration-eeui-to-expo.md 的 Phase 5),
让服务端和前端同时识别旧 EEUI 壳与新 Expo 壳的 User-Agent,并让 eeui.js 的同步返回
方法在 Expo 壳下优先读取 injectedJS 启动时写入的 __EXPO_INIT_DATA__ / __EXPO_VARIATES__
缓存,避免原本同步 API 变成 Promise 后破坏调用方。

后端:
- Base::isEEUIApp():同时匹配 kuaifan_eeui / dootask_expo
- UserDevice:android_(kuaifan_eeui|dootask_expo) 正则捕获标识段,版本号按实际段名取
- IndexController PDF 预览:浏览器分类兼容 android_dootask_expo / ios_dootask_expo
- SystemController::prefetch:$isApp 同时接受两种 UA
- resources/views/download.blade.php:/eeui|dootask_expo/i

前端:
- app.js:
  - isEEUIApp 正则新增 dootask_expo
  - $preload 等待条件改为 requireModuleJs 可用 OR window.__EXPO_BRIDGE_READY__,
    避免 Expo 壳下等 15 秒超时
- eeui.js:以下几个同步 getter 在 Expo 壳下先读 window.__EXPO_* 再回落到原生:
  - eeuiAppVersion / eeuiAppLocalVersion → __EXPO_INIT_DATA__.version
  - eeuiAppGetPageInfo → __EXPO_INIT_DATA__.pageInfo
  - eeuiAppGetThemeName → __EXPO_INIT_DATA__.themeName
  - eeuiAppKeyboardStatus → __EXPO_INIT_DATA__.keyboardVisible
  - eeuiAppGetVariate → __EXPO_VARIATES__[key]
  - eeuiAppGetCachesString → __EXPO_CACHES__[key](RN 侧后续要同步 broadcast)

旧 EEUI 壳不受影响:只读缓存不存在时自动回落到原有 $A.eeuiModule() 调用,
行为与改动前一致。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 09:44:53 +00:00
14 changed files with 78 additions and 371 deletions

View File

@@ -1,193 +0,0 @@
name: "iOS Publish"
# Required GitHub Secrets:
#
# IOS_CERTIFICATE_BASE64 - Apple distribution certificate (.p12) encoded in base64
# IOS_CERTIFICATE_PASSWORD - Password for the .p12 certificate
# IOS_PROVISION_PROFILE_BASE64 - App Store provisioning profile (.mobileprovision) encoded in base64
# ASC_API_KEY_P8_BASE64 - App Store Connect API key (.p8) encoded in base64
# ASC_API_KEY_ID - App Store Connect API Key ID
# ASC_ISSUER_ID - App Store Connect Issuer ID
on:
workflow_dispatch:
jobs:
prepare-assets:
name: Prepare iOS Assets
runs-on: ubuntu-latest
outputs:
version: ${{ steps.get-version.outputs.version }}
steps:
- uses: actions/checkout@v4
- name: Get version from package.json
id: get-version
run: |
VERSION=$(node -p "require('./package.json').version")
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Version: $VERSION"
- uses: actions/setup-node@v4
with:
node-version: 20
- name: Install dependencies
run: npm install
- name: Install electron dependencies
run: |
pushd electron
npm install
popd
- name: Init mobile submodule
run: |
git submodule init
git submodule update --remote "resources/mobile"
- name: Build app assets
run: ./cmd appbuild publish
- name: Upload iOS platform artifacts
uses: actions/upload-artifact@v4
with:
name: ios-platform
path: resources/mobile/platforms/ios/
retention-days: 1
build-ios:
name: Build & Submit iOS
needs: prepare-assets
runs-on: macos-15
environment: build
steps:
- uses: actions/checkout@v4
- name: Init mobile submodule
run: |
git submodule init
git submodule update --remote "resources/mobile"
- name: Download prepared assets
uses: actions/download-artifact@v4
with:
name: ios-platform
path: resources/mobile/platforms/ios/
- name: Select Xcode
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: latest-stable
- name: Install CocoaPods
run: |
if [ -f "resources/mobile/platforms/ios/eeuiApp/Podfile" ]; then
cd resources/mobile/platforms/ios/eeuiApp
pod install
fi
- name: Import signing certificate
env:
IOS_CERTIFICATE_BASE64: ${{ secrets.IOS_CERTIFICATE_BASE64 }}
IOS_CERTIFICATE_PASSWORD: ${{ secrets.IOS_CERTIFICATE_PASSWORD }}
run: |
# Create temporary keychain
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
KEYCHAIN_PASSWORD=$(openssl rand -hex 20)
security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH"
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
# Import certificate
CERTIFICATE_PATH=$RUNNER_TEMP/certificate.p12
echo "$IOS_CERTIFICATE_BASE64" | base64 --decode > "$CERTIFICATE_PATH"
security import "$CERTIFICATE_PATH" \
-P "$IOS_CERTIFICATE_PASSWORD" \
-A \
-t cert \
-f pkcs12 \
-k "$KEYCHAIN_PATH"
security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security list-keychain -d user -s "$KEYCHAIN_PATH"
- name: Import provisioning profile
env:
IOS_PROVISION_PROFILE_BASE64: ${{ secrets.IOS_PROVISION_PROFILE_BASE64 }}
run: |
PROFILE_PATH=$RUNNER_TEMP/profile.mobileprovision
echo "$IOS_PROVISION_PROFILE_BASE64" | base64 --decode > "$PROFILE_PATH"
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
cp "$PROFILE_PATH" ~/Library/MobileDevice/Provisioning\ Profiles/
- name: Build archive
run: |
cd resources/mobile/platforms/ios/eeuiApp
xcodebuild archive \
-workspace eeuiApp.xcworkspace \
-scheme eeuiApp \
-configuration Release \
-archivePath $RUNNER_TEMP/eeuiApp.xcarchive \
-allowProvisioningUpdates \
CODE_SIGN_STYLE=Manual \
| xcpretty
- name: Export IPA
run: |
cd resources/mobile/platforms/ios/eeuiApp
# Generate ExportOptions.plist
cat > $RUNNER_TEMP/ExportOptions.plist << 'PLIST'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>method</key>
<string>app-store</string>
<key>uploadBitcode</key>
<false/>
<key>uploadSymbols</key>
<true/>
</dict>
</plist>
PLIST
xcodebuild -exportArchive \
-archivePath $RUNNER_TEMP/eeuiApp.xcarchive \
-exportOptionsPlist $RUNNER_TEMP/ExportOptions.plist \
-exportPath $RUNNER_TEMP/ipa-output \
-allowProvisioningUpdates \
| xcpretty
- name: Submit to App Store Connect
env:
ASC_API_KEY_ID: ${{ secrets.ASC_API_KEY_ID }}
ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
ASC_API_KEY_P8_BASE64: ${{ secrets.ASC_API_KEY_P8_BASE64 }}
run: |
# Prepare API key
mkdir -p ~/private_keys
echo "$ASC_API_KEY_P8_BASE64" | base64 --decode > ~/private_keys/AuthKey_${ASC_API_KEY_ID}.p8
# Find and upload IPA
IPA_PATH=$(find $RUNNER_TEMP/ipa-output -name "*.ipa" | head -1)
echo "Uploading: $IPA_PATH"
xcrun altool --upload-app \
-f "$IPA_PATH" \
--type ios \
--apiKey "$ASC_API_KEY_ID" \
--apiIssuer "$ASC_ISSUER_ID"
- name: Clean up
if: always()
run: |
security delete-keychain $RUNNER_TEMP/app-signing.keychain-db 2>/dev/null || true
rm -f $RUNNER_TEMP/certificate.p12
rm -f $RUNNER_TEMP/profile.mobileprovision
rm -rf ~/private_keys

View File

@@ -131,8 +131,6 @@ jobs:
include:
- platform: "macos-latest"
build_type: "mac"
- platform: "ubuntu-latest"
build_type: "android"
- platform: "windows-latest"
build_type: "windows"
@@ -147,69 +145,8 @@ jobs:
with:
node-version: 20.x
# Android 构建步骤
- name: (Android) Build Js
if: matrix.build_type == 'android'
uses: nick-fields/retry@v2
with:
timeout_minutes: 10
max_attempts: 3
command: |
git submodule init
git submodule update --remote "resources/mobile"
./cmd appbuild publish
- name: (Android) Setup JDK 11
if: matrix.build_type == 'android'
uses: actions/setup-java@v3
with:
distribution: "zulu"
java-version: "11"
- name: (Android) Build App
if: matrix.build_type == 'android'
uses: nick-fields/retry@v2
with:
timeout_minutes: 20
max_attempts: 5
command: |
cd resources/mobile/platforms/android/eeuiApp
chmod +x ./gradlew
./gradlew assembleRelease --quiet
- name: (Android) Upload File
if: matrix.build_type == 'android'
env:
UPLOAD_TOKEN: ${{ secrets.UPLOAD_TOKEN }}
UPLOAD_URL: ${{ secrets.UPLOAD_URL }}
run: |
node ./electron/build.js android-upload
- name: (Android) Upload Release
if: matrix.build_type == 'android'
uses: actions/github-script@v7
env:
RELEASE_ID: ${{ needs.create-release.outputs.release_id }}
with:
script: |
const fs = require('fs');
const path = require('path');
const globby = require('globby');
// 查找 APK 文件
const files = await globby('resources/mobile/platforms/android/eeuiApp/app/build/outputs/apk/release/*.apk');
for (const file of files) {
const data = await fs.promises.readFile(file);
await github.rest.repos.uploadReleaseAsset({
owner: context.repo.owner,
repo: context.repo.repo,
release_id: process.env.RELEASE_ID,
name: path.basename(file),
data: data
});
}
# 移动端构建已迁移到 dootask-app 仓库EAS Build
# dootask-app/.github/workflows/build.yml
# Mac 构建步骤
- name: (Mac) Build Client

3
.gitmodules vendored
View File

@@ -1,6 +1,3 @@
[submodule "resources/drawio"]
path = resources/drawio
url = https://github.com/jgraph/drawio.git
[submodule "resources/mobile"]
path = resources/mobile
url = https://github.com/kuaifan/dootask-app.git

View File

@@ -18,14 +18,25 @@ npm run build # 编译前端
说明:
- 执行 `npm run build` 作用是生成网页端;
- 客户端 (Windows、Mac、Android) 会通过 GitHub Actions 自动生成并发布所以如果要自动发布只需要提交git并推送即可
- 如果想手动生成客户端执行 `./cmd electron` 根据提示选择操作。
- 桌面客户端Windows、Mac会通过 GitHub Actions 自动生成并发布;所以,如果要自动发布只需要提交 git 并推送即可;
- 如果想手动生成桌面客户端执行 `./cmd electron` 根据提示选择操作。
## 编译移动端 App
## 编译 App
移动端iOS / Android已迁移到独立仓库 [kuaifan/dootask-app](https://github.com/kuaifan/dootask-app)
Expo + EAS Build。构建流程
```shell
./cmd appbuild publish # 编译生成App需要的资源
# 1. 本仓库:构建前端资源
./cmd appbuild
# 2. 拷贝到 dootask-app 仓库
cp -r public/* ~/workspaces/dootask-app/assets/web/
# 3. dootask-app 仓库EAS Build 本地或 CI 触发
cd ~/workspaces/dootask-app
npx eas build --platform android --profile preview
# 或在 dootask-app 的 GitHub Actions 里手动触发 "EAS Build" workflow
```
编译完后进入 `resources/mobile` EEUI框架目录内打包 Android 或 iOS 应用Android 以实现 GitHub Actions 自动发布)
详见 dootask-app 仓库的 README。

View File

@@ -1557,7 +1557,8 @@ class SystemController extends AbstractController
{
$userAgent = strtolower(Request::server('HTTP_USER_AGENT'));
$isMain = str_contains($userAgent, 'maintaskwindow');
$isApp = str_contains($userAgent, 'kuaifan_eeui');
$isApp = str_contains($userAgent, 'kuaifan_eeui')
|| str_contains($userAgent, 'dootask_expo');
$version = Base::getVersion();
$array = [];

View File

@@ -457,12 +457,16 @@ class IndexController extends InvokeController
'button' => Doo::translate('点击下载'),
]);
}
// 浏览器类型
// 浏览器类型(兼容旧 EEUI 与新 Expo 壳)
$browser = 'none';
if (str_contains($userAgent, 'chrome') || str_contains($userAgent, 'android_kuaifan_eeui')) {
$browser = str_contains($userAgent, 'android_kuaifan_eeui') ? 'android-mobile' : 'chrome-desktop';
} elseif (str_contains($userAgent, 'safari') || str_contains($userAgent, 'ios_kuaifan_eeui')) {
$browser = str_contains($userAgent, 'ios_kuaifan_eeui') ? 'safari-mobile' : 'safari-desktop';
$isAndroidApp = str_contains($userAgent, 'android_kuaifan_eeui')
|| str_contains($userAgent, 'android_dootask_expo');
$isIosApp = str_contains($userAgent, 'ios_kuaifan_eeui')
|| str_contains($userAgent, 'ios_dootask_expo');
if (str_contains($userAgent, 'chrome') || $isAndroidApp) {
$browser = $isAndroidApp ? 'android-mobile' : 'chrome-desktop';
} elseif (str_contains($userAgent, 'safari') || $isIosApp) {
$browser = $isIosApp ? 'safari-mobile' : 'safari-desktop';
}
// electron 直接在线预览查看
if (str_contains($userAgent, 'electron') || str_contains($browser, 'desktop')) {

View File

@@ -129,8 +129,8 @@ class UserDevice extends AbstractModel
}
}
if (preg_match("/android_kuaifan_eeui/i", $ua)) {
// Android 客户端
if (preg_match("/android_(kuaifan_eeui|dootask_expo)/i", $ua, $m)) {
// Android 客户端(兼容旧 EEUI 与新 Expo 壳)
$result['app_type'] = 'Android';
if ($dd->getBrandName() && $dd->getModel()) {
// 厂商+型号
@@ -145,9 +145,9 @@ class UserDevice extends AbstractModel
// 平板
$result['app_name'] = 'Phablet';
}
$result['app_version'] = self::getAfterVersion($ua, 'kuaifan_eeui/');
} elseif (preg_match("/ios_kuaifan_eeui/i", $ua)) {
// iOS 客户端
$result['app_version'] = self::getAfterVersion($ua, $m[1] . '/');
} elseif (preg_match("/ios_(kuaifan_eeui|dootask_expo)/i", $ua, $m)) {
// iOS 客户端(兼容旧 EEUI 与新 Expo 壳)
$result['app_type'] = 'iOS';
if (preg_match("/(macintosh|ipad)/i", $ua)) {
// iPad
@@ -156,7 +156,7 @@ class UserDevice extends AbstractModel
// iPhone
$result['app_name'] = 'iPhone';
}
$result['app_version'] = self::getAfterVersion($ua, 'kuaifan_eeui/');
$result['app_version'] = self::getAfterVersion($ua, $m[1] . '/');
} elseif (preg_match("/dootask/i", $ua)) {
// DooTask 客户端
$result['app_type'] = $osInfo['name'];

View File

@@ -1841,13 +1841,14 @@ class Base
}
/**
* 是否是App移动端
* 是否是App移动端(兼容旧 EEUI 壳与新 Expo 壳)
* @return bool
*/
public static function isEEUIApp()
{
$userAgent = strtolower(Request::server('HTTP_USER_AGENT'));
return str_contains($userAgent, 'kuaifan_eeui');
return str_contains($userAgent, 'kuaifan_eeui')
|| str_contains($userAgent, 'dootask_expo');
}
/**

18
cmd
View File

@@ -833,23 +833,19 @@ case "$1" in
;;
"appbuild"|"buildapp")
shift 1
# 移动端已迁移到独立仓库 dootask-appExpo + EAS Build但前端资源的
# post-processing生成 config.js、把 manifest 里的 css/js 注入 index.html、
# 拷贝 language/)仍然走 electron/build.js 的 startBuild({id:'app'}) 分支。
# 产物在 electron/public/;实际移动端打包在 dootask-app 仓库执行。
electron_operate app "$@"
echo ""
echo "前端资源已构建至 electron/public/"
echo "同步到 dootask-appcp -r electron/public/* ~/wwwroot/dootask-app/assets/web/"
;;
"electron")
shift 1
electron_operate "$@"
;;
"eeui")
shift 1
cli="$@"
por=""
if [[ "$cli" == "build" ]]; then
cli="build --simple"
elif [[ "$cli" == "dev" ]]; then
por="-p 8880:8880"
fi
docker run $TTY_FLAG --rm -v ${WORK_DIR}/resources/mobile:/work -w /work ${por} kuaifan/eeui-cli:0.0.1 eeui ${cli}
;;
"npm")
shift 1
npm "$@"

70
electron/build.js vendored
View File

@@ -566,42 +566,9 @@ async function startBuild(data) {
indexString = indexString.replace("<!--script-->", `<script type="module" src="./${manifestContent['resources/assets/js/app.js']['file']}"></script>`);
fs.writeFileSync(indexFile, indexString, 'utf8');
//
if (data.id === 'app') {
const eeuiDir = path.resolve(__dirname, "../resources/mobile");
const eeuiRun = `docker run --rm -v ${eeuiDir}:/work -w /work kuaifan/eeui-cli:0.0.1`
const publicDir = path.resolve(__dirname, "../resources/mobile/src/public");
fse.removeSync(publicDir)
fse.copySync(electronDir, publicDir)
if (argv[3] === "publish") {
// Android config
const gradleFile = path.resolve(eeuiDir, "platforms/android/eeuiApp/local.properties")
let gradleResult = fs.existsSync(gradleFile) ? fs.readFileSync(gradleFile, 'utf8') : "";
gradleResult = gradleResult.replace(/(versionCode|versionName)\s*=\s*(.+?)(\n|$)/g, '')
gradleResult += `versionCode = ${config.codeVerson}\nversionName = ${config.version}\n`
fs.writeFileSync(gradleFile, gradleResult, 'utf8')
// iOS config
const xcconfigFile = path.resolve(eeuiDir, "platforms/ios/eeuiApp/Config/Version.xcconfig")
let xcconfigResult = fs.existsSync(xcconfigFile) ? fs.readFileSync(xcconfigFile, 'utf8') : "";
xcconfigResult = xcconfigResult.replace(/(VERSION_CODE|VERSION_NAME)\s*=\s*(.+?)(\n|$)/g, '')
xcconfigResult += `VERSION_CODE = ${config.codeVerson}\nVERSION_NAME = ${config.version}\n`
fs.writeFileSync(xcconfigFile, xcconfigResult, 'utf8')
}
if (['build', 'publish'].includes(argv[3])) {
if (!fs.existsSync(path.resolve(eeuiDir, "node_modules"))) {
child_process.execSync(`${eeuiRun} npm install`, {stdio: "inherit", cwd: "resources/mobile"});
}
child_process.execSync(`${eeuiRun} eeui build --simple`, {stdio: "inherit", cwd: "resources/mobile"});
} else {
[
path.resolve(publicDir, "../../platforms/ios/eeuiApp/bundlejs/eeui/public"),
path.resolve(publicDir, "../../platforms/android/eeuiApp/app/src/main/assets/eeui/public"),
].some(dir => {
fse.removeSync(dir)
fse.copySync(electronDir, dir)
})
}
return;
}
// 注移动端data.id === 'app')的 EEUI 打包逻辑已随着迁移到 dootask-app 仓库
// 而移除。前端资源现在通过 ./cmd appbuild= web_build prod构建到 public/
// 实际的 iOS/Android 打包在 dootask-app 仓库用 EAS Build 执行。
const output = `dist/${data.id.replace(/\./g, '-')}/${platform}`
// package.json Backup
fse.copySync(packageFile, packageBakFile)
@@ -698,12 +665,7 @@ if (["dev"].includes(argv[2])) {
child_process.spawn("npx", ["vite", "--", "fromcmd", "electronDev"], {stdio: "inherit"});
child_process.spawn("npm", ["run", "start-quiet"], {stdio: "inherit", cwd: "electron"});
} else if (["app"].includes(argv[2])) {
// 编译前端页面给 App
let mobileSrcDir = path.resolve(__dirname, "../resources/mobile");
if (!fs.existsSync(mobileSrcDir)) {
console.error("resources/mobile 未找到");
process.exit()
}
// 编译前端页面给移动端 Appdootask-app 仓库消费 electron/public/ 目录)
startBuild({
name: 'App',
id: 'app',
@@ -717,30 +679,6 @@ if (["dev"].includes(argv[2])) {
notarize: false,
}
})
} else if (["android-upload"].includes(argv[2])) {
// 上传安卓文件GitHub Actions
(async () => {
const publisher = createPublisher()
if (!publisher) {
console.error("缺少 UPLOAD_TOKEN 或 UPLOAD_URL 环境变量")
process.exit(1)
}
const releaseDir = path.resolve(__dirname, "../resources/mobile/platforms/android/eeuiApp/app/build/outputs/apk/release");
if (!fs.existsSync(releaseDir)) {
console.error("发布文件未找到")
process.exit(1)
}
const files = fs.readdirSync(releaseDir)
for (const filename of files) {
const localFile = path.join(releaseDir, filename)
if (/\.apk$/.test(filename) && fs.existsSync(localFile) && fs.statSync(localFile).isFile()) {
await publisher.uploadPackage(localFile, { platform: 'android' })
}
}
})().catch(err => {
console.error(err.message || err)
process.exit(1)
})
} else if (["release"].includes(argv[2])) {
// 通知官网发布完成GitHub Actions
(async () => {

View File

@@ -1,5 +1,5 @@
const isElectron = !!(window && window.process && window.process.type && window.electron);
const isEEUIApp = window && window.navigator && /eeui/i.test(window.navigator.userAgent);
const isEEUIApp = window && window.navigator && /eeui|dootask_expo/i.test(window.navigator.userAgent);
const isSoftware = isElectron || isEEUIApp;
document.getElementById("app")?.setAttribute("data-preload", "false");
@@ -325,14 +325,15 @@ const $preload = async () => {
document.getElementById("app")?.setAttribute("data-preload", "true")
if ($A.isEEUIApp) {
// 同时等待旧 EEUI 的 requireModuleJs 与新 Expo 壳注入的 __EXPO_BRIDGE_READY__
const requireTime = new Date().getTime();
while (typeof requireModuleJs !== "function") {
while (typeof requireModuleJs !== "function" && !window.__EXPO_BRIDGE_READY__) {
await new Promise(resolve => setTimeout(resolve, 200));
if (new Date().getTime() - requireTime > 15 * 1000) {
break
}
}
if (typeof requireModuleJs !== "function") {
if (typeof requireModuleJs !== "function" && !window.__EXPO_BRIDGE_READY__) {
const errorTip = $A.L("加载失败,请重启软件")
const errorView = document.querySelector(".app-view-loading")
if (errorView) {

View File

@@ -35,14 +35,14 @@ import {languageName} from "../language";
})
},
// 获取eeui版本号
// 获取eeui版本号Expo 壳下改为读取启动时注入的 __EXPO_INIT_DATA__兼容旧 EEUI
eeuiAppVersion() {
return $A.eeuiModule()?.getVersion();
return window.__EXPO_INIT_DATA__?.version ?? $A.eeuiModule()?.getVersion();
},
// 获取本地软件版本号
eeuiAppLocalVersion() {
return $A.eeuiModule()?.getLocalVersion();
return window.__EXPO_INIT_DATA__?.version ?? $A.eeuiModule()?.getLocalVersion();
},
// Alert
@@ -61,8 +61,12 @@ import {languageName} from "../language";
return $A.eeuiModule()?.rewriteUrl(val);
},
// 获取页面信息
// 获取页面信息Expo 壳在 injectedJS 启动时写入 __EXPO_INIT_DATA__.pageInfo
eeuiAppGetPageInfo(pageName) {
const cached = window.__EXPO_INIT_DATA__?.pageInfo;
if (cached) {
return pageName ? { ...cached, pageName } : cached;
}
return $A.eeuiModule()?.getPageInfo(pageName || "");
},
@@ -152,13 +156,16 @@ import {languageName} from "../language";
$A.eeuiModule()?.checkUpdate();
},
// 获取主题名称 light|dark
// 获取主题名称 light|darkExpo 壳:启动时注入 + 系统变更推送更新 __EXPO_INIT_DATA__.themeName
eeuiAppGetThemeName() {
return $A.eeuiModule()?.getThemeName();
return window.__EXPO_INIT_DATA__?.themeName ?? $A.eeuiModule()?.getThemeName();
},
// 判断软键盘是否可见
// 判断软键盘是否可见Expo 壳keyboardDidShow/Hide 会同步更新 __EXPO_INIT_DATA__.keyboardVisible
eeuiAppKeyboardStatus() {
if (window.__EXPO_INIT_DATA__ && typeof window.__EXPO_INIT_DATA__.keyboardVisible === "boolean") {
return window.__EXPO_INIT_DATA__.keyboardVisible;
}
return $A.eeuiModule()?.keyboardStatus();
},
@@ -167,8 +174,12 @@ import {languageName} from "../language";
$A.eeuiModule()?.setVariate(key, value);
},
// 获取全局变量
// 获取全局变量Expo 壳setVariate 时 RN 会 broadcast 到所有 WebView 的 __EXPO_VARIATES__
eeuiAppGetVariate(key, defaultVal = "") {
const cache = window.__EXPO_VARIATES__;
if (cache && Object.prototype.hasOwnProperty.call(cache, key)) {
return cache[key];
}
return $A.eeuiModule()?.getVariate(key, defaultVal);
},
@@ -177,8 +188,12 @@ import {languageName} from "../language";
$A.eeuiModule()?.setCachesString(key, value, expired);
},
// 获取缓存数据
// 获取缓存数据Expo 壳:若 __EXPO_CACHES__ 已 hydrate 就同步读取,否则回落到原生桥)
eeuiAppGetCachesString(key, defaultVal = "") {
const cache = window.__EXPO_CACHES__;
if (cache && Object.prototype.hasOwnProperty.call(cache, key)) {
return cache[key];
}
return $A.eeuiModule()?.getCachesString(key, defaultVal);
},

Submodule resources/mobile deleted from 4e8ee678b3

View File

@@ -78,7 +78,7 @@
document.body.classList.add("dark");
}
//
const isEEUIApp = window && window.navigator && /eeui/i.test(window.navigator.userAgent);
const isEEUIApp = window && window.navigator && /eeui|dootask_expo/i.test(window.navigator.userAgent);
if (isEEUIApp) {
document.querySelector(".link").addEventListener('click', function (e) {
e.preventDefault();