@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,24 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
plugins: {
|
|
3
|
+
// 0. Tailwind CSS 必须置于顶部优先编译
|
|
4
|
+
tailwindcss: {},
|
|
5
|
+
// 1. 自动添加浏览器前缀(iOS 12 强烈依赖 -webkit- 前缀)
|
|
6
|
+
autoprefixer: {
|
|
7
|
+
overrideBrowserslist: [
|
|
8
|
+
"iOS >= 12",
|
|
9
|
+
"Android >= 7",
|
|
10
|
+
"Chrome >= 55",
|
|
11
|
+
"Safari >= 12",
|
|
12
|
+
],
|
|
13
|
+
},
|
|
14
|
+
// 2. 将 px 自动按比例转换为 vw 实现移动端自适应
|
|
15
|
+
"postcss-px-to-viewport-8-plugin": {
|
|
16
|
+
viewportWidth: 375, // 设计稿宽度(以 375px 移动端逻辑像素为基准)
|
|
17
|
+
unitPrecision: 5, // 保留 5 位小数
|
|
18
|
+
viewportUnit: "vw", // 转换目标单位
|
|
19
|
+
selectorBlackList: [".ignore", ".hairlines"], // 忽略转换的 CSS 类名
|
|
20
|
+
minPixelValue: 1, // 小于等于 1px 不做转换
|
|
21
|
+
mediaQuery: false, // 是否在 @media 查询中转换
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="46" fill="none" viewBox="0 0 48 46"><path fill="#863bff" d="M25.946 44.938c-.664.845-2.021.375-2.021-.698V33.937a2.26 2.26 0 0 0-2.262-2.262H10.287c-.92 0-1.456-1.04-.92-1.788l7.48-10.471c1.07-1.497 0-3.578-1.842-3.578H1.237c-.92 0-1.456-1.04-.92-1.788L10.013.474c.214-.297.556-.474.92-.474h28.894c.92 0 1.456 1.04.92 1.788l-7.48 10.471c-1.07 1.498 0 3.579 1.842 3.579h11.377c.943 0 1.473 1.088.89 1.83L25.947 44.94z" style="fill:#863bff;fill:color(display-p3 .5252 .23 1);fill-opacity:1"/><mask id="a" width="48" height="46" x="0" y="0" maskUnits="userSpaceOnUse" style="mask-type:alpha"><path fill="#000" d="M25.842 44.938c-.664.844-2.021.375-2.021-.698V33.937a2.26 2.26 0 0 0-2.262-2.262H10.183c-.92 0-1.456-1.04-.92-1.788l7.48-10.471c1.07-1.498 0-3.579-1.842-3.579H1.133c-.92 0-1.456-1.04-.92-1.787L9.91.473c.214-.297.556-.474.92-.474h28.894c.92 0 1.456 1.04.92 1.788l-7.48 10.471c-1.07 1.498 0 3.578 1.842 3.578h11.377c.943 0 1.473 1.088.89 1.832L25.843 44.94z" style="fill:#000;fill-opacity:1"/></mask><g mask="url(#a)"><g filter="url(#b)"><ellipse cx="5.508" cy="14.704" fill="#ede6ff" rx="5.508" ry="14.704" style="fill:#ede6ff;fill:color(display-p3 .9275 .9033 1);fill-opacity:1" transform="matrix(.00324 1 1 -.00324 -4.47 31.516)"/></g><g filter="url(#c)"><ellipse cx="10.399" cy="29.851" fill="#ede6ff" rx="10.399" ry="29.851" style="fill:#ede6ff;fill:color(display-p3 .9275 .9033 1);fill-opacity:1" transform="matrix(.00324 1 1 -.00324 -39.328 7.883)"/></g><g filter="url(#d)"><ellipse cx="5.508" cy="30.487" fill="#7e14ff" rx="5.508" ry="30.487" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.814 -25.913 -14.639)scale(1 -1)"/></g><g filter="url(#e)"><ellipse cx="5.508" cy="30.599" fill="#7e14ff" rx="5.508" ry="30.599" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.814 -32.644 -3.334)scale(1 -1)"/></g><g filter="url(#f)"><ellipse cx="5.508" cy="30.599" fill="#7e14ff" rx="5.508" ry="30.599" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="matrix(.00324 1 1 -.00324 -34.34 30.47)"/></g><g filter="url(#g)"><ellipse cx="14.072" cy="22.078" fill="#ede6ff" rx="14.072" ry="22.078" style="fill:#ede6ff;fill:color(display-p3 .9275 .9033 1);fill-opacity:1" transform="rotate(93.35 24.506 48.493)scale(-1 1)"/></g><g filter="url(#h)"><ellipse cx="3.47" cy="21.501" fill="#7e14ff" rx="3.47" ry="21.501" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.009 28.708 47.59)scale(-1 1)"/></g><g filter="url(#i)"><ellipse cx="3.47" cy="21.501" fill="#7e14ff" rx="3.47" ry="21.501" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.009 28.708 47.59)scale(-1 1)"/></g><g filter="url(#j)"><ellipse cx=".387" cy="8.972" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(39.51 .387 8.972)"/></g><g filter="url(#k)"><ellipse cx="47.523" cy="-6.092" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 47.523 -6.092)"/></g><g filter="url(#l)"><ellipse cx="41.412" cy="6.333" fill="#47bfff" rx="5.971" ry="9.665" style="fill:#47bfff;fill:color(display-p3 .2799 .748 1);fill-opacity:1" transform="rotate(37.892 41.412 6.333)"/></g><g filter="url(#m)"><ellipse cx="-1.879" cy="38.332" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 -1.88 38.332)"/></g><g filter="url(#n)"><ellipse cx="-1.879" cy="38.332" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 -1.88 38.332)"/></g><g filter="url(#o)"><ellipse cx="35.651" cy="29.907" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 35.651 29.907)"/></g><g filter="url(#p)"><ellipse cx="38.418" cy="32.4" fill="#47bfff" rx="5.971" ry="15.297" style="fill:#47bfff;fill:color(display-p3 .2799 .748 1);fill-opacity:1" transform="rotate(37.892 38.418 32.4)"/></g></g><defs><filter id="b" width="60.045" height="41.654" x="-19.77" y="16.149" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="7.659"/></filter><filter id="c" width="90.34" height="51.437" x="-54.613" y="-7.533" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="7.659"/></filter><filter id="d" width="79.355" height="29.4" x="-49.64" y="2.03" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="e" width="79.579" height="29.4" x="-45.045" y="20.029" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="f" width="79.579" height="29.4" x="-43.513" y="21.178" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="g" width="74.749" height="58.852" x="15.756" y="-17.901" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="7.659"/></filter><filter id="h" width="61.377" height="25.362" x="23.548" y="2.284" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="i" width="61.377" height="25.362" x="23.548" y="2.284" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="j" width="56.045" height="63.649" x="-27.636" y="-22.853" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="k" width="54.814" height="64.646" x="20.116" y="-38.415" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="l" width="33.541" height="35.313" x="24.641" y="-11.323" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="m" width="54.814" height="64.646" x="-29.286" y="6.009" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="n" width="54.814" height="64.646" x="-29.286" y="6.009" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="o" width="54.814" height="64.646" x="8.244" y="-2.416" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="p" width="39.409" height="43.623" x="18.713" y="10.588" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter></defs></svg>
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* pnpm install 后轻量准备:若已安装 mise,则自动 trust + 安装工具链。
|
|
4
|
+
* 未安装 mise 时静默跳过(开发可用系统 Node;构建时会给出友好提示)。
|
|
5
|
+
*/
|
|
6
|
+
import { spawnSync } from "node:child_process";
|
|
7
|
+
import fs from "node:fs";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
|
|
11
|
+
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
12
|
+
|
|
13
|
+
function findMise() {
|
|
14
|
+
const fromPath = spawnSync("mise", ["--version"], {
|
|
15
|
+
encoding: "utf8",
|
|
16
|
+
shell: process.platform === "win32",
|
|
17
|
+
});
|
|
18
|
+
if (fromPath.status === 0) return "mise";
|
|
19
|
+
|
|
20
|
+
for (const candidate of [
|
|
21
|
+
"/opt/homebrew/bin/mise",
|
|
22
|
+
"/usr/local/bin/mise",
|
|
23
|
+
path.join(process.env.HOME ?? "", ".local/bin/mise"),
|
|
24
|
+
]) {
|
|
25
|
+
if (candidate && fs.existsSync(candidate)) return candidate;
|
|
26
|
+
}
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const mise = findMise();
|
|
31
|
+
if (!mise) {
|
|
32
|
+
process.exit(0);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
spawnSync(mise, ["trust", root], {
|
|
36
|
+
cwd: root,
|
|
37
|
+
stdio: "ignore",
|
|
38
|
+
shell: process.platform === "win32",
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
spawnSync(mise, ["install"], {
|
|
42
|
+
cwd: root,
|
|
43
|
+
stdio: "inherit",
|
|
44
|
+
shell: process.platform === "win32",
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
process.exit(0);
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* 无感调用 mise:自动信任本项目 mise.toml,再执行给定子命令。
|
|
4
|
+
* 用户日常只需跑 pnpm scripts,无需手动 mise trust / 改配置。
|
|
5
|
+
*/
|
|
6
|
+
import { spawnSync } from "node:child_process";
|
|
7
|
+
import fs from "node:fs";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
|
|
11
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
12
|
+
const root = path.resolve(path.dirname(__filename), "..");
|
|
13
|
+
|
|
14
|
+
function findMise() {
|
|
15
|
+
const fromPath = spawnSync("mise", ["--version"], {
|
|
16
|
+
encoding: "utf8",
|
|
17
|
+
shell: process.platform === "win32",
|
|
18
|
+
});
|
|
19
|
+
if (fromPath.status === 0) {
|
|
20
|
+
return "mise";
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const candidates = [
|
|
24
|
+
"/opt/homebrew/bin/mise",
|
|
25
|
+
"/usr/local/bin/mise",
|
|
26
|
+
path.join(process.env.HOME ?? "", ".local/bin/mise"),
|
|
27
|
+
];
|
|
28
|
+
for (const candidate of candidates) {
|
|
29
|
+
if (candidate && fs.existsSync(candidate)) {
|
|
30
|
+
return candidate;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function printMiseMissing() {
|
|
37
|
+
console.error(`
|
|
38
|
+
[ybais] 未检测到 mise,但本项目的 \`pnpm build\` 依赖它(按 Git 分支选择 test/prod)。
|
|
39
|
+
|
|
40
|
+
请安装后重试(安装后无需改任何 mise 配置,直接 pnpm build 即可):
|
|
41
|
+
|
|
42
|
+
curl https://mise.run | sh
|
|
43
|
+
|
|
44
|
+
或 macOS:
|
|
45
|
+
|
|
46
|
+
brew install mise
|
|
47
|
+
|
|
48
|
+
安装后重新打开终端,再执行:
|
|
49
|
+
|
|
50
|
+
pnpm build
|
|
51
|
+
`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @param {string[]} miseArgs mise 子命令参数,如 ["run", "build"]
|
|
56
|
+
* @param {{ installTools?: boolean }} [opts]
|
|
57
|
+
*/
|
|
58
|
+
export function runMise(miseArgs, opts = {}) {
|
|
59
|
+
const mise = findMise();
|
|
60
|
+
if (!mise) {
|
|
61
|
+
printMiseMissing();
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
spawnSync(mise, ["trust", root], {
|
|
66
|
+
cwd: root,
|
|
67
|
+
stdio: "ignore",
|
|
68
|
+
shell: process.platform === "win32",
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
if (opts.installTools) {
|
|
72
|
+
const install = spawnSync(mise, ["install"], {
|
|
73
|
+
cwd: root,
|
|
74
|
+
stdio: "inherit",
|
|
75
|
+
shell: process.platform === "win32",
|
|
76
|
+
});
|
|
77
|
+
if (install.status !== 0) {
|
|
78
|
+
process.exit(install.status ?? 1);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const result = spawnSync(mise, miseArgs, {
|
|
83
|
+
cwd: root,
|
|
84
|
+
stdio: "inherit",
|
|
85
|
+
shell: process.platform === "win32",
|
|
86
|
+
});
|
|
87
|
+
process.exit(result.status ?? 1);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const isMain =
|
|
91
|
+
process.argv[1] !== undefined &&
|
|
92
|
+
path.resolve(process.argv[1]) === __filename;
|
|
93
|
+
|
|
94
|
+
if (isMain) {
|
|
95
|
+
const args = process.argv.slice(2);
|
|
96
|
+
if (args.length === 0) {
|
|
97
|
+
console.error(
|
|
98
|
+
"Usage: node scripts/with-mise.mjs [--install-tools] <mise-args...>",
|
|
99
|
+
);
|
|
100
|
+
process.exit(1);
|
|
101
|
+
}
|
|
102
|
+
if (args[0] === "--install-tools") {
|
|
103
|
+
runMise(args.slice(1), { installTools: true });
|
|
104
|
+
} else {
|
|
105
|
+
runMise(args, { installTools: false });
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 应用侧 Alova 装配:读取 Vite env,注入 bridge / error-guard。
|
|
3
|
+
*/
|
|
4
|
+
import { createYbaisAlova } from "@ybais/alova";
|
|
5
|
+
import { goLogin } from "@ybais/bridge";
|
|
6
|
+
import { reportError } from "@ybais/error-guard";
|
|
7
|
+
|
|
8
|
+
export const API_BASE_URL = import.meta.env.DEV
|
|
9
|
+
? "/proxyApi"
|
|
10
|
+
: import.meta.env.VITE_API_BASE_URL;
|
|
11
|
+
|
|
12
|
+
export const CRYPTO_APP_KEY = import.meta.env.VITE_CRYPTO_APP_KEY;
|
|
13
|
+
export const CRYPTO_APP_SECRET = import.meta.env.VITE_CRYPTO_APP_SECRET;
|
|
14
|
+
|
|
15
|
+
export const alovaInstance = createYbaisAlova({
|
|
16
|
+
baseURL: API_BASE_URL,
|
|
17
|
+
cryptoAppKey: CRYPTO_APP_KEY,
|
|
18
|
+
cryptoAppSecret: CRYPTO_APP_SECRET,
|
|
19
|
+
reportError,
|
|
20
|
+
onAuthFail: () => {
|
|
21
|
+
void goLogin().catch(() => {
|
|
22
|
+
// 浏览器无 Bridge:忽略,由业务页自行引导登录
|
|
23
|
+
});
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
export {
|
|
28
|
+
ApiError,
|
|
29
|
+
TokenBootstrap,
|
|
30
|
+
applyBusinessCodeHandlers,
|
|
31
|
+
businessCodeHandlers,
|
|
32
|
+
BUSINESS_CODE_HANDLER_COOLDOWN_MS,
|
|
33
|
+
getToken,
|
|
34
|
+
isApiSuccess,
|
|
35
|
+
removeToken,
|
|
36
|
+
resetBusinessCodeHandlerCooldown,
|
|
37
|
+
setToken,
|
|
38
|
+
showApiError,
|
|
39
|
+
SUCCESS_CODE,
|
|
40
|
+
syncTokenFromUrl,
|
|
41
|
+
TOKEN_STORAGE_KEY,
|
|
42
|
+
unwrapApiResponse,
|
|
43
|
+
} from "@ybais/alova";
|
|
44
|
+
|
|
45
|
+
export type {
|
|
46
|
+
ApiResponse,
|
|
47
|
+
BusinessCodeHandler,
|
|
48
|
+
MethodMeta,
|
|
49
|
+
UnwrapApiResponseOptions,
|
|
50
|
+
} from "@ybais/alova";
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 七牛上传单例:token API + AES 来自本应用 env。
|
|
3
|
+
*/
|
|
4
|
+
import { createUploader } from "@ybais/qiniu-upload";
|
|
5
|
+
import { fetchQiniuToken } from "@/api/qiniu";
|
|
6
|
+
|
|
7
|
+
export const uploader = createUploader({
|
|
8
|
+
fetchToken: () => fetchQiniuToken(),
|
|
9
|
+
aesKey: import.meta.env.VITE_QINIU_AES_KEY ?? "",
|
|
10
|
+
aesIv: import.meta.env.VITE_QINIU_AES_IV ?? "",
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
export {
|
|
14
|
+
compressQiniuImageUrl,
|
|
15
|
+
dataURLtoFile,
|
|
16
|
+
createUploader,
|
|
17
|
+
Uploader,
|
|
18
|
+
} from "@ybais/qiniu-upload";
|
|
19
|
+
|
|
20
|
+
export type {
|
|
21
|
+
CreateUploaderOptions,
|
|
22
|
+
FetchQiniuTokenResult,
|
|
23
|
+
UploadOptions,
|
|
24
|
+
UploadResult,
|
|
25
|
+
} from "@ybais/qiniu-upload";
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { goBack } from "@ybais/bridge";
|
|
2
|
+
import { router } from "@/router";
|
|
3
|
+
import { Link } from "@tanstack/react-router";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 路由未匹配时的 404 页(挂到 root `notFoundComponent`)
|
|
7
|
+
*/
|
|
8
|
+
export function NotFoundComponent() {
|
|
9
|
+
const handleGoBack = () => {
|
|
10
|
+
// App 内走原生 goBack;浏览器无 Bridge 时 reject,回退到路由 history
|
|
11
|
+
void goBack().catch(() => {
|
|
12
|
+
router.history.back();
|
|
13
|
+
});
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<div className="min-h-screen flex flex-col items-center justify-center bg-slate-50 px-6 py-10 text-center">
|
|
18
|
+
<div className="w-full max-w-md rounded-2xl bg-white px-5 py-6 shadow-sm ring-1 ring-slate-200">
|
|
19
|
+
<p className="text-sm font-medium text-slate-400">404</p>
|
|
20
|
+
<h1 className="mt-2 text-lg font-semibold text-slate-900">
|
|
21
|
+
页面不存在
|
|
22
|
+
</h1>
|
|
23
|
+
<p className="mt-3 text-sm leading-relaxed text-slate-600">
|
|
24
|
+
你访问的页面无效或已被移除,请返回首页继续使用。
|
|
25
|
+
</p>
|
|
26
|
+
|
|
27
|
+
<div className="mt-6 flex flex-col sm:flex-row sm:justify-center">
|
|
28
|
+
<Link
|
|
29
|
+
to="/"
|
|
30
|
+
replace
|
|
31
|
+
className="rounded-xl bg-slate-900 px-4 py-2.5 text-sm font-medium text-white active:opacity-80"
|
|
32
|
+
>
|
|
33
|
+
返回首页
|
|
34
|
+
</Link>
|
|
35
|
+
<button
|
|
36
|
+
type="button"
|
|
37
|
+
className="mt-2 rounded-xl bg-slate-100 px-4 py-2.5 text-sm font-medium text-slate-800 active:opacity-80 sm:mt-0 sm:ml-2"
|
|
38
|
+
onClick={handleGoBack}
|
|
39
|
+
>
|
|
40
|
+
返回上一页
|
|
41
|
+
</button>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export default NotFoundComponent;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type ErrorReport, FallbackPage } from "@ybais/error-guard";
|
|
2
|
+
|
|
3
|
+
export function RouterErrorComponent({
|
|
4
|
+
error,
|
|
5
|
+
reset,
|
|
6
|
+
}: {
|
|
7
|
+
error: unknown;
|
|
8
|
+
reset: () => void;
|
|
9
|
+
}) {
|
|
10
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
11
|
+
const report: ErrorReport = {
|
|
12
|
+
kind: "page",
|
|
13
|
+
message: err.message || "路由渲染失败",
|
|
14
|
+
error,
|
|
15
|
+
stack: err.stack,
|
|
16
|
+
fatal: true,
|
|
17
|
+
timestamp: Date.now(),
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<FallbackPage
|
|
22
|
+
report={report}
|
|
23
|
+
onRetry={reset}
|
|
24
|
+
onReload={() => window.location.reload()}
|
|
25
|
+
/>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# lib 装配说明
|
|
2
|
+
|
|
3
|
+
可复用基础设施已拆为 `@ybais/*` 包,本目录**不再**放置实现代码。
|
|
4
|
+
|
|
5
|
+
| 能力 | 包 / 装配入口 |
|
|
6
|
+
|------|----------------|
|
|
7
|
+
| Bridge | `@ybais/bridge` / `@ybais/bridge/react` |
|
|
8
|
+
| Clipboard | `@ybais/clipboard` / `@ybais/clipboard/react` |
|
|
9
|
+
| Center toast | `@ybais/center-toast`(样式:`@ybais/center-toast/style.css`) |
|
|
10
|
+
| Crypto | `@ybais/crypto` |
|
|
11
|
+
| Error guard | `@ybais/error-guard` |
|
|
12
|
+
| Alova | `@ybais/alova` + 本应用 [`src/api/alova.ts`](../api/alova.ts) |
|
|
13
|
+
| Qiniu | `@ybais/qiniu-upload` + [`src/api/uploader.ts`](../api/uploader.ts) |
|
|
14
|
+
| Updater | `@ybais/updater` |
|
|
15
|
+
| VConsole | `@ybais/vconsole` |
|
|
16
|
+
|
|
17
|
+
业务工具(如 `cn`)放在 `@/utils`,不要写回本目录。
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { StrictMode } from "react";
|
|
2
|
+
import { createRoot } from "react-dom/client";
|
|
3
|
+
import { RouterProvider } from "@tanstack/react-router";
|
|
4
|
+
import { configureBridge } from "@ybais/bridge";
|
|
5
|
+
import {
|
|
6
|
+
ErrorGuardProvider,
|
|
7
|
+
reportDebug,
|
|
8
|
+
setupErrorGuard,
|
|
9
|
+
} from "@ybais/error-guard";
|
|
10
|
+
import { startUpdater } from "@ybais/updater";
|
|
11
|
+
import { TokenBootstrap } from "@/api/alova";
|
|
12
|
+
// @@vconsole
|
|
13
|
+
// 仅 test 静态可达,便于 prod 构建剔除整个模块
|
|
14
|
+
if (import.meta.env.VITE_APP_ENV === "test") {
|
|
15
|
+
void import("@ybais/vconsole").then((m) =>
|
|
16
|
+
m.setupVConsole({
|
|
17
|
+
appEnv: import.meta.env.VITE_APP_ENV,
|
|
18
|
+
gitBranch: import.meta.env.VITE_GIT_BRANCH,
|
|
19
|
+
isDev: import.meta.env.DEV,
|
|
20
|
+
}),
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
// @@/vconsole
|
|
24
|
+
|
|
25
|
+
import { router } from "./router";
|
|
26
|
+
|
|
27
|
+
import "./styles/index.css";
|
|
28
|
+
import "@ybais/center-toast/style.css";
|
|
29
|
+
|
|
30
|
+
configureBridge({
|
|
31
|
+
onUnavailable: (error, context) => {
|
|
32
|
+
reportDebug(`原生能力不可用:${context.method}`, {
|
|
33
|
+
source: "bridge",
|
|
34
|
+
error,
|
|
35
|
+
detail: {
|
|
36
|
+
method: context.method,
|
|
37
|
+
reason: context.reason,
|
|
38
|
+
params: context.params,
|
|
39
|
+
},
|
|
40
|
+
description: "浏览器预览属预期,真机 WebView 可用",
|
|
41
|
+
});
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
setupErrorGuard({
|
|
46
|
+
isDev: import.meta.env.DEV,
|
|
47
|
+
});
|
|
48
|
+
startUpdater({
|
|
49
|
+
isProd: import.meta.env.PROD,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const rootElement = document.getElementById("root")!;
|
|
53
|
+
if (!rootElement.innerHTML) {
|
|
54
|
+
const root = createRoot(rootElement);
|
|
55
|
+
root.render(
|
|
56
|
+
<StrictMode>
|
|
57
|
+
<ErrorGuardProvider>
|
|
58
|
+
<TokenBootstrap>
|
|
59
|
+
<RouterProvider router={router} />
|
|
60
|
+
</TokenBootstrap>
|
|
61
|
+
</ErrorGuardProvider>
|
|
62
|
+
</StrictMode>,
|
|
63
|
+
);
|
|
64
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
|
|
3
|
+
// @ts-nocheck
|
|
4
|
+
|
|
5
|
+
// noinspection JSUnusedGlobalSymbols
|
|
6
|
+
|
|
7
|
+
// This file was automatically generated by TanStack Router.
|
|
8
|
+
// You should NOT make any changes in this file as it will be overwritten.
|
|
9
|
+
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
|
|
10
|
+
|
|
11
|
+
import { Route as rootRouteImport } from './routes/__root'
|
|
12
|
+
import { Route as IndexRouteImport } from './routes/index'
|
|
13
|
+
|
|
14
|
+
const IndexRoute = IndexRouteImport.update({
|
|
15
|
+
id: '/',
|
|
16
|
+
path: '/',
|
|
17
|
+
getParentRoute: () => rootRouteImport,
|
|
18
|
+
} as any)
|
|
19
|
+
|
|
20
|
+
export interface FileRoutesByFullPath {
|
|
21
|
+
'/': typeof IndexRoute
|
|
22
|
+
}
|
|
23
|
+
export interface FileRoutesByTo {
|
|
24
|
+
'/': typeof IndexRoute
|
|
25
|
+
}
|
|
26
|
+
export interface FileRoutesById {
|
|
27
|
+
__root__: typeof rootRouteImport
|
|
28
|
+
'/': typeof IndexRoute
|
|
29
|
+
}
|
|
30
|
+
export interface FileRouteTypes {
|
|
31
|
+
fileRoutesByFullPath: FileRoutesByFullPath
|
|
32
|
+
fullPaths: '/'
|
|
33
|
+
fileRoutesByTo: FileRoutesByTo
|
|
34
|
+
to: '/'
|
|
35
|
+
id: '__root__' | '/'
|
|
36
|
+
fileRoutesById: FileRoutesById
|
|
37
|
+
}
|
|
38
|
+
export interface RootRouteChildren {
|
|
39
|
+
IndexRoute: typeof IndexRoute
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
declare module '@tanstack/react-router' {
|
|
43
|
+
interface FileRoutesByPath {
|
|
44
|
+
'/': {
|
|
45
|
+
id: '/'
|
|
46
|
+
path: '/'
|
|
47
|
+
fullPath: '/'
|
|
48
|
+
preLoaderRoute: typeof IndexRouteImport
|
|
49
|
+
parentRoute: typeof rootRouteImport
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const rootRouteChildren: RootRouteChildren = {
|
|
55
|
+
IndexRoute: IndexRoute,
|
|
56
|
+
}
|
|
57
|
+
export const routeTree = rootRouteImport
|
|
58
|
+
._addFileChildren(rootRouteChildren)
|
|
59
|
+
._addFileTypes<FileRouteTypes>()
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { NotFoundComponent, RouterErrorComponent } from "@/components";
|
|
2
|
+
import { createRootRoute, Outlet } from "@tanstack/react-router";
|
|
3
|
+
|
|
4
|
+
const RootLayout = () => <Outlet />;
|
|
5
|
+
|
|
6
|
+
export const Route = createRootRoute({
|
|
7
|
+
component: RootLayout,
|
|
8
|
+
errorComponent: RouterErrorComponent,
|
|
9
|
+
notFoundComponent: NotFoundComponent,
|
|
10
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { startRpCheck } from "@ybais/bridge";
|
|
2
|
+
import { useCopyText } from "@ybais/clipboard/react";
|
|
3
|
+
import { cn } from "@/utils/cn";
|
|
4
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
5
|
+
|
|
6
|
+
export const Route = createFileRoute("/")({
|
|
7
|
+
component: Index,
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
function Index() {
|
|
11
|
+
const { copy, copied, } = useCopyText();
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<div className="p-2">
|
|
15
|
+
<h3 className="mb-2">Welcome Home!</h3>
|
|
16
|
+
|
|
17
|
+
<div className="space-x-3">
|
|
18
|
+
<button
|
|
19
|
+
className="bg-blue-500 text-white p-2 rounded-md text-sm"
|
|
20
|
+
type="button"
|
|
21
|
+
onClick={startRpCheck}
|
|
22
|
+
>
|
|
23
|
+
Click me
|
|
24
|
+
</button>
|
|
25
|
+
|
|
26
|
+
<button
|
|
27
|
+
className={cn(
|
|
28
|
+
"text-white p-2 rounded-md text-sm",
|
|
29
|
+
copied ? "bg-emerald-600" : "bg-green-500",
|
|
30
|
+
)}
|
|
31
|
+
type="button"
|
|
32
|
+
onClick={() => void copy("啊就打算建档立卡随机掉落空间")}
|
|
33
|
+
>
|
|
34
|
+
{copied ? "🎉复制成功🎉" : "点击复制"}
|
|
35
|
+
</button>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
@import "./variant.css";
|
|
2
|
+
@import "./webview-compat.css";
|
|
3
|
+
|
|
4
|
+
@tailwind base;
|
|
5
|
+
@tailwind components;
|
|
6
|
+
@tailwind utilities;
|
|
7
|
+
|
|
8
|
+
/* 移动端全局常规行为控制 */
|
|
9
|
+
html,
|
|
10
|
+
body {
|
|
11
|
+
margin: 0;
|
|
12
|
+
padding: 0;
|
|
13
|
+
width: 100%;
|
|
14
|
+
height: 100%;
|
|
15
|
+
/* 禁用全局点击灰色高亮框 */
|
|
16
|
+
-webkit-tap-highlight-color: transparent;
|
|
17
|
+
/* 禁用全局文本选中(如果部分内容需复制,可在对应组件上单独覆盖为 text) */
|
|
18
|
+
-webkit-user-select: none;
|
|
19
|
+
user-select: none;
|
|
20
|
+
/* 开启平滑弹性滚动 */
|
|
21
|
+
-webkit-overflow-scrolling: touch;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/* 全局样式中禁用文本意外选中与默认 touch 反馈 */
|
|
25
|
+
body {
|
|
26
|
+
-webkit-touch-callout: none;
|
|
27
|
+
-webkit-user-select: none;
|
|
28
|
+
user-select: none;
|
|
29
|
+
-webkit-tap-highlight-color: transparent;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/* 允许输入框等交互元素可选中 */
|
|
33
|
+
input,
|
|
34
|
+
textarea {
|
|
35
|
+
-webkit-user-select: auto;
|
|
36
|
+
user-select: auto;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
#root {
|
|
40
|
+
width: 100%;
|
|
41
|
+
min-height: 100%;
|
|
42
|
+
}
|