This commit is contained in:
ditiqi 2025-02-17 15:03:20 +08:00
parent 8dadd6e4c5
commit 38a0a91963
15 changed files with 1578 additions and 25 deletions

View File

@ -111,3 +111,20 @@ RUN mkdir -p /data/uploads
# 暴露 80 端口
EXPOSE 80
# 使用 linuxserver/firefox:latest 作为基础镜像
FROM linuxserver/firefox:latest
# 安装中文字体和语言包
RUN apt-get update && \
apt-get install -y fonts-noto-cjk language-pack-zh-hans && \
locale-gen zh_CN.UTF-8 && \
update-locale LANG=zh_CN.UTF-8
# 设置环境变量
ENV LANG=zh_CN.UTF-8 \
LANGUAGE=zh_CN:zh \
LC_ALL=zh_CN.UTF-8
# 继续使用基础镜像的入口点
ENTRYPOINT ["/init"]

58
apps/web/browserCheck.js Normal file
View File

@ -0,0 +1,58 @@
// browserCheck.js
// 检查浏览器版本的函数
function checkBrowserVersion() {
const userAgent = navigator.userAgent;
const chromeMatch = userAgent.match(/Chrome\/(\d+)/);
if (chromeMatch) {
const chromeVersion = parseInt(chromeMatch[1], 10);
if (chromeVersion < 80) {
showMustDownloadPrompt();
} else if (chromeVersion < 100) {
showUpdatePrompt();
}
} else if (userAgent.includes("MSIE") || userAgent.includes("Trident/")) {
showUpdatePrompt();
}
}
function showUpdatePrompt() {
const result = window.confirm(
"检测到您的浏览器版本较低,建议更新到最新版本以获得更好的体验。是否下载新版浏览器?"
);
if (result) {
// 提供下载链接
const link = document.createElement("a"); // 创建一个链接元素
link.href = "/chrome.exe"; // 替换为您在 public 目录中的安装包路径
link.download = "chrome.exe"; // 设置下载文件名
document.body.appendChild(link); // 将链接添加到文档中
link.click(); // 模拟点击下载
document.body.removeChild(link); // 下载后移除链接
// 打开预览地址
// window.open(previewUrl, "_blank"); // 在新标签页中打开预览地址
}
}
function showMustDownloadPrompt() {
const previewUrl = "http://192.168.139.239:8095"; // 获取预览地址
const result = window.confirm(
"检测到您的浏览器版本过低,可能无法正常使用网站功能。是否立即下载推荐浏览器并打开预览地址进行预览?"
);
if (result) {
// 提供下载链接
const link = document.createElement("a"); // 创建一个链接元素
link.href = "/chrome.exe"; // 替换为您在 public 目录中的安装包路径
link.download = "chrome.exe"; // 设置下载文件名
document.body.appendChild(link); // 将链接添加到文档中
link.click(); // 模拟点击下载
document.body.removeChild(link); // 下载后移除链接
// 打开预览地址
window.open(previewUrl, "_blank"); // 在新标签页中打开预览地址
}
}
// 在页面加载时检查浏览器版本
window.onload = checkBrowserVersion;

View File

@ -18,6 +18,7 @@
<body>
<div id="root"></div>
<script src="browserCheck.js"></script>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

BIN
apps/web/public/chrome.exe Normal file

Binary file not shown.

View File

@ -65,7 +65,7 @@ export function PostDetailProvider({
) {
return true;
} else if (!post?.isPublic) {
if (keyCode === post?.id) {
if (keyCode === post?.id || keyCode === post?.meta?.ownCode) {
return true;
} else {
return false;

View File

@ -7,6 +7,7 @@ import { VisitType } from "@nice/common";
import Header from "../PostHeader/Header";
import Content from "../PostHeader/Content";
import { Button, Input, Skeleton } from "antd";
import toast from "react-hot-toast";
export default function PostDetailLayout() {
const { post, user, canSee, setKeyCode, isLoading } =

View File

@ -10,6 +10,7 @@ import toast from "react-hot-toast";
import { api } from "@nice/client";
import { RoleName } from "@nice/common";
import { useNavigate } from "react-router-dom";
import PublicOrNotSelector from "./public-or-not-selector/PublicOrNotSelector";
export function LetterBasicForm() {
const { onSubmit, receiverId, termId, form } = useLetterEditor();
@ -82,18 +83,23 @@ export function LetterBasicForm() {
<TermSelect placeholder="选择信件分类" />
</Form.Item>
</div>
<Form.Item
name="title"
label="标题"
style={{ width: "100%" }}
rules={[{ required: true, message: "请选择收件人" }]}>
<Input
<div className="flex gap-2">
<Form.Item
name="title"
label="标题"
style={{ width: "100%" }}
maxLength={20}
showCount
placeholder="请输入信件标题"
/>
</Form.Item>
rules={[{ required: true, message: "请选择收件人" }]}>
<Input
style={{ width: "100%" }}
maxLength={20}
showCount
placeholder="请输入信件标题"
/>
</Form.Item>
<Form.Item name="isPublic" initialValue={true}>
<PublicOrNotSelector />
</Form.Item>
</div>
{/* Tags Input */}
{/* <Form.Item name={["meta", "tags"]} className="mb-6">
<Select
@ -157,11 +163,7 @@ export function LetterBasicForm() {
}}></Button> */}
{/* Footer Actions */}
<div className="flex flex-col-reverse sm:flex-row items-center justify-between gap-4 mt-2 ">
<Form.Item name="isPublic" valuePropName="checked">
<Checkbox className="text-gray-600 hover:text-gray-900 transition-colors text-sm">
</Checkbox>
</Form.Item>
<div></div>
<div className="flex gap-2 ">
{
<Form.Item name={["meta", "signature"]}>

View File

@ -0,0 +1,71 @@
import React from "react";
import { Select } from "antd";
import { useEffect, useState } from "react";
export default function PublicOrNotSelector({
value,
onChange,
}: {
value?: boolean;
onChange?: (value?: boolean) => void;
}) {
const [isBrowserSupported, setIsBrowserSupported] = useState(true);
useEffect(() => {
const userAgent = navigator.userAgent;
const browserVersion = parseInt(userAgent.split("/").pop());
setIsBrowserSupported(browserVersion >= 87);
}, []);
const options = [
{ value: true, label: "公开" },
{ value: false, label: "私密" },
];
// 处理 Antd Select 的 onChange
const handleAntdChange = (newValue) => {
if (onChange) {
onChange(newValue);
}
};
// 处理原生 select 的 onChange
const handleNativeChange = (e) => {
if (onChange) {
// 将字符串 "true"/"false" 转换为布尔值
onChange(e.target.value === "true");
}
};
if (isBrowserSupported) {
return (
<Select
value={value}
onChange={handleAntdChange}
style={{ width: "100%" }}>
{options.map((option) => (
<Select.Option
key={String(option.value)}
value={option.value}>
{option.label}
</Select.Option>
))}
</Select>
);
} else {
return (
<select
value={String(value)}
onChange={handleNativeChange}
style={{ width: "100%", height: "32px", borderRadius: "2px" }}>
{options.map((option) => (
<option
key={String(option.value)}
value={String(option.value)}>
{option.label}
</option>
))}
</select>
);
}
}

View File

@ -7,6 +7,7 @@ export const env: {
LIB_URL: string;
HOME_URL: string;
HOME_TEXT: string;
PREVIEW_URL: string;
} = {
APP_NAME: import.meta.env.PROD
? (window as any).env.VITE_APP_APP_NAME
@ -32,6 +33,9 @@ export const env: {
HOME_TEXT: import.meta.env.PROD
? (window as any).env.VITE_APP_HOME_TEXT
: import.meta.env.VITE_APP_HOME_TEXT,
PREVIEW_URL: import.meta.env.PROD
? (window as any).env.VITE_APP_PREVIEW_URL
: import.meta.env.VITE_APP_PREVIEW_URL,
};
console.log(env);

View File

@ -8,13 +8,16 @@ import { ModuleRegistry } from "@ag-grid-community/core";
import { LicenseManager } from "@ag-grid-enterprise/core";
import { ClientSideRowModelModule } from "@ag-grid-community/client-side-row-model";
import { checkBrowserVersion } from "./utils/check-browser.js";
ModuleRegistry.registerModules([ClientSideRowModelModule]);
LicenseManager.setLicenseKey(
'LICENSE_KEY_BODY[version=v3][0102]_EXPIRY_NDg4NDc0ODcwNTExMw==094bf1c7852b11df1841f4d14457ae96'
"LICENSE_KEY_BODY[version=v3][0102]_EXPIRY_NDg4NDc0ODcwNTExMw==094bf1c7852b11df1841f4d14457ae96"
);
// 在渲染之前检查浏览器版本
checkBrowserVersion();
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<App />

View File

@ -0,0 +1,29 @@
export function checkBrowserVersion() {
const userAgent = navigator.userAgent;
const chromeMatch = userAgent.match(/Chrome\/(\d+)/);
if (chromeMatch) {
const chromeVersion = parseInt(chromeMatch[1], 10);
if (chromeVersion < 80) {
showUpdatePrompt();
}
} else if (userAgent.includes("MSIE") || userAgent.includes("Trident/")) {
showUpdatePrompt();
}
}
export function showUpdatePrompt() {
const result = window.confirm(
"检测到您的浏览器版本过低,建议更新到最新版本以获得更好的体验。是否下载新版浏览器?"
);
if (result) {
// 创建下载链接
const link = document.createElement("a");
link.href = "/browsers/chrome_installer.exe"; // 假设安装包放在 public/browsers 目录下
link.download = "chrome_installer.exe";
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
}

9
config/browserless.js Normal file
View File

@ -0,0 +1,9 @@
module.exports = async ({ page, context }) => {
await page.setViewport({ width: 1920, height: 1080 });
await page.goto("http://192.168.139.239:8090", {
waitUntil: "networkidle0",
});
await page.evaluate(() => {
document.documentElement.requestFullscreen();
});
};

View File

@ -231,6 +231,7 @@ export interface PostMeta {
signature?: string;
ip?: string;
tags?: string[];
ownCode?: string;
}
export type RowModelResult = {
rowData: any[];

File diff suppressed because it is too large Load Diff