add 17
This commit is contained in:
parent
8dadd6e4c5
commit
38a0a91963
17
Dockerfile
17
Dockerfile
|
@ -111,3 +111,20 @@ RUN mkdir -p /data/uploads
|
||||||
|
|
||||||
# 暴露 80 端口
|
# 暴露 80 端口
|
||||||
EXPOSE 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"]
|
|
@ -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;
|
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
<script src="browserCheck.js"></script>
|
||||||
<script type="module" src="/src/main.tsx"></script>
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
Binary file not shown.
|
@ -65,7 +65,7 @@ export function PostDetailProvider({
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
} else if (!post?.isPublic) {
|
} else if (!post?.isPublic) {
|
||||||
if (keyCode === post?.id) {
|
if (keyCode === post?.id || keyCode === post?.meta?.ownCode) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { VisitType } from "@nice/common";
|
||||||
import Header from "../PostHeader/Header";
|
import Header from "../PostHeader/Header";
|
||||||
import Content from "../PostHeader/Content";
|
import Content from "../PostHeader/Content";
|
||||||
import { Button, Input, Skeleton } from "antd";
|
import { Button, Input, Skeleton } from "antd";
|
||||||
|
import toast from "react-hot-toast";
|
||||||
|
|
||||||
export default function PostDetailLayout() {
|
export default function PostDetailLayout() {
|
||||||
const { post, user, canSee, setKeyCode, isLoading } =
|
const { post, user, canSee, setKeyCode, isLoading } =
|
||||||
|
|
|
@ -10,6 +10,7 @@ import toast from "react-hot-toast";
|
||||||
import { api } from "@nice/client";
|
import { api } from "@nice/client";
|
||||||
import { RoleName } from "@nice/common";
|
import { RoleName } from "@nice/common";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import PublicOrNotSelector from "./public-or-not-selector/PublicOrNotSelector";
|
||||||
|
|
||||||
export function LetterBasicForm() {
|
export function LetterBasicForm() {
|
||||||
const { onSubmit, receiverId, termId, form } = useLetterEditor();
|
const { onSubmit, receiverId, termId, form } = useLetterEditor();
|
||||||
|
@ -82,18 +83,23 @@ export function LetterBasicForm() {
|
||||||
<TermSelect placeholder="选择信件分类" />
|
<TermSelect placeholder="选择信件分类" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</div>
|
</div>
|
||||||
<Form.Item
|
<div className="flex gap-2">
|
||||||
name="title"
|
<Form.Item
|
||||||
label="标题"
|
name="title"
|
||||||
style={{ width: "100%" }}
|
label="标题"
|
||||||
rules={[{ required: true, message: "请选择收件人" }]}>
|
|
||||||
<Input
|
|
||||||
style={{ width: "100%" }}
|
style={{ width: "100%" }}
|
||||||
maxLength={20}
|
rules={[{ required: true, message: "请选择收件人" }]}>
|
||||||
showCount
|
<Input
|
||||||
placeholder="请输入信件标题"
|
style={{ width: "100%" }}
|
||||||
/>
|
maxLength={20}
|
||||||
</Form.Item>
|
showCount
|
||||||
|
placeholder="请输入信件标题"
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item name="isPublic" initialValue={true}>
|
||||||
|
<PublicOrNotSelector />
|
||||||
|
</Form.Item>
|
||||||
|
</div>
|
||||||
{/* Tags Input */}
|
{/* Tags Input */}
|
||||||
{/* <Form.Item name={["meta", "tags"]} className="mb-6">
|
{/* <Form.Item name={["meta", "tags"]} className="mb-6">
|
||||||
<Select
|
<Select
|
||||||
|
@ -157,11 +163,7 @@ export function LetterBasicForm() {
|
||||||
}}></Button> */}
|
}}></Button> */}
|
||||||
{/* Footer Actions */}
|
{/* Footer Actions */}
|
||||||
<div className="flex flex-col-reverse sm:flex-row items-center justify-between gap-4 mt-2 ">
|
<div className="flex flex-col-reverse sm:flex-row items-center justify-between gap-4 mt-2 ">
|
||||||
<Form.Item name="isPublic" valuePropName="checked">
|
<div></div>
|
||||||
<Checkbox className="text-gray-600 hover:text-gray-900 transition-colors text-sm">
|
|
||||||
是否公开
|
|
||||||
</Checkbox>
|
|
||||||
</Form.Item>
|
|
||||||
<div className="flex gap-2 ">
|
<div className="flex gap-2 ">
|
||||||
{
|
{
|
||||||
<Form.Item name={["meta", "signature"]}>
|
<Form.Item name={["meta", "signature"]}>
|
||||||
|
|
|
@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,6 +7,7 @@ export const env: {
|
||||||
LIB_URL: string;
|
LIB_URL: string;
|
||||||
HOME_URL: string;
|
HOME_URL: string;
|
||||||
HOME_TEXT: string;
|
HOME_TEXT: string;
|
||||||
|
PREVIEW_URL: string;
|
||||||
} = {
|
} = {
|
||||||
APP_NAME: import.meta.env.PROD
|
APP_NAME: import.meta.env.PROD
|
||||||
? (window as any).env.VITE_APP_APP_NAME
|
? (window as any).env.VITE_APP_APP_NAME
|
||||||
|
@ -32,6 +33,9 @@ export const env: {
|
||||||
HOME_TEXT: import.meta.env.PROD
|
HOME_TEXT: import.meta.env.PROD
|
||||||
? (window as any).env.VITE_APP_HOME_TEXT
|
? (window as any).env.VITE_APP_HOME_TEXT
|
||||||
: import.meta.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);
|
console.log(env);
|
||||||
|
|
|
@ -8,13 +8,16 @@ import { ModuleRegistry } from "@ag-grid-community/core";
|
||||||
import { LicenseManager } from "@ag-grid-enterprise/core";
|
import { LicenseManager } from "@ag-grid-enterprise/core";
|
||||||
|
|
||||||
import { ClientSideRowModelModule } from "@ag-grid-community/client-side-row-model";
|
import { ClientSideRowModelModule } from "@ag-grid-community/client-side-row-model";
|
||||||
|
import { checkBrowserVersion } from "./utils/check-browser.js";
|
||||||
|
|
||||||
ModuleRegistry.registerModules([ClientSideRowModelModule]);
|
ModuleRegistry.registerModules([ClientSideRowModelModule]);
|
||||||
|
|
||||||
LicenseManager.setLicenseKey(
|
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(
|
ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<App />
|
<App />
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
});
|
||||||
|
};
|
|
@ -231,6 +231,7 @@ export interface PostMeta {
|
||||||
signature?: string;
|
signature?: string;
|
||||||
ip?: string;
|
ip?: string;
|
||||||
tags?: string[];
|
tags?: string[];
|
||||||
|
ownCode?: string;
|
||||||
}
|
}
|
||||||
export type RowModelResult = {
|
export type RowModelResult = {
|
||||||
rowData: any[];
|
rowData: any[];
|
||||||
|
|
1365
pnpm-lock.yaml
1365
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue