Compare commits

43 Commits
main ... dev

Author SHA1 Message Date
eric
1628efd971 's' 2026-03-10 12:37:36 -05:00
eric
75b21f3e6f 's' 2026-01-15 09:13:37 -06:00
eric
61500c5a6b 's' 2026-01-14 23:56:39 -06:00
eric
b7daa8cb20 's' 2026-01-14 23:56:15 -06:00
eric
f968a05ea5 's' 2026-01-12 13:09:19 -06:00
eric
b0086bfbd2 's' 2026-01-12 12:57:42 -06:00
eric
fc169823c9 's' 2026-01-08 04:39:54 -06:00
eric
a6f99fe82a 's' 2026-01-08 04:12:18 -06:00
eric
672c090311 's' 2026-01-06 05:25:56 -06:00
eric
f345559f2f 's' 2026-01-01 20:20:47 -06:00
root
f455ac17a7 online 2026-01-01 19:50:25 +08:00
eric
5b71b7b7f6 's' 2025-12-27 06:47:06 -06:00
eric
4d96aec152 's' 2025-12-25 01:16:28 -06:00
eric
d311f41dcf 's' 2025-12-18 23:01:13 -06:00
eric
e5ea2ae7c9 's' 2025-12-18 03:07:13 -06:00
eric
c824641a9e 's' 2025-12-18 02:46:52 -06:00
eric
a7ba3b5375 's' 2025-12-17 06:55:36 -06:00
eric
1e1374f665 's' 2025-12-16 09:38:45 -06:00
eric
edce5df9d1 's' 2025-12-14 07:24:10 -06:00
eric
38ecc9a082 'pc首页优化 2025-12-09 01:45:28 +08:00
eric
d0ce5231d6 '优化电子书样式PC+mobile' 2025-12-09 01:37:34 +08:00
eric
66d33c6703 's' 2025-12-04 09:39:54 +08:00
eric
edc65b9d22 's' 2025-12-04 02:05:52 +08:00
eric
181bd3f9c0 's' 2025-12-03 17:35:18 +08:00
eric
8def20ac27 's' 2025-12-03 17:28:11 +08:00
eric
2906d1d3fc 's' 2025-09-25 19:50:58 +08:00
eric
afa521fae9 's' 2025-09-25 19:29:11 +08:00
eric
c2449cd2f0 's' 2025-09-25 19:15:03 +08:00
eric
b725918a85 's' 2025-09-19 16:07:54 +08:00
eric
f17c993447 's' 2025-09-11 22:55:54 +08:00
eric
21ba85ac5c 's 2025-08-31 19:43:24 +08:00
eric
7097f53525 'ss' 2025-08-29 15:20:19 +08:00
eric
853a55b4e9 's' 2025-08-23 00:35:06 +08:00
eric
b38885b46c 'ss' 2025-08-21 08:39:32 +08:00
eric
7b42bbd69c 'ss' 2025-08-20 20:44:25 +08:00
eric
77b121029f 'ss' 2025-08-20 20:43:56 +08:00
eric
df754f3c27 'ss' 2025-08-19 12:14:30 +08:00
WLT\Eric.hu
6ff6cdbcb3 'ss' 2025-07-28 10:41:27 +08:00
WLT\Eric.hu
478d6c5747 'ss' 2025-07-28 09:25:05 +08:00
WLT\Eric.hu
2fc368896e 'ss' 2025-07-28 09:23:18 +08:00
eric
cd8ebe4bcb 'ss' 2025-07-28 06:45:52 +08:00
eric
863a0166b7 '图片修改' 2025-07-28 06:41:44 +08:00
eric
487b6cf158 '文案修改' 2025-07-28 06:30:28 +08:00
92 changed files with 2900 additions and 1475 deletions

0
.env.development Normal file → Executable file
View File

3
.env.production Normal file → Executable file
View File

@@ -1 +1,2 @@
process.env.TARO_API_API="https://api.nomadcna.com"
# process.env.TARO_API_API="https://api.nomadcna.com"
process.env.TARO_API_API="https://api.hackrobot.cn"

3
.gitignore vendored Normal file → Executable file
View File

@@ -4,4 +4,5 @@ deploy_versions/
.rn_temp/
node_modules/
.DS_Store
.swc
.swc
/dist/.user.ini

0
README.md Normal file → Executable file
View File

0
babel.config.js Normal file → Executable file
View File

0
config/dev.js Normal file → Executable file
View File

102
config/index.js Normal file → Executable file
View File

@@ -1,133 +1,111 @@
import path from "path";
// const { GenerateSW } = require("workbox-webpack-plugin");
const WebpackObfuscator = require("webpack-obfuscator");
const config = {
projectName: "cityh5",
date: "2023-12-2",
designWidth: 750,
deviceRatio: {
640: 2.34 / 2,
750: 1,
828: 1.81 / 2,
},
deviceRatio: { 640: 2.34 / 2, 750: 1, 828: 1.81 / 2 },
sourceRoot: "src",
outputRoot: "dist",
plugins: [],
defineConstants: {},
copy: {
patterns: [],
options: {},
},
copy: { patterns: [], options: {} },
framework: "react",
compiler: "webpack5",
cache: {
enable: false, // Webpack 持久化缓存配置建议开启。默认配置请参考https://docs.taro.zone/docs/config-detail#cache
enable: true, // ✅ 开启缓存,加快构建
type: "filesystem",
},
h5: {
webpackChain(chain) {
// ✅ 避免 undefined.js统一使用标准占位符
chain.output.filename("static/js/[name].[contenthash:8].js");
chain.output.chunkFilename("static/js/[name].[contenthash:8].js");
chain
.plugin("MiniCssExtractPlugin")
.use(require("mini-css-extract-plugin"), [
{
filename: "static/css/[name].[contenthash:8].css",
chunkFilename: "static/css/[name].[contenthash:8].css",
},
]);
chain.plugin("MiniCssExtractPlugin").use(require("mini-css-extract-plugin"), [
{
filename: "static/css/[name].[contenthash:8].css",
chunkFilename: "static/css/[name].[contenthash:8].css",
},
]);
if (process.env.NODE_ENV === "production") {
// js代码混淆
// ✅ 保留原有混淆逻辑
chain.plugin("obfuscator").use(WebpackObfuscator, [
{
rotateStringArray: true,
stringArray: true,
stringArrayThreshold: 0.5, // 降低比例,提高速度
stringArrayThreshold: 0.5,
compact: true,
controlFlowFlattening: false, // 关闭,提高速度
deadCodeInjection: false, // 关闭,提高速度
controlFlowFlattening: false,
deadCodeInjection: false,
debugProtection: false,
disableConsoleOutput: true,
selfDefending: false, // 关闭,提高速度
simplify: true, // 开启简化,提高性能
// 禁用对 async/await 的转换
target: "browser", // 或设置为适合目标环境的 ECMAScript 版本
disableOptimizations: true, // 禁用某些优化来避免处理 async/await
selfDefending: false,
simplify: true,
target: "browser",
disableOptimizations: true,
},
["**/*.js", "!node_modules/**"], // 避免对 node_modules 进行混淆
["**/*.js", "!node_modules/**"],
]);
}
// ✅ markdown 支持
chain.module
.rule("markdown")
.test(/\.md$/)
.use("raw-loader")
.loader("raw-loader")
.end();
// ✅ ico 支持
chain.module
.rule("ico")
.test(/\.ico$/)
.use("file-loader")
.loader("file-loader")
.options({
name: "static/images/[name].[ext]",
});
.options({ name: "static/images/[name].[ext]" });
},
esnextModules: ["taro-ui"],
publicPath: "/",
// publicPath: `/v${new Date().getTime()}/`,
publicPath: "/", // ✅ 部署到根目录;如果是子路径,需要改成 /xxx/
staticDirectory: "static",
postcss: {
autoprefixer: {
enable: true,
config: {},
},
autoprefixer: { enable: true, config: {} },
cssModules: {
enable: false, // 默认为 false如需使用 css modules 功能,则设为 true
enable: false,
config: {
namingPattern: "module", // 转换模式,取值为 global/module
namingPattern: "module",
generateScopedName: "[name]__[local]___[hash:base64:5]",
},
},
},
router: {
// basename: '/myapp',
mode: "browser", // 或者是 'hash'
mode: "browser",
customRoutes: {
// "页面路径": "自定义路由"
"/pages/index/index": "/",
"/pages/book/index": "/book",
// '/pages/detail/index': ['/detail'], // 可以通过数组为页面配置多个自定义路由
// "pages/my/index": "/my",
// "pages/meetup/index": "/meetup",
// "pages/meetup/Detail/index": "/detail",
},
},
// defineConstants: {
// HOST: '"/api"'
// },
devServer: {
// host: 'nomad.hackrobot.cn', // 替换为您的域名
// port: 10087, // 选择一个适当的端口号
proxy: {
"/api/": {
target: JSON.parse('"http://localhost:8700"'), //本地调试端口
pathRewrite: {
"^/api/": "/",
},
target: "http://localhost:8700",
pathRewrite: { "^/api/": "/" },
changeOrigin: true,
},
},
// 允许特定域名访问
allowedHosts: ["nomadro.com", "localhost", ".nomadro.com","hackrobot.cn"], // 允许所有 *.nomadyt.com 子域访问
allowedHosts: ["nomadro.com", "localhost", ".nomadro.com", "hackrobot.cn",".ngrok-free.app"],
},
htmlPluginOption: {
favicon: path.resolve(__dirname, "..", "src", "images", "dgnomad.png"), // 同样处理 favicon 路径
favicon: path.resolve(__dirname, "..", "src", "images", "dgnomad.png"),
meta: {
"cache-control": "no-cache, no-store, must-revalidate",
pragma: "no-cache",

152
config/indexBack.js Executable file
View File

@@ -0,0 +1,152 @@
import path from "path";
// const { GenerateSW } = require("workbox-webpack-plugin");
const WebpackObfuscator = require("webpack-obfuscator");
const config = {
projectName: "cityh5",
date: "2023-12-2",
designWidth: 750,
deviceRatio: {
640: 2.34 / 2,
750: 1,
828: 1.81 / 2,
},
sourceRoot: "src",
outputRoot: "dist",
plugins: [],
defineConstants: {},
copy: {
patterns: [],
options: {},
},
framework: "react",
compiler: "webpack5",
cache: {
enable: false, // Webpack 持久化缓存配置建议开启。默认配置请参考https://docs.taro.zone/docs/config-detail#cache
},
h5: {
webpackChain(chain) {
chain.output.filename("static/js/[name].[contenthash:8].js");
chain.output.chunkFilename("static/js/[name].[contenthash:8].js");
chain
.plugin("MiniCssExtractPlugin")
.use(require("mini-css-extract-plugin"), [
{
filename: "static/css/[name].[contenthash:8].css",
chunkFilename: "static/css/[name].[contenthash:8].css",
},
]);
if (process.env.NODE_ENV === "production") {
// js代码混淆
chain.plugin("obfuscator").use(WebpackObfuscator, [
{
rotateStringArray: true,
stringArray: true,
stringArrayThreshold: 0.5, // 降低比例,提高速度
compact: true,
controlFlowFlattening: false, // 关闭,提高速度
deadCodeInjection: false, // 关闭,提高速度
debugProtection: false,
disableConsoleOutput: true,
selfDefending: false, // 关闭,提高速度
simplify: true, // 开启简化,提高性能
// 禁用对 async/await 的转换
target: "browser", // 或设置为适合目标环境的 ECMAScript 版本
disableOptimizations: true, // 禁用某些优化来避免处理 async/await
},
["**/*.js", "!node_modules/**"], // 避免对 node_modules 进行混淆
]);
}
chain.module
.rule("markdown")
.test(/\.md$/)
.use("raw-loader")
.loader("raw-loader")
.end();
chain.module
.rule("ico")
.test(/\.ico$/)
.use("file-loader")
.loader("file-loader")
.options({
name: "static/images/[name].[ext]",
});
},
esnextModules: ["taro-ui"],
publicPath: "/",
// publicPath: `/v${new Date().getTime()}/`,
staticDirectory: "static",
postcss: {
autoprefixer: {
enable: true,
config: {},
},
cssModules: {
enable: false, // 默认为 false如需使用 css modules 功能,则设为 true
config: {
namingPattern: "module", // 转换模式,取值为 global/module
generateScopedName: "[name]__[local]___[hash:base64:5]",
},
},
},
router: {
// basename: '/myapp',
mode: "browser", // 或者是 'hash'
customRoutes: {
// "页面路径": "自定义路由"
"/pages/index/index": "/",
"/pages/book/index": "/book",
// '/pages/detail/index': ['/detail'], // 可以通过数组为页面配置多个自定义路由
// "pages/my/index": "/my",
// "pages/meetup/index": "/meetup",
// "pages/meetup/Detail/index": "/detail",
},
},
// defineConstants: {
// HOST: '"/api"'
// },
devServer: {
// host: 'nomad.hackrobot.cn', // 替换为您的域名
// port: 10087, // 选择一个适当的端口号
proxy: {
"/api/": {
target: JSON.parse('"http://localhost:8700"'), //本地调试端口
pathRewrite: {
"^/api/": "/",
},
changeOrigin: true,
},
},
// 允许特定域名访问
allowedHosts: [
"nomadro.com",
"localhost",
".nomadro.com",
"hackrobot.cn",
], // 允许所有 *.nomadyt.com 子域访问
},
htmlPluginOption: {
favicon: path.resolve(__dirname, "..", "src", "images", "dgnomad.png"), // 同样处理 favicon 路径
meta: {
"cache-control": "no-cache, no-store, must-revalidate",
pragma: "no-cache",
expires: "0",
},
},
target: ["web", "browserslist:> 0.25%, not dead"],
},
};
module.exports = function (merge) {
if (process.env.NODE_ENV === "development") {
return merge({}, config, require("./dev"));
}
return merge({}, config, require("./prod"));
};

2
config/prod.js Normal file → Executable file
View File

@@ -3,7 +3,7 @@ module.exports = {
NODE_ENV: '"production"'
},
defineConstants: {
'process.env.TARO_API_API': JSON.stringify("https://api.nomadcna.com")
'process.env.TARO_API_API': JSON.stringify("https://api.hackrobot.cn")
},
mini: {},
h5: {

3
package.json Normal file → Executable file
View File

@@ -63,13 +63,16 @@
"@types/react": "^18.0.0",
"@types/webpack-env": "^1.13.6",
"babel-preset-taro": "3.6.20",
"compression-webpack-plugin": "^11.1.0",
"eslint": "^8.12.0",
"eslint-config-taro": "3.6.20",
"eslint-plugin-import": "^2.12.0",
"eslint-plugin-react": "^7.8.2",
"eslint-plugin-react-hooks": "^1.6.1",
"glob": "^11.0.3",
"javascript-obfuscator": "^4.1.1",
"postcss": "^8.4.18",
"purgecss-webpack-plugin": "^7.0.2",
"react-refresh": "^0.11.0",
"stylelint": "9.3.0",
"ts-node": "^10.9.1",

0
project.config.json Normal file → Executable file
View File

0
roboth5.sh Normal file → Executable file
View File

0
src/app.config.js Normal file → Executable file
View File

0
src/app.js Normal file → Executable file
View File

0
src/app.scss Normal file → Executable file
View File

0
src/images/AI.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 320 KiB

After

Width:  |  Height:  |  Size: 320 KiB

0
src/images/AIGC.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

0
src/images/avatar.jpg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

0
src/images/book.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 623 KiB

After

Width:  |  Height:  |  Size: 623 KiB

0
src/images/book.svg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

0
src/images/bookqr.jpg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 326 KiB

After

Width:  |  Height:  |  Size: 326 KiB

0
src/images/cat.jpg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

0
src/images/code.svg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

0
src/images/demo.svg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 140 KiB

After

Width:  |  Height:  |  Size: 140 KiB

0
src/images/dgnomad.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

0
src/images/emoji.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

0
src/images/girl.jpg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

0
src/images/group.svg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
src/images/hackrobot.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

0
src/images/host.svg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

0
src/images/joinGroup.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 50 KiB

0
src/images/laptop.svg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

0
src/images/loft.jpg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

0
src/images/loft.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 966 KiB

After

Width:  |  Height:  |  Size: 966 KiB

0
src/images/meetup.jpg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

0
src/images/nomad.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

0
src/images/nomadro.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 229 KiB

After

Width:  |  Height:  |  Size: 229 KiB

0
src/images/nomadvloglife.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 120 KiB

After

Width:  |  Height:  |  Size: 120 KiB

0
src/images/phone.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
src/images/phone2.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

0
src/images/qrcode.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 131 KiB

After

Width:  |  Height:  |  Size: 131 KiB

0
src/images/robot.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

0
src/images/robot.svg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

0
src/images/robot192.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

0
src/images/ssh.svg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 625 B

After

Width:  |  Height:  |  Size: 625 B

BIN
src/images/tiktok.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

0
src/images/touxiang.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 154 KiB

After

Width:  |  Height:  |  Size: 154 KiB

0
src/images/travel.svg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
src/images/web.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

0
src/images/youtube copy.svg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 613 B

After

Width:  |  Height:  |  Size: 613 B

0
src/images/youtube.ico Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 66 KiB

BIN
src/images/youtube.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

0
src/images/youtube.svg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 613 B

After

Width:  |  Height:  |  Size: 613 B

0
src/images/yt2.svg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

0
src/images/yt3.svg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

0
src/images/yt4.svg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

0
src/images/ytGpt.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 230 KiB

After

Width:  |  Height:  |  Size: 230 KiB

47
src/index.html Normal file → Executable file
View File

@@ -1,34 +1,21 @@
<!DOCTYPE html>
<html>
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<meta content="width=device-width,initial-scale=1,user-scalable=no" name="viewport">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-touch-fullscreen" content="yes">
<meta name="format-detection" content="telephone=no,address=no">
<meta name="apple-mobile-web-app-status-bar-style" content="white">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" >
<link rel="apple-touch-icon" href="/static/images/images/dgnomad.png" />
<!-- <link rel="apple-touch-icon" href="/static/images/images/youtube.svg" /> -->
<link
rel="icon"
sizes="192x192"
href="/static/images/images/dgnomad.png"
/>
<title>数字游民</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no">
<title>数字游民:地理套利与自动化杠杆</title>
<meta name="description" content="数字游民">
<meta property="og:description" content="数字游民">
<meta name="twitter:description" content="数字游民">
<!-- Icons -->
<link rel="apple-touch-icon" href="https://minioweb.hackrobot.cn/hackrobot/phone2.png" />
<link rel="icon" sizes="192x192" href="https://minioweb.hackrobot.cn/hackrobot/phone2.png" />
<!-- ✅ 提前建立连接 -->
<link rel="preconnect" href="https://minioweb.hackrobot.cn" crossorigin>
<link rel="dns-prefetch" href="https://minioweb.hackrobot.cn">
<!-- ✅ 预加载 LCP 图片 (WebP 优先PNG 回退) -->
<link rel="preload" as="image" href="https://minioweb.hackrobot.cn/hackrobot/phone2.png" type="image/png" fetchpriority="high">
<!-- JS 注入 -->
<script src="https://unpkg.com/pocketbase/dist/pocketbase.umd.js"></script>
<script><%= htmlWebpackPlugin.options.script %></script>
<!-- 禁止缓存 -->
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
</head>
<body>
<div id="app"></div>
</body>
</html>

1344
src/pages/book/book.md Normal file → Executable file

File diff suppressed because it is too large Load Diff

0
src/pages/book/index.config.js Normal file → Executable file
View File

29
src/pages/book/index.scss Normal file → Executable file
View File

@@ -69,10 +69,16 @@
// color: #72d572;
}
// img {
// max-width: 100%;
// height: auto;
// /* 保持图像的纵横比 */
// }
img {
max-width: 100%;
height: auto;
/* 保持图像的纵横比 */
/* 如果需要让 img 当 flex item 居中,不需要额外样式 */
display: block;
margin: 0 auto;
}
.at-drawer__content {
@@ -101,7 +107,6 @@
flex-direction: column;
align-items: center;
justify-content: center;
// background-color: yellow;
.book_left {
width: 400px;
@@ -110,9 +115,12 @@
}
.markdown-body {
width: 100%;
max-width: 1400px;
font-size: 22px;
// width: 100%;
width: 700px;
// max-width: 1400px;
// font-size: 22px;
font-size: 18px;
// color: red;
// background-color: #72d572;
}
@@ -126,8 +134,15 @@
.joinSalon {
// width: 100%;
// height: 100px;
display: none;
// background-color: yellow;
}
}
.Book img {
/* 如果需要让 img 当 flex item 居中,不需要额外样式 */
display: block;
margin: 0 auto;
}
}

473
src/pages/book/index.tsx Normal file → Executable file
View File

@@ -1,6 +1,7 @@
// @ts-nocheck
import { Component } from "react";
import { View, Text, Image } from "@tarojs/components";
import Taro, { useRouter } from "@tarojs/taro";
import "./index.scss";
import { AtButton, AtInput, AtTabBar } from "taro-ui";
import React, { useCallback, useEffect, useState, useRef } from "react";
@@ -8,7 +9,6 @@ import { AtGrid } from "taro-ui";
import { Swiper, SwiperItem } from "@tarojs/components";
import { AtCard } from "taro-ui";
import { AtImagePicker } from "taro-ui";
import Taro from "@tarojs/taro";
import { AtAvatar } from "taro-ui";
import { AtTag } from "taro-ui";
import { AtList, AtListItem } from "taro-ui";
@@ -16,7 +16,46 @@ import { AtForm, AtMessage, AtDrawer, AtIcon } from "taro-ui";
import { marked } from "marked";
import infoText from "./info.md";
import bookText from "./book.md";
import { getPayEnv, getDeviceFromEnv } from "../../utils/payEnv";
// import youtubeIco from "../../images/youtube.ico";
import hackrobot from "../../images/hackrobot.jpg";
// PocketBase 配置
const PB_URL = "https://pocketbase.hackrobot.cn";
const pb = new PocketBase(PB_URL);
/** PC 扫码支付:生成带二维码和轮询的 HTML 页面 */
function buildQrcodePageHtml(imgSrc, orderId, returnUrl, apiBase) {
const esc = (s) =>
String(s)
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;");
const statusUrl = `${apiBase}/nomadvip/order_status?order_id=${encodeURIComponent(orderId)}`;
return `<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>微信扫码支付</title>
<style>*{box-sizing:border-box;}body{font-family:system-ui;display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:100vh;margin:0;background:linear-gradient(135deg,#667eea 0%,#764ba2 100%);}
.card{background:#fff;border-radius:20px;padding:40px;box-shadow:0 25px 50px -12px rgba(0,0,0,.25);max-width:420px;}
h2{color:#1e293b;margin:0 0 8px;font-size:1.5rem;}.hint{color:#64748b;font-size:14px;margin-bottom:20px;}
.qr-wrap img{width:200px;height:200px;border-radius:12px;border:2px solid #e2e8f0;background:#fff;}
.tip{font-size:13px;color:#64748b;margin-top:16px;}.success-wrap{display:none;}.success-wrap.show{display:block;}
.wait-wrap.hide{display:none;}.success-icon{font-size:4rem;margin-bottom:16px;}
.success-title{font-size:1.25rem;color:#16a34a;font-weight:600;margin-bottom:8px;}
.success-desc{color:#64748b;font-size:14px;margin-bottom:24px;}
</style></head><body><div class="card">
<div class="wait-wrap" id="waitWrap"><h2>微信扫码支付</h2><p class="hint">请使用微信扫描下方二维码完成支付</p>
<div class="qr-wrap"><img src="${esc(imgSrc)}" alt="支付二维码"/></div><p class="tip" id="tip">等待支付中...</p></div>
<div class="success-wrap" id="successWrap"><div class="success-icon">✅</div><div class="success-title">支付成功</div>
<div class="success-desc">感谢您的支持,正在返回...</div></div></div>
<script>
(function(){var orderId="${esc(orderId)}";var returnUrl="${esc(returnUrl)}";var statusUrl="${esc(statusUrl)}";
var waitWrap=document.getElementById("waitWrap");var successWrap=document.getElementById("successWrap");
function showSuccess(){waitWrap.classList.add("hide");successWrap.classList.add("show");
setTimeout(function(){window.location.href=returnUrl;},1500);}
function check(){fetch(statusUrl).then(function(r){return r.json();}).then(function(d){
if(d&&d.paid){showSuccess();return;}setTimeout(check,1000);}).catch(function(){setTimeout(check,1000);});}
setTimeout(check,1000);})();
</script></body></html>`;
}
marked.setOptions({
breaks: true, // 是否回车换行
@@ -48,9 +87,149 @@ const Book = () => {
const [headers, setHeaders] = useState([]); // 存储所有 <h1> 的内容
const contentRef = useRef(null); // 用于获取渲染后的 DOM
const [paidType, setPaidType] = useState(null); // 新增:支付类型 'vip' | 'book' | null
// 把这些变量名全部改为 userId
const [userId, setUserId] = useState(Taro.getStorageSync("userId") || null);
const [loading, setLoading] = useState(true); // 新增:加载状态
const router = useRouter(); // Taro 3+ 获取路由参数
const [vipbook, setVipbook] = useState(false); //是否在vip群打开
// 在组件挂载时检测 referrer
useEffect(() => {
// window.localStorage.removeItem("book");
if (router && router.params && router.params.type === 'vipbook') {
setVipbook(true)
} else {
setVipbook(false)
}
}, []);
// 保存 userId 到本地
const saveUserId = (id) => {
Taro.setStorageSync("userId", id);
setUserId(id);
};
// 检查本地是否已有支付类型
const getLocalPaidType = () => {
return Taro.getStorageSync("paidType") || null;
};
// 保存支付类型到本地
const savePaidType = (type) => {
Taro.setStorageSync("paidType", type);
setPaidType(type);
};
// 查询 PocketBase 是否支付过
const checkPaymentFromPB = async (userId) => {
// window.alert('查询pocketbase')
try {
// 查询 payments 表中该 user_id 的记录,按创建时间倒序(最新的一笔)
const records = await pb.collection("payments").getList(1, 1, {
filter: `user_id = "${userId}"`,
sort: "-created", // 最新支付优先
});
if (records.items.length > 0) {
alert("records.items[0]", records.items[0]);
const latestType = records.items[0].type; // 取最新一笔的 type
const dbUserId = records.items[0].user_id; // 数据库里的真实 user_id
// window.alert('latestType', dbUserId)
savePaidType(latestType);
saveUserId(dbUserId); // 正确保存真实的 user_id
console.log("从 PocketBase 查询到已支付:", latestType);
return latestType;
} else {
// window.alert('未找到支付记录')
console.log("未找到支付记录");
savePaidType(null);
return null;
}
} catch (err) {
// window.alert('查询 PocketBase 失败', err)
console.error("查询 PocketBase 失败:", err);
// 网络错误时不影响本地缓存,使用本地旧值
return getLocalPaidType();
} finally {
}
};
// 清空所有本地存储(测试专用)
const clearAllStorage = () => {
try {
// 清空 Taro 的 storage推荐方式
Taro.clearStorageSync();
// 同时清空 window.localStorageH5 环境兼容)
if (typeof window !== "undefined" && window.localStorage) {
window.localStorage.clear();
}
// 可选:手动清除你知道的关键字段,防止残留
Taro.removeStorageSync("userId");
Taro.removeStorageSync("paidType");
Taro.removeStorageSync("openid");
Taro.removeStorageSync("wxid");
// 状态重置
setPaidType(null);
setUserName(null);
setLoading(false);
Taro.showToast({
title: "本地存储已清空",
icon: "success",
duration: 2000,
});
// 可选:刷新页面让效果立即生效
setTimeout(() => {
// window.location.reload();
}, 1000);
} catch (error) {
console.error("清空存储失败:", error);
Taro.showToast({
title: "清空存储失败",
icon: "none",
});
}
};
useEffect(() => {
// 判断是否在微信
isWeChatFun();
// 方法1Taro 3+ 推荐方式(支持小程序 + H5
if (router && router.params && router.params.type === "clean") {
clearAllStorage();
return;
}
const init = async () => {
const currentUserId = getOrCreateUserId(); // 确保 userId 存在
const localType = getLocalPaidType();
const localUserId = Taro.getStorageSync("userId");
// 只有当本地同时有 paidType 和 userId 时,才信任本地缓存
if (localType && localUserId) {
setPaidType(localType);
setUserId(localUserId);
setLoading(false);
console.log("使用本地缓存支付状态:", localType);
return;
}
// 否则去 PocketBase 查最新状态
await checkPaymentFromPB(currentUserId);
setLoading(false);
};
init();
}, []);
useEffect(() => {
@@ -98,49 +277,197 @@ const Book = () => {
}
};
const payh5 = (type, total_fee = 1000) => {
// console.log("111");
/** 微信内xorpay 收银台 */
const payWechatXorpay = (type, total_fee) => {
const userId = getOrCreateUserId();
const returnUrl = window.location.href;
Taro.request({
method: "POST",
url: `${process.env.TARO_API_API}/payh5`, //仅为示例,并非真实的接口地址
// url: "https://pay.hackrobot.cn${process.env.TARO_API_API}/payh5", //仅为示例,并非真实的接口地址
url: `${process.env.TARO_API_API}/nomadvip/payh5`,
data: {
callback_url: window.location.href,
openid: window.localStorage.getItem("openid"),
total_fee: total_fee, // 金额,单位:分
type: type, //订单类型 book/meetup/video/vip
user_id: userId,
total_fee,
type,
channel: "wxpay",
return_url: returnUrl,
provider: "xorpay",
},
header: {
"content-type": "application/json", // 默认值
},
success: function (res) {
console.log(res.data);
// window.alert(res.data.jsapi);
WeixinJSBridge.invoke(
"getBrandWCPayRequest",
{
// 以下6个支付参数通过payjs的jsapi接口获取
// appId: "wxc5205a653b0259bf",
// timeStamp: "1701401644",
// nonceStr: "KhOYB0wFV6j9qyQK",
// package: "prepay_id=wx01113404850024729b35f4d69b73500000",
// signType: "MD5",
// paySign: "2A823C8D47CCF871C6C65A56DC228CF8",
...(res?.data?.jsapi ?? {}),
},
function (res) {
// 支付成功
if (res.err_msg == "get_brand_wcpay_request:ok") {
window.localStorage.setItem(type, 1); //订单类型 book/meetup/video/vip
WeixinJSBridge.call("closeWindow");
}
header: { "content-type": "application/json" },
success: (res) => {
if (res.data?.status === "ok" && res.data?.xorpay_params) {
const form = document.createElement("form");
form.method = "POST";
form.action = res.data.xorpay_url;
form.style.display = "none";
for (const key in res.data.xorpay_params) {
const input = document.createElement("input");
input.type = "hidden";
input.name = key;
input.value = res.data.xorpay_params[key];
form.appendChild(input);
}
);
document.body.appendChild(form);
form.submit();
} else {
Taro.showToast({ title: "支付发起失败", icon: "none" });
}
},
fail: () => Taro.showToast({ title: "网络错误", icon: "none" }),
});
};
/** PCzpay 扫码,新窗口展示二维码 */
const payPcZpay = (type, total_fee) => {
const userId = getOrCreateUserId();
const returnUrl = window.location.href;
Taro.request({
method: "POST",
url: `${process.env.TARO_API_API}/nomadvip/payh5/redirect`,
data: {
user_id: userId,
total_fee,
type,
channel: "wxpay",
return_url: returnUrl,
device: "pc",
provider: "zpay",
},
header: { "content-type": "application/json" },
success: (res) => {
const d = res.data;
if (d?.status === "ok" && d?.qrcode_mode && d?.img) {
const apiBase = (process.env.TARO_API_API || "").replace(/\/$/, "");
const html = buildQrcodePageHtml(d.img, d.order_id, d.return_url || returnUrl, apiBase);
const win = window.open("", "_blank");
if (win) {
win.document.write(html);
win.document.close();
} else {
Taro.showToast({ title: "请允许弹窗以显示支付二维码", icon: "none" });
}
} else {
Taro.showToast({ title: "支付发起失败", icon: "none" });
}
},
fail: () => Taro.showToast({ title: "网络错误", icon: "none" }),
});
};
/** 手机浏览器zpay 唤醒微信form 提交到 redirect 接口触发 302 */
const payH5Zpay = (type, total_fee) => {
const userId = getOrCreateUserId();
const returnUrl = window.location.href;
const form = document.createElement("form");
form.method = "POST";
form.action = `${(process.env.TARO_API_API || "").replace(/\/$/, "")}/nomadvip/payh5/redirect`;
form.style.display = "none";
const fields = [
["user_id", userId],
["total_fee", String(total_fee)],
["type", type],
["channel", "wxpay"],
["return_url", returnUrl],
["device", "h5"],
["provider", "zpay"],
];
fields.forEach(([k, v]) => {
const input = document.createElement("input");
input.type = "hidden";
input.name = k;
input.value = v;
form.appendChild(input);
});
document.body.appendChild(form);
form.submit();
};
// 生成或获取 userId修改为无下划线
const getOrCreateUserId = () => {
let userId = Taro.getStorageSync("userId");
if (!userId) {
// 修改这里:去掉下划线
userId = `user${Date.now()}`; // 示例user1766043770158
Taro.setStorageSync("userId", userId);
console.log("新生成 userId:", userId);
}
return userId;
};
/** 统一支付入口PC扫码(zpay) / 手机浏览器唤醒(zpay) / 微信内(xorpay) */
const handlePay = (type, total_fee) => {
const env = getPayEnv();
const device = getDeviceFromEnv(env);
if (device === "wechat") {
payWechatXorpay(type, total_fee);
} else if (device === "pc") {
payPcZpay(type, total_fee);
} else {
payH5Zpay(type, total_fee);
}
};
/* eslint-disable-next-line no-unused-vars */
const _legacyPayh5 = (type, total_fee) => {
const userId = getOrCreateUserId();
Taro.request({
method: "POST",
url: `${process.env.TARO_API_API}/payh5`,
data: {
total_fee: total_fee,
type: type,
// callback_url 临时不传
user_id: userId, // ← 新增字段
},
header: {
"content-type": "application/json",
},
success: (res) => {
if (res.data?.status === "ok" && res.data?.xorpay_params) {
// 1. 乐观更新:立即缓存 paidType用户看到“加载中”或直接显示已支付
// savePaidType(type);
// saveUserName(userId); // ← 乐观缓存当前 userId
// Taro.showToast({
// title: "正在跳转支付,请稍后...",
// icon: "loading",
// duration: 2000,
// });
// 创建隐藏 form POST 到 XorPay
const form = document.createElement("form");
form.method = "POST";
form.action = res.data.xorpay_url;
form.style.display = "none";
for (const key in res.data.xorpay_params) {
const input = document.createElement("input");
input.type = "hidden";
input.name = key;
input.value = res.data.xorpay_params[key];
form.appendChild(input);
}
document.body.appendChild(form);
form.submit();
// 2. 可选:提交后清理 form避免内存泄漏
setTimeout(() => {
document.body.removeChild(form);
}, 1000);
} else {
Taro.showToast({
title: "支付发起失败",
icon: "none",
});
}
},
fail: () => {
Taro.showToast({
title: "网络错误",
icon: "none",
});
},
});
};
return (
<>
<View className="book_header_div"></View>
@@ -158,7 +485,7 @@ const Book = () => {
>
<AtIcon value="chevron-left"></AtIcon>
</View>
<View className="book_header_middle">{`📗 数字游民`}</View>
<View className="book_header_middle">{`云手机`}</View>
<View
className="book_header_right"
onClick={() => {
@@ -174,7 +501,9 @@ const Book = () => {
className="markdown-body" // 添加 GitHub Markdown 主题类
dangerouslySetInnerHTML={{
// __html: marked(markText),
__html: marked(book == 1 ? bookText : infoText),
// __html: marked(book == 1 ? bookText : infoText),
__html: marked(paidType=='book'|| vipbook ? bookText : infoText),
}}
></View>
@@ -197,48 +526,50 @@ const Book = () => {
</View> */}
</AtDrawer>
{/* 判断是否是微信 */}
{!isWeChat && (
// 公众号二维码
<div
style={{
color: "red",
display: "flex", // 关键flex 布局
justifyContent: "center", // 水平居中
alignItems: "center", // 垂直居中(可选,如果你想整块垂直也居中)
flexDirection: "column", // 可选:让内容垂直排列(如果下面还有文字)
margin: "20px 0", // 可选:上下留点间距
}}
>
<Image
className="hackrobot"
src={hackrobot}
style={{
width: "300px",
height: "300px",
maxWidth: "none",
maxHeight: "none",
}}
// src={`https://minioweb.hackrobot.cn/hackrobot/propless.svg`}
lazyLoad
></Image>
</div>
)}
{/* 判断有没有wxid */}
{book !=1 ? (
{/* {book != 1 ? ( */}
{paidType != "book" ? (
<AtButton
type="primary"
size="normal"
className="joinSalon"
onClick={() => {
if (book == 1) {
return false;
} else {
if (!!isWeChat) {
// 19.8元
payh5("book", 1980);
} else {
window.location.href = `https://mp.weixin.qq.com/s/GBU3THKaXRRkdpZXH_18jw`;
}
}
if (paidType == "book" || vipbook) return;
handlePay("book", 19900);
}}
>
{`阅读更多`}
{`🔒阅读更多`}
</AtButton>
) : <span style={{color:'green'}}>....</span>}
{/* <AtButton
type="primary"
size="normal"
className="joinSalon"
onClick={() => {
if (book == 1) {
return false;
} else {
if (!!isWeChat) {
// 198元
payh5("group", 29800);
} else {
window.location.href = `https://mp.weixin.qq.com/s/GBU3THKaXRRkdpZXH_18jw`;
}
}
}}
>
{`游民社群`}
</AtButton> */}
) : // <span style={{ color: "green" }}>持续更新中....</span>
null}
</View>
</>
);

1215
src/pages/book/info copy.md Executable file

File diff suppressed because it is too large Load Diff

637
src/pages/book/info.md Normal file → Executable file
View File

@@ -1,609 +1,108 @@
# 前言
## 🚀 数字游民
# 低成本工作室: 云手机
<img src="https://hackrobot-1258475618.cos.ap-shenzhen-fsi.myqcloud.com/fqwfqwff.jpg?imageSlim" title="" alt="fqwfqwff.jpg" width="333">
> 电子书+视频教程
对于普通人而言,最好的杠杆依然是`媒体``代码`  
## Tiktok无人直播
如今有了AIGC的加持  
![grok-video-52c89ea7-eaf7-4208-adb3-4d2503200d83_20260106_011733_2509.gif](https://minioweb.hackrobot.cn/hackrobot/grok-video-52c89ea7-eaf7-4208-adb3-4d2503200d83_20260106_011733_2509.gif)
以前创作的门槛,已经不再是`门槛`    
<!-- ![2024-12-22-01-00-03-Escrcpy.png](https://hackrobot-1258475618.cos.ap-shenzhen-fsi.myqcloud.com/2024-12-22-01-00-03-Escrcpy.png?imageSlim)   -->
数字游民,翻译自`digital nomad`,指的是依靠依靠互联网获取收入,并且像游牧民族一样旅居各个地方的群体
<!-- > tiktokz直播  
## 目录
- 被动收入:YouTube运营
- 副业:wordpress建站
<video style="max-width:100%;height:auto;" controls>
- 独立开发:sass业务
  <source src="https://hackrobot-1258475618.cos.ap-shenzhen-fsi.myqcloud.com/9.%20tiktok%E7%9B%B4%E6%92%AD-2.mp4" type="video/mp4">
- 一人公司/分布式团队
  您的浏览器不支持 video 标签。
- 展望与结束语
</video> -->
本书不是`教人赚钱`的指南,但作为参考,应该可以给你一些启发
### 目录
持续更新中,未完待续
> 📱系统安装
- 开源硬件选择 (百元级),
- 刷入安卓系统(Android)
- 获取root权限(magisk面具)
- lsposed环境 (xposed外挂模块)
- 谷歌框架
> 🚫风控设置
- 机型参数修改
- 静态住宅ip (链式代理)
- gps定位 (fake location)
- 权限控制
- 安卓虚拟机 (多开系统)
> 💻远程控制
- frp内网穿透
- scrcpy (adb远控)
- qscrcpy(群控)
> 💰应用场景
- Tikrok无人直播
---
# 远程工作
## Youtube无人直播
大多数人成为数字游民,还是依赖一份旱涝保收的`远程工作`
<img title="" src="https://minioweb.hackrobot.cn/hackrobot/qqwfqwf_20260107_221850_7499.jpg" alt="qqwfqwf_20260107_221850_7499.jpg" width="368">
然后经营自己的副业,直到成为主页
### 目录
![2025-04-03-12-54-51-Hire a Freelance Developer for Programming Jobs _ Fiverr.png](https://hackrobot-1258475618.cos.ap-shenzhen-fsi.myqcloud.com/2025-04-03-12-54-51-Hire%20a%20Freelance%20Developer%20for%20Programming%20Jobs%20_%20Fiverr.png?imageSlim)
> 📱系统安装
以下是海外常用的远程工作平台
- 开源硬件选择 (百元级),
- [fiverr](https://go.fiverr.com/visit/?bta=1111508&brand=fiverrmarketplace)
- 刷入linux系统(server服务器)
- [upwork](https://www.upwork.com/)
> 💻软件配置
- [remoteok](https://remoteok.com/)
- 解决科学姿势上网 (clash安装)
英语和重要,必要的沟通,还是要行
- 直播间实时录屏/转发
## 谨防欺诈
- srs直播服务器
![afafwff.jpg](https://hackrobot-1258475618.cos.ap-shenzhen-fsi.myqcloud.com/afafwff.jpg?imageSlim)
- 安卓虚拟机 (Redroid容器)
很多工作机会,并不是真实的,平台也不一定能监管到位
- 浏览器.设置油猴脚本外挂
从人力性价比来说,印度的更便宜,有原生的`语言和网络优势`,以及`汇率`
> 💰Youtube无人直播
而菲律宾也是`呼叫中心`的核心外包国家
- ypp频道收益
作为一名中国人,在很多业务上,并没有优势,很多活跃都是外贸采购人员
但平台可以帮助我们确认:才是才是需求高的`业务方向`
## 国内平台
<img src="https://hackrobot-1258475618.cos.ap-shenzhen-fsi.myqcloud.com/wegwgweg.jpg?imageSlim" title="" alt="wegwgweg.jpg" width="383">
国内有很多XX社区,在做致力于数字游民的事业
但由于本土众所周知的原因
大多都是套了一层壳,本质还是`劳务中介``二房东`的生意
不是创始人不够努力,也不是技术和运营的问题,而是土壤天生不允许发芽
如果寻求远程,可以在某直聘`搜索职位时,加上远程`
有些`开发,客户,运营`是存在远程岗位的
话说,这是一个向企业和用户双方都收费的平台🤣...可以试试
- 非ypp频道 (积累播放时长)
---
# 被动收入:YouTube运营
## 低成本工作室 : homelab
你可以在 [nomadyt.com](https://nomadyt.com) 阅读,关于youtube运营的电子书
![grok-video-b3d8e227-238c-42fd-b934-28841cd4b733_20260106_011523_2084.gif](https://minioweb.hackrobot.cn/hackrobot/grok-video-b3d8e227-238c-42fd-b934-28841cd4b733_20260106_011523_2084.gif)
<img src="https://hackrobot-1258475618.cos.ap-shenzhen-fsi.myqcloud.com/2025-03-30-01-40-00-Youtube%E8%BF%90%E8%90%A5%E7%AC%94%E8%AE%B0.png?imageSlim" title="" alt="2025-03-30-01-40-00-Youtube运营笔记.png" width="189">
> 📱工作室硬件
但是本书,会展示更多运营的细节
- 开源硬件选择
比如无人直播,除了`ffmpeg推流`,以及`obs开源软件`之外
- 智能家居:homeasstant
如何使用`安卓开发板+采集卡`的方案,模拟移动端直播,权重更高,平台推荐,且防封
- P2P组网 (异地远程办公)
除此之外,例如使用`链式代理`,如何选择`静态住宅ip`,也是出海业务的基础
如果没有掌握,一切都是徒劳,会面临0播,限流,甚至封号
---
# wordpess建站
> 网站已死?网站永生
网站,在国内,似乎是一个很古老的词,因为它是互联网的下等公民
在移动互联网时代,被各种`小程序`或者`快应用`取代
最不济也是用`自研app`,圈地画牢,获取用户的数据
而web网站,或者H5,都是排除在生态之外,用户信任度也较低
> 但如果目光望向海外呢
各种当下火热的AI,都是`网页为先,支持pwa`
如果想要成为成为独立开发者,只需要接入stripe支付即可
各种独立站,电子商务,私域也让各种商家大赚特赚
> wordpress建站
![afwqfqwf.png](https://hackrobot-1258475618.cos.ap-shenzhen-fsi.myqcloud.com/afwqfqwf.png?imageSlim)
wordpress是基于php开发的开源建站程序,拥有最丰富的`主题和插件`生态
即便是小白,也能分分钟上线一个网站,可以是:
- 个人博客
- 企业官网
- woocommerce独立站
- 网校/电子书/论坛
- 等等
一夜之间上线网站,全球访问,是一件很简单的事情
如果想要盈利,最简单的就是接入`谷歌的广告`,自动化嵌入
## 一夜之间上线网站
> 购买域名
可以在 [ namesilo](https://www.namesilo.com/?rid=3bd4047mc) 购买域名,也就是类似`google.com`的网址
因为是海外服务商,所以不需要备案
<img src="https://hackrobot-1258475618.cos.ap-shenzhen-fsi.myqcloud.com/2025-04-02-08-15-14-Low-Cost%20Domain%20Names%20%26%20Hosting%20from%20%240.99%20_%20NameSilo.png?imageSlim" title="" alt="2025-04-02-08-15-14-Low-Cost Domain Names & Hosting from $0.99 _ NameSilo.png" width="480">
> 购买vps服务器
域名虽然是英文字母, 但是用户访问后,实际是`google.com==>8.8.8.8`,
dns会把网址解析到 服务器的默认`公网ip`地址
因为是海外的厂商,所以也不要备案
在 [vultr](https://www.vultr.com/?ref=8985003) 购买一个vps服务器,后,建议立即用一键脚本部署`宝塔面板`
自带wordpress安装程序
<img title="" src="https://hackrobot-1258475618.cos.ap-shenzhen-fsi.myqcloud.com/2025-04-02-08-18-13-index.tsx%20-%20nomadro.com%20-%20Visual%20Studio%20Code.png?imageSlim" alt="2025-04-02-08-18-13-index.tsx - nomadro.com - Visual Studio Code.png" width="469">
## adsense赚美金
> wordpress插件: [google site kit](https://wordpress.org/plugins/google-site-kit/)
<img title="" src="https://hackrobot-1258475618.cos.ap-shenzhen-fsi.myqcloud.com/afqfqfqwf.jpg?imageSlim" alt="afqfqfqwf.jpg" width="466">
插件会自动帮你关联`google分析`,与`adsense账户`
当你点击申请后,官方会人工审核网站的`内容和流量`,一般2周,快的次日就有结果
## 拓展其他收益:affiliate
<img src="https://hackrobot-1258475618.cos.ap-shenzhen-fsi.myqcloud.com/2025-04-02-09-25-35-images.jpg.png?imageSlim" title="" alt="2025-04-02-09-25-35-images.jpg.png" width="457">
仅仅在wordpress自动放广告,如果不是站群,收益不会太多
ps: 站群:可以基于`k8s,docker`技术,自动批量建站
但大多数人,都是技术小白,远不到玩站群的层次,也没必要
在自己没核心业务:比如无职业资源`律师,医生,销售`也没有商品.产品等背景下
建议采用`affiliate佣金`模式,增加收入,别入
- amazon: 商品佣金 + kindle书籍
- vps服务器: [vultr](https://www.vultr.com/?ref=8985003) 和域名平台: [namesilo](https://www.namesilo.com/?rid=3bd4047mc)
- 金融:[ wise](https://wise.com/invite/amc/xiaoshuangh)钱包 和 [okx](https://okx.com/join/84999861)交易所
- 等等
affiliate marketing的好处是,推广的服务基本都是互联网平台
而且.平台/推广者/用户,三方收益
平台有新客户,新用户有优惠,推广者有提成
---
# 独立开发sass
> web优先,ios app其次
<img title="" src="https://hackrobot-1258475618.cos.ap-shenzhen-fsi.myqcloud.com/fqwfqwfqw.jpg?imageSlim" alt="fqwfqwfqw.jpg" width="323">
web只需要解决`登录``支付`2个核心问题,优势就太多了
- 几分钟上线
- 快速迭代更新
- 用户点击进入(短信/文章/聊天框)
大多数人,之所以选择iOS app, 是因为它的生态:
- `推送`,`全球分发`,`云数据`,`身份id认证``支付收款`
`下载`的成本很高,在测试用户是否具有粘性之前
用web先行测试是一种策略
## 数字游民CNA
如果你了解过数字游民,那么一定会知晓`nomadlist`这个网站
模仿它的特色,我正在尝试开发类似的网站,针对中文用户
目前仍然很简陋,但会持续迭代,可访问: [nomadcna](https://nomadcna.com/)
<img src="https://hackrobot-1258475618.cos.ap-shenzhen-fsi.myqcloud.com/2025-04-02-08-38-51-%E6%95%B0%E5%AD%97%E6%B8%B8%E6%B0%91CNA.png?imageSlim" title="" alt="2025-04-02-08-38-51-数字游民CNA.png" width="391">
## 技术栈
在nomadlist的创始人的`MAKER`一书中,作者用着`最古老的jquery和php以及sqlite数据库`,却开发了平均 40k$/m纯利润各种网站,包括 `AI 工具站/电子商务`,其数据都在X上实时更新
其实只需要5$/月的  [vultr](https://www.vultr.com/?ref=8985003) 服务器,就足以支撑业务
<img src="https://hackrobot-1258475618.cos.ap-shenzhen-fsi.myqcloud.com/2025-04-02-08-46-57-%40levelsio%20(%40levelsio)%20_%20X.png?imageSlim" title="" alt="2025-04-02-08-46-57-@levelsio (@levelsio) _ X.png" width="427">
本人比较推荐基于js的react生态,它可以满足
- react开发web网站
- chrome插件
- electron开发pc客户端软件
- react native开发iOS app
当然,如果你想开发一个完整的应用,会涉及很多,体验/功能 比如`用户无法感知`
- 埋点:分析/错误监控
- 多语言 (翻译)
- 加固混淆 (防止逆向渗透)
- pwa (将web进行app化)
- wasm,three.js,webVR (边缘计算能力与3D可视化)
思虑越多,越难以行动,完美主义很致命
即便是一个`hello world`,当它被各个世界的用户,可点击访问时
你就成功了一半,因为它以为,除了`自媒体作品`之外,你有了`创造产品`的能力
<img src="https://hackrobot-1258475618.cos.ap-shenzhen-fsi.myqcloud.com/fqwfwqfqwf.png?imageSlim" title="" alt="fqwfwqfqwf.png" width="390">
## 收益: admob广告与内购
<img src="https://hackrobot-1258475618.cos.ap-shenzhen-fsi.myqcloud.com/wqfqwfqwf.png?imageSlim" title="" alt="wqfqwfqwf.png" width="400">
以iOS app为例子,收益的方式有很多
- admob广告
- 购买下载 (一次性买断)
- 内购:订阅制 (月付)
如果不想向用户收费,可以直接接入admob广告,也是google 广告
进一步可以采取pro升级模式,用户付费后,免广告,且提供高级功能
在国内,用户付费意愿较低
如果用户基础大,也可以采取`开屏广告``广告位`形式获取获取企业厂商赞助
最理想的模式的是`订阅制`,基于`1000个粉丝`的理论,开发者就可以实现客观的收入了
但风险也是对等的
一旦成功,就会无数团队模仿,利用资金/资源/投放广告,来碾压开发者
如果建立护城河,是一个难题
---
# 地理套利
数字游民最本质的优势在于`地理套利`
![qwfqfwqf.jpg](https://hackrobot-1258475618.cos.ap-shenzhen-fsi.myqcloud.com/qwfqfwqf.jpg?imageSlim)
更详细的优势如下:
- 地理优势:东亚气候温暖,风景唯美,适合旅居
- 生活成本:三四线城市消费水平低,物价便宜
- 汇率差:赚美元,换算成泰铢可以达到1:30,人民币1:7左右
- 当地特产/文化:比如泰国旅游业,印度仿制药,香港金融等等
- 时间差:淡季旅行性价比高,错峰出行,看见不一样的文化
即便是每月1000$/m的收益,也可以过得很舒服惬意,也拥有无限的可能性
## 大理与乌托邦
在中国境内,`大理`是所有裸辞打工人的第一站
逃离钢筋水泥,回归自然田园
![dwqdqwf.jpg](https://hackrobot-1258475618.cos.ap-shenzhen-fsi.myqcloud.com/dwqdqwf.jpg?imageSlim)
房租编译,风景怡人,各个XX社区,遍地开花
很多东南亚的客户,也来这里旅行,通过 [ctrip](https://www.trip.com/nomadcna?Allianceid=6238114&SID=193455508&trip_sub1=&trip_sub3=D2700672) 订票,也就是某程英文版
其实`惠州`也值得一试,离广东的`深圳/广州`一线城市距离近
![wgwggg.jpg](https://hackrobot-1258475618.cos.ap-shenzhen-fsi.myqcloud.com/wgwggg.jpg?imageSlim)
房价/物价便宜,气候温暖
乘坐高铁,到附近省份也方便
至于`鹤岗`,个人认为纯粹不值得,生活在南方的人,无法理解北方的酷寒
不知道鹅毛大雪几米深的恐怖,也不知道暖气停供一晚,如何撑得过零下几十度
连救护车开到了医院大门,即便轮胎上了防滑链,都有没法开进去的情况
需要男护士/男医生踩着雪抬进去
当选择一个环境的时候,有时候,气候,是保证一个人生命安全的关键
## 泰国:曼谷与清迈
据nomadlist统计报告,泰国`曼谷`是最受欢迎的数字游民聚集地
![2025-04-03-01-37-27-Nomads.com - Best Places to Live for Digital Nomads (formerly Nomad List).png](https://hackrobot-1258475618.cos.ap-shenzhen-fsi.myqcloud.com/2025-04-03-01-37-27-Nomads.com%20-%20Best%20Places%20to%20Live%20for%20Digital%20Nomads%20(formerly%20Nomad%20List).png?imageSlim)
其次是`清迈`
而且该国对数字游民,也比较友好,出台了很多`针对性的政策`
但不得不提醒的是,泰国也被列为最危险的国家
- 不禁枪支
- 风月场所合法,比如芭提雅
- 周边国家,存在贩毒与电诈
- 存在人口贩卖和地下奴隶市场
当一个博主从`印度`或者`泰国`回来,用"很安全,我不就是好好的吗?"来解答你的疑惑时
这个人要么愚蠢,要么下贱
`幸存者偏差`的意思是,只有或者的人,才有机会开口说话
那么遭遇危险的人,已无法发生
---
# 多元杠杆
成为一个数字游民,就必须让自己的过程,产生多元的杠杆价值,放大收益
![qfqwffwq.jpg](https://hackrobot-1258475618.cos.ap-shenzhen-fsi.myqcloud.com/qfqwffwq.jpg?imageSlim)
这里有个框架,3个关键词,可以套用在任何事物上,如下
- `直播`live
- `社群`互动
- `录制`存档
在框架的基础,我也会解释`副产品`的优势
现在我们举个栗子
假设你是一个开发者,`正在coding编程`,这个如何实现多元化呢?
![QWQWFQWF.png](https://hackrobot-1258475618.cos.ap-shenzhen-fsi.myqcloud.com/QWQWFQWF.png?imageSlim)
链路如下:
1. 实时直播,手机=>拍着正在敲键盘的你
2. 交流互动,针对观众的提问(看副屏),进行回答,让业务有实时的反馈
3. OBS后台录屏 (副产品:技术视频教程)
4. 编码本身 (软件产品,ios app,观众可以成为种子用户)
5. Api互动: 根据打赏的礼物,控制`显示器/esp32`进行趣味玩法
6. 橱窗带货:比如桌面炫酷/萌萌的摆件
总结一下,实时编码,不仅让业务有`即时反馈`,也会产生很多`副产品`
增加了收益渠道
## RPA自动化
![qwfqwfqwf.jpg](https://hackrobot-1258475618.cos.ap-shenzhen-fsi.myqcloud.com/qwfqwfqwf.jpg?imageSlim)
前面我们提到了直播,可以实现多元化杠杆,但,它是一个实时同步的过程
那么有没有24小时无人值守,自动化的方案呢?
有,比如
- 基于ffmmpeg/obs直播推流 (录播素材)
- 社媒mitm爬虫:数据分析/自动点赞评论
- 发卡平台: 虚拟资源,卡密网盘发货
- N8N: 平台与流程自动化,积木一样串联工作流
- 群机器人bot: telegram和微信群
- 等等
缺点是最技术的要求较高
---
# 分布式团队
成为一个数字游民,并不意味着一个人完成所有的项目过程
参与或者成立一个分布式团队,进行远程办公,也是必要的
<img src="https://hackrobot-1258475618.cos.ap-shenzhen-fsi.myqcloud.com/fqwfqfqwf.jpg?imageSlim" title="" alt="fqwfqfqwf.jpg" width="397">
可以雇佣兼职/外包,或者让用户产生内容 [UGC]
接下来我会推荐`开源`的远程工具
1. 任务看板:`planka`
2. `eliment `(基于martix斜体的聊天工具)
3. 文档wiki/知识库: `docmost`
使用开源的好处在于:`低成本,保护隐私,完全控制,可拓展`
> 任务看板planka
以任务卡片的形式,给成员安排任务
设置子任务/完成时间/上传附件/以及评论@
所有进度一目了然
<img src="https://hackrobot-1258475618.cos.ap-shenzhen-fsi.myqcloud.com/2025-04-02-11-11-29-Sample%20Project%20_%20Sample%20Board.png?imageSlim" title="" alt="2025-04-02-11-11-29-Sample Project _ Sample Board.png" width="463">
> eliment
eleliment只是基于martrix协议的一个客户端
目前支持PC/mac/Android/ios/linux全平台
可以聊天/视频语音通话
后台可以进行消息统计/分析,防止商业机密泄露
<img src="https://hackrobot-1258475618.cos.ap-shenzhen-fsi.myqcloud.com/2025-04-02-11-14-33-Element%20_%20Secure%20collaboration%20and%20messaging.png?imageSlim" title="" alt="2025-04-02-11-14-33-Element _ Secure collaboration and messaging.png" width="469">
> wiki文档: docmost
它内置编辑器支持 Mermaid、Draw.io 和 Excalidraw 图表工具
可以将团队的文档整理输出
方便成员学习,分享
<img src="https://hackrobot-1258475618.cos.ap-shenzhen-fsi.myqcloud.com/qwdqwdqwd.png?imageSlim" title="" alt="qwdqwdqwd.png" width="506">
> 如何搭建?
购买  [vultr](https://www.vultr.com/?ref=8985003) 服务器,按照官方提供的`docker镜像`,一键安装
---
# 一人公司
> 数字游民与一人公司区别很大
![qwqwfqwf.jpg](https://hackrobot-1258475618.cos.ap-shenzhen-fsi.myqcloud.com/qwqwfqwf.jpg?imageSlim)
数字游民一般是以`远程工作`为主,本质还是偶雇佣关系,把工位换成了远程旅居
也进行一些`副业`
但一人公司,更偏向于`创业`
经营`自媒体/独立开发/电商`等收益上限比较高的互联网业务
## 基础设备
一人公司,也意味着硬件和软件资产的流程化,比如
- windows办公主机
- 安卓手机:产品测试/社媒运营
- linux服务器:应用部署
<img title="" src="https://hackrobot-1258475618.cos.ap-shenzhen-fsi.myqcloud.com/1000029229.jpg?imageSlim" alt="1000029229.jpg" width="339">
图示是作者用mini主机+开发板打造的远程操作系统
可以随时随地,`远控/群控`
利用[vultr](https://www.vultr.com/?ref=8985003) 服务器,设置`frp内网穿透`即可,或者`wireguard点对点VPN异地组网`
## 开源部署
<img src="https://hackrobot-1258475618.cos.ap-shenzhen-fsi.myqcloud.com/2025-04-02-11-46-55-Uptime%20Kuma%20Status.png?imageSlim" title="" alt="2025-04-02-11-46-55-Uptime Kuma Status.png" width="395">
经营业务,必须准备大量的基础服务来支撑自动化运营,比如
- erp系统: ERPNext
- scrm客服:chatwoot
- 邮件营销:邮件营销
- 项目开发: `禅道`(项目管理) /` gitlab`(代码仓库)/`YAPI`接口文档
- 运维CI/CD:jenkins自动部署/`rancher`容器管理
- 埋点监控: `Sentry`错误监控/`plausible`统计/`Uptime Kuma`服务状态
- 流程自动化`N8N`
- 等等
都是开源免费
- 缺点:需要自己部署,维护
- 优点:省下数以万计的费用
---
# 展望与结束语
![efqwfegwegweg.jpg](https://hackrobot-1258475618.cos.ap-shenzhen-fsi.myqcloud.com/efqwfegwegweg.jpg?imageSlim)
在以上内容,并没有提及2个领域:`跨境电商``加密货币`
前者偏向于实体,成本高,链路复杂,市场因政策变化频繁
后者遇到投机主义,一不小心就容易倾家荡产
换个思路,不做卖家,可以做amazon affiliate, 买入闲置,用涨跌来跨越大周期,穿越牛熊
原本只打算写2个小册
- [youtube运营笔记](https://nomadyt.com/) (free)
- [从零开始,成为数字游民](https://nomadro.com/) (试读80% +pay)
后续会酌情增加
- [一人公司](https://nomadvps.com/) (预订)
最初的是打算写技术书籍的~~
<img src="https://hackrobot-1258475618.cos.ap-shenzhen-fsi.myqcloud.com/2025-04-03-02-14-22-AI%20%E4%B8%8E%20RPA%E8%87%AA%E5%8A%A8%E5%8C%96.png?imageSlim" title="" alt="2025-04-03-02-14-22-AI 与 RPA自动化.png" width="375">
> 不管是生意还是创业,第一原则,先找到`买家`
在某个领域里,你发现了需求,或者有人为你的创造买单,就有收益,然后放大
如果没有调研市场
就默默地闭门造车,结局会很惨
持续更新中.....
- 待更新

0
src/pages/group/index.config.js Normal file → Executable file
View File

0
src/pages/group/index.scss Normal file → Executable file
View File

0
src/pages/group/index.tsx Normal file → Executable file
View File

0
src/pages/index/index.config.js Normal file → Executable file
View File

203
src/pages/index/index.jsx Normal file → Executable file
View File

@@ -1,5 +1,5 @@
import { Component } from "react";
import Taro from "@tarojs/taro";
import Taro,{useRouter} from "@tarojs/taro";
import { View, Text, Image, Button } from "@tarojs/components";
import "./index.scss";
import { AtTabBar } from "taro-ui";
@@ -40,7 +40,7 @@ import bookqr from "../../images/bookqr.jpg";
import loft from "../../images/loft.png";
import AIGC from "../../images/AIGC.png";
import robot192 from "../../images/robot192.png";
import youtube from "../../images/youtube.svg";
// import youtube from "../../images/youtube.svg";
import ssh from "../../images/ssh.svg";
import robot from "../../images/robot.svg";
import host from "../../images/host.svg";
@@ -49,12 +49,20 @@ import ytGpt from "../../images/ytGpt.png";
import demo from "../../images/demo.svg";
import AI from "../../images/AI.png";
import nomadro from "../../images/nomadro.png";
import phone2 from "../../images/phone2.png";
import code from "../../images/code.svg";
import laptop from "../../images/laptop.svg";
import travel from "../../images/travel.svg";
import group from "../../images/group.svg";
import book from "../../images/book.svg";
import youtubesvg from "../../images/youtube.svg";
import dgnomad from "../../images/dgnomad.png";
import tiktok from "../../images/tiktok.png";
import youtube from "../../images/youtube.png";
import web from "../../images/web.png";
// export default class Index extends Component {
const Index = () => {
const [current, setcurrent] = useState(0);
@@ -63,7 +71,9 @@ const Index = () => {
const [userInfo, setuserInfo] = useState({});
const [open, setopen] = useState(false);
const [referer, setreferer] = useState(window.document.referrer);
// const [referer, setreferer] = useState(window.document.referrer);
const [referer, setreferer] = useState("");
const [isOpened, setisOpened] = useState(false);
const [meetupQR, setmeetupQR] = useState(false);
@@ -75,6 +85,18 @@ const Index = () => {
const [bookpay, setbookpay] = useState(0);
const [isWeChat, setisWeChat] = useState(false);
const [vipbook, setVipbook] = useState(false); //是否在vip群打开
const router = useRouter(); // Taro 3+ 获取路由参数
// 在组件挂载时检测 referrer
useEffect(() => {
if (router && router.params && router.params.type === 'vipbook') {
setVipbook(true)
} else {
setVipbook(false)
}
}, [router.params]);
const getOpenidFromUrl = (url) => {
const pattern = /[?&]openid=([^&#]+)/;
@@ -134,10 +156,29 @@ const Index = () => {
},
});
} else {
let nowUrl = window.location.href;
// let nowUrl = window.location.href;
// window.localStorage.removeItem("openid");
// // let payUrl = `https://payjs.cn/api/openid?mchid=1561724891&callback_url=${nowUrl}`;
// let payUrl = `https://xorpay.com/api/openid/8220&callback_url=${nowUrl}`;
// window.location.href = payUrl;
// 清除旧 openid可选建议保留
window.localStorage.removeItem("openid");
let payUrl = `https://payjs.cn/api/openid?mchid=1561724891&callback_url=${nowUrl}`;
window.location.href = payUrl;
// 当前完整 URL包括 ? 参数和 # hash
let nowUrl = window.location.href;
// 正确构造 XorPay openid 获取地址
// 1. 参数名必须是 callback
// 2. 值必须 encodeURIComponent 编码
let payUrl = `https://xorpay.com/api/openid/8220?callback=${encodeURIComponent(
nowUrl
)}`;
console.log("跳转获取 openid 的 URL:", payUrl);
// 示例输出https://xorpay.com/api/openid/8220?callback=http%3A%2F%2F192.168.31.222%3A10086%2F%23%2Fpay
// window.location.href = payUrl;
}
};
@@ -157,7 +198,7 @@ const Index = () => {
}
}
},
complete: function (finy) {},
complete: function (finy) { },
});
};
@@ -188,7 +229,7 @@ const Index = () => {
window.localStorage.removeItem("userInfo");
window.localStorage.removeItem("openid");
window.localStorage.removeItem("wxid");
window.location.href = "https://nomadro.com";
window.location.href = "https://hackrobot.cn";
} else {
// 有完整的用户信息userInfo
setuserInfo(res?.data?.userInfo);
@@ -227,9 +268,9 @@ const Index = () => {
header: {
"Content-Type": "application/json",
},
success: function (res) {},
fail: function (err) {},
complete: function (finy) {},
success: function (res) { },
fail: function (err) { },
complete: function (finy) { },
});
};
@@ -248,9 +289,9 @@ const Index = () => {
header: {
"Content-Type": "application/json",
},
success: function (res) {},
fail: function (err) {},
complete: function (finy) {},
success: function (res) { },
fail: function (err) { },
complete: function (finy) { },
});
};
@@ -258,9 +299,8 @@ const Index = () => {
const tgMessage = () => {
Taro.request({
method: "GET",
url: `${
process.env.TARO_API_API
}/tgMessage?openid=${window.localStorage.getItem("openid")}`, //仅为示例,并非真实的接口地址
url: `${process.env.TARO_API_API
}/tgMessage?openid=${window.localStorage.getItem("openid")}`, //仅为示例,并非真实的接口地址
// url: "https://pay.hackrobot.cn${process.env.TARO_API_API}/payh5", //仅为示例,并非真实的接口地址
success: function (res) {
console.log("访问提醒发送成功");
@@ -271,11 +311,10 @@ const Index = () => {
// 入群申请
useEffect(() => {
let inwechat = isWeChatFun();
if (inwechat) {
// 获取openId
getOpenid();
// getOpenid();
} else {
// window.alert('不在微信')
}
@@ -295,56 +334,22 @@ const Index = () => {
setopen(value);
};
const payh5 = (type, total_fee = 1000) => {
// console.log("111");
Taro.request({
method: "POST",
url: `${process.env.TARO_API_API}/payh5`,
// url: "${process.env.TARO_API_API}/payh5",
// url: "https://pay.hackrobot.cn${process.env.TARO_API_API}/payh5", //仅为示例,并非真实的接口地址
data: {
callback_url: window.location.href,
openid: window.localStorage.getItem("openid"),
total_fee: total_fee, // 金额,单位:分
type: type, //订单类型 book/meetup/video/vip
},
header: {
"content-type": "application/json", // 默认值
},
success: function (res) {
console.log(res.data);
// window.alert(res.data.jsapi);
WeixinJSBridge.invoke(
"getBrandWCPayRequest",
{
// 以下6个支付参数通过payjs的jsapi接口获取
// appId: "wxc5205a653b0259bf",
// timeStamp: "1701401644",
// nonceStr: "KhOYB0wFV6j9qyQK",
// package: "prepay_id=wx01113404850024729b35f4d69b73500000",
// signType: "MD5",
// paySign: "2A823C8D47CCF871C6C65A56DC228CF8",
...(res?.data?.jsapi ?? {}),
},
function (res) {
// 支付成功
if (res.err_msg == "get_brand_wcpay_request:ok") {
window.localStorage.setItem(type, 1); //订单类型 book/meetup/video/vip
WeixinJSBridge.call("closeWindow");
}
}
);
},
});
};
const goRead = () => {
const goRead2 = () => {
Taro.navigateTo({
url: "/pages/book/index",
});
};
const goRead = () => {
// 极简:直接用 router.params 构建参数字符串(无参数时自动为空)
const paramsStr = router.params
? '?' + new URLSearchParams(router.params).toString()
: '';
Taro.navigateTo({
url: `/pages/book/index${paramsStr}`,
});
};
return (
<>
@@ -354,7 +359,9 @@ const Index = () => {
{/* 参考资料https://readmake.com/ */}
<View className="index-book animate__animated animate__pulse">
<View className="index_book_left">
<View className="left_title">从零开始,成为数字游民</View>
<View className="left_title">低成本工作室:云手机</View>
{/* <View className="left_title">《云手机》</View> */}
{/* <View className="left_text">手册3</View> */}
{/* <View className="left_mobile">
<Text>🚀 边旅行转赚钱</Text>
@@ -374,34 +381,48 @@ const Index = () => {
<View className="cardItem_left">
<Image
className="cardItem_left_img youtubeSvg"
src={travel}
// src={travel}
src={tiktok}
// src={`https://minioweb.hackrobot.cn/hackrobot/travel.svg`}
lazyLoad
></Image>
</View>
<View className="cardItem_right">
<View className="cardItem_rightUp">地理套利</View>
<View className="cardItem_rightDown">
边旅行边赚钱,无国界
</View>
<View className="cardItem_rightUp">Tiktok</View>
<View className="cardItem_rightDown">无人直播</View>
</View>
</View>
<View className="cardItem">
<View className="cardItem_left">
<Image className="cardItem_left_img" src={laptop}></Image>
<Image
className="cardItem_left_img"
src={laptop}
// src={youtube}
// src={`https://minioweb.hackrobot.cn/hackrobot/laptop.svg`}
lazyLoad
></Image>
</View>
<View className="cardItem_right">
<View className="cardItem_rightUp">远程办公</View>
<View className="cardItem_rightDown">
自由职业,分布式团队
</View>
<View className="cardItem_rightUp">Youtube</View>
<View className="cardItem_rightDown">自媒体</View>
</View>
</View>
<View className="cardItem">
<View className="cardItem_left">
<Image className="cardItem_left_img" src={group}></Image>
<Image
className="cardItem_left_img"
src={group}
// src={web}
// src={`https://minioweb.hackrobot.cn/hackrobot/propless.svg`}
lazyLoad
></Image>
</View>
<View className="cardItem_right">
<View className="cardItem_rightUp">游民社区</View>
<View className="cardItem_rightDown">信息交流,快人一步</View>
<View className="cardItem_rightUp">web</View>
<View className="cardItem_rightDown">web:saas+web3</View>
</View>
</View>
</View>
@@ -422,9 +443,14 @@ const Index = () => {
</View>
<View className="index_book_img">
<Image
className="book_img"
// style="width: 40%px;height:100%;background: #fff;"
src={nomadro}
className="book_img"
src={phone2}
// src={dgnomad}
// src="https://minioweb.hackrobot.cn/hackrobot/phone2.png"
fetchpriority="high" // 提示浏览器尽快下载
decoding="async" // 异步解码加快渲染
lazyLoad={false} // 禁止延迟加载LCP 图必须优先加载
alt="云手机封面"
/>
</View>
</View>
@@ -436,8 +462,12 @@ const Index = () => {
// style="width: 40%px;height:100%;background: #fff;"
// src={book}
src={laptop}
// src={phone2}
// src={tiktok}
lazyLoad
/>
<View className="bookImg_text">远程办公</View>
<View className="bookImg_text">无人直播</View>
</View>
<View className="bookImg">
@@ -445,16 +475,21 @@ const Index = () => {
// style="width: 40%px;height:100%;background: #fff;"
// src={book}
src={travel}
// src={youtube}
lazyLoad
/>
<View className="bookImg_text">地理套利</View>
<View className="bookImg_text">RPA自动化</View>
</View>
<View className="bookImg">
<Image
// style="width: 40%px;height:100%;background: #fff;"
// src={book}
src={code}
// src={web}
lazyLoad
/>
<View className="bookImg_text">独立开发</View>
<View className="bookImg_text">web:saas+web3</View>
</View>
</View>
</View>

16
src/pages/index/index.scss Normal file → Executable file
View File

@@ -74,8 +74,8 @@
// background-color: #69b1ff;
display: flex;
flex-direction: column;
// align-items: center;
// justify-content: center;
align-items: center;
justify-content: center;
margin-right: 40px;
.cardItem_left_img {
width: 80px;
@@ -143,8 +143,9 @@
}
.index_book_img {
margin-top: 80px;
width: 400px;
height: 400px;
width: 300px;
height: 300px;
margin-bottom: 40px;
}
}
@@ -186,12 +187,17 @@
@media (min-width: 1536px) {
.index {
// padding: 100px;
// height: 100vh;
height: 90vh;
padding-top: 20px;
padding-bottom: 10px;
padding-left: 20px;
padding-right: 20px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
// background-color: aqua;
// color: red;
position: relative;
.index-book {

0
src/pages/meetup/Detail/index.jsx Normal file → Executable file
View File

0
src/pages/meetup/Detail/index.scss Normal file → Executable file
View File

0
src/pages/meetup/index.jsx Normal file → Executable file
View File

0
src/pages/meetup/index.scss Normal file → Executable file
View File

0
src/pages/model/index.config.js Normal file → Executable file
View File

0
src/pages/model/index.scss Normal file → Executable file
View File

0
src/pages/model/index.tsx Normal file → Executable file
View File

0
src/pages/my/index.jsx Normal file → Executable file
View File

0
src/pages/my/index.scss Normal file → Executable file
View File

0
src/pages/planet/index.config.js Normal file → Executable file
View File

0
src/pages/planet/index.scss Normal file → Executable file
View File

0
src/pages/planet/index.tsx Normal file → Executable file
View File

0
src/pages/salon/index.config.js Normal file → Executable file
View File

0
src/pages/salon/index.scss Normal file → Executable file
View File

0
src/pages/salon/index.tsx Normal file → Executable file
View File

2
src/pages/sphone/index.config.js Normal file → Executable file
View File

@@ -1,3 +1,3 @@
export default {
navigationBarTitleText: '云手机'
navigationBarTitleText: '数字游民'
}

0
src/pages/sphone/index.scss Normal file → Executable file
View File

0
src/pages/sphone/index.tsx Normal file → Executable file
View File

0
src/pages/sphone/info.md Normal file → Executable file
View File

0
src/pages/sphone/pay.md Normal file → Executable file
View File

27
src/utils/payEnv.ts Normal file
View File

@@ -0,0 +1,27 @@
/**
* 支付环境检测PC / H5 / 微信浏览器
* 参考 nomadvip app/lib/payEnv.ts
*/
export function isWeChat() {
if (typeof navigator === "undefined") return false;
return /MicroMessenger/i.test(navigator.userAgent);
}
export function isMobile() {
if (typeof navigator === "undefined") return false;
return /AppleWebKit.*Mobile|Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
navigator.userAgent
);
}
export function getPayEnv() {
if (isWeChat()) return "wechat";
if (isMobile()) return "h5";
return "pc";
}
export function getDeviceFromEnv(env) {
if (env === "wechat") return "wechat";
return env === "pc" ? "pc" : "h5";
}

117
yarn.lock Normal file → Executable file
View File

@@ -4116,6 +4116,11 @@ commander@10.0.0:
resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.0.tgz#71797971162cd3cf65f0b9d24eb28f8d303acdf1"
integrity sha512-zS5PnTI22FIRM6ylNW8G4Ap0IEOyk62fhLSD0+uHRT9McRCLGpkVNvao4bjimpK/GShynyQkFFxHhwMcETmduA==
commander@^12.1.0:
version "12.1.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-12.1.0.tgz#01423b36f501259fdaac4d0e4d60c96c991585d3"
integrity sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==
commander@^2.19.0, commander@^2.20.0, commander@^2.8.1:
version "2.20.3"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
@@ -4153,6 +4158,14 @@ compressible@~2.0.18:
dependencies:
mime-db ">= 1.43.0 < 2"
compression-webpack-plugin@^11.1.0:
version "11.1.0"
resolved "https://registry.yarnpkg.com/compression-webpack-plugin/-/compression-webpack-plugin-11.1.0.tgz#ee340d2029cf99ccecdea9ad1410b377d15b48b3"
integrity sha512-zDOQYp10+upzLxW+VRSjEpRRwBXJdsb5lBMlRxx1g8hckIFBpe3DTI0en2w7h+beuq89576RVzfiXrkdPGrHhA==
dependencies:
schema-utils "^4.2.0"
serialize-javascript "^6.0.2"
compression@^1.7.4:
version "1.8.1"
resolved "https://registry.yarnpkg.com/compression/-/compression-1.8.1.tgz#4a45d909ac16509195a9a28bd91094889c180d79"
@@ -5124,6 +5137,14 @@ enhanced-resolve@^5.17.2, enhanced-resolve@^5.9.3:
graceful-fs "^4.2.4"
tapable "^2.2.0"
enhanced-resolve@^5.17.3:
version "5.18.3"
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz#9b5f4c5c076b8787c78fe540392ce76a88855b44"
integrity sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==
dependencies:
graceful-fs "^4.2.4"
tapable "^2.2.0"
entities@^1.1.1:
version "1.1.2"
resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56"
@@ -6266,7 +6287,7 @@ for-own@^0.1.4:
dependencies:
for-in "^1.0.1"
foreground-child@^3.1.0:
foreground-child@^3.1.0, foreground-child@^3.3.1:
version "3.3.1"
resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.1.tgz#32e8e9ed1b68a3497befb9ac2b6adf92a638576f"
integrity sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==
@@ -6547,6 +6568,18 @@ glob@10.2.6:
minipass "^5.0.0 || ^6.0.2"
path-scurry "^1.7.0"
glob@^11.0.0, glob@^11.0.3:
version "11.0.3"
resolved "https://registry.yarnpkg.com/glob/-/glob-11.0.3.tgz#9d8087e6d72ddb3c4707b1d2778f80ea3eaefcd6"
integrity sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==
dependencies:
foreground-child "^3.3.1"
jackspeak "^4.1.1"
minimatch "^10.0.3"
minipass "^7.1.2"
package-json-from-dist "^1.0.0"
path-scurry "^2.0.0"
glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.6:
version "7.2.3"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
@@ -7898,6 +7931,13 @@ jackspeak@^2.0.3:
optionalDependencies:
"@pkgjs/parseargs" "^0.11.0"
jackspeak@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-4.1.1.tgz#96876030f450502047fc7e8c7fcf8ce8124e43ae"
integrity sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==
dependencies:
"@isaacs/cliui" "^8.0.2"
jake@^10.8.5:
version "10.9.2"
resolved "https://registry.yarnpkg.com/jake/-/jake-10.9.2.tgz#6ae487e6a69afec3a5e167628996b59f35ae2b7f"
@@ -8531,6 +8571,11 @@ lru-cache@^10.2.0:
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119"
integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==
lru-cache@^11.0.0:
version "11.2.2"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.2.2.tgz#40fd37edffcfae4b2940379c0722dc6eeaa75f24"
integrity sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==
lru-cache@^4.1.2:
version "4.1.5"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd"
@@ -8923,7 +8968,7 @@ minimalistic-assert@^1.0.0:
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==
minimatch@*:
minimatch@*, minimatch@^10.0.3:
version "10.0.3"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.0.3.tgz#cf7a0314a16c4d9ab73a7730a0e8e3c3502d47aa"
integrity sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==
@@ -8992,7 +9037,7 @@ minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6, minimist@^1.2.8:
resolved "https://registry.yarnpkg.com/minipass/-/minipass-6.0.2.tgz#542844b6c4ce95b202c0995b0a471f1229de4c81"
integrity sha512-MzWSV5nYVT7mVyWCwn2o7JH13w2TBRmmSqSRCKzTw+lmft9X4z+3wjvs06Tzijo5z4W/kahUCDpRXTF+ZrmF/w==
"minipass@^5.0.0 || ^6.0.2 || ^7.0.0":
"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2:
version "7.1.2"
resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707"
integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==
@@ -9639,6 +9684,11 @@ p-try@^2.0.0:
resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
package-json-from-dist@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505"
integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==
package-json@^6.3.0, package-json@^6.5.0:
version "6.5.0"
resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0"
@@ -9809,6 +9859,14 @@ path-scurry@^1.7.0:
lru-cache "^10.2.0"
minipass "^5.0.0 || ^6.0.2 || ^7.0.0"
path-scurry@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-2.0.0.tgz#9f052289f23ad8bf9397a2a0425e7b8615c58580"
integrity sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==
dependencies:
lru-cache "^11.0.0"
minipass "^7.1.2"
path-to-regexp@0.1.12:
version "0.1.12"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.12.tgz#d5e1a12e478a976d432ef3c58d534b9923164bb7"
@@ -10242,7 +10300,7 @@ postcss-selector-parser@^3.1.0:
indexes-of "^1.0.1"
uniq "^1.0.1"
postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4, postcss-selector-parser@^6.0.5, postcss-selector-parser@^6.0.9:
postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4, postcss-selector-parser@^6.0.5, postcss-selector-parser@^6.0.9, postcss-selector-parser@^6.1.2:
version "6.1.2"
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz#27ecb41fb0e3b6ba7a1ec84fff347f734c7929de"
integrity sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==
@@ -10325,7 +10383,7 @@ postcss@^7.0.1, postcss@^7.0.23, postcss@^7.0.32, postcss@^7.0.36:
picocolors "^0.2.1"
source-map "^0.6.1"
postcss@^8.2.14, postcss@^8.3.5, postcss@^8.4.18, postcss@^8.4.33, postcss@^8.5.6:
postcss@^8.2.14, postcss@^8.3.5, postcss@^8.4.18, postcss@^8.4.33, postcss@^8.4.47, postcss@^8.5.6:
version "8.5.6"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.6.tgz#2825006615a619b4f62a9e7426cc120b349a8f3c"
integrity sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==
@@ -10476,6 +10534,24 @@ pupa@^2.1.1:
dependencies:
escape-goat "^2.0.0"
purgecss-webpack-plugin@^7.0.2:
version "7.0.2"
resolved "https://registry.yarnpkg.com/purgecss-webpack-plugin/-/purgecss-webpack-plugin-7.0.2.tgz#76e7591ac1fac8d10538ad1659df586dae23d588"
integrity sha512-YKN93Ndg/T/FsJaUL7KJdFBh/kfGi3pH7sm1OmVbhzb11mmS+vbRAQ5UIvqUd70kQrJ57+ZpPkDtRv66d89q9A==
dependencies:
purgecss "^7.0.2"
webpack ">=5.95.0"
purgecss@^7.0.2:
version "7.0.2"
resolved "https://registry.yarnpkg.com/purgecss/-/purgecss-7.0.2.tgz#b7dccc3ead65a4301eed98e014793719a511c633"
integrity sha512-4Ku8KoxNhOWi9X1XJ73XY5fv+I+hhTRedKpGs/2gaBKU8ijUiIKF/uyyIyh7Wo713bELSICF5/NswjcuOqYouQ==
dependencies:
commander "^12.1.0"
glob "^11.0.0"
postcss "^8.4.47"
postcss-selector-parser "^6.1.2"
qs@6.13.0:
version "6.13.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906"
@@ -13191,6 +13267,37 @@ webpack-virtual-modules@^0.5.0:
resolved "https://registry.yarnpkg.com/webpack-virtual-modules/-/webpack-virtual-modules-0.5.0.tgz#362f14738a56dae107937ab98ea7062e8bdd3b6c"
integrity sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw==
webpack@>=5.95.0:
version "5.101.3"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.101.3.tgz#3633b2375bb29ea4b06ffb1902734d977bc44346"
integrity sha512-7b0dTKR3Ed//AD/6kkx/o7duS8H3f1a4w3BYpIriX4BzIhjkn4teo05cptsxvLesHFKK5KObnadmCHBwGc+51A==
dependencies:
"@types/eslint-scope" "^3.7.7"
"@types/estree" "^1.0.8"
"@types/json-schema" "^7.0.15"
"@webassemblyjs/ast" "^1.14.1"
"@webassemblyjs/wasm-edit" "^1.14.1"
"@webassemblyjs/wasm-parser" "^1.14.1"
acorn "^8.15.0"
acorn-import-phases "^1.0.3"
browserslist "^4.24.0"
chrome-trace-event "^1.0.2"
enhanced-resolve "^5.17.3"
es-module-lexer "^1.2.1"
eslint-scope "5.1.1"
events "^3.2.0"
glob-to-regexp "^0.4.1"
graceful-fs "^4.2.11"
json-parse-even-better-errors "^2.3.1"
loader-runner "^4.2.0"
mime-types "^2.1.27"
neo-async "^2.6.2"
schema-utils "^4.3.2"
tapable "^2.1.1"
terser-webpack-plugin "^5.3.11"
watchpack "^2.4.1"
webpack-sources "^3.3.3"
webpack@^5.78.0:
version "5.100.2"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.100.2.tgz#e2341facf9f7de1d702147c91bcb65b693adf9e8"