This commit is contained in:
longdayi 2025-01-27 00:29:36 +08:00
commit 9abc34c031
15 changed files with 159 additions and 56 deletions

View File

@ -26,7 +26,6 @@ export class PostService extends BaseService<Prisma.PostDelegate> {
}
onModuleInit() {
EventBus.on('updatePostState', ({ id }) => {
console.log('updatePostState');
updatePostState(id);
});
}
@ -34,7 +33,6 @@ export class PostService extends BaseService<Prisma.PostDelegate> {
args: Prisma.PostCreateArgs,
params: { staff?: UserProfile; tx?: Prisma.PostDelegate },
) {
console.log('params?.staff?.id', params?.staff?.id);
args.data.authorId = params?.staff?.id;
args.data.updatedAt = new Date();
// args.data.resources

View File

@ -10,7 +10,6 @@ export class VisitService extends BaseService<Prisma.VisitDelegate> {
async create(args: Prisma.VisitCreateArgs, staff?: UserProfile) {
const { postId, messageId } = args.data;
const clientIp = (args.data.meta as any)?.ip;
console.log('visit create');
const visitorId = args.data.visitorId || staff?.id;
let result;
const existingVisit = await db.visit.findFirst({

View File

@ -15,13 +15,11 @@ export class PostQueueService implements OnModuleInit {
constructor(@InjectQueue('general') private generalQueue: Queue) {}
onModuleInit() {
EventBus.on('updateVisitCount', ({ id, objectType, visitType }) => {
console.log('updateVisitCount');
if (objectType === ObjectType.POST) {
this.addUpdateVisitCountJob({ id, type: visitType });
}
});
EventBus.on('updatePostState', ({ id }) => {
console.log('updatePostState');
this.addUpdatePostState({ id });
});
}

View File

@ -88,7 +88,6 @@ export class TusService implements OnModuleInit {
upload: Upload,
) {
try {
console.log('upload.id', upload.id);
const resource = await this.resourceService.update({
where: { fileId: this.getFileId(upload.id) },
data: { status: ResourceStatus.UPLOADED },

View File

@ -43,6 +43,11 @@ export const RegisterForm = ({ onSubmit, isLoading }: RegisterFormProps) => {
<AvatarUploader
className="rounded-lg"
placeholder="点击上传头像"
onChange={(url) => {
if (url) {
console.log(url);
}
}}
style={{
width: `100%`,
height: 210,
@ -123,7 +128,7 @@ export const RegisterForm = ({ onSubmit, isLoading }: RegisterFormProps) => {
</Form.Item>
<Form.Item noStyle name={"office"}>
<Input
placeholder="请输入办公地点"
placeholder="请输入办公地点"
autoComplete="off"
spellCheck={false}
allowClear
@ -151,10 +156,10 @@ export const RegisterForm = ({ onSubmit, isLoading }: RegisterFormProps) => {
noStyle
rules={[
{ message: "请输入证件号" },
{
pattern: /^\d{5,12}$/,
message: "请输入有效的证件号5-12位数字",
},
// {
// pattern: /^\d{5,12}$/,
// message: "请输入有效的证件号5-12位数字",
// },
]}>
<Input placeholder="证件号" />
</Form.Item>

View File

@ -1,11 +1,16 @@
import PostDetail from "@web/src/components/models/post/detail/PostDetail";
import { useEffect } from "react";
import { useParams } from "react-router-dom";
export default function LetterDetailPage() {
const { id } = useParams();
useEffect(() => {
window.scrollTo(0, 0);
}, []);
return <div className="shadow-elegant border-2 border-white rounded-xl bg-slate-200">
<PostDetail id={id}></PostDetail>
</div>;
return (
<div className="shadow-elegant border-2 border-white rounded-xl bg-slate-200">
<PostDetail id={id}></PostDetail>
</div>
);
}

View File

@ -10,3 +10,16 @@ export const defaultModules = {
["clean"],
],
};
// 如果需要自定义 tooltip
const customTooltip = {
header: {
1: "标题 1",
2: "标题 2",
3: "标题 3",
4: "标题 4",
5: "标题 5",
6: "标题 6",
false: "正文",
},
};

View File

@ -36,6 +36,8 @@ const AvatarUploader: React.FC<AvatarUploaderProps> = ({
const [file, setFile] = useState<UploadingFile | null>(null);
const avatarRef = useRef<HTMLImageElement>(null);
const [previewUrl, setPreviewUrl] = useState<string>(value || "");
const [compressedUrl, setCompressedUrl] = useState<string>(value || "");
const [url, setUrl] = useState<string>(value || "");
const [uploading, setUploading] = useState(false);
const inputRef = useRef<HTMLInputElement>(null);
@ -46,7 +48,9 @@ const AvatarUploader: React.FC<AvatarUploaderProps> = ({
const handleChange = async (event: React.ChangeEvent<HTMLInputElement>) => {
const selectedFile = event.target.files?.[0];
if (!selectedFile) return;
// Create an object URL for the selected file
const objectUrl = URL.createObjectURL(selectedFile);
setPreviewUrl(objectUrl);
setFile({
name: selectedFile.name,
progress: 0,
@ -70,7 +74,7 @@ const AvatarUploader: React.FC<AvatarUploaderProps> = ({
}));
setUrl(result.url);
setPreviewUrl(result.compressedUrl);
setCompressedUrl(result.compressedUrl);
// 直接使用 result 中的最新值
resolve(compressed ? result.compressedUrl : result.url);
},
@ -80,6 +84,9 @@ const AvatarUploader: React.FC<AvatarUploaderProps> = ({
file?.fileKey
);
});
// await new Promise((resolve) => setTimeout(resolve,4999)); // 方法1使用 await 暂停执行
// 使用 resolved 的最新值调用 onChange
// 强制刷新 Avatar 组件
setAvatarKey((prev) => prev + 1); // 修改 key 强制重新挂载
onChange?.(uploadedUrl);
console.log(uploadedUrl)

View File

@ -47,7 +47,6 @@ export default function StaffForm() {
rank,
office,
} = values;
console.log("photoUrl", photoUrl);
setFormLoading(true);
try {
if (data && user?.id) {
@ -185,13 +184,13 @@ export default function StaffForm() {
/>
</Form.Item>
<Form.Item
rules={[
{
required: false,
pattern: /^\d{5,18}$/,
message: "请输入正确的证件号(数字)",
},
]}
// rules={[
// {
// required: false,
// pattern: /^\d{5,18}$/,
// message: "请输入正确的证件号(数字)",
// },
// ]}
noStyle
name={"officerId"}>
<Input
@ -229,7 +228,7 @@ export default function StaffForm() {
</Form.Item>
<Form.Item noStyle name={"office"}>
<Input
placeholder="请输入办公地点(可选)"
placeholder="请输入办公地点(可选)"
autoComplete="off"
spellCheck={false}
allowClear

View File

@ -48,6 +48,7 @@ export default function PostCommentEditor() {
toast.success("发布成功!");
setContent("");
setFileIds([]); // 重置上传组件状态
setSignature("");
setUploaderKey(uploaderKey + 1);
} catch (error) {
toast.error("发布失败,请稍后重试");
@ -91,7 +92,6 @@ export default function PostCommentEditor() {
key={uploaderKey}
value={fileIds}
onChange={(value) => {
console.log("ids", value);
setFileIds(value);
}}
/>

View File

@ -45,6 +45,7 @@ export function LetterFormProvider({
const onSubmit = async (data: LetterFormData) => {
try {
console.log(data);
const receivers = data?.receivers;
const terms = data?.terms;
delete data.receivers;
@ -78,14 +79,14 @@ export function LetterFormProvider({
: undefined,
},
});
const formattedDateTime = dayjs().format('YYYY-MM-DD HH:mm:ss')
const formattedDateTime = dayjs().format("YYYY-MM-DD HH:mm:ss");
// 创建包含信件编号和提交时间的文本
const fileContent = `信件编号: ${result.id}\n投递时间: ${formattedDateTime}`;
// 创建包含信件编号和提交时间的Blob对象
const blob = new Blob([fileContent], { type: 'text/plain' });
const blob = new Blob([fileContent], { type: "text/plain" });
// 创建下载链接
const downloadUrl = window.URL.createObjectURL(blob);
const link = document.createElement('a');
const link = document.createElement("a");
link.href = downloadUrl;
link.download = `信件编号-${result.id}.txt`; // 设置下载文件名
document.body.appendChild(link);
@ -93,10 +94,16 @@ export function LetterFormProvider({
document.body.removeChild(link);
window.URL.revokeObjectURL(downloadUrl);
toast.success(`信件投递成功!信件编号已保存到本地,请妥善保管用于进度查询`, {
duration: 5000 // 10秒
});
navigate(`/${result.id}/detail`, { replace: true });
toast.success(
`信件投递成功!信件编号已保存到本地,请妥善保管用于进度查询`,
{
duration: 5000, // 10秒
}
);
// navigate(`/${result.id}/detail`, {
// replace: true,
// state: { scrollToTop: true },
// });
form.resetFields();
} catch (error) {
console.error("Error submitting form:", error);

View File

@ -6,12 +6,39 @@ import { TusUploader } from "@web/src/components/common/uploader/TusUploader";
import StaffSelect from "../../../staff/staff-select";
import TermSelect from "../../../term/term-select";
import TabPane from "antd/es/tabs/TabPane";
import toast from "react-hot-toast";
export function LetterBasicForm() {
const { onSubmit, receiverId, termId, form } = useLetterEditor();
const handleFinish = async (values: any) => {
await onSubmit(values);
};
const handleSubmit = async () => {
try {
await form.validateFields();
form.submit();
// toast.success("提交成功!");
} catch (error) {
// 提取所有错误信息
const errorMessages = (error as any).errorFields
.map((field) => field.errors[0])
.filter(Boolean);
// 显示 toast 错误提示
toast.error(
<div className="flex flex-col gap-1">
<b></b>
{errorMessages.map((msg, i) => (
<span key={i}>· {msg}</span>
))}
</div>,
{
duration: 5000, // 显示 5 秒
position: "top-center",
}
);
}
};
return (
<div className=" p-6 ">
<Form
@ -63,13 +90,15 @@ export function LetterBasicForm() {
<TabPane tab="正文" key="1">
<Form.Item
name="content"
rules={[{ required: true, message: "请输入内容" }]}
rules={[
{ required: true, message: "请输入正文内容" },
]}
required={false}>
<div className="rounded-xl border border-white hover:ring-1 ring-white transition-all duration-300 ease-in-out bg-slate-100">
<QuillEditor
maxLength={10000}
placeholder="请输入内容"
minRows={16}
minRows={6}
onChange={(content) =>
form.setFieldValue("content", content)
}
@ -78,22 +107,30 @@ export function LetterBasicForm() {
</Form.Item>
</TabPane>
<TabPane tab="附件" key="2">
<Form.Item name="resources" required={false}>
<div className="rounded-xl border border-white hover:ring-1 ring-white transition-all duration-300 ease-in-out bg-slate-100">
<div className="rounded-xl border border-white hover:ring-1 ring-white transition-all duration-300 ease-in-out bg-slate-100">
<Form.Item name="resources" required={false}>
<TusUploader
onChange={async (resources) => {
// console.log(resources);
form.setFieldValue(
"resources",
resources
);
// console.log(
// form.getFieldValue("resources")
// );
}}
/>
</div>
</Form.Item>
</Form.Item>
</div>
</TabPane>
</Tabs>
{/* <Button
onClick={() => {
console.log(form.getFieldValue("resources"));
}}></Button> */}
{/* Footer Actions */}
<div className="flex flex-col-reverse sm:flex-row items-center justify-between gap-4 ">
<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">
@ -112,7 +149,7 @@ export function LetterBasicForm() {
</Form.Item>
<Button
type="primary"
onClick={() => form.submit()}
onClick={handleSubmit}
size="large"
icon={<SendOutlined />}
className="w-full sm:w-40">

View File

@ -204,13 +204,15 @@ export default function StaffForm() {
</Form.Item>
<Form.Item
noStyle
rules={[
{
required: false,
pattern: /^\d{5,18}$/,
message: "请输入正确的证件号(数字)",
},
]}
rules={
[
// {
// required: false,
// pattern: /^\d{5,18}$/,
// message: "请输入正确的证件号(数字)",
// },
]
}
name={"officerId"}
label="证件号">
<Input
@ -248,7 +250,7 @@ export default function StaffForm() {
</Form.Item>
<Form.Item noStyle name={"office"}>
<Input
placeholder="请输入办公地点(可选)"
placeholder="请输入办公地点(可选)"
autoComplete="off"
spellCheck={false}
allowClear

View File

@ -13,7 +13,7 @@
border-bottom-left-radius: 8px;
border-bottom-right-radius: 8px;
border: none;
@apply text-base
@apply text-base;
}
.ag-custom-dragging-class {
@ -41,8 +41,6 @@
/* 垂直居中 */
}
/* 滚动条轨道 */
::-webkit-scrollbar-track {
border-radius: 10px;
@ -69,7 +67,9 @@
}
/* 覆盖 Ant Design 的默认样式 raido button左侧的按钮*/
.ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled)::before {
.ant-radio-button-wrapper-checked:not(
.ant-radio-button-wrapper-disabled
)::before {
background-color: unset !important;
}
@ -82,6 +82,38 @@
display: none !important;
}
.no-wrap-header .ant-table-thead>tr>th {
.no-wrap-header .ant-table-thead > tr > th {
white-space: nowrap;
}
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
content: "标题 1";
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
content: "标题 2";
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
content: "标题 3";
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
content: "标题 4";
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
content: "标题 5";
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before {
content: "标题 6";
}
/* 针对下拉菜单中的选项 */
.ql-snow .ql-picker.ql-header .ql-picker-item:not([data-value])::before,
.ql-snow .ql-picker.ql-header .ql-picker-label:not([data-value])::before {
content: "正文" !important;
}

View File

@ -59,6 +59,8 @@ server {
# 文件上传处理位置
location /uploads/ {
# 文件实际存储路径
alias /data/uploads/;
# 文件传输性能优化