|
@@ -1,4 +1,4 @@
|
|
|
-import React, { useState, useEffect } from 'react';
|
|
|
|
|
|
|
+import React from 'react';
|
|
|
import { useForm } from 'react-hook-form';
|
|
import { useForm } from 'react-hook-form';
|
|
|
import { zodResolver } from '@hookform/resolvers/zod';
|
|
import { zodResolver } from '@hookform/resolvers/zod';
|
|
|
import { Button } from '@/client/components/ui/button';
|
|
import { Button } from '@/client/components/ui/button';
|
|
@@ -9,74 +9,41 @@ import { createAreaSchema, updateAreaSchema } from '@d8d/server/modules/areas/ar
|
|
|
import type { CreateAreaInput, UpdateAreaInput } from '@d8d/server/modules/areas/area.schema';
|
|
import type { CreateAreaInput, UpdateAreaInput } from '@d8d/server/modules/areas/area.schema';
|
|
|
import { AreaLevel } from '@d8d/server/modules/areas/area.entity';
|
|
import { AreaLevel } from '@d8d/server/modules/areas/area.entity';
|
|
|
import { DisabledStatus } from '@/share/types';
|
|
import { DisabledStatus } from '@/share/types';
|
|
|
-import { AreaSelect } from './AreaSelect';
|
|
|
|
|
-import { useQuery } from '@tanstack/react-query';
|
|
|
|
|
-import { areaClient } from '@/client/api';
|
|
|
|
|
|
|
|
|
|
interface AreaFormProps {
|
|
interface AreaFormProps {
|
|
|
area?: UpdateAreaInput & { id?: number };
|
|
area?: UpdateAreaInput & { id?: number };
|
|
|
onSubmit: (data: CreateAreaInput | UpdateAreaInput) => Promise<void>;
|
|
onSubmit: (data: CreateAreaInput | UpdateAreaInput) => Promise<void>;
|
|
|
onCancel: () => void;
|
|
onCancel: () => void;
|
|
|
isLoading?: boolean;
|
|
isLoading?: boolean;
|
|
|
- defaultLevel?: number;
|
|
|
|
|
|
|
+ /** 智能预填的层级 */
|
|
|
|
|
+ smartLevel?: number;
|
|
|
|
|
+ /** 智能预填的父级ID */
|
|
|
|
|
+ smartParentId?: number;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// 辅助函数:根据层级值获取显示名称
|
|
|
|
|
+const getLevelDisplayName = (level: number | undefined): string => {
|
|
|
|
|
+ switch (level) {
|
|
|
|
|
+ case AreaLevel.PROVINCE:
|
|
|
|
|
+ return '省/直辖市';
|
|
|
|
|
+ case AreaLevel.CITY:
|
|
|
|
|
+ return '市';
|
|
|
|
|
+ case AreaLevel.DISTRICT:
|
|
|
|
|
+ return '区/县';
|
|
|
|
|
+ default:
|
|
|
|
|
+ return '未知层级';
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
export const AreaForm: React.FC<AreaFormProps> = ({
|
|
export const AreaForm: React.FC<AreaFormProps> = ({
|
|
|
area,
|
|
area,
|
|
|
onSubmit,
|
|
onSubmit,
|
|
|
onCancel,
|
|
onCancel,
|
|
|
isLoading = false,
|
|
isLoading = false,
|
|
|
- defaultLevel
|
|
|
|
|
|
|
+ smartLevel,
|
|
|
|
|
+ smartParentId
|
|
|
}) => {
|
|
}) => {
|
|
|
const isEditing = !!area;
|
|
const isEditing = !!area;
|
|
|
- const [parentAreaInfo, setParentAreaInfo] = useState<{
|
|
|
|
|
- provinceId?: number;
|
|
|
|
|
- cityId?: number;
|
|
|
|
|
- }>({});
|
|
|
|
|
-
|
|
|
|
|
- // 查询父级区域的完整层级信息
|
|
|
|
|
- const { data: parentAreaData } = useQuery({
|
|
|
|
|
- queryKey: ['area', 'parent', area?.parentId],
|
|
|
|
|
- queryFn: async () => {
|
|
|
|
|
- if (!area?.parentId || area.parentId === 0) return null;
|
|
|
|
|
- const res = await areaClient[':id'].$get({
|
|
|
|
|
- param: { id: area.parentId }
|
|
|
|
|
- });
|
|
|
|
|
- if (res.status !== 200) throw new Error('获取父级区域信息失败');
|
|
|
|
|
- return await res.json();
|
|
|
|
|
- },
|
|
|
|
|
- enabled: isEditing && !!area?.parentId && area.parentId > 0,
|
|
|
|
|
- staleTime: 5 * 60 * 1000,
|
|
|
|
|
- gcTime: 10 * 60 * 1000,
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- // 根据父级区域信息设置层级关系
|
|
|
|
|
- useEffect(() => {
|
|
|
|
|
- if (parentAreaData) {
|
|
|
|
|
- const parentArea = parentAreaData;
|
|
|
|
|
- if (parentArea.level === AreaLevel.PROVINCE) {
|
|
|
|
|
- // 父级是省份,当前区域是城市
|
|
|
|
|
- setParentAreaInfo({
|
|
|
|
|
- provinceId: parentArea.id
|
|
|
|
|
- });
|
|
|
|
|
- } else if (parentArea.level === AreaLevel.CITY) {
|
|
|
|
|
- // 父级是城市,当前区域是区县,需要查询城市的父级省份
|
|
|
|
|
- const fetchProvinceId = async () => {
|
|
|
|
|
- const res = await areaClient[':id'].$get({
|
|
|
|
|
- param: { id: parentArea.parentId! }
|
|
|
|
|
- });
|
|
|
|
|
- if (res.status === 200) {
|
|
|
|
|
- const provinceResult = await res.json();
|
|
|
|
|
- setParentAreaInfo({
|
|
|
|
|
- provinceId: provinceResult.id,
|
|
|
|
|
- cityId: parentArea.id
|
|
|
|
|
- });
|
|
|
|
|
- }
|
|
|
|
|
- };
|
|
|
|
|
- fetchProvinceId();
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }, [parentAreaData]);
|
|
|
|
|
|
|
|
|
|
const form = useForm<CreateAreaInput | UpdateAreaInput>({
|
|
const form = useForm<CreateAreaInput | UpdateAreaInput>({
|
|
|
resolver: zodResolver(isEditing ? updateAreaSchema : createAreaSchema),
|
|
resolver: zodResolver(isEditing ? updateAreaSchema : createAreaSchema),
|
|
@@ -87,9 +54,9 @@ export const AreaForm: React.FC<AreaFormProps> = ({
|
|
|
code: area.code,
|
|
code: area.code,
|
|
|
isDisabled: area.isDisabled,
|
|
isDisabled: area.isDisabled,
|
|
|
} : {
|
|
} : {
|
|
|
- parentId: 0,
|
|
|
|
|
|
|
+ parentId: smartParentId || 0,
|
|
|
name: '',
|
|
name: '',
|
|
|
- level: defaultLevel || AreaLevel.PROVINCE,
|
|
|
|
|
|
|
+ level: smartLevel ?? AreaLevel.PROVINCE,
|
|
|
code: '',
|
|
code: '',
|
|
|
isDisabled: DisabledStatus.ENABLED,
|
|
isDisabled: DisabledStatus.ENABLED,
|
|
|
},
|
|
},
|
|
@@ -104,110 +71,49 @@ export const AreaForm: React.FC<AreaFormProps> = ({
|
|
|
<Form {...form}>
|
|
<Form {...form}>
|
|
|
<form onSubmit={form.handleSubmit(handleSubmit)} className="space-y-6">
|
|
<form onSubmit={form.handleSubmit(handleSubmit)} className="space-y-6">
|
|
|
<div className="grid grid-cols-1 gap-6">
|
|
<div className="grid grid-cols-1 gap-6">
|
|
|
- {/* 层级选择 */}
|
|
|
|
|
|
|
+ {/* 层级显示(只读) */}
|
|
|
<FormField
|
|
<FormField
|
|
|
control={form.control}
|
|
control={form.control}
|
|
|
name="level"
|
|
name="level"
|
|
|
render={({ field }) => (
|
|
render={({ field }) => (
|
|
|
<FormItem>
|
|
<FormItem>
|
|
|
<FormLabel>层级</FormLabel>
|
|
<FormLabel>层级</FormLabel>
|
|
|
- <Select onValueChange={(value) => field.onChange(Number(value))} defaultValue={field.value?.toString()}>
|
|
|
|
|
- <FormControl>
|
|
|
|
|
- <SelectTrigger>
|
|
|
|
|
- <SelectValue placeholder="选择层级" />
|
|
|
|
|
- </SelectTrigger>
|
|
|
|
|
- </FormControl>
|
|
|
|
|
- <SelectContent>
|
|
|
|
|
- <SelectItem value={AreaLevel.PROVINCE.toString()}>
|
|
|
|
|
- 省/直辖市
|
|
|
|
|
- </SelectItem>
|
|
|
|
|
- <SelectItem value={AreaLevel.CITY.toString()}>
|
|
|
|
|
- 市
|
|
|
|
|
- </SelectItem>
|
|
|
|
|
- <SelectItem value={AreaLevel.DISTRICT.toString()}>
|
|
|
|
|
- 区/县
|
|
|
|
|
- </SelectItem>
|
|
|
|
|
- </SelectContent>
|
|
|
|
|
- </Select>
|
|
|
|
|
|
|
+ <FormControl>
|
|
|
|
|
+ <Input
|
|
|
|
|
+ value={getLevelDisplayName(field.value)}
|
|
|
|
|
+ disabled
|
|
|
|
|
+ className="bg-muted"
|
|
|
|
|
+ />
|
|
|
|
|
+ </FormControl>
|
|
|
<FormDescription>
|
|
<FormDescription>
|
|
|
- 选择省市区层级
|
|
|
|
|
|
|
+ 根据操作上下文自动设置的层级
|
|
|
</FormDescription>
|
|
</FormDescription>
|
|
|
<FormMessage />
|
|
<FormMessage />
|
|
|
</FormItem>
|
|
</FormItem>
|
|
|
)}
|
|
)}
|
|
|
/>
|
|
/>
|
|
|
|
|
|
|
|
- {/* 父级区域选择 */}
|
|
|
|
|
|
|
+ {/* 父级区域显示(只读) */}
|
|
|
<FormField
|
|
<FormField
|
|
|
control={form.control}
|
|
control={form.control}
|
|
|
name="parentId"
|
|
name="parentId"
|
|
|
- render={({ field }) => {
|
|
|
|
|
- const level = form.watch('level');
|
|
|
|
|
- const showParentSelect = level !== AreaLevel.PROVINCE;
|
|
|
|
|
-
|
|
|
|
|
- // 根据当前层级和编辑状态设置AreaSelect的值
|
|
|
|
|
- const getAreaSelectValue = () => {
|
|
|
|
|
- if (!isEditing || !area) {
|
|
|
|
|
- return {};
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (level === AreaLevel.CITY) {
|
|
|
|
|
- // 城市级别:需要省份ID
|
|
|
|
|
- return { provinceId: parentAreaInfo.provinceId };
|
|
|
|
|
- } else if (level === AreaLevel.DISTRICT) {
|
|
|
|
|
- // 区县级别:需要省份ID和城市ID
|
|
|
|
|
- return {
|
|
|
|
|
- provinceId: parentAreaInfo.provinceId,
|
|
|
|
|
- cityId: parentAreaInfo.cityId
|
|
|
|
|
- };
|
|
|
|
|
- }
|
|
|
|
|
- return {};
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- return (
|
|
|
|
|
- <FormItem>
|
|
|
|
|
- <FormLabel>父级区域</FormLabel>
|
|
|
|
|
- {showParentSelect ? (
|
|
|
|
|
- <>
|
|
|
|
|
- <FormControl>
|
|
|
|
|
- <AreaSelect
|
|
|
|
|
- value={getAreaSelectValue()}
|
|
|
|
|
- onChange={(value) => {
|
|
|
|
|
- // 根据层级设置父级ID
|
|
|
|
|
- if (level === AreaLevel.CITY) {
|
|
|
|
|
- field.onChange(value.provinceId || 0);
|
|
|
|
|
- } else if (level === AreaLevel.DISTRICT) {
|
|
|
|
|
- field.onChange(value.cityId || 0);
|
|
|
|
|
- }
|
|
|
|
|
- }}
|
|
|
|
|
- required
|
|
|
|
|
- />
|
|
|
|
|
- </FormControl>
|
|
|
|
|
- <FormDescription>
|
|
|
|
|
- {level === AreaLevel.CITY
|
|
|
|
|
- ? '选择所属省份'
|
|
|
|
|
- : '选择所属城市'}
|
|
|
|
|
- </FormDescription>
|
|
|
|
|
- </>
|
|
|
|
|
- ) : (
|
|
|
|
|
- <>
|
|
|
|
|
- <FormControl>
|
|
|
|
|
- <Input
|
|
|
|
|
- type="number"
|
|
|
|
|
- value={0}
|
|
|
|
|
- disabled
|
|
|
|
|
- className="bg-muted"
|
|
|
|
|
- />
|
|
|
|
|
- </FormControl>
|
|
|
|
|
- <FormDescription>
|
|
|
|
|
- 省/直辖市没有父级区域
|
|
|
|
|
- </FormDescription>
|
|
|
|
|
- </>
|
|
|
|
|
- )}
|
|
|
|
|
- <FormMessage />
|
|
|
|
|
- </FormItem>
|
|
|
|
|
- );
|
|
|
|
|
- }}
|
|
|
|
|
|
|
+ render={({ field }) => (
|
|
|
|
|
+ <FormItem>
|
|
|
|
|
+ <FormLabel>父级区域</FormLabel>
|
|
|
|
|
+ <FormControl>
|
|
|
|
|
+ <Input
|
|
|
|
|
+ type="number"
|
|
|
|
|
+ value={field.value || 0}
|
|
|
|
|
+ disabled
|
|
|
|
|
+ className="bg-muted"
|
|
|
|
|
+ />
|
|
|
|
|
+ </FormControl>
|
|
|
|
|
+ <FormDescription>
|
|
|
|
|
+ 根据操作上下文自动设置的父级区域ID
|
|
|
|
|
+ </FormDescription>
|
|
|
|
|
+ <FormMessage />
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ )}
|
|
|
/>
|
|
/>
|
|
|
|
|
|
|
|
{/* 区域名称 */}
|
|
{/* 区域名称 */}
|