diff --git a/apps/server/src/models/rbac/rolemap.router.ts b/apps/server/src/models/rbac/rolemap.router.ts index 72ae5a4..e610cc2 100755 --- a/apps/server/src/models/rbac/rolemap.router.ts +++ b/apps/server/src/models/rbac/rolemap.router.ts @@ -1,9 +1,6 @@ import { Injectable } from '@nestjs/common'; import { TrpcService } from '@server/trpc/trpc.service'; -import { - ObjectType, - RoleMapMethodSchema, -} from '@nice/common'; +import { ObjectType, RoleMapMethodSchema } from '@nice/common'; import { RoleMapService } from './rolemap.service'; @Injectable() @@ -11,7 +8,7 @@ export class RoleMapRouter { constructor( private readonly trpc: TrpcService, private readonly roleMapService: RoleMapService, - ) { } + ) {} router = this.trpc.router({ deleteAllRolesForObject: this.trpc.protectProcedure .input(RoleMapMethodSchema.deleteWithObject) @@ -67,5 +64,10 @@ export class RoleMapRouter { .query(async ({ input }) => { return this.roleMapService.getStaffsNotMap(input); }), + getStaffIdsByRoleNames: this.trpc.procedure + .input(RoleMapMethodSchema.getStaffIdsByRoleNames) + .query(async ({ input }) => { + return this.roleMapService.getStaffIdsByRoleNames(input); + }), }); } diff --git a/apps/server/src/models/rbac/rolemap.service.ts b/apps/server/src/models/rbac/rolemap.service.ts index d3c971a..9b2c7db 100755 --- a/apps/server/src/models/rbac/rolemap.service.ts +++ b/apps/server/src/models/rbac/rolemap.service.ts @@ -44,7 +44,7 @@ export class RoleMapService extends RowModelService { ) { const { roleId, domainId } = request; // Base conditions - let condition = super.createGetRowsFilters(request, staff); + const condition = super.createGetRowsFilters(request, staff); if (isFieldCondition(condition)) return; // Adding conditions based on parameters existence if (roleId) { @@ -64,10 +64,7 @@ export class RoleMapService extends RowModelService { return condition; } - protected async getRowDto( - row: any, - staff?: UserProfile, - ): Promise { + protected async getRowDto(row: any, staff?: UserProfile): Promise { if (!row.id) return row; return row; } @@ -126,15 +123,17 @@ export class RoleMapService extends RowModelService { data: roleMaps, }); }); - const wrapResult = Promise.all(result.map(async item => { - const staff = await db.staff.findMany({ - include: { department: true }, - where: { - id: item.objectId - } - }) - return { ...item, staff } - })) + const wrapResult = Promise.all( + result.map(async (item) => { + const staff = await db.staff.findMany({ + include: { department: true }, + where: { + id: item.objectId, + }, + }); + return { ...item, staff }; + }), + ); return wrapResult; } async addRoleForObjects( @@ -187,11 +186,11 @@ export class RoleMapService extends RowModelService { { objectId: staffId, objectType: ObjectType.STAFF }, ...(deptId || ancestorDeptIds.length > 0 ? [ - { - objectId: { in: [deptId, ...ancestorDeptIds].filter(Boolean) }, - objectType: ObjectType.DEPARTMENT, - }, - ] + { + objectId: { in: [deptId, ...ancestorDeptIds].filter(Boolean) }, + objectType: ObjectType.DEPARTMENT, + }, + ] : []), ]; // Helper function to fetch roles based on domain ID. @@ -260,7 +259,9 @@ export class RoleMapService extends RowModelService { // const processedItems = await Promise.all(items.map(item => this.genRoleMapDto(item))); return { items, totalCount }; } - async getStaffsNotMap(data: z.infer) { + async getStaffsNotMap( + data: z.infer, + ) { const { domainId, roleId } = data; let staffs = await db.staff.findMany({ where: { @@ -280,6 +281,35 @@ export class RoleMapService extends RowModelService { ); return staffs; } + async getStaffIdsByRoleNames( + data: z.infer, + ) { + const { roleNames } = data; + const roles = await db.role.findMany({ + where: { + name: { + in: roleNames, + }, + }, + select: { + id: true, + }, + }); + const roleMaps = await db.roleMap.findMany({ + where: { + roleId: { + in: roles.map((role) => role.id), + }, + objectType: ObjectType.STAFF, + }, + select: { + id: true, + objectId: true, + }, + }); + const staffIds = roleMaps.map((roleMap) => roleMap.objectId); + return staffIds; + } /** * 更新角色映射 * @param data 包含更新信息的数据 @@ -300,7 +330,9 @@ export class RoleMapService extends RowModelService { * @param data 包含角色ID和域ID的数据 * @returns 角色映射详情,包含部门ID和员工ID列表 */ - async getRoleMapDetail(data: z.infer) { + async getRoleMapDetail( + data: z.infer, + ) { const { roleId, domainId } = data; const res = await db.roleMap.findMany({ where: { roleId, domainId } }); diff --git a/apps/server/src/models/resource/resource.module.ts b/apps/server/src/models/resource/resource.module.ts index 153bc6e..a2b9dbf 100644 --- a/apps/server/src/models/resource/resource.module.ts +++ b/apps/server/src/models/resource/resource.module.ts @@ -4,7 +4,7 @@ import { ResourceService } from './resource.service'; import { TrpcService } from '@server/trpc/trpc.service'; @Module({ - exports: [ResourceRouter, ResourceService], - providers: [ResourceRouter, ResourceService, TrpcService], + exports: [ResourceRouter, ResourceService], + providers: [ResourceRouter, ResourceService, TrpcService], }) -export class ResourceModule { } +export class ResourceModule {} diff --git a/apps/server/src/models/resource/resource.router.ts b/apps/server/src/models/resource/resource.router.ts index 6d4290f..30af7b5 100644 --- a/apps/server/src/models/resource/resource.router.ts +++ b/apps/server/src/models/resource/resource.router.ts @@ -3,68 +3,75 @@ import { TrpcService } from '@server/trpc/trpc.service'; import { Prisma, UpdateOrderSchema } from '@nice/common'; import { ResourceService } from './resource.service'; import { z, ZodType } from 'zod'; -const ResourceCreateArgsSchema: ZodType = z.any() -const ResourceCreateManyInputSchema: ZodType = z.any() -const ResourceDeleteManyArgsSchema: ZodType = z.any() -const ResourceFindManyArgsSchema: ZodType = z.any() -const ResourceFindFirstArgsSchema: ZodType = z.any() -const ResourceWhereInputSchema: ZodType = z.any() -const ResourceSelectSchema: ZodType = z.any() +const ResourceCreateArgsSchema: ZodType = z.any(); +const ResourceCreateManyInputSchema: ZodType = + z.any(); +const ResourceDeleteManyArgsSchema: ZodType = + z.any(); +const ResourceFindManyArgsSchema: ZodType = + z.any(); +const ResourceFindFirstArgsSchema: ZodType = + z.any(); +const ResourceWhereInputSchema: ZodType = z.any(); +const ResourceSelectSchema: ZodType = z.any(); @Injectable() export class ResourceRouter { - constructor( - private readonly trpc: TrpcService, - private readonly resourceService: ResourceService, - ) { } - router = this.trpc.router({ - create: this.trpc.protectProcedure - .input(ResourceCreateArgsSchema) - .mutation(async ({ ctx, input }) => { - const { staff } = ctx; - return await this.resourceService.create(input, { staff }); - }), - createMany: this.trpc.protectProcedure.input(z.array(ResourceCreateManyInputSchema)) - .mutation(async ({ ctx, input }) => { - const { staff } = ctx; + constructor( + private readonly trpc: TrpcService, + private readonly resourceService: ResourceService, + ) {} + router = this.trpc.router({ + create: this.trpc.protectProcedure + .input(ResourceCreateArgsSchema) + .mutation(async ({ ctx, input }) => { + const { staff } = ctx; + return await this.resourceService.create(input, { staff }); + }), + createMany: this.trpc.protectProcedure + .input(z.array(ResourceCreateManyInputSchema)) + .mutation(async ({ ctx, input }) => { + const { staff } = ctx; - return await this.resourceService.createMany({ data: input }, staff); - }), - deleteMany: this.trpc.procedure - .input(ResourceDeleteManyArgsSchema) - .mutation(async ({ input }) => { - return await this.resourceService.deleteMany(input); - }), - findFirst: this.trpc.procedure - .input(ResourceFindFirstArgsSchema) // Assuming StaffMethodSchema.findMany is the Zod schema for finding staffs by keyword - .query(async ({ input }) => { - return await this.resourceService.findFirst(input); - }), - softDeleteByIds: this.trpc.protectProcedure - .input(z.object({ ids: z.array(z.string()) })) // expect input according to the schema - .mutation(async ({ input }) => { - return this.resourceService.softDeleteByIds(input.ids); - }), - updateOrder: this.trpc.protectProcedure - .input(UpdateOrderSchema) - .mutation(async ({ input }) => { - return this.resourceService.updateOrder(input); - }), - findMany: this.trpc.procedure - .input(ResourceFindManyArgsSchema) // Assuming StaffMethodSchema.findMany is the Zod schema for finding staffs by keyword - .query(async ({ input }) => { - return await this.resourceService.findMany(input); - }), - findManyWithCursor: this.trpc.protectProcedure - .input(z.object({ - cursor: z.any().nullish(), - take: z.number().nullish(), - where: ResourceWhereInputSchema.nullish(), - select: ResourceSelectSchema.nullish() - })) - .query(async ({ ctx, input }) => { - const { staff } = ctx; - return await this.resourceService.findManyWithCursor(input); - }), - }); + return await this.resourceService.createMany({ data: input }, staff); + }), + deleteMany: this.trpc.procedure + .input(ResourceDeleteManyArgsSchema) + .mutation(async ({ input }) => { + return await this.resourceService.deleteMany(input); + }), + findFirst: this.trpc.procedure + .input(ResourceFindFirstArgsSchema) // Assuming StaffMethodSchema.findMany is the Zod schema for finding staffs by keyword + .query(async ({ input }) => { + return await this.resourceService.findFirst(input); + }), + softDeleteByIds: this.trpc.protectProcedure + .input(z.object({ ids: z.array(z.string()) })) // expect input according to the schema + .mutation(async ({ input }) => { + return this.resourceService.softDeleteByIds(input.ids); + }), + updateOrder: this.trpc.protectProcedure + .input(UpdateOrderSchema) + .mutation(async ({ input }) => { + return this.resourceService.updateOrder(input); + }), + findMany: this.trpc.procedure + .input(ResourceFindManyArgsSchema) // Assuming StaffMethodSchema.findMany is the Zod schema for finding staffs by keyword + .query(async ({ input }) => { + return await this.resourceService.findMany(input); + }), + findManyWithCursor: this.trpc.protectProcedure + .input( + z.object({ + cursor: z.any().nullish(), + take: z.number().nullish(), + where: ResourceWhereInputSchema.nullish(), + select: ResourceSelectSchema.nullish(), + }), + ) + .query(async ({ ctx, input }) => { + const { staff } = ctx; + return await this.resourceService.findManyWithCursor(input); + }), + }); } diff --git a/apps/server/src/models/staff/staff.module.ts b/apps/server/src/models/staff/staff.module.ts index fa681dc..c9e787f 100755 --- a/apps/server/src/models/staff/staff.module.ts +++ b/apps/server/src/models/staff/staff.module.ts @@ -12,4 +12,4 @@ import { StaffRowService } from './staff.row.service'; exports: [StaffService, StaffRouter, StaffRowService], controllers: [StaffController], }) -export class StaffModule { } +export class StaffModule {} diff --git a/apps/server/src/models/staff/staff.service.ts b/apps/server/src/models/staff/staff.service.ts index a5868aa..9844c37 100755 --- a/apps/server/src/models/staff/staff.service.ts +++ b/apps/server/src/models/staff/staff.service.ts @@ -44,7 +44,7 @@ export class StaffService extends BaseService { ...data, password: await argon2.hash((data.password || '123456') as string), }; - + const result = await super.create({ ...args, data: createData }); this.emitDataChangedEvent(result, CrudOperation.CREATED); return result; diff --git a/apps/server/src/trpc/trpc.module.ts b/apps/server/src/trpc/trpc.module.ts index 978c0af..222c9c9 100755 --- a/apps/server/src/trpc/trpc.module.ts +++ b/apps/server/src/trpc/trpc.module.ts @@ -15,6 +15,8 @@ import { WebSocketModule } from '@server/socket/websocket.module'; import { RoleMapModule } from '@server/models/rbac/rbac.module'; import { TransformModule } from '@server/models/transform/transform.module'; +import { ResourceModule } from '@server/models/resource/resource.module'; + @Module({ imports: [ AuthModule, @@ -30,6 +32,7 @@ import { TransformModule } from '@server/models/transform/transform.module'; PostModule, VisitModule, WebSocketModule, + ResourceModule, ], controllers: [], providers: [TrpcService, TrpcRouter, Logger], diff --git a/apps/server/src/trpc/trpc.router.ts b/apps/server/src/trpc/trpc.router.ts index dd0190c..7550867 100755 --- a/apps/server/src/trpc/trpc.router.ts +++ b/apps/server/src/trpc/trpc.router.ts @@ -13,10 +13,10 @@ import { VisitRouter } from '@server/models/visit/visit.router'; import { RoleMapRouter } from '@server/models/rbac/rolemap.router'; import { TransformRouter } from '@server/models/transform/transform.router'; import { RoleRouter } from '@server/models/rbac/role.router'; +import { ResourceRouter } from '../models/resource/resource.router'; @Injectable() export class TrpcRouter { - logger = new Logger(TrpcRouter.name); constructor( private readonly trpc: TrpcService, @@ -31,10 +31,10 @@ export class TrpcRouter { private readonly app_config: AppConfigRouter, private readonly message: MessageRouter, private readonly visitor: VisitRouter, - - ) { } + private readonly resource: ResourceRouter, + ) {} getRouter() { - return + return; } appRouter = this.trpc.router({ transform: this.transform.router, @@ -48,6 +48,7 @@ export class TrpcRouter { message: this.message.router, app_config: this.app_config.router, visitor: this.visitor.router, + resource: this.resource.router, }); wss: WebSocketServer = undefined; diff --git a/apps/server/src/trpc/types.ts b/apps/server/src/trpc/types.ts index cd7e491..fc6ce2c 100644 --- a/apps/server/src/trpc/types.ts +++ b/apps/server/src/trpc/types.ts @@ -1,3 +1,3 @@ -import { TrpcRouter } from "./trpc.router"; +import { TrpcRouter } from './trpc.router'; export type AppRouter = TrpcRouter[`appRouter`]; diff --git a/apps/web/src/app/main/letter/editor/page.tsx b/apps/web/src/app/main/letter/editor/page.tsx index 4caa91f..f624693 100644 --- a/apps/web/src/app/main/letter/editor/page.tsx +++ b/apps/web/src/app/main/letter/editor/page.tsx @@ -9,7 +9,7 @@ export default function LetterEditorPage() { const termId = searchParams.get("termId"); return ( -
+
diff --git a/apps/web/src/app/main/letter/write/page.tsx b/apps/web/src/app/main/letter/write/page.tsx index c7da2f7..de44eae 100644 --- a/apps/web/src/app/main/letter/write/page.tsx +++ b/apps/web/src/app/main/letter/write/page.tsx @@ -1,4 +1,4 @@ -import { useState, useCallback, useEffect } from "react"; +import { useState, useCallback, useEffect, useMemo } from "react"; import { motion, AnimatePresence } from "framer-motion"; import { useSearchParams } from "react-router-dom"; @@ -9,136 +9,157 @@ import DepartmentSelect from "@web/src/components/models/department/department-s import debounce from "lodash/debounce"; import { SearchOutlined } from "@ant-design/icons"; import WriteHeader from "./WriteHeader"; +import { ObjectType, RoleName } from "@nice/common"; export default function WriteLetterPage() { - const [searchParams] = useSearchParams(); - const termId = searchParams.get("termId"); - const [searchQuery, setSearchQuery] = useState(""); - const [selectedDept, setSelectedDept] = useState(); - const [currentPage, setCurrentPage] = useState(1); - const pageSize = 10; - const { getTerm } = useTerm(); + const [searchParams] = useSearchParams(); + const termId = searchParams.get("termId"); + const [searchQuery, setSearchQuery] = useState(""); + const [selectedDept, setSelectedDept] = useState(); + const [currentPage, setCurrentPage] = useState(1); + const pageSize = 10; + const { getTerm } = useTerm(); + const { data: enabledStaffIds, isLoading: roleMapIsLoading } = + api.rolemap.getStaffIdsByRoleNames.useQuery({ + roleNames: [RoleName.Leader, RoleName.Organization], + }); + // eslint-disable-next-line react-hooks/exhaustive-deps - const { data, isLoading, error } = - api.staff.findManyWithPagination.useQuery({ - page: currentPage, - pageSize, - where: { - deptId: selectedDept, - OR: [ - { - showname: { - contains: searchQuery, - }, - }, - { - username: { - contains: searchQuery, - }, - }, + const { data, isLoading, error } = + api.staff.findManyWithPagination.useQuery( + { + page: currentPage, + pageSize, + where: { + id: enabledStaffIds + ? { + in: enabledStaffIds, + } + : undefined, + deptId: selectedDept, + OR: [ + { + showname: { + contains: searchQuery, + }, + }, + { + username: { + contains: searchQuery, + }, + }, + { + meta: { + path: ["rank"], // 指定 JSON 字段的路径 + string_contains: searchQuery, // 对 rank 字段进行模糊搜索 + }, + }, + ], + }, + orderBy: { + order: "asc", + }, + }, + { + enabled: !roleMapIsLoading, + } + ); - ], - }, - orderBy: { - order: "desc", - } - }); + const resetPage = useCallback(() => { + setCurrentPage(1); + }, []); - const resetPage = useCallback(() => { - setCurrentPage(1); - }, []); + // Reset page when search or department changes + useEffect(() => { + resetPage(); + }, [searchQuery, selectedDept, resetPage]); - // Reset page when search or department changes - useEffect(() => { - resetPage(); - }, [searchQuery, selectedDept, resetPage]); + return ( +
+ +
+
+
+ + + } + placeholder="搜索领导姓名或职级..." + onChange={debounce( + (e) => setSearchQuery(e.target.value), + 300 + )} + size="large" + /> +
+ {error && ( + + )} +
+ + {isLoading ? ( +
+ +
+ ) : data?.items.length > 0 ? ( + + {data?.items.map((item: any) => ( + + ))} + + ) : ( + + + + )} +
- return ( -
- -
-
-
- - - } - placeholder="搜索领导姓名或职级..." - onChange={debounce( - (e) => setSearchQuery(e.target.value), - 300 - )} - size="large" - /> -
- {error && ( - - )} -
- - {isLoading ? ( -
- -
- ) : data?.items.length > 0 ? ( - - {data?.items.map((item: any) => ( - - ))} - - ) : ( - - - - )} -
- - {/* Pagination */} - {data?.items.length > 0 && ( -
- { - setCurrentPage(page); - window.scrollTo(0, 0); - }} - showSizeChanger={false} - showTotal={(total) => `共 ${total} 条记录`} - /> -
- )} -
-
- ); + {/* Pagination */} + {data?.items.length > 0 && ( +
+ { + setCurrentPage(page); + window.scrollTo(0, 0); + }} + showSizeChanger={false} + showTotal={(total) => `共 ${total} 条记录`} + /> +
+ )} +
+
+ ); } diff --git a/apps/web/src/components/common/uploader/AvatarUploader.tsx b/apps/web/src/components/common/uploader/AvatarUploader.tsx index 5082427..ae831a3 100644 --- a/apps/web/src/components/common/uploader/AvatarUploader.tsx +++ b/apps/web/src/components/common/uploader/AvatarUploader.tsx @@ -2,6 +2,7 @@ import { env } from "@web/src/env"; import { message, Progress, Spin, theme } from "antd"; import React, { useState, useEffect, useRef } from "react"; import { useTusUpload } from "@web/src/hooks/useTusUpload"; +import { Avatar } from "antd/lib"; export interface AvatarUploaderProps { value?: string; @@ -69,7 +70,6 @@ const AvatarUploader: React.FC = ({ file?.fileKey ); }); - setPreviewUrl(`http://${env.SERVER_IP}/uploads/${fileId}`); onChange?.(fileId); message.success("头像上传成功"); } catch (error) { @@ -94,7 +94,6 @@ const AvatarUploader: React.FC = ({ background: token.colorBgContainer, ...style, // 应用外部传入的样式 }}> -
{previewUrl}
= ({ style={{ display: "none" }} /> {previewUrl ? ( - Avatar ) : ( diff --git a/apps/web/src/components/common/uploader/TusUploader.tsx b/apps/web/src/components/common/uploader/TusUploader.tsx index 47034e6..12f8f9d 100644 --- a/apps/web/src/components/common/uploader/TusUploader.tsx +++ b/apps/web/src/components/common/uploader/TusUploader.tsx @@ -130,9 +130,8 @@ export const TusUploader = ({ value = [], onChange }: TusUploaderProps) => {

@@ -143,7 +142,7 @@ export const TusUploader = ({ value = [], onChange }: TusUploaderProps) => {

支持单个或批量上传文件

{/* 正在上传的文件 */} {(uploadingFiles.length > 0 || completedFiles.length > 0) && ( -
+
{uploadingFiles.map((file) => (
-
-
-
-
- 首长机关信箱 -

聆怀若水,应语如风;纾难化困,践诺成春

-
-
- -
-
- {!isAuthenticated ? ( - +
+
+
+
+ {/** 在这里放置logo */} + {isLoading ? ( +
+ ) : ( + logoUrl && ( + Logo + ) + )} +
+
+ +
+
+ {!isAuthenticated ? ( + - - + - 登录 - - ) : ( - - )} -
-
-
- -
- - ); + 登录 + + ) : ( + + )} +
+
+
+ +
+ + ); }); diff --git a/apps/web/src/components/layout/main/navigation.tsx b/apps/web/src/components/layout/main/navigation.tsx index 27a1302..66517b6 100644 --- a/apps/web/src/components/layout/main/navigation.tsx +++ b/apps/web/src/components/layout/main/navigation.tsx @@ -28,7 +28,7 @@ export default function Navigation({ className }: NavigationProps) { return (
- { - setFileIds(value); - }} - /> +
+ { + setFileIds(value); + }} + /> +
diff --git a/apps/web/src/components/models/post/detail/PostHeader/PostHateButton.tsx b/apps/web/src/components/models/post/detail/PostHeader/PostHateButton.tsx index 3d5cb18..a19dfec 100644 --- a/apps/web/src/components/models/post/detail/PostHeader/PostHateButton.tsx +++ b/apps/web/src/components/models/post/detail/PostHeader/PostHateButton.tsx @@ -36,8 +36,9 @@ export default function PostHateButton({ post }: { post: PostDto }) { type={post?.hated ? "primary" : "default"} style={{ backgroundColor: post?.hated ? "#ff4d4f" : "#fff", - borderColor: "#ff4d4f", + borderColor: post?.hated ? "transparent" : "#ff4d4f", color: post?.hated ? "#fff" : "#ff4d4f", + boxShadow: "none", // 去除阴影 }} shape="round" icon={post?.hated ? : } diff --git a/apps/web/src/components/models/post/detail/PostHeader/PostSendButton.tsx b/apps/web/src/components/models/post/detail/PostHeader/PostSendButton.tsx new file mode 100644 index 0000000..e0a7298 --- /dev/null +++ b/apps/web/src/components/models/post/detail/PostHeader/PostSendButton.tsx @@ -0,0 +1,32 @@ +import { PostDto, VisitType } from "@nice/common"; +import { useVisitor } from "@nice/client"; +import { Button, Tooltip } from "antd"; +import { + DislikeFilled, + DislikeOutlined, + SendOutlined, +} from "@ant-design/icons"; +import { useAuth } from "@web/src/providers/auth-provider"; + +export default function PostSendButton({ post }: { post: PostDto }) { + const { user } = useAuth(); + const { hate, unHate } = useVisitor(); + function sendPost() { + if (post?.authorId) { + window.open(`/editor?receiverId=${post?.authorId}`, "_blank"); + } + } + return ( + + ); +} diff --git a/apps/web/src/components/models/post/detail/PostResources.tsx b/apps/web/src/components/models/post/detail/PostResources.tsx index 328bae4..714b549 100644 --- a/apps/web/src/components/models/post/detail/PostResources.tsx +++ b/apps/web/src/components/models/post/detail/PostResources.tsx @@ -4,7 +4,7 @@ import { DownloadOutlined } from "@ant-design/icons"; import { PostDto } from "@nice/common"; import { env } from "@web/src/env"; import { getFileIcon } from "./utils"; -import { formatFileSize } from '@nice/utils'; +import { formatFileSize } from "@nice/utils"; export default function PostResources({ post }: { post: PostDto }) { const { resources } = useMemo(() => { if (!post?.resources) return { resources: [] }; @@ -99,7 +99,9 @@ export default function PostResources({ post }: { post: PostDto }) { {resource.meta.size && - formatFileSize(resource.meta.size)} + formatFileSize( + resource.meta.size + )}
diff --git a/apps/web/src/components/models/post/editor/form/LetterBasicForm.tsx b/apps/web/src/components/models/post/editor/form/LetterBasicForm.tsx index d3b6cd2..4252c4e 100644 --- a/apps/web/src/components/models/post/editor/form/LetterBasicForm.tsx +++ b/apps/web/src/components/models/post/editor/form/LetterBasicForm.tsx @@ -65,7 +65,7 @@ export function LetterBasicForm() { name="content" rules={[{ required: true, message: "请输入内容" }]} required={false}> -
+
-
+
+ form.setFieldValue( @@ -88,12 +89,13 @@ export function LetterBasicForm() { ) } /> -
+ +
{/* Footer Actions */} -
+
是否公开 diff --git a/apps/web/src/components/models/role/role-editor/role-modal.tsx b/apps/web/src/components/models/role/role-editor/role-modal.tsx index e8b4411..1897af7 100644 --- a/apps/web/src/components/models/role/role-editor/role-modal.tsx +++ b/apps/web/src/components/models/role/role-editor/role-modal.tsx @@ -4,28 +4,24 @@ import { RoleEditorContext } from "./role-editor"; import RoleForm from "./role-form"; export default function RoleModal() { - const { - roleForm, - editRoleId, - roleModalOpen, setRoleModalOpen - } = useContext(RoleEditorContext); + const { roleForm, editRoleId, roleModalOpen, setRoleModalOpen } = + useContext(RoleEditorContext); - const handleOk = async () => { - roleForm.submit() - }; + const handleOk = async () => { + roleForm.submit(); + }; - const handleCancel = () => setRoleModalOpen(false); + const handleCancel = () => setRoleModalOpen(false); - return ( - - - - ); + return ( + + + + ); } diff --git a/apps/web/src/hooks/useTusUpload.ts b/apps/web/src/hooks/useTusUpload.ts index b3f0b35..a1ddf1d 100644 --- a/apps/web/src/hooks/useTusUpload.ts +++ b/apps/web/src/hooks/useTusUpload.ts @@ -1,5 +1,6 @@ import { useState } from "react"; import * as tus from "tus-js-client"; +import { env } from "../env"; // useTusUpload.ts interface UploadProgress { @@ -27,7 +28,16 @@ export function useTusUpload() { } return parts.slice(uploadIndex + 1, uploadIndex + 5).join("/"); }; - + const getResourceUrl = (url: string) => { + const parts = url.split("/"); + const uploadIndex = parts.findIndex((part) => part === "upload"); + if (uploadIndex === -1 || uploadIndex + 4 >= parts.length) { + throw new Error("Invalid upload URL format"); + } + const resUrl = `http://${env.SERVER_IP}/uploads/${parts.slice(uploadIndex + 1, uploadIndex + 6).join("/")}`; + console.log(resUrl); + return resUrl; + }; const handleFileUpload = async ( file: File, onSuccess: (result: UploadResult) => void, @@ -52,7 +62,7 @@ export function useTusUpload() { metadata: { filename: file.name, filetype: file.type, - size: file.size as any + size: file.size as any, }, onProgress: (bytesUploaded, bytesTotal) => { const progress = Number( @@ -67,13 +77,14 @@ export function useTusUpload() { try { if (upload.url) { const fileId = getFileId(upload.url); + const url = getResourceUrl(upload.url); setIsUploading(false); setUploadProgress((prev) => ({ ...prev, [fileKey]: 100, })); onSuccess({ - url: upload.url, + url, fileId, }); } diff --git a/packages/client/src/api/hooks/useVisitor.ts b/packages/client/src/api/hooks/useVisitor.ts index 86cdc58..30e4e5e 100644 --- a/packages/client/src/api/hooks/useVisitor.ts +++ b/packages/client/src/api/hooks/useVisitor.ts @@ -8,6 +8,7 @@ export function useVisitor() { const create = api.visitor.create.useMutation({ onSuccess() { utils.visitor.invalidate(); + // utils.post.invalidate(); }, }); diff --git a/packages/common/src/enum.ts b/packages/common/src/enum.ts index 035c58a..d4701ea 100755 --- a/packages/common/src/enum.ts +++ b/packages/common/src/enum.ts @@ -201,3 +201,10 @@ export const PostStateLabels = { [PostState.PROCESSING]: "处理中", [PostState.RESOLVED]: "已完成", }; +export enum RoleName { + Basic = "基层", // 基层 + Organization = "机关", // 机关 + Leader = "领导", // 领导 + DomainAdmin = "域管理员", // 域管理员 + RootAdmin = "根管理员", // 根管理员 +} diff --git a/packages/common/src/schema.ts b/packages/common/src/schema.ts index ab44cd9..7ff9417 100755 --- a/packages/common/src/schema.ts +++ b/packages/common/src/schema.ts @@ -320,6 +320,9 @@ export const RoleMapMethodSchema = { domainId: z.string().nullish(), roleId: z.string().nullish(), }), + getStaffIdsByRoleNames: z.object({ + roleNames: z.array(z.string()), + }), }; export const RoleMethodSchema = { create: z.object({