R95 A5 合同台账权限请求降级

这个提交包含在:
2026-05-06 00:30:36 +08:00
父节点 e4e47fea3b
当前提交 8b7e043860
+39 -17
查看文件
@@ -19,6 +19,8 @@ import { fetchEmployees } from "../api/employees";
import { mapApiErrorMessage } from "../api/errorMessages";
import { fetchProjects, type ConfidentialityLevel } from "../api/projects";
import { isApiError } from "../api/types";
import { useAuth } from "../auth/AuthContext";
import { hasAnyPermission } from "../auth/permissions";
import { PageHeader } from "../components/common/PageHeader";
import { StatusBadge } from "../components/common/StatusBadge";
import { Button } from "../components/ui/button";
@@ -124,7 +126,9 @@ function buildFiltersPayload(filters: FiltersState): ContractRegistryFilters {
};
}
function buildSavePayload(form: EditorState) {
function buildSavePayload(form: EditorState, options: { includeOwnerEmployeeId?: boolean } = {}) {
const includeOwnerEmployeeId = options.includeOwnerEmployeeId ?? true;
return {
contractNo: form.contractNo.trim(),
projectId: form.projectId || null,
@@ -139,7 +143,7 @@ function buildSavePayload(form: EditorState) {
confidentialityLevel: form.confidentialityLevel,
archiveLocation: form.archiveLocation.trim(),
offlineArchiveNo: form.offlineArchiveNo.trim() || null,
ownerEmployeeId: form.ownerEmployeeId || undefined,
...(includeOwnerEmployeeId && form.ownerEmployeeId ? { ownerEmployeeId: form.ownerEmployeeId } : {}),
status: form.status,
};
}
@@ -170,8 +174,10 @@ function nextPersistedStatus(item: ContractRegistryItem): ContractRegistryPersis
export function ContractsRegistryPage() {
const queryClient = useQueryClient();
const { permissions } = useAuth();
const [searchParams] = useSearchParams();
const initialProjectId = searchParams.get("projectId") ?? "";
const canBrowseEmployees = hasAnyPermission(permissions, ["system.users", "report.dashboard.view"]);
const [filters, setFilters] = useState<FiltersState>({
projectId: initialProjectId,
@@ -199,15 +205,21 @@ export function ContractsRegistryPage() {
queryFn: () => fetchProjects({}),
});
const employeesQuery = useQuery({
enabled: canBrowseEmployees,
queryKey: ["employees", "contracts-page"],
queryFn: () => fetchEmployees({ page: 1, pageSize: 100, status: "active" }),
});
const projectOptions = projectsQuery.data?.items ?? [];
const employeeOptions = employeesQuery.data?.items ?? [];
const employeeOptions = canBrowseEmployees ? employeesQuery.data?.items ?? [] : [];
const contracts = contractsQuery.data?.items ?? [];
const expiringItems = expiringQuery.data?.items ?? [];
const editingItem = editorId ? contracts.find((item) => item.id === editorId) ?? null : null;
const readonlyOwnerLabel = editingItem?.owner
? `${editingItem.owner.name} · ${editingItem.owner.employeeNo}`
: editor.ownerEmployeeId
? "保留现有责任人"
: "默认当前用户";
const amountInvalid = Boolean(editor.amount.trim()) && !AMOUNT_PATTERN.test(editor.amount.trim());
const refreshAll = async () => {
@@ -224,10 +236,11 @@ export function ContractsRegistryPage() {
const saveMutation = useMutation({
mutationFn: async () => {
const payload = buildSavePayload(editor, { includeOwnerEmployeeId: canBrowseEmployees });
if (editorId) {
return patchContractRegistry(editorId, buildSavePayload(editor));
return patchContractRegistry(editorId, payload);
}
return createContractRegistry(buildSavePayload(editor));
return createContractRegistry(payload);
},
onSuccess: async () => {
await refreshAll();
@@ -537,18 +550,27 @@ export function ContractsRegistryPage() {
<div className="space-y-2">
<Label>责任人</Label>
<select
className="select-std"
value={editor.ownerEmployeeId}
onChange={(event) => setEditor((current) => ({ ...current, ownerEmployeeId: event.target.value }))}
>
<option value="">默认当前用户</option>
{employeeOptions.map((item) => (
<option key={item.id} value={item.id}>
{item.name} · {item.employeeNo}
</option>
))}
</select>
{canBrowseEmployees ? (
<select
className="select-std"
value={editor.ownerEmployeeId}
onChange={(event) => setEditor((current) => ({ ...current, ownerEmployeeId: event.target.value }))}
>
<option value="">默认当前用户</option>
{employeeOptions.map((item) => (
<option key={item.id} value={item.id}>
{item.name} · {item.employeeNo}
</option>
))}
</select>
) : (
<div className="rounded-lg border border-zinc-200 bg-zinc-50 px-3 py-2 text-sm">
<div className="font-medium text-zinc-900">{readonlyOwnerLabel}</div>
<div className="mt-1 text-xs text-zinc-500">
当前账号无人员目录权限,新建时使用当前用户,编辑时保留原责任人。
</div>
</div>
)}
</div>
<Button