| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306 |
- import React, { useState, useEffect } from 'react';
- import { useForm } from 'react-hook-form';
- import { zodResolver } from '@hookform/resolvers/zod';
- import { Button } from '@/client/components/ui/button';
- import { Input } from '@/client/components/ui/input';
- import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/client/components/ui/select';
- import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from '@/client/components/ui/form';
- import { createAreaSchema, updateAreaSchema } 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 { DisabledStatus } from '@/share/types';
- import { AreaSelect } from './AreaSelect';
- import { useQuery } from '@tanstack/react-query';
- import { areaClient } from '@/client/api';
- interface AreaFormProps {
- area?: UpdateAreaInput & { id?: number };
- onSubmit: (data: CreateAreaInput | UpdateAreaInput) => Promise<void>;
- onCancel: () => void;
- isLoading?: boolean;
- defaultLevel?: number;
- }
- export const AreaForm: React.FC<AreaFormProps> = ({
- area,
- onSubmit,
- onCancel,
- isLoading = false,
- defaultLevel
- }) => {
- 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>({
- resolver: zodResolver(isEditing ? updateAreaSchema : createAreaSchema),
- defaultValues: area ? {
- parentId: area.parentId,
- name: area.name,
- level: area.level,
- code: area.code,
- isDisabled: area.isDisabled,
- } : {
- parentId: 0,
- name: '',
- level: defaultLevel || AreaLevel.PROVINCE,
- code: '',
- isDisabled: DisabledStatus.ENABLED,
- },
- });
- const handleSubmit = async (data: CreateAreaInput | UpdateAreaInput) => {
- await onSubmit(data);
- };
- return (
- <Form {...form}>
- <form onSubmit={form.handleSubmit(handleSubmit)} className="space-y-6">
- <div className="grid grid-cols-1 gap-6">
- {/* 层级选择 */}
- <FormField
- control={form.control}
- name="level"
- render={({ field }) => (
- <FormItem>
- <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>
- <FormDescription>
- 选择省市区层级
- </FormDescription>
- <FormMessage />
- </FormItem>
- )}
- />
- {/* 父级区域选择 */}
- <FormField
- control={form.control}
- 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>
- );
- }}
- />
- {/* 区域名称 */}
- <FormField
- control={form.control}
- name="name"
- render={({ field }) => (
- <FormItem>
- <FormLabel>区域名称</FormLabel>
- <FormControl>
- <Input
- placeholder="输入区域名称"
- {...field}
- />
- </FormControl>
- <FormDescription>
- 输入省市区名称,如:北京市、上海市、朝阳区等
- </FormDescription>
- <FormMessage />
- </FormItem>
- )}
- />
- {/* 行政区划代码 */}
- <FormField
- control={form.control}
- name="code"
- render={({ field }) => (
- <FormItem>
- <FormLabel>行政区划代码</FormLabel>
- <FormControl>
- <Input
- placeholder="输入行政区划代码"
- {...field}
- />
- </FormControl>
- <FormDescription>
- 输入标准的行政区划代码
- </FormDescription>
- <FormMessage />
- </FormItem>
- )}
- />
- {/* 状态选择 */}
- <FormField
- control={form.control}
- name="isDisabled"
- render={({ field }) => (
- <FormItem>
- <FormLabel>状态</FormLabel>
- <Select onValueChange={(value) => field.onChange(Number(value))} defaultValue={field.value?.toString()}>
- <FormControl>
- <SelectTrigger>
- <SelectValue placeholder="选择状态" />
- </SelectTrigger>
- </FormControl>
- <SelectContent>
- <SelectItem value={DisabledStatus.ENABLED.toString()}>
- 启用
- </SelectItem>
- <SelectItem value={DisabledStatus.DISABLED.toString()}>
- 禁用
- </SelectItem>
- </SelectContent>
- </Select>
- <FormDescription>
- 选择省市区状态
- </FormDescription>
- <FormMessage />
- </FormItem>
- )}
- />
- </div>
- {/* 表单操作按钮 */}
- <div className="flex justify-end gap-4">
- <Button
- type="button"
- variant="outline"
- onClick={onCancel}
- disabled={isLoading}
- >
- 取消
- </Button>
- <Button
- type="submit"
- disabled={isLoading}
- >
- {isLoading ? '提交中...' : isEditing ? '更新' : '创建'}
- </Button>
- </div>
- </form>
- </Form>
- );
- };
|