Sfoglia il codice sorgente

feat (1.66.2 -> 1.66.3): 添加用户账户管理功能并优化界面

- 在 config.ts 中添加获取和更新用户信息的API接口
- 在 UserAccount.tsx 中实现用户账户配置组件,支持昵称修改
- 在 user_info/index.ts 中添加nickname字段和更新action
- 在 Login.tsx 中保存nickname到用户状态
- 在 BaseInfo.tsx 中集成nickname作为检查医师默认值,并将选择器改为AutoComplete
- 在 SystemHome/index.tsx 中导出UserAccount组件
- 更新国际化文本,将"患者编号"改为"患者编号/病案号"
- 优化 BodyPositionDetail.tsx 代码格式

改动文件:
- scripts/output/i18n/zh.js
- src/API/config.ts
- src/API/security/userActions.ts
- src/assets/i18n/messages/en.js
- src/assets/i18n/messages/zh.js
- src/pages/exam/components/BodyPositionDetail.tsx
- src/pages/patient/DiagnosticReport/components/BaseInfo.tsx
- src/pages/security/Login.tsx
- src/pages/system/SettingsModal/sections/SystemHome/index.tsx
- src/states/user_info/index.ts
- src/pages/system/SettingsModal/sections/SystemHome/UserAccount.tsx
szy 2 giorni fa
parent
commit
372463beaf

+ 27 - 0
CHANGELOG.md

@@ -2,6 +2,33 @@
 
 本项目的所有重要变更都将记录在此文件中.
 
+## [1.66.3] - 2026-1-16 14:07
+
+feat (1.66.2 -> 1.66.3): 添加用户账户管理功能并优化界面
+
+- 在 config.ts 中添加获取和更新用户信息的API接口
+- 在 UserAccount.tsx 中实现用户账户配置组件,支持昵称修改
+- 在 user_info/index.ts 中添加nickname字段和更新action
+- 在 Login.tsx 中保存nickname到用户状态
+- 在 BaseInfo.tsx 中集成nickname作为检查医师默认值,并将选择器改为AutoComplete
+- 在 SystemHome/index.tsx 中导出UserAccount组件
+- 更新国际化文本,将"患者编号"改为"患者编号/病案号"
+- 优化 BodyPositionDetail.tsx 代码格式
+
+改动文件:
+
+- scripts/output/i18n/zh.js
+- src/API/config.ts
+- src/API/security/userActions.ts
+- src/assets/i18n/messages/en.js
+- src/assets/i18n/messages/zh.js
+- src/pages/exam/components/BodyPositionDetail.tsx
+- src/pages/patient/DiagnosticReport/components/BaseInfo.tsx
+- src/pages/security/Login.tsx
+- src/pages/system/SettingsModal/sections/SystemHome/index.tsx
+- src/states/user_info/index.ts
+- src/pages/system/SettingsModal/sections/SystemHome/UserAccount.tsx
+
 ## [1.66.2] - 2026-1-15 18:06
 
 feat (1.66.1 -> 1.66.2): 实现诊断报告医师管理和身份证识别功能

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "zsis",
-  "version": "1.66.2",
+  "version": "1.66.3",
   "private": true,
   "description": "医学成像系统",
   "main": "main.js",

+ 2 - 2
scripts/output/i18n/zh.js

@@ -44,8 +44,8 @@
   "searchPanel.startDate": "开始日期",
   "searchPanel.endDate": "结束日期",
   "searchPanel.search": "查询",
-  "register.patientId": "患者编号",
-  "register.patientId.placeholder": "请输入患者编号",
+  "register.patientId": "患者编号/病案号",
+  "register.patientId.placeholder": "请输入患者编号/病案号",
   "animal.register.patientId.placeholder": "请输入宠物编号",
   "register.patientName": "患者姓名",
   "register.patientName.placeholder": "请输入患者姓名",

+ 30 - 0
src/API/config.ts

@@ -1,5 +1,6 @@
 import store from '../states/store';
 import { getPlatformInfo } from '../utils/platform';
+import axiosInstance from './interceptor';
 
 /**
  * 安全的 store 访问器
@@ -129,4 +130,33 @@ export function getIpPort(): string {
   return '';
 }
 
+export interface UserInfoResponse {
+  code: string;
+  description: string;
+  solution: string;
+  data: {
+    uid: number;
+    name: string;
+    nickname: string;
+    avatar: string;
+    perms: string[];
+  };
+}
+
+// 获取当前用户信息
+export const getUserInfo = async (): Promise<UserInfoResponse> => {
+  const response = await axiosInstance.get('/auth/settings/userinfo');
+  return response.data;
+};
 
+// 修改当前用户信息
+export const updateUserInfo = async ({
+  nickname,
+}: {
+  nickname: string;
+}): Promise<UserInfoResponse> => {
+  const response = await axiosInstance.post('/auth/settings/userinfo', {
+    nickname,
+  });
+  return response.data;
+};

+ 1 - 0
src/API/security/userActions.ts

@@ -11,6 +11,7 @@ export interface LoginApiResponse {
     uid: number;
     name: string;
     avatar: string;
+    nickname: string;
   };
 }
 // 创建带默认请求头的axios实例

+ 2 - 2
src/assets/i18n/messages/en.js

@@ -44,9 +44,9 @@ export default {
   "searchPanel.startDate": "Start Date",
   "searchPanel.endDate": "End Date",
   "searchPanel.search": "Search",
-  "register.patientId": "Patient ID",
+  "register.patientId": "Patient ID/Medical Record Number",
   "animal.register.patientId": "pet ID",
-  "register.patientId.placeholder": "Enter patient ID",
+  "register.patientId.placeholder": "Enter patient ID/Medical Record Number",
   "animal.register.patientId.placeholder": "Enter pet ID",
   "register.patientName": "Patient Name",
   "animal.register.patientName": "pet nickname",

+ 2 - 2
src/assets/i18n/messages/zh.js

@@ -44,9 +44,9 @@ export default {
   "searchPanel.startDate": "开始日期",
   "searchPanel.endDate": "结束日期",
   "searchPanel.search": "查询",
-  "register.patientId": "病案号",
+  "register.patientId": "患者编号/病案号",
   "animal.register.patientId": "宠物编号",
-  "register.patientId.placeholder": "请输入患者编号",
+  "register.patientId.placeholder": "请输入患者编号/病案号",
   "animal.register.patientId.placeholder": "请输入宠物编号",
   "register.patientName": "患者姓名",
   "animal.register.patientName": "宠物昵称",

+ 4 - 3
src/pages/exam/components/BodyPositionDetail.tsx

@@ -15,9 +15,10 @@ const BodyPositionDetail: React.FC = () => {
   const wrapRef = useRef<HTMLDivElement>(null);
 
   // 🆕 根据曝光状态选择图像源
-  const imageUrl = bodyPositionDetail.expose_status === 'Exposed'
-    ? getExposedImageUrl(bodyPositionDetail.sop_instance_uid)  // 已曝光:缩略图
-    : getViewIconUrl(bodyPositionDetail.body_position_image);  // 未曝光:示意图
+  const imageUrl =
+    bodyPositionDetail.expose_status === 'Exposed'
+      ? getExposedImageUrl(bodyPositionDetail.sop_instance_uid) // 已曝光:缩略图
+      : getViewIconUrl(bodyPositionDetail.body_position_image); // 未曝光:示意图
 
   return (
     <Flex vertical className="h-full">

+ 20 - 12
src/pages/patient/DiagnosticReport/components/BaseInfo.tsx

@@ -1,5 +1,5 @@
 import React, { useEffect, useState } from 'react';
-import { Row, Col, Input, Select, Form } from 'antd';
+import { Row, Col, Input, Select, Form, AutoComplete } from 'antd';
 import { useDiagnosticData } from '@/hooks/useDiagnosticData';
 import { PlusOutlined } from '@ant-design/icons';
 import { DepartmentModal } from './DepartmentModal';
@@ -46,6 +46,7 @@ export const BaseInfo: React.FC = () => {
   const departments = useAppSelector(
     (state: RootState) => state.department.departments
   );
+  const { nickname } = useAppSelector((state: RootState) => state.userInfo);
   const examinationPhysicians = useAppSelector(
     (state: RootState) => state.examinationPhysician.names
   );
@@ -86,6 +87,7 @@ export const BaseInfo: React.FC = () => {
     getExaminationPhysicianData();
     getReviewPhysicianData();
     getSexOption();
+    setRadiologist(nickname);
   }, []);
 
   return (
@@ -118,7 +120,7 @@ export const BaseInfo: React.FC = () => {
           </Form.Item>
         </Col> */}
           <Col span={6}>
-            <Form.Item label="病案号">
+            <Form.Item label="患者编号/病案号">
               <Input
                 disabled
                 value={medical_record_number}
@@ -221,13 +223,16 @@ export const BaseInfo: React.FC = () => {
 
           <Col span={6}>
             <Form.Item label="检查医师">
-              <Select
+              <AutoComplete
+                allowClear
                 style={{ width: '90%' }}
+                options={examinationPhysicians.map((item) => ({
+                  label: item.name,
+                  value: item.name,
+                }))}
                 value={radiologist}
-                fieldNames={{ label: 'name', value: 'name' }}
-                onChange={(v) => setRadiologist(v)}
-                options={examinationPhysicians}
-              />
+                onChange={setRadiologist}
+              ></AutoComplete>
             </Form.Item>
             <PlusOutlined
               style={{
@@ -242,13 +247,16 @@ export const BaseInfo: React.FC = () => {
           </Col>
           <Col span={6}>
             <Form.Item label="审核医师">
-              <Select
+              <AutoComplete
+                allowClear
                 style={{ width: '90%' }}
+                options={reviewPhysicians.map((item) => ({
+                  label: item.name,
+                  value: item.name,
+                }))}
                 value={reviewPhysician}
-                fieldNames={{ label: 'name', value: 'name' }}
-                onChange={(v) => setReviewPhysician(v)}
-                options={reviewPhysicians}
-              />
+                onChange={setReviewPhysician}
+              ></AutoComplete>
             </Form.Item>
             <PlusOutlined
               style={{

+ 1 - 0
src/pages/security/Login.tsx

@@ -63,6 +63,7 @@ const Login: React.FC = () => {
           uid: result.data.uid,
           name: result.data.name,
           avatar: result.data.avatar,
+          nickname: result.data.nickname,
         };
         // 5. 分发 redux action
         dispatch(setUserInfo(userInfo));

+ 74 - 0
src/pages/system/SettingsModal/sections/SystemHome/UserAccount.tsx

@@ -0,0 +1,74 @@
+import React, { useEffect, useState } from 'react';
+import { Card, Button, Space, Form, Input, message } from 'antd';
+import { SPACING } from '../../constants';
+import { getUserInfo, updateUserInfo } from '@/API/config';
+import { updateUserNickName } from '@/states/user_info';
+import { useAppDispatch } from '@/states/store';
+
+const UserAccount: React.FC = () => {
+  const [form] = Form.useForm();
+  const [submitting, setSubmitting] = useState(false);
+  const dispatch = useAppDispatch();
+  const onFinish = async (values: { nickname: string }) => {
+    // 如果正在提交,则直接返回,防止重复提交
+    if (submitting) {
+      return;
+    }
+
+    setSubmitting(true);
+    try {
+      const res = await updateUserInfo(values);
+      if (res?.code === '0x000000') {
+        message.success('修改成功');
+        dispatch(updateUserNickName(values.nickname));
+        // form.resetFields();
+      }
+    } catch (error) {
+      console.error('更新用户信息失败:', error);
+      message.error('修改失败,请重试');
+    } finally {
+      setSubmitting(false);
+    }
+  };
+
+  const getUserAccountConfig = async () => {
+    const res = await getUserInfo();
+    if (res?.code !== '0x000000') {
+      return message.error('获取用户信息失败');
+    }
+
+    const formValues = {
+      nickname: res?.data?.nickname || '',
+    };
+    form.setFieldsValue(formValues);
+  };
+
+  useEffect(() => {
+    getUserAccountConfig();
+  }, [form]);
+
+  return (
+    <div style={{ padding: SPACING.LG }}>
+      <Card title="用户账户配置" variant="borderless">
+        <Form layout="vertical" form={form} onFinish={onFinish}>
+          <Form.Item
+            name="nickname"
+            label="系统用户名"
+            rules={[{ required: true, message: '请输入系统用户名' }]}
+          >
+            <Input allowClear placeholder="请输入系统用户名" />
+          </Form.Item>
+          <Form.Item>
+            <Space>
+              <Button type="primary" htmlType="submit" loading={submitting}>
+                {submitting ? '保存中...' : '保存'}
+              </Button>
+            </Space>
+          </Form.Item>
+        </Form>
+      </Card>
+    </div>
+  );
+};
+
+export default UserAccount;

+ 2 - 4
src/pages/system/SettingsModal/sections/SystemHome/index.tsx

@@ -10,10 +10,8 @@ export { default as SiteInfo } from './SiteInfo';
 // 关于系统 - 已实现
 export { default as AboutSystem } from './AboutSystem';
 
-// 用户账户
-export const UserAccount: React.FC = () => (
-  <PlaceholderSection title="用户账户" />
-);
+// 用户账户 - 已实现
+export { default as UserAccount } from './UserAccount';
 
 // 磁盘空间管理
 export const DiskSpaceManagement: React.FC = () => (

+ 12 - 2
src/states/user_info/index.ts

@@ -6,6 +6,7 @@ interface UserInfoState {
   uid: number;
   name: string;
   avatar: string;
+  nickname: string;
 }
 
 const initialState: UserInfoState = {
@@ -14,6 +15,7 @@ const initialState: UserInfoState = {
   uid: 0,
   name: '',
   avatar: '',
+  nickname: '',
 };
 
 const userInfoSlice = createSlice({
@@ -29,15 +31,23 @@ const userInfoSlice = createSlice({
       state.uid = 0;
       state.name = '';
       state.avatar = '';
+      state.nickname = '';
     },
     updateUserName(state, action: PayloadAction<string>) {
       state.name = action.payload;
     },
+    updateUserNickName(state, action: PayloadAction<string>) {
+      state.nickname = action.payload;
+    },
   },
 });
 
-export const { setUserInfo, clearUserInfo, updateUserName } =
-  userInfoSlice.actions;
+export const {
+  setUserInfo,
+  clearUserInfo,
+  updateUserName,
+  updateUserNickName,
+} = userInfoSlice.actions;
 
 export default userInfoSlice.reducer;