@ybais/create-ybais-app 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -0
- package/dist/index.js +371 -0
- package/package.json +34 -0
- package/template/.cursor/rules/ybais-app.mdc +21 -0
- package/template/.cursor/skills/ybais-app/SKILL.md +140 -0
- package/template/.cursor/skills/ybais-app/reference.md +180 -0
- package/template/.env +2 -0
- package/template/.env.prod +8 -0
- package/template/.env.test +8 -0
- package/template/.mise/tasks/build +107 -0
- package/template/.oxlintrc.json +22 -0
- package/template/AGENTS.md +25 -0
- package/template/README.md +251 -0
- package/template/dist/assets/css/index-bQrzgOlr.css +1 -0
- package/template/dist/assets/js/dist-CX33qWwE.js +2 -0
- package/template/dist/assets/js/dist-legacy-BnxRxjXx.js +1 -0
- package/template/dist/assets/js/index-BNqkt-js.js +2 -0
- package/template/dist/assets/js/index-legacy-DPO2FCfH.js +1 -0
- package/template/dist/assets/js/polyfills-Dsaw-v00.js +1 -0
- package/template/dist/assets/js/polyfills-legacy-DyZJWH2U.js +1 -0
- package/template/dist/assets/js/rolldown-runtime-LhxUO5vB.js +1 -0
- package/template/dist/assets/js/rolldown-runtime-legacy-BptCTa2h.js +1 -0
- package/template/dist/assets/js/routes-D2ibRZk6.js +1 -0
- package/template/dist/assets/js/routes-legacy-DGEWjKnS.js +1 -0
- package/template/dist/assets/js/vconsole.min-BcOaunN2.js +1 -0
- package/template/dist/assets/js/vconsole.min-legacy-Bub-moxG.js +1 -0
- package/template/dist/favicon.svg +1 -0
- package/template/dist/index.html +29 -0
- package/template/dist/version.json +4 -0
- package/template/index.html +19 -0
- package/template/mise.toml +11 -0
- package/template/package.json +60 -0
- package/template/plugins/app-version.ts +88 -0
- package/template/pnpm-lock.yaml +3359 -0
- package/template/pnpm-workspace.yaml +7 -0
- package/template/postcss.config.js +24 -0
- package/template/public/favicon.svg +1 -0
- package/template/scripts/run-build.mjs +5 -0
- package/template/scripts/setup-mise.mjs +47 -0
- package/template/scripts/with-mise.mjs +107 -0
- package/template/src/api/alova.d.ts +7 -0
- package/template/src/api/alova.ts +50 -0
- package/template/src/api/index.ts +8 -0
- package/template/src/api/qiniu.ts +8 -0
- package/template/src/api/uploader.ts +25 -0
- package/template/src/components/NotFoundComponent/index.tsx +48 -0
- package/template/src/components/RouterErrorComponent/index.tsx +27 -0
- package/template/src/components/index.ts +2 -0
- package/template/src/lib/README.md +17 -0
- package/template/src/main.tsx +64 -0
- package/template/src/routeTree.gen.ts +59 -0
- package/template/src/router.ts +10 -0
- package/template/src/routes/__root.tsx +10 -0
- package/template/src/routes/index.tsx +39 -0
- package/template/src/styles/index.css +42 -0
- package/template/src/styles/variant.css +18 -0
- package/template/src/styles/webview-compat.css +49 -0
- package/template/src/utils/cn.ts +63 -0
- package/template/src/vite-env.d.ts +41 -0
- package/template/tailwind.config.js +14 -0
- package/template/tsconfig.app.json +37 -0
- package/template/tsconfig.json +7 -0
- package/template/tsconfig.node.json +28 -0
- package/template/vite.config.ts +131 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# YBAIS App — Skill 扩展参考
|
|
2
|
+
|
|
3
|
+
SKILL.md 放强制约定;本文件补充决策细节与文档索引。按需阅读。
|
|
4
|
+
|
|
5
|
+
## WebView 兼容(与 `vite.config.ts` 对齐)
|
|
6
|
+
|
|
7
|
+
运行环境是 **App 内嵌在线 WebView**,不是「最新 Chrome 桌面站」。最低版本以构建配置为准:
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
// vite.config.ts — @vitejs/plugin-legacy
|
|
11
|
+
targets: ["ios >= 12", "android >= 7", "chrome >= 55", "safari >= 12"]
|
|
12
|
+
// build.cssTarget: "chrome55"
|
|
13
|
+
// additionalLegacy/ModernPolyfills:
|
|
14
|
+
// regenerator-runtime、abortcontroller-polyfill、
|
|
15
|
+
// queueMicrotask、Promise.allSettled、String.replaceAll
|
|
16
|
+
// (modern 默认按 chrome>=105 收集,会漏掉 App WebView;Web API 亦不在 core-js usage 内)
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
| 项 | 要求 |
|
|
20
|
+
|----|------|
|
|
21
|
+
| iOS | ≥ 12(WKWebView / Safari 12 心智) |
|
|
22
|
+
| Android | ≥ 7(系统 WebView,内核可能偏旧) |
|
|
23
|
+
| Chrome / Safari | ≥ 55 / ≥ 12 |
|
|
24
|
+
| CSS | 压缩目标 `chrome55`;避免仅靠新特性且无降级 |
|
|
25
|
+
| 图片 | 默认勿强制 webp |
|
|
26
|
+
| Vite | **保留** legacy;**勿**另设 `build.target` 与插件抢控制权 |
|
|
27
|
+
| 产物 | modern + legacy 双 chunk + polyfills;发版提交整包 `dist/` |
|
|
28
|
+
| 已知必补 Web/ES | `AbortController`;`queueMicrotask`;`Promise.allSettled`;`String.replaceAll`(TanStack 路径解码) |
|
|
29
|
+
| 全局补丁 | [`src/styles/webview-compat.css`](../../../src/styles/webview-compat.css)(`-webkit-user-select`、sonner gap/inset 降级) |
|
|
30
|
+
|
|
31
|
+
### 三维排查备忘(依赖 / 自研 / CSS)
|
|
32
|
+
|
|
33
|
+
**依赖**:React 18 `createRoot` 可保留;alova/TanStack 依赖的 AbortController、allSettled、queueMicrotask、replaceAll 须在 `additional*Polyfills`;sonner 运行时 CSS **绕过** PostCSS,靠 `webview-compat.css` 覆盖。
|
|
34
|
+
|
|
35
|
+
**自研**:`dataURLtoFile` 须对 `File` 构造 feature detect;复制用 `@ybais/clipboard`(勿裸用 `navigator.clipboard`);业务代码避免直接依赖未 polyfill 的 Web API。
|
|
36
|
+
|
|
37
|
+
**CSS 禁止(业务源码)**:
|
|
38
|
+
|
|
39
|
+
- `100dvh` / `svh` / `lvh`(用 `min-h-screen` / `100vh`)
|
|
40
|
+
- flex/grid **`gap-*`**(用 `mt-*` / `ml-*` / `space-y-*`)
|
|
41
|
+
- `max()` 写进组件 props 当样式(如 sonner `mobileOffset`);用 `calc(12px + var(--sat, 0px))` 或固定 px
|
|
42
|
+
- 依赖 `inset`、`overflow-wrap: anywhere`、`:has()`、`oklch`、`@container` 且无降级
|
|
43
|
+
|
|
44
|
+
实现与评审时的默认假设:
|
|
45
|
+
|
|
46
|
+
- 语法可写现代 TS/ES,由 legacy + core-js / regenerator 兜底;**业务逻辑不要依赖**「仅很新内核才有」的 API(或需 feature detect + 降级)。
|
|
47
|
+
- 样式优先移动端安全区与小屏;**禁止**上述 CSS 清单;慎用 `backdrop-filter`、新色域/容器查询等无降级方案。
|
|
48
|
+
- Bridge 仅在注入后的 WebView 可用;浏览器预览须处理 `BridgeUnavailableError`。
|
|
49
|
+
- 鸿蒙等若注入 `AndroidBridge`,仍走现有 bridge 封装,勿另造通道。
|
|
50
|
+
- Tailwind `:where()` preflight 在 Safari 12 可能部分失效,业务页用显式 utility,不因此降 Tailwind 大版本。
|
|
51
|
+
|
|
52
|
+
产品若调整 App 最低系统版本:先改 `vite.config.ts` 的 `targets` / `cssTarget`,再改 SKILL、本文件与 README「兼容性」说明,保持三处一致。
|
|
53
|
+
|
|
54
|
+
## 技术栈锁定
|
|
55
|
+
|
|
56
|
+
- React 18.3 + TypeScript + Vite 8
|
|
57
|
+
- TanStack Router(file routes + autoCodeSplitting)
|
|
58
|
+
- Alova + fetch adapter + ReactHook
|
|
59
|
+
- Tailwind 3 + postcss-px-to-viewport
|
|
60
|
+
- `@vitejs/plugin-legacy`:**iOS ≥ 12 / Android ≥ 7 / Chrome ≥ 55 / Safari ≥ 12**,`cssTarget: chrome55`
|
|
61
|
+
- pnpm + mise 分支构建(`mise.toml`:`node = "26"` / `pnpm = "11"`;engines 下限 Node `>=26.4.0`、pnpm `>=11.9.0`)
|
|
62
|
+
|
|
63
|
+
不要默认换成 Next.js、React Router、axios、yarn/npm 作为项目标准,除非用户明确要求迁移。
|
|
64
|
+
|
|
65
|
+
## 环境与模式
|
|
66
|
+
|
|
67
|
+
| Mode | 典型用途 | 环境文件 |
|
|
68
|
+
|------|----------|----------|
|
|
69
|
+
| `test` | 日常开发 / 测试包 | `.env.test` |
|
|
70
|
+
| `prod` | 生产包 | `.env.prod` |
|
|
71
|
+
|
|
72
|
+
- `pnpm build`:Git 分支名 `master` → `build:prod`,否则 `build:test`
|
|
73
|
+
- 构建摘要:`.mise/tasks/build`
|
|
74
|
+
- 版本注入:`plugins/app-version.ts` → meta `app-version` + `version.json`
|
|
75
|
+
- 覆盖版本:构建时设 `APP_VERSION`
|
|
76
|
+
|
|
77
|
+
## 导入约定
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
import { callNative } from "@ybais/bridge";
|
|
81
|
+
import { alovaInstance } from "@/api/alova";
|
|
82
|
+
import { uploader } from "@/api/uploader";
|
|
83
|
+
import { startUpdater } from "@ybais/updater";
|
|
84
|
+
import { reportError } from "@ybais/error-guard";
|
|
85
|
+
import { useNativeCall, useNativeCallback } from "@ybais/bridge/react";
|
|
86
|
+
import { requestCrypto, md5 } from "@ybais/crypto";
|
|
87
|
+
import { useCopyText } from "@ybais/clipboard/react";
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
应用装配(env / Native 副作用)放在 `src/api/`;共享实现在 `@ybais/*`。
|
|
91
|
+
|
|
92
|
+
## API 层模式
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
// src/api/example.ts
|
|
96
|
+
import { alovaInstance } from "@/api/alova";
|
|
97
|
+
|
|
98
|
+
export const fetchFoo = () => alovaInstance.Get<Foo>("/api/foo");
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
- 成功业务码:`SUCCESS_CODE`(默认 200)
|
|
102
|
+
- 非成功:`businessCodeHandlers`(如 `301` → `goLogin`,经 `createYbaisAlova({ onAuthFail })`),再抛 `ApiError`
|
|
103
|
+
- `returnFullResponse`:不自动抛业务错;用于**按 code 做接口级业务判断**,调用方先分支再视情况 `unwrapApiResponse`
|
|
104
|
+
- 错误体系:`page` / `business` / `runtime` / `debug` / `chunk`。业务可恢复 → `reportError`/`showApiError`;调试 → **`reportDebug`**;用户向失败文案 → **`showCenterToast`(仅此)**。
|
|
105
|
+
- vConsole(可选):仅 test 且非 master;URL `?vconsole=1` / `enableVConsole()`;prod/master 杜绝。
|
|
106
|
+
|
|
107
|
+
## Bridge 模式
|
|
108
|
+
|
|
109
|
+
| 场景 | API |
|
|
110
|
+
|------|-----|
|
|
111
|
+
| 请求-响应调 Native | `callNative` 或 MessageHandle 封装(`goLogin` / `pay` 等) |
|
|
112
|
+
| Native 推命名回调 | `onNative` / `offNative` 或 `useNativeCallback` |
|
|
113
|
+
| 扩展回调 payload 类型 | `NativeCallbackMap`(`@ybais/bridge`) |
|
|
114
|
+
|
|
115
|
+
协议文档见 `@ybais/bridge` 包内 `原生与web交互MessageHandle.md`。
|
|
116
|
+
|
|
117
|
+
Native 侧示例:`window.AuthCallback("success" \| "fail")`、`window.authSuccess()`、`window.faceAuthSuccess()`。
|
|
118
|
+
|
|
119
|
+
## 上传模式
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
import { uploader } from "@/api/uploader";
|
|
123
|
+
import {
|
|
124
|
+
compressQiniuImageUrl,
|
|
125
|
+
buildImageView2Query,
|
|
126
|
+
} from "@ybais/qiniu-upload";
|
|
127
|
+
// 只要拼 URL、不要直传:from "@ybais/qiniu-upload/image-process"
|
|
128
|
+
|
|
129
|
+
await uploader.upload(file, { prefix: "avatar", onProgress: (p) => {} });
|
|
130
|
+
compressQiniuImageUrl("https://cdn.example.com/photo.jpg");
|
|
131
|
+
uploader.compressKey("upload/abc.jpg");
|
|
132
|
+
await uploader.uploadDataURL(dataUrl, "sign.png");
|
|
133
|
+
uploader.abort();
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
默认 WebView 友好 imageView2:`mode: 2, w: 750, q: 75, ignore-error/1`,不转 webp。支持直接传入七牛 URL 或 key+domain 压缩。
|
|
137
|
+
|
|
138
|
+
## 发版与托管(在线 WebView)
|
|
139
|
+
|
|
140
|
+
约定:**构建产物 `dist/` 提交进 Git**,由仓库同步到 WebView 可访问的静态地址(或 GitLab Pages / 拉取仓库目录)。
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
pnpm build # 或 build:prod / build:test
|
|
144
|
+
git add dist
|
|
145
|
+
git commit -m "chore: release dist"
|
|
146
|
+
git push
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
注意:
|
|
150
|
+
|
|
151
|
+
- 每次发版替换**整包** `dist/`(含 `index.html`、`assets/*`、`version.json`)
|
|
152
|
+
- `.gitignore` **不要**忽略 `dist`
|
|
153
|
+
- 若前面还有 CDN/Nginx,缓存仍建议:`index.html` / `version.json` → `no-cache`;`assets/*` → 长缓存
|
|
154
|
+
|
|
155
|
+
updater 依赖发布后可匿名(或 App 内)GET 到新的 `version.json`。
|
|
156
|
+
|
|
157
|
+
## 目录速查
|
|
158
|
+
|
|
159
|
+
```
|
|
160
|
+
src/
|
|
161
|
+
routes/ # 页面
|
|
162
|
+
api/ # 接口
|
|
163
|
+
lib/bridge|alova|upload|crypto|updater|error-guard/
|
|
164
|
+
styles/
|
|
165
|
+
main.tsx
|
|
166
|
+
plugins/app-version.ts
|
|
167
|
+
.mise/tasks/build
|
|
168
|
+
.env / .env.test / .env.prod
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## 完整模块 README
|
|
172
|
+
|
|
173
|
+
- [../../../README.md](../../../README.md)
|
|
174
|
+
- [../../../src/lib/README.md](../../../src/lib/README.md)
|
|
175
|
+
- [../../../src/lib/bridge/README.md](../../../src/lib/bridge/README.md)
|
|
176
|
+
- [../../../src/lib/alova/README.md](../../../src/lib/alova/README.md)
|
|
177
|
+
- [../../../src/lib/upload/README.md](../../../src/lib/upload/README.md)
|
|
178
|
+
- [../../../src/lib/crypto/README.md](../../../src/lib/crypto/README.md)
|
|
179
|
+
- [../../../src/lib/updater/README.md](../../../src/lib/updater/README.md)
|
|
180
|
+
- [../../../src/lib/error-guard/README.md](../../../src/lib/error-guard/README.md)
|
package/template/.env
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#MISE description="master → prod build, otherwise test"
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
BOLD=$(printf '\033[1m')
|
|
6
|
+
DIM=$(printf '\033[2m')
|
|
7
|
+
CYAN=$(printf '\033[36m')
|
|
8
|
+
GREEN=$(printf '\033[32m')
|
|
9
|
+
YELLOW=$(printf '\033[33m')
|
|
10
|
+
RESET=$(printf '\033[0m')
|
|
11
|
+
|
|
12
|
+
branch="$(git branch --show-current)"
|
|
13
|
+
if [ "$branch" = "master" ]; then
|
|
14
|
+
mode="prod"
|
|
15
|
+
script="build:prod"
|
|
16
|
+
else
|
|
17
|
+
mode="test"
|
|
18
|
+
script="build:test"
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
# 按 Vite 优先级合并:.env → .env.local → .env.[mode] → .env.[mode].local
|
|
22
|
+
# 输出: max_key\tKEY\tVALUE\tSOURCE
|
|
23
|
+
merged_env_tsv() {
|
|
24
|
+
local mode="$1"
|
|
25
|
+
local tmp max_key key val file line
|
|
26
|
+
|
|
27
|
+
tmp="$(mktemp)"
|
|
28
|
+
# shellcheck disable=SC2064
|
|
29
|
+
trap "rm -f '$tmp'" EXIT
|
|
30
|
+
|
|
31
|
+
for file in \
|
|
32
|
+
".env" \
|
|
33
|
+
".env.local" \
|
|
34
|
+
".env.${mode}" \
|
|
35
|
+
".env.${mode}.local"
|
|
36
|
+
do
|
|
37
|
+
[ -f "$file" ] || continue
|
|
38
|
+
while IFS= read -r line || [ -n "$line" ]; do
|
|
39
|
+
case "$line" in
|
|
40
|
+
''|'#'*) continue ;;
|
|
41
|
+
esac
|
|
42
|
+
case "$line" in
|
|
43
|
+
*=*) ;;
|
|
44
|
+
*) continue ;;
|
|
45
|
+
esac
|
|
46
|
+
key="${line%%=*}"
|
|
47
|
+
val="${line#*=}"
|
|
48
|
+
case "$key" in
|
|
49
|
+
''|*[!A-Za-z0-9_]*|[0-9]*) continue ;;
|
|
50
|
+
esac
|
|
51
|
+
case "$val" in
|
|
52
|
+
\"*\") val="${val#\"}"; val="${val%\"}" ;;
|
|
53
|
+
\'*\') val="${val#\'}"; val="${val%\'}" ;;
|
|
54
|
+
esac
|
|
55
|
+
if [ -s "$tmp" ]; then
|
|
56
|
+
awk -F '\t' -v k="$key" '$1 != k' "$tmp" > "${tmp}.new"
|
|
57
|
+
mv "${tmp}.new" "$tmp"
|
|
58
|
+
fi
|
|
59
|
+
printf '%s\t%s\t%s\n' "$key" "$val" "$file" >> "$tmp"
|
|
60
|
+
done < "$file"
|
|
61
|
+
done
|
|
62
|
+
|
|
63
|
+
if [ ! -s "$tmp" ]; then
|
|
64
|
+
echo "__EMPTY__"
|
|
65
|
+
return
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
max_key=0
|
|
69
|
+
while IFS=$'\t' read -r key _ _; do
|
|
70
|
+
[ "${#key}" -gt "$max_key" ] && max_key="${#key}"
|
|
71
|
+
done < "$tmp"
|
|
72
|
+
|
|
73
|
+
sort -t "$(printf '\t')" -k1,1 "$tmp" | while IFS=$'\t' read -r key val src; do
|
|
74
|
+
printf '%s\t%s\t%s\t%s\n' "$max_key" "$key" "$val" "$src"
|
|
75
|
+
done
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
node_ver="$(node -v 2>/dev/null || echo "n/a")"
|
|
79
|
+
pnpm_ver="$(pnpm -v 2>/dev/null || echo "n/a")"
|
|
80
|
+
|
|
81
|
+
echo ""
|
|
82
|
+
echo "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
|
|
83
|
+
echo "${BOLD} Vite Build Summary${RESET}"
|
|
84
|
+
echo "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
|
|
85
|
+
printf " ${DIM}%-10s${RESET} ${GREEN}%s${RESET}\n" "Branch" "$branch"
|
|
86
|
+
printf " ${DIM}%-10s${RESET} ${YELLOW}%s${RESET}\n" "Mode" "$mode"
|
|
87
|
+
printf " ${DIM}%-10s${RESET} pnpm run %s\n" "Script" "$script"
|
|
88
|
+
printf " ${DIM}%-10s${RESET} %s\n" "Node" "$node_ver"
|
|
89
|
+
printf " ${DIM}%-10s${RESET} %s\n" "pnpm" "$pnpm_ver"
|
|
90
|
+
echo "${CYAN}──────────────────────────────────────────────────${RESET}"
|
|
91
|
+
echo " ${BOLD}Environment Variables${RESET} ${DIM}(Vite 合并后生效值)${RESET}"
|
|
92
|
+
echo ""
|
|
93
|
+
|
|
94
|
+
env_out="$(merged_env_tsv "$mode")"
|
|
95
|
+
if [ "$env_out" = "__EMPTY__" ]; then
|
|
96
|
+
echo " ${DIM}(无环境变量文件或均为空)${RESET}"
|
|
97
|
+
else
|
|
98
|
+
echo "$env_out" | while IFS=$'\t' read -r max_key key val src; do
|
|
99
|
+
printf " ${BOLD}%-*s${RESET} %s\n" "$max_key" "$key" "$val"
|
|
100
|
+
printf " ${DIM}%-*s ← %s${RESET}\n" "$max_key" "" "$src"
|
|
101
|
+
done
|
|
102
|
+
fi
|
|
103
|
+
|
|
104
|
+
echo "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
|
|
105
|
+
echo ""
|
|
106
|
+
|
|
107
|
+
pnpm run "$script"
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "./node_modules/oxlint/configuration_schema.json",
|
|
3
|
+
"plugins": ["react", "typescript", "oxc"],
|
|
4
|
+
"rules": {
|
|
5
|
+
"react/rules-of-hooks": "error",
|
|
6
|
+
"react/only-export-components": [
|
|
7
|
+
"warn",
|
|
8
|
+
{
|
|
9
|
+
"allowConstantExport": true,
|
|
10
|
+
"allowExportNames": ["Route"]
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
},
|
|
14
|
+
"overrides": [
|
|
15
|
+
{
|
|
16
|
+
"files": ["src/routes/**"],
|
|
17
|
+
"rules": {
|
|
18
|
+
"react/only-export-components": "off"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
]
|
|
22
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# AGENTS.md — YBAIS App
|
|
2
|
+
|
|
3
|
+
本仓库由 `create-ybais-app` 生成,目标运行环境为 **App 内嵌在线 WebView**。
|
|
4
|
+
|
|
5
|
+
## 给 AI 代理的指令
|
|
6
|
+
|
|
7
|
+
1. 优先阅读并遵守:
|
|
8
|
+
- [.cursor/skills/ybais-app/SKILL.md](.cursor/skills/ybais-app/SKILL.md)
|
|
9
|
+
- [README.md](README.md)
|
|
10
|
+
- [src/lib/README.md](src/lib/README.md)
|
|
11
|
+
2. 需要模块细节或 WebView 兼容决策时再打开 Skill 的 [reference.md](.cursor/skills/ybais-app/reference.md) 或对应 `@ybais/*` 包 README。
|
|
12
|
+
3. 实现功能时:路由 → `src/routes/`;API → `src/api/` + `alovaInstance`([`src/api/alova.ts`](src/api/alova.ts));Native → `@ybais/bridge` 或 `@ybais/bridge/react`;上传 → `@/api/uploader` / `@ybais/qiniu-upload`;纯工具 → `@/utils/<name>`(如 `cn`);可恢复错误 → `reportError` / `showApiError`(勿另造 toast)。**按 `@ybais/<pkg>` / `@/utils/<file>` 深路径导入,禁止再造聚合 barrel。**
|
|
13
|
+
4. 使用 **pnpm**(>=11.9.0)与 **Node**(>=26.4.0,见 `package.json` engines);发版构建后提交 `dist/`,勿忽略该目录。可选 **vConsole**(脚手架询问):仅 test / 非 master。
|
|
14
|
+
5. **WebView 最低兼容**(与 `vite.config.ts` 对齐):**iOS ≥ 12 / Android ≥ 7 / Chrome ≥ 55 / Safari ≥ 12**,`cssTarget: chrome55`。保留 legacy;勿强制 webp;无 Bridge 时做 H5 回退;按移动端安全区布局。
|
|
15
|
+
|
|
16
|
+
## 常用命令
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pnpm install
|
|
20
|
+
pnpm dev
|
|
21
|
+
pnpm build
|
|
22
|
+
pnpm lint
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
`pnpm build` 会自动 trust 本项目 mise 配置;无需手动改 `mise.toml`。
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
# YBAIS App
|
|
2
|
+
|
|
3
|
+
面向 **App 内嵌 WebView** 的 React + Vite 工程模板(由 `create-ybais-app` 脚手架生成)。开箱包含:双环境构建、JSBridge、请求签名、七牛上传、发版检测与旧机兼容(legacy)。
|
|
4
|
+
|
|
5
|
+
## 前置要求
|
|
6
|
+
|
|
7
|
+
- Node.js `>=26.4.0`(`package.json` engines;有 mise 时取最新 `26.x`)
|
|
8
|
+
- [pnpm](https://pnpm.io/) `>=11.9.0`(同上;有 mise 时取最新 `11.x`,与 `packageManager` 对齐)
|
|
9
|
+
- [mise](https://mise.jdx.dev/)(**构建**需要;日常 `pnpm build` 会自动信任配置,**无需改 mise.toml**)
|
|
10
|
+
- Git
|
|
11
|
+
|
|
12
|
+
### 推荐:装一次 mise(之后无感)
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
curl https://mise.run | sh
|
|
16
|
+
# 或: brew install mise
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
然后照常:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pnpm install # 若已装 mise,会自动 trust + 安装 node/pnpm
|
|
23
|
+
pnpm dev
|
|
24
|
+
pnpm build # 自动 trust 并调用 mise 任务,无需手动 mise trust / mise run
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
未安装 mise 时:`pnpm dev` 仍可用系统 Node;`pnpm build` 会打印安装说明。
|
|
28
|
+
|
|
29
|
+
## 快速开始
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pnpm install
|
|
33
|
+
pnpm dev # test 模式,默认 --host,便于真机/模拟器访问
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
浏览器打开终端提示的本地地址。非 WebView 环境下 JSBridge 会 reject(可用 `.catch` 做 H5 回退)。
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pnpm build # 推荐:按分支自动选环境
|
|
40
|
+
pnpm preview # 先 build 再本地预览
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
## 目录结构
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
├── index.html
|
|
49
|
+
├── package.json
|
|
50
|
+
├── vite.config.ts # base: "./",legacy,app-version 插件
|
|
51
|
+
├── scripts/
|
|
52
|
+
│ ├── run-build.mjs # pnpm build 入口:自动 trust + mise run build
|
|
53
|
+
│ ├── setup-mise.mjs # postinstall:有 mise 则自动 trust/install
|
|
54
|
+
│ └── with-mise.mjs # mise 封装
|
|
55
|
+
├── plugins/
|
|
56
|
+
│ └── app-version.ts # 注入 meta + version.json
|
|
57
|
+
├── .mise/tasks/build # 按分支构建并打印环境摘要
|
|
58
|
+
├── .env / .env.test / .env.prod
|
|
59
|
+
├── public/
|
|
60
|
+
└── src/
|
|
61
|
+
├── main.tsx # 入口;configureBridge + setupErrorGuard + TokenBootstrap + startUpdater
|
|
62
|
+
├── router.ts # TanStack Router
|
|
63
|
+
├── routes/ # 文件系统路由(插件自动生成 routeTree)
|
|
64
|
+
├── api/ # 接口 + alova/uploader 装配(env 注入)
|
|
65
|
+
├── utils/ # 纯工具(按文件深路径导入,如 `@/utils/cn`)
|
|
66
|
+
├── styles/ # 全局样式 / 安全区等
|
|
67
|
+
├── vite-env.d.ts # 环境变量与 Window 类型
|
|
68
|
+
└── lib/ # 仅装配说明;实现已拆为 `@ybais/*`
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
路径别名:`@/*` → `src/*`。基础设施用 **`@ybais/*`**;工具函数放 `src/utils/`,**按文件按需导入**(如 `import { cn } from "@/utils/cn"`),勿做聚合 barrel,以便 tree-shake。
|
|
72
|
+
|
|
73
|
+
## 常用脚本
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
| 命令 | 说明 |
|
|
77
|
+
| ------------------------------------- | -------------------------------------------- |
|
|
78
|
+
| `pnpm dev` | Vite dev,`--mode test --host` |
|
|
79
|
+
| `pnpm dev:test` / `pnpm dev:prod` | 指定环境变量模式 |
|
|
80
|
+
| `pnpm build` | 自动 trust mise 配置并按分支选环境(master→prod,否则 test) |
|
|
81
|
+
| `pnpm build:test` / `pnpm build:prod` | 强制指定模式构建 |
|
|
82
|
+
| `pnpm lint` | oxlint |
|
|
83
|
+
| `pnpm preview` | build + `vite preview --host` |
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
构建前会打印分支、模式及合并后的环境变量摘要(见 `.mise/tasks/build`)。
|
|
87
|
+
|
|
88
|
+
## 环境变量
|
|
89
|
+
|
|
90
|
+
仅 `VITE_` 前缀会打进客户端。加载优先级与 [Vite 文档](https://cn.vite.dev/guide/env-and-mode) 一致:`.env` → `.env.local` → `.env.[mode]` → `.env.[mode].local`。
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
| 变量 | 说明 |
|
|
94
|
+
| ------------------------------------------ | ------------------------------------------------ |
|
|
95
|
+
| `VITE_APP_TITLE` | 应用标题(公共 `.env`) |
|
|
96
|
+
| `VITE_APP_ENV` | `test` | `prod` |
|
|
97
|
+
| `VITE_GIT_BRANCH` | 构建注入的 git 分支(用于 master 上杜绝 vConsole;无需写入 `.env`) |
|
|
98
|
+
| `VITE_API_BASE_URL` | API 基地址(打包进客户端;开发时 Vite 把 `/proxyApi` 代理到此地址) |
|
|
99
|
+
| `VITE_QINIU_AES_KEY` / `VITE_QINIU_AES_IV` | 七牛 token 解密 |
|
|
100
|
+
| `APP_VERSION` | (构建时,非 VITE)覆盖写入的版本号 |
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
本地敏感覆盖请用 `.env.*.local`(已 gitignore),勿提交密钥。
|
|
104
|
+
|
|
105
|
+
类型声明:`[src/vite-env.d.ts](./src/vite-env.d.ts)`。
|
|
106
|
+
|
|
107
|
+
## 技术栈
|
|
108
|
+
|
|
109
|
+
- **React 18** + **TypeScript** + **Vite 8**
|
|
110
|
+
- **TanStack Router**(文件路由 + 自动 code splitting)
|
|
111
|
+
- **Alova** 请求层(鉴权、签名、业务码)
|
|
112
|
+
- **Tailwind CSS 3** + **postcss-px-to-viewport**(移动端 rem/vw 适配)
|
|
113
|
+
- **@vitejs/plugin-legacy**:与 App 底线对齐 — **iOS ≥ 12 / Android ≥ 7 / Chrome ≥ 55 / Safari ≥ 12**(见 `vite.config.ts`)
|
|
114
|
+
- **mise**:分支感知构建任务
|
|
115
|
+
- **qiniu-js** + **crypto-js**:上传与加解密
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
## 业务库 `@ybais/*`
|
|
120
|
+
|
|
121
|
+
详细用法见装配索引与各包 README:
|
|
122
|
+
|
|
123
|
+
**[src/lib/README.md](./src/lib/README.md)**
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
| 包 / 入口 | 一句话 |
|
|
127
|
+
| --------- | ------ |
|
|
128
|
+
| `@ybais/bridge` / `@ybais/bridge/react` | `callNative` / `onNative` / MessageHandle |
|
|
129
|
+
| `@ybais/alova` + [`src/api/alova.ts`](./src/api/alova.ts) | `createYbaisAlova`、Token、签名、`ApiError` |
|
|
130
|
+
| `@ybais/qiniu-upload` + [`src/api/uploader.ts`](./src/api/uploader.ts) | `createUploader` / imageView2 |
|
|
131
|
+
| `@ybais/crypto` | `requestCrypto`、`md5` |
|
|
132
|
+
| `@ybais/updater` | 生产环境发版检测 + chunk 失败自动刷新 |
|
|
133
|
+
| `@ybais/error-guard` | 体系化错误与 `reportDebug` |
|
|
134
|
+
| `@ybais/clipboard` / `@ybais/clipboard/react` | `copyText` / `useCopyText` |
|
|
135
|
+
| `@ybais/vconsole` | 可插拔调试面板(脚手架可选;仅 test) |
|
|
136
|
+
| `@ybais/center-toast` | 面向用户的错误居中提示(需引入 `style.css`) |
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
接口示例放在 `[src/api/](./src/api/)`,按模块继续扩展即可。
|
|
140
|
+
|
|
141
|
+
## 路由
|
|
142
|
+
|
|
143
|
+
使用 TanStack Router 文件路由:在 `src/routes/` 下新增页面后,开发时由插件更新 `routeTree.gen.ts`。
|
|
144
|
+
|
|
145
|
+
- `__root.tsx`:根布局
|
|
146
|
+
- `index.tsx`:首页
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
## 发版与部署
|
|
151
|
+
|
|
152
|
+
本工程约定将 `dist/` **提交到 Git**:构建后推送仓库,供 App WebView 或静态服务拉取。
|
|
153
|
+
|
|
154
|
+
### 发版流程
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
pnpm build # master→prod,其它分支→test;或 pnpm build:prod
|
|
158
|
+
git add dist
|
|
159
|
+
git status # 确认含 index.html、assets/{js,css,img}/…、version.json
|
|
160
|
+
git commit -m "chore: release"
|
|
161
|
+
git push
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
- `.gitignore` **不忽略** `dist/`(勿重新加回忽略规则)
|
|
165
|
+
- 每次发版提交**整包**替换,避免残留旧 chunk
|
|
166
|
+
- 源码与产物可同仓;密钥仍勿提交(`.env.*.local`)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
### 版本文件
|
|
171
|
+
|
|
172
|
+
构建会注入:
|
|
173
|
+
|
|
174
|
+
- `index.html` → `<meta name="app-version" content="..." />`
|
|
175
|
+
- `dist/version.json` → `{ version, builtAt }`
|
|
176
|
+
|
|
177
|
+
生产环境入口已调用 `startUpdater()`:轮询/回前台比对版本;`vite:preloadError` 时最多自动硬刷新一次。详见 [updater 手册](./src/lib/updater/README.md)。
|
|
178
|
+
|
|
179
|
+
### 缓存建议
|
|
180
|
+
|
|
181
|
+
若 Git 仓库前还有 Nginx / CDN / Pages:
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
| 资源 | 建议 |
|
|
185
|
+
| -------------------------------------------------------- | -------------------------------------- |
|
|
186
|
+
| `index.html`、`version.json` | `Cache-Control: no-cache` 或 `no-store` |
|
|
187
|
+
| `assets/js/*`、`assets/css/*`、`assets/img/*` 等(文件名含 hash) | 可长期缓存,如 `max-age=31536000, immutable` |
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
### 静态托管注意
|
|
193
|
+
|
|
194
|
+
`vite.config` 中 `base: "./"`,适合相对路径嵌入 App / 子目录部署。部署后请确认 WebView 能访问同级的 `version.json`(与 `index.html` 同级)。
|
|
195
|
+
|
|
196
|
+
## 兼容性
|
|
197
|
+
|
|
198
|
+
面向 **App 内嵌 WebView**,最低版本与 `[vite.config.ts](./vite.config.ts)` 中 legacy `targets` 一致:
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
| 平台 / 引擎 | 最低版本 |
|
|
202
|
+
| ------- | ---- |
|
|
203
|
+
| iOS | ≥ 12 |
|
|
204
|
+
| Android | ≥ 7 |
|
|
205
|
+
| Chrome | ≥ 55 |
|
|
206
|
+
| Safari | ≥ 12 |
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
- Legacy 产物含 modern + legacy 双 chunk 与 polyfills(含 `regenerator-runtime`、`AbortController`、`queueMicrotask`、`Promise.allSettled`、`String.replaceAll`)
|
|
210
|
+
- CSS 压缩目标:`cssTarget: chrome55`;业务勿用 `100dvh` / flex `gap`;全局补丁见 `src/styles/webview-compat.css`
|
|
211
|
+
- JS 压缩:Terser + `ascii_only`,降低旧内核 Unicode 解析问题
|
|
212
|
+
- 七牛默认 imageView2 **不转 webp**,降低旧 WebView 兼容问题
|
|
213
|
+
- AI / 开发约定见 `[.cursor/skills/ybais-app/SKILL.md](./.cursor/skills/ybais-app/SKILL.md)`
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
## 常见问题
|
|
218
|
+
|
|
219
|
+
`pnpm build` **提示未检测到 mise**
|
|
220
|
+
安装 [mise](https://mise.jdx.dev/) 后重开终端即可,**不必**手动改 `mise.toml` 或执行 `mise trust`(脚本会自动处理)。
|
|
221
|
+
|
|
222
|
+
**浏览器里 callNative / Bridge 封装失败**
|
|
223
|
+
属预期:无 `webkit.messageHandlers` / `AndroidBridge` 时会 **reject** `BridgeUnavailableError`(sonner 警告 + 控制台日志,**不**进全屏 Fallback)。可用 `.catch` 做 H5 回退。
|
|
224
|
+
|
|
225
|
+
**vConsole 怎么开?**
|
|
226
|
+
脚手架需选择启用。测试环境打开带 `?vconsole=1` 的地址,或调用 `enableVConsole()`。`prod` / `master` 分支下 API 为 no-op,产物不应带入面板。详见 [vconsole 手册](./src/lib/vconsole/README.md)。
|
|
227
|
+
|
|
228
|
+
**test 模式接口 404 / 跨域**
|
|
229
|
+
仅 **开发** 时 alova base 为 `/proxyApi`(Vite 代理到 `VITE_API_BASE_URL`)。`build:test` / `build:prod` 产物直接请求 `VITE_API_BASE_URL`,不含 `/proxyApi`。
|
|
230
|
+
|
|
231
|
+
**缺少 antd-mobile 等依赖**
|
|
232
|
+
业务错误经 `showApiError` → `reportError` → sonner Toast;勿平行再造 toast 体系。详见 [error-guard 手册](./src/lib/error-guard/README.md)。
|
|
233
|
+
|
|
234
|
+
## 许可证
|
|
235
|
+
|
|
236
|
+
随主仓库 / 团队约定。
|
|
237
|
+
|
|
238
|
+
## AI / Cursor 友好
|
|
239
|
+
|
|
240
|
+
脚手架已内置项目级 Agent 指引,创建工程后即可被 Cursor 等工具加载:
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
| 文件 | 作用 |
|
|
244
|
+
| -------------------------------------------------------------------------------- | ------------------------------------- |
|
|
245
|
+
| [.cursor/skills/ybais-app/SKILL.md](./.cursor/skills/ybais-app/SKILL.md) | Skill:开发规范、**WebView 最低兼容**、模块用法、检查清单 |
|
|
246
|
+
| [.cursor/skills/ybais-app/reference.md](./.cursor/skills/ybais-app/reference.md) | 扩展参考:兼容决策细节与文档索引 |
|
|
247
|
+
| [.cursor/rules/ybais-app.mdc](./.cursor/rules/ybais-app.mdc) | 始终生效的硬性约定(含 iOS/Android 底线) |
|
|
248
|
+
| [AGENTS.md](./AGENTS.md) | 通用 AI Agent 入口说明 |
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
业务细节仍以 `src/lib/**/README.md` 为准。在 Cursor 中也可手动引用 Skill:`ybais-app`。
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:root{--sat:env(safe-area-inset-top);--sab:env(safe-area-inset-bottom);--sal:env(safe-area-inset-left);--sar:env(safe-area-inset-right)}html,body{user-select:none;-webkit-user-select:none!important}input,textarea{user-select:auto;-webkit-user-select:auto!important}[data-sonner-toast][data-styled=true]{gap:0!important}[data-sonner-toast][data-styled=true]>[data-icon]{margin-right:1.6vw!important}[data-sonner-toast][data-styled=true] [data-content]{gap:0!important}[data-sonner-toast][data-styled=true] [data-content]>[data-description]{margin-top:.53333vw}[data-sonner-toast]{overflow-wrap:break-word!important;word-wrap:break-word!important}.sonner-loading-wrapper{top:0!important;bottom:0!important;left:0!important;right:0!important}*,:before,:after,::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 transparent;--tw-ring-shadow:0 0 transparent;--tw-shadow:0 0 transparent;--tw-shadow-colored:0 0 transparent;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border:0 solid #e5e7eb}:before,:after{--tw-content:""}html,:host{-webkit-text-size-adjust:100%;tab-size:4;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Noto Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}body{line-height:inherit;margin:0}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-feature-settings:normal;font-variation-settings:normal;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-feature-settings:inherit;font-variation-settings:inherit;font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button{-webkit-appearance:button;background-color:transparent;background-image:none}input:where([type=button]){-webkit-appearance:button;background-color:transparent;background-image:none}input:where([type=reset]){-webkit-appearance:button;background-color:transparent;background-image:none}input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-.53333vw}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{margin:0;padding:0;list-style:none}dialog{padding:0}textarea{resize:vertical}input::-webkit-input-placeholder{opacity:1;color:#9ca3af}textarea::-webkit-input-placeholder{opacity:1;color:#9ca3af}input::-webkit-input-placeholder{opacity:1;color:#9ca3af}input::placeholder{opacity:1;color:#9ca3af}textarea::-webkit-input-placeholder{opacity:1;color:#9ca3af}textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.mb-2{margin-bottom:.5rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-6{margin-top:1.5rem}.flex{display:flex}.min-h-screen{min-height:100vh}.w-full{width:100%}.max-w-md{max-width:28rem}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-center{justify-content:center}.space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.75rem * var(--tw-space-x-reverse));margin-left:calc(.75rem * calc(1 - var(--tw-space-x-reverse)))}.rounded-2xl{border-radius:1rem}.rounded-md{border-radius:.375rem}.rounded-xl{border-radius:.75rem}.bg-blue-500{--tw-bg-opacity:1;background-color:rgba(59,130,246,var(--tw-bg-opacity,1))}.bg-emerald-600{--tw-bg-opacity:1;background-color:rgba(5,150,105,var(--tw-bg-opacity,1))}.bg-green-500{--tw-bg-opacity:1;background-color:rgba(34,197,94,var(--tw-bg-opacity,1))}.bg-slate-100{--tw-bg-opacity:1;background-color:rgba(241,245,249,var(--tw-bg-opacity,1))}.bg-slate-50{--tw-bg-opacity:1;background-color:rgba(248,250,252,var(--tw-bg-opacity,1))}.bg-slate-900{--tw-bg-opacity:1;background-color:rgba(15,23,42,var(--tw-bg-opacity,1))}.bg-white{--tw-bg-opacity:1;background-color:rgba(255,255,255,var(--tw-bg-opacity,1))}.p-2{padding:.5rem}.px-4{padding-left:1rem;padding-right:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-10{padding-top:2.5rem;padding-bottom:2.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.text-center{text-align:center}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.font-medium{font-weight:500}.font-semibold{font-weight:600}.leading-relaxed{line-height:1.625}.text-slate-400{--tw-text-opacity:1;color:rgba(148,163,184,var(--tw-text-opacity,1))}.text-slate-600{--tw-text-opacity:1;color:rgba(71,85,105,var(--tw-text-opacity,1))}.text-slate-800{--tw-text-opacity:1;color:rgba(30,41,59,var(--tw-text-opacity,1))}.text-slate-900{--tw-text-opacity:1;color:rgba(15,23,42,var(--tw-text-opacity,1))}.text-white{--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity,1))}.shadow-sm{--tw-shadow:0 1px .53333vw 0 rgba(0,0,0,.05);--tw-shadow-colored:0 1px .53333vw 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 transparent), var(--tw-ring-shadow,0 0 transparent), var(--tw-shadow)}.ring-1{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow,0 0 transparent)}.ring-slate-200{--tw-ring-opacity:1;--tw-ring-color:rgba(226,232,240,var(--tw-ring-opacity,1))}html,body{-webkit-tap-highlight-color:transparent;user-select:none;-webkit-overflow-scrolling:touch;width:100%;height:100%;margin:0;padding:0}body{-webkit-touch-callout:none;user-select:none;-webkit-tap-highlight-color:transparent}input,textarea{user-select:auto}#root{width:100%;min-height:100%}.active\:opacity-80:active{opacity:.8}@media (min-width:640px){.sm\:ml-2{margin-left:.5rem}.sm\:mt-0{margin-top:0}.sm\:flex-row{flex-direction:row}.sm\:justify-center{justify-content:center}}.ybais-center-toast-root{z-index:999999;pointer-events:none;position:fixed;top:0;bottom:0;left:0;right:0}.ybais-center-toast-mask{pointer-events:none;background-color:transparent;position:absolute;top:0;bottom:0;left:0;right:0}.ybais-center-toast-mask--blocking{pointer-events:auto}.ybais-center-toast-wrap{color:#fff;text-align:center;box-sizing:border-box;pointer-events:auto;opacity:0;word-wrap:break-word;overflow-wrap:break-word;background-color:rgba(0,0,0,.75);border-radius:2.13333vw;min-width:32vw;max-width:80vw;padding:3.2vw 4.26667vw;transition:opacity .2s,transform .2s;position:absolute;left:50%;transform:translate(-50%,-50%)scale(.92)}.ybais-center-toast-wrap--top{top:20%}.ybais-center-toast-wrap--center{top:50%}.ybais-center-toast-wrap--bottom{top:80%}.ybais-center-toast-wrap--enter,.ybais-center-toast-wrap--visible{opacity:1;transform:translate(-50%,-50%)scale(1)}.ybais-center-toast-wrap--exit{opacity:0;transform:translate(-50%,-50%)scale(.92)}.ybais-center-toast__icon{color:#fff;width:9.6vw;height:9.6vw;margin:0 auto 2.13333vw;display:block}.ybais-center-toast__icon-svg{width:100%;height:100%;display:block}.ybais-center-toast__icon-svg--loading{animation:.8s linear infinite ybais-center-toast-spin}.ybais-center-toast__content{color:#fff;margin:0;font-size:3.73333vw;font-weight:400;line-height:1.5}@keyframes ybais-center-toast-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@media (prefers-reduced-motion:reduce){.ybais-center-toast-wrap{transition:none}.ybais-center-toast__icon-svg--loading{animation:none}}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./vconsole.min-BcOaunN2.js","./rolldown-runtime-LhxUO5vB.js"])))=>i.map(i=>d[i]);
|
|
2
|
+
import{n}from"./rolldown-runtime-LhxUO5vB.js";import{t as o}from"./index-BNqkt-js.js";var e=null;function t(n){return"test"===(n?.appEnv??e?.appEnv)&&"master"!==(n?.gitBranch??e?.gitBranch??"").trim().toLowerCase()}var r="ybais_vconsole";function i(n){if("undefined"!=typeof window)try{"on"===n?window.localStorage.setItem(r,"1"):"off"===n?window.localStorage.setItem(r,"0"):window.localStorage.removeItem(r)}catch{}}function f(){const n=function(){if("undefined"==typeof window)return null;const n=new URLSearchParams(window.location.search).get("vconsole");return"1"===n||"true"===n||"on"===n?"on":"0"===n||"false"===n||"off"===n?"off":null}();return"on"===n?(i("on"),"on"):"off"===n?(i("off"),"off"):function(){if("undefined"==typeof window)return null;try{const n=window.localStorage.getItem(r);if("1"===n)return"on";if("0"===n)return"off"}catch{}return null}()}var l=null,c=null;async function a(){if(!t())return!1;if(l)return!0;if(c)return c;const r=function(){if(!e)throw new Error("[@ybais/vconsole] \u672a\u914d\u7f6e\uff1a\u8bf7\u5148\u8c03\u7528 configureVConsole() \u6216 setupVConsole({ ... })");return e}();return c=(async()=>{try{if("test"!==r.appEnv)return!1;const e=await o(()=>import("./vconsole.min-BcOaunN2.js").then(o=>n(o.default,1)),__vite__mapDeps([0,1]),import.meta.url),t=e.default??e;if("function"!=typeof t)throw new Error("vconsole \u5bfc\u51fa\u4e0d\u662f\u6784\u9020\u51fd\u6570");return l=new t({theme:"dark"}),i("on"),r.isDev&&console.info("[vconsole] \u5df2\u5f00\u542f"),!0}catch(e){return console.warn("[vconsole] \u52a0\u8f7d\u5931\u8d25\uff08\u672a\u5b89\u88c5\u4f9d\u8d56\u6216\u6784\u5efa\u5df2\u5254\u9664\uff09\u3002\u811a\u624b\u67b6\u9700\u9009\u62e9\u542f\u7528 vConsole\u3002",e),!1}finally{c=null}})()}async function s(n){if(function(n){e=n}(n),!t())return void(n.isDev&&console.info("[vconsole] \u5f53\u524d\u73af\u5883\u4e0d\u5141\u8bb8\uff08\u9700 appEnv=test \u4e14\u5206\u652f\u975e master\uff09"));const o=f();"on"===o||null===o&&Boolean(n.isDev)?await a():n.isDev&&"off"===o&&console.info("[vconsole] \u5df2\u5173\u95ed\uff08localStorage/URL\uff09\u3002\u52a0 ?vconsole=1 \u53ef\u91cd\u65b0\u5f00\u542f")}export{s as setupVConsole};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
!function(){function n(){/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */var e,r,o="function"==typeof Symbol?Symbol:{},i=o.iterator||"@@iterator",u=o.toStringTag||"@@toStringTag";function a(n,o,i,u){var a=o&&o.prototype instanceof f?o:f,l=Object.create(a.prototype);return t(l,"_invoke",function(n,t,o){var i,u,a,f=0,l=o||[],s=!1,v={p:0,n:0,v:e,a:p,f:p.bind(e,4),d:function(n,t){return i=n,u=0,a=e,v.n=t,c}};function p(n,t){for(u=n,a=t,r=0;!s&&f&&!o&&r<l.length;r++){var o,i=l[r],p=v.p,d=i[2];n>3?(o=d===t)&&(a=i[(u=i[4])?5:(u=3,3)],i[4]=i[5]=e):i[0]<=p&&((o=n<2&&p<i[1])?(u=0,v.v=t,v.n=i[1]):p<d&&(o=n<3||i[0]>t||t>d)&&(i[4]=n,i[5]=t,v.n=d,u=0))}if(o||n>1)return c;throw s=!0,t}return function(o,l,d){if(f>1)throw TypeError("Generator is already running");for(s&&1===l&&p(l,d),u=l,a=d;(r=u<2?e:a)||!s;){i||(u?u<3?(u>1&&(v.n=-1),p(u,a)):v.n=a:v.v=a);try{if(f=2,i){if(u||(o="next"),r=i[o]){if(!(r=r.call(i,a)))throw TypeError("iterator result is not an object");if(!r.done)return r;a=r.value,u<2&&(u=0)}else 1===u&&(r=i.return)&&r.call(i),u<2&&(a=TypeError("The iterator does not provide a '"+o+"' method"),u=1);i=e}else if((r=(s=v.n<0)?a:n.call(t,v))!==c)break}catch(r){i=e,u=1,a=r}finally{f=1}}return{value:r,done:s}}}(n,i,u),!0),l}var c={};function f(){}function l(){}function s(){}r=Object.getPrototypeOf;var v=[][i]?r(r([][i]())):(t(r={},i,function(){return this}),r),p=s.prototype=f.prototype=Object.create(v);function d(n){return Object.setPrototypeOf?Object.setPrototypeOf(n,s):(n.__proto__=s,t(n,u,"GeneratorFunction")),n.prototype=Object.create(p),n}return l.prototype=s,t(p,"constructor",s),t(s,"constructor",l),l.displayName="GeneratorFunction",t(s,u,"GeneratorFunction"),t(p),t(p,u,"Generator"),t(p,i,function(){return this}),t(p,"toString",function(){return"[object Generator]"}),(n=function(){return{w:a,m:d}})()}function t(n,e,r,o){var i=Object.defineProperty;try{i({},"",{})}catch(n){i=0}t=function(n,e,r,o){function u(e,r){t(n,e,function(n){return this._invoke(e,r,n)})}e?i?i(n,e,{value:r,enumerable:!o,configurable:!o,writable:!o}):n[e]=r:(u("next",0),u("throw",1),u("return",2))},t(n,e,r,o)}function e(n,t,e,r,o,i,u){try{var a=n[i](u),c=a.value}catch(n){return void e(n)}a.done?t(c):Promise.resolve(c).then(r,o)}function r(n){return function(){var t=this,r=arguments;return new Promise(function(o,i){var u=n.apply(t,r);function a(n){e(u,o,i,a,c,"next",n)}function c(n){e(u,o,i,a,c,"throw",n)}a(void 0)})}}System.register(["./rolldown-runtime-legacy-BptCTa2h.js","./index-legacy-DPO2FCfH.js"],function(t,e){var o,i,u,a,c,f;function l(n){u=n}function s(){if(!u)throw new Error("[@ybais/vconsole] \u672a\u914d\u7f6e\uff1a\u8bf7\u5148\u8c03\u7528 configureVConsole() \u6216 setupVConsole({ ... })");return u}function v(n){var t,e,r,o,i;return"test"===(null!==(t=null==n?void 0:n.appEnv)&&void 0!==t?t:null===(e=u)||void 0===e?void 0:e.appEnv)&&"master"!==(null!==(r=null!==(o=null==n?void 0:n.gitBranch)&&void 0!==o?o:null===(i=u)||void 0===i?void 0:i.gitBranch)&&void 0!==r?r:"").trim().toLowerCase()}function p(n){if("undefined"!=typeof window)try{"on"===n?window.localStorage.setItem(a,"1"):"off"===n?window.localStorage.setItem(a,"0"):window.localStorage.removeItem(a)}catch(t){}}function d(){var n=function(){if("undefined"==typeof window)return null;var n=new URLSearchParams(window.location.search).get("vconsole");return"1"===n||"true"===n||"on"===n?"on":"0"===n||"false"===n||"off"===n?"off":null}();return"on"===n?(p("on"),"on"):"off"===n?(p("off"),"off"):function(){if("undefined"==typeof window)return null;try{var n=window.localStorage.getItem(a);if("1"===n)return"on";if("0"===n)return"off"}catch(t){}return null}()}function w(){return y.apply(this,arguments)}function y(){return(y=r(n().m(function t(){var u;return n().w(function(t){for(;;)switch(t.n){case 0:if(v()){t.n=1;break}return t.a(2,!1);case 1:if(!c){t.n=2;break}return t.a(2,!0);case 2:if(!f){t.n=3;break}return t.a(2,f);case 3:return u=s(),f=r(n().m(function t(){var r,a,l,s;return n().w(function(n){for(;;)switch(n.p=n.n){case 0:if(n.p=0,"test"===u.appEnv){n.n=1;break}return n.a(2,!1);case 1:return n.n=2,i(function(){return e.import("./vconsole.min-legacy-Bub-moxG.js").then(function(n){return o(n.default,1)})},void 0,e.meta.url);case 2:if(a=n.v,"function"==typeof(l=null!==(r=a.default)&&void 0!==r?r:a)){n.n=3;break}throw new Error("vconsole \u5bfc\u51fa\u4e0d\u662f\u6784\u9020\u51fd\u6570");case 3:return c=new l({theme:"dark"}),p("on"),u.isDev&&console.info("[vconsole] \u5df2\u5f00\u542f"),n.a(2,!0);case 4:return n.p=4,s=n.v,console.warn("[vconsole] \u52a0\u8f7d\u5931\u8d25\uff08\u672a\u5b89\u88c5\u4f9d\u8d56\u6216\u6784\u5efa\u5df2\u5254\u9664\uff09\u3002\u811a\u624b\u67b6\u9700\u9009\u62e9\u542f\u7528 vConsole\u3002",s),n.a(2,!1);case 5:return n.p=5,f=null,n.f(5);case 6:return n.a(2)}},t,null,[[0,4,5,6]])}))(),t.a(2,f)}},t)}))).apply(this,arguments)}function h(){return(h=r(n().m(function t(e){var r;return n().w(function(n){for(;;)switch(n.n){case 0:if(l(e),v()){n.n=1;break}return e.isDev&&console.info("[vconsole] \u5f53\u524d\u73af\u5883\u4e0d\u5141\u8bb8\uff08\u9700 appEnv=test \u4e14\u5206\u652f\u975e master\uff09"),n.a(2);case 1:if(!("on"===(r=d())||null===r&&Boolean(e.isDev))){n.n=3;break}return n.n=2,w();case 2:return n.a(2);case 3:e.isDev&&"off"===r&&console.info("[vconsole] \u5df2\u5173\u95ed\uff08localStorage/URL\uff09\u3002\u52a0 ?vconsole=1 \u53ef\u91cd\u65b0\u5f00\u542f");case 4:return n.a(2)}},t)}))).apply(this,arguments)}return t("setupVConsole",function(n){return h.apply(this,arguments)}),{setters:[function(n){o=n.n},function(n){i=n.t}],execute:function(){u=null,a="ybais_vconsole",c=null,f=null}}})}();
|