PostForm.tsx 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. import React, { useState, useImperativeHandle, forwardRef } from 'react';
  2. import { postApi, PostVO, PostStatus } from '../api/Post';
  3. import { toast } from 'sonner';
  4. import { Modal, Form, Input, Button, Select, Spin } from 'antd';
  5. import { DICT_TYPE, getIntDictOptions } from '../utils/dict';
  6. import { useTranslation } from 'react-i18next';
  7. interface PostFormProps {
  8. onSuccess?: () => void;
  9. }
  10. export interface PostFormRef {
  11. open: (type: string, id?: number) => void;
  12. }
  13. const PostForm = forwardRef<PostFormRef, PostFormProps>(({ onSuccess }, ref) => {
  14. const { t } = useTranslation();
  15. const [dialogVisible, setDialogVisible] = useState(false);
  16. const [dialogTitle, setDialogTitle] = useState('');
  17. const [formType, setFormType] = useState<'create' | 'update'>('create');
  18. const [formLoading, setFormLoading] = useState(false);
  19. const [currentId, setCurrentId] = useState<number | undefined>();
  20. const [form] = Form.useForm();
  21. // 获取最新的字典选项(每次调用都从缓存中获取最新数据)
  22. const getStatusOptions = () => getIntDictOptions(DICT_TYPE.COMMON_STATUS);
  23. // 暴露方法给父组件
  24. useImperativeHandle(ref, () => ({
  25. open: (type: string, id?: number) => {
  26. setDialogTitle(type === 'create' ? t('common.addPost') : t('common.editPost'));
  27. setFormType(type as 'create' | 'update');
  28. setCurrentId(id);
  29. form.resetFields();
  30. if (id) {
  31. loadPostData(id);
  32. } else {
  33. // 从字典中获取默认启用状态值
  34. const defaultStatus = getStatusOptions().find(opt => opt.label === '启用' || opt.colorType === 'success')?.value ?? getStatusOptions()[0]?.value ?? 1;
  35. form.setFieldsValue({
  36. name: '',
  37. code: '',
  38. sort: 0,
  39. status: defaultStatus,
  40. remark: '',
  41. });
  42. setFormLoading(false);
  43. }
  44. setDialogVisible(true);
  45. },
  46. }));
  47. // 加载岗位数据
  48. const loadPostData = async (id: number) => {
  49. setFormLoading(true);
  50. try {
  51. const res = await postApi.getPost(id);
  52. const data = (res as any)?.data || res;
  53. form.setFieldsValue({
  54. name: data.name,
  55. code: data.code,
  56. sort: data.sort ?? 0,
  57. status: typeof data.status === 'string' ? Number(data.status) : (data.status ?? (getStatusOptions().find(opt => opt.label === '启用' || opt.colorType === 'success')?.value ?? getStatusOptions()[0]?.value ?? 1)),
  58. remark: data.remark || '',
  59. });
  60. } catch (error: any) {
  61. toast.error(error.message || t('common.error'));
  62. } finally {
  63. setFormLoading(false);
  64. }
  65. };
  66. // 提交表单
  67. const submitForm = async () => {
  68. try {
  69. const values = await form.validateFields();
  70. setFormLoading(true);
  71. const submitData: PostVO = {
  72. ...values,
  73. sort: Number(values.sort) || 0,
  74. status: Number(values.status),
  75. };
  76. if (formType === 'create') {
  77. await postApi.createPost(submitData);
  78. toast.success(t('common.addSuccess'));
  79. } else {
  80. if (currentId) {
  81. submitData.id = currentId;
  82. }
  83. await postApi.updatePost(submitData);
  84. toast.success(t('common.updateSuccess'));
  85. }
  86. setDialogVisible(false);
  87. onSuccess?.();
  88. } catch (error: any) {
  89. if (error.errorFields) {
  90. // 表单验证错误
  91. return;
  92. }
  93. toast.error(error.message || t('common.saveFailed'));
  94. } finally {
  95. setFormLoading(false);
  96. }
  97. };
  98. return (
  99. <Modal
  100. title={dialogTitle}
  101. open={dialogVisible}
  102. onCancel={() => setDialogVisible(false)}
  103. onOk={submitForm}
  104. confirmLoading={formLoading}
  105. okText={t('common.confirm')}
  106. cancelText={t('common.cancel')}
  107. width={600}
  108. destroyOnClose
  109. >
  110. <Spin spinning={formLoading && formType === 'update'}>
  111. <Form
  112. form={form}
  113. layout="horizontal"
  114. labelCol={{ span: 4 }}
  115. wrapperCol={{ span: 20 }}
  116. initialValues={{
  117. sort: 0,
  118. status: getStatusOptions().find(opt => opt.label === '启用' || opt.colorType === 'success')?.value ?? getStatusOptions()[0]?.value ?? 1,
  119. remark: '',
  120. }}
  121. >
  122. <Form.Item
  123. label={t('form.postName')}
  124. name="name"
  125. rules={[
  126. { required: true, message: t('form.postNamePlaceholder') },
  127. { max: 50, message: t('common.error') },
  128. ]}
  129. >
  130. <Input placeholder={t('form.postNamePlaceholder')} />
  131. </Form.Item>
  132. <Form.Item
  133. label={t('form.postCode')}
  134. name="code"
  135. rules={[
  136. { required: true, message: t('form.postCodePlaceholder') },
  137. { max: 50, message: t('common.error') },
  138. ]}
  139. >
  140. <Input placeholder={t('form.postCodePlaceholder')} />
  141. </Form.Item>
  142. <Form.Item
  143. label={t('table.postSort')}
  144. name="sort"
  145. rules={[{ required: true, message: t('form.sortPlaceholder') }]}
  146. >
  147. <Input type="number" placeholder={t('form.sortPlaceholder')} min={0} />
  148. </Form.Item>
  149. <Form.Item
  150. label={t('form.status')}
  151. name="status"
  152. rules={[{ required: true, message: t('common.pleaseSelect') }]}
  153. >
  154. <Select placeholder={t('common.pleaseSelect')}>
  155. {getStatusOptions().map((option) => (
  156. <Select.Option key={option.value} value={option.value}>
  157. {option.label}
  158. </Select.Option>
  159. ))}
  160. </Select>
  161. </Form.Item>
  162. <Form.Item
  163. label={t('form.remark')}
  164. name="remark"
  165. >
  166. <Input.TextArea placeholder={t('form.remarkPlaceholder')} rows={4} />
  167. </Form.Item>
  168. </Form>
  169. </Spin>
  170. </Modal>
  171. );
  172. });
  173. PostForm.displayName = 'PostForm';
  174. export default PostForm;