LanguageSettingModal.tsx 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. import React, { useEffect } from 'react';
  2. import { Modal, Select, Button, Space, Typography, message, Spin } from 'antd';
  3. import { GlobalOutlined } from '@ant-design/icons';
  4. import { useAppDispatch, useAppSelector } from '../states/store';
  5. import {
  6. loadAvailableLanguages,
  7. updateSystemLanguage,
  8. setSelectedLanguage,
  9. } from '../states/i18nSlice';
  10. const { Text } = Typography;
  11. interface LanguageSettingModalProps {
  12. visible: boolean;
  13. onClose: () => void;
  14. }
  15. const LanguageSettingModal: React.FC<LanguageSettingModalProps> = ({
  16. visible,
  17. onClose,
  18. }) => {
  19. const dispatch = useAppDispatch();
  20. // 从 Redux 获取状态
  21. const {
  22. availableLanguages,
  23. selectedLanguage,
  24. currentSystemLocale,
  25. languagesLoading,
  26. savingLanguage,
  27. languageError,
  28. } = useAppSelector((state) => state.i18n);
  29. // 加载语言列表
  30. useEffect(() => {
  31. if (visible && availableLanguages.length === 0) {
  32. dispatch(loadAvailableLanguages());
  33. }
  34. }, [visible, dispatch, availableLanguages.length]);
  35. // 显示错误消息
  36. useEffect(() => {
  37. if (languageError) {
  38. message.error({
  39. content: languageError,
  40. duration: 3,
  41. });
  42. }
  43. }, [languageError]);
  44. const handleSave = async (): Promise<void> => {
  45. if (!selectedLanguage) {
  46. message.warning('请选择一个语言');
  47. return;
  48. }
  49. try {
  50. await dispatch(updateSystemLanguage(selectedLanguage)).unwrap();
  51. message.success({
  52. content: '语言设置已保存',
  53. duration: 2,
  54. });
  55. message.info({
  56. content: '请重启电脑以应用新的语言设置',
  57. duration: 3,
  58. });
  59. onClose();
  60. } catch (error) {
  61. // 错误已经在 Redux slice 中处理并通过 languageError 显示
  62. console.error('修改语言失败:', error);
  63. }
  64. };
  65. const handleCancel = (): void => {
  66. onClose();
  67. };
  68. const handleLanguageChange = (value: string): void => {
  69. dispatch(setSelectedLanguage(value));
  70. };
  71. return (
  72. <Modal
  73. title={
  74. <Space>
  75. <GlobalOutlined />
  76. <span>语言设置</span>
  77. </Space>
  78. }
  79. open={visible}
  80. onCancel={handleCancel}
  81. footer={null}
  82. centered
  83. width={500}
  84. >
  85. <div style={{ padding: '20px 0' }}>
  86. {languagesLoading ? (
  87. <div style={{ textAlign: 'center', padding: '40px 0' }}>
  88. <Spin size="large" tip="加载中..." />
  89. </div>
  90. ) : (
  91. <>
  92. <Space direction="vertical" size="large" style={{ width: '100%' }}>
  93. {currentSystemLocale && (
  94. <div>
  95. <Text style={{ fontSize: 14 }}>
  96. 当前语言:<Text strong>{currentSystemLocale}</Text>
  97. </Text>
  98. </div>
  99. )}
  100. <div>
  101. <Text
  102. strong
  103. style={{ display: 'block', marginBottom: 8, fontSize: 14 }}
  104. >
  105. 选择语言:
  106. </Text>
  107. <Select
  108. style={{ width: '100%' }}
  109. size="large"
  110. value={selectedLanguage}
  111. onChange={handleLanguageChange}
  112. disabled={savingLanguage}
  113. placeholder="请选择语言"
  114. >
  115. {availableLanguages.map((lang) => (
  116. <Select.Option key={lang.language} value={lang.language}>
  117. {lang.display}
  118. </Select.Option>
  119. ))}
  120. </Select>
  121. </div>
  122. <Space
  123. style={{
  124. width: '100%',
  125. justifyContent: 'flex-end',
  126. marginTop: 16,
  127. }}
  128. >
  129. <Button
  130. size="large"
  131. onClick={handleCancel}
  132. disabled={savingLanguage}
  133. >
  134. 取消
  135. </Button>
  136. <Button
  137. type="primary"
  138. size="large"
  139. onClick={handleSave}
  140. loading={savingLanguage}
  141. disabled={!selectedLanguage}
  142. >
  143. 保存
  144. </Button>
  145. </Space>
  146. </Space>
  147. </>
  148. )}
  149. </div>
  150. </Modal>
  151. );
  152. };
  153. export default LanguageSettingModal;