From ff7c625d9fc206ac54d5afad0e0699e5616dc52d Mon Sep 17 00:00:00 2001
From: hejianhao <15708179461@qq.com>
Date: 星期二, 18 八月 2026 17:19:03 +0800
Subject: [PATCH] feat0806: 修复编辑诊所页面样式

---
 tcm-training-admin/src/pages/member/clinic/manage.tsx                                                        |   34 +++++++++++++++++++++++++++-------
 tcm-training-admin-backend/src/main/java/com/tcm/training/admin/utils/VzanHttpUtil.java                      |    3 +++
 tcm-training-admin-backend/src/main/java/com/tcm/training/admin/agent/service/ProvincialAgentAppService.java |   22 ++++++++++++++++++++--
 3 files changed, 50 insertions(+), 9 deletions(-)

diff --git a/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/agent/service/ProvincialAgentAppService.java b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/agent/service/ProvincialAgentAppService.java
index 1cb442d..f479151 100644
--- a/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/agent/service/ProvincialAgentAppService.java
+++ b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/agent/service/ProvincialAgentAppService.java
@@ -1,6 +1,8 @@
 package com.tcm.training.admin.agent.service;
 
+import cn.hutool.json.JSONUtil;
 import cn.idev.excel.FastExcel;
+import com.fasterxml.jackson.databind.JsonNode;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
@@ -17,6 +19,8 @@
 import com.tcm.training.admin.common.PageResponse;
 import com.tcm.training.admin.live.entity.Clinic;
 import com.tcm.training.admin.live.mapper.ClinicMapper;
+import com.tcm.training.admin.utils.VzanHttpUtil;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -27,19 +31,22 @@
 import java.util.Collections;
 import java.util.List;
 
+@Slf4j
 @Service
 public class ProvincialAgentAppService {
     private final ProvincialAgentMapper agentMapper;
     private final AgentRegionMapper regionMapper;
     private final SysRegionMapper sysRegionMapper;
     private final ClinicMapper clinicMapper;
+    private final VzanHttpUtil vzanHttpUtil;
 
     public ProvincialAgentAppService(ProvincialAgentMapper agentMapper, AgentRegionMapper regionMapper,
-                                     SysRegionMapper sysRegionMapper, ClinicMapper clinicMapper) {
+                                     SysRegionMapper sysRegionMapper, ClinicMapper clinicMapper, VzanHttpUtil vzanHttpUtil) {
         this.agentMapper = agentMapper;
         this.regionMapper = regionMapper;
         this.sysRegionMapper = sysRegionMapper;
         this.clinicMapper = clinicMapper;
+        this.vzanHttpUtil = vzanHttpUtil;
     }
 
     public PageResponse<ProvincialAgentListItemVO> page(int page, int pageSize, String regionCode,
@@ -95,6 +102,17 @@
         saveRegions(agent, request);
         if (firstAgent) {
             clinicMapper.update(null, new LambdaUpdateWrapper<Clinic>().set(Clinic::getAgentId, agent.getId()));
+        }
+        log.info("调用微赞创建代理,入参 agentMobile={}, agentName={}, levelCode={}, parentMobile={}, storeName={}",
+                request.getContactInfo(), request.getAgentName(), 2, null, request.getAgentName());
+        try {
+            JsonNode vzanResponse = vzanHttpUtil.createAgent(
+                    request.getContactInfo(), request.getAgentName(), 2, null, request.getAgentName());
+            log.info("微赞创建代理成功,出参={}", JSONUtil.toJsonStr(vzanResponse));
+        } catch (RuntimeException ex) {
+            log.error("微赞创建代理失败,本地事务将回滚,入参 agentMobile={}, agentName={}, levelCode={}, parentMobile={}, storeName={}",
+                    request.getContactInfo(), request.getAgentName(), 1, null, request.getAgentName(), ex);
+            throw ex;
         }
         return detail(agent.getId());
     }
@@ -234,4 +252,4 @@
             this.name = name;
         }
     }
-}
\ No newline at end of file
+}
diff --git a/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/utils/VzanHttpUtil.java b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/utils/VzanHttpUtil.java
index c8657ec..f1f87ba 100644
--- a/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/utils/VzanHttpUtil.java
+++ b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/utils/VzanHttpUtil.java
@@ -1,5 +1,6 @@
 package com.tcm.training.admin.utils;
 
+import cn.hutool.json.JSONUtil;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import lombok.extern.slf4j.Slf4j;
@@ -208,8 +209,10 @@
         headers.setContentType(MediaType.APPLICATION_JSON);
         headers.setBearerAuth(getToken());
         try {
+            log.info("微赞最终请求,url={}, bearerToken={}, body={}", url, headers.getFirst(HttpHeaders.AUTHORIZATION), JSONUtil.toJsonStr(body));
             ResponseEntity<String> response = restOperations.postForEntity(
                     url, new HttpEntity<Map<String, Object>>(body, headers), String.class);
+            log.info("微赞原始响应,url={}, responseBody={}", url, JSONUtil.toJsonStr(response.getBody()));
             return parseBusinessResponse(response.getBody(), operationName);
         } catch (RestClientException ex) {
             throw new IllegalStateException("调用微赞" + operationName + "接口失败", ex);
diff --git a/tcm-training-admin/src/pages/member/clinic/manage.tsx b/tcm-training-admin/src/pages/member/clinic/manage.tsx
index fbc5da8..c6482aa 100644
--- a/tcm-training-admin/src/pages/member/clinic/manage.tsx
+++ b/tcm-training-admin/src/pages/member/clinic/manage.tsx
@@ -1,6 +1,6 @@
 import { history, useAccess, useParams } from '@umijs/max';
 import { PlusOutlined } from '@ant-design/icons';
-import { Button, Form, Input, Select, Upload, message } from 'antd';
+import { Button, Form, Image, Input, Select, Upload, message } from 'antd';
 import React, { useEffect, useState } from 'react';
 import RegionCascader from '@/components/RegionCascader';
 import { getClinicDetail, getProvincialAgentOptions, updateClinic, uploadClinicImage } from './service';
@@ -11,10 +11,14 @@
   const access = useAccess(); const params = useParams<{ id: string }>();
   const [form] = Form.useForm<FormValues>(); const [loading, setLoading] = useState(false);
   const [agentOptions, setAgentOptions] = useState<{ label: string; value: number }[]>([]);
+  const [avatar, setAvatar] = useState('');
+  const [businessLicenseUrl, setBusinessLicenseUrl] = useState('');
+  const [uploadingAvatar, setUploadingAvatar] = useState(false);
+  const [uploadingLicense, setUploadingLicense] = useState(false);
   useEffect(() => {
     if (!access['member:clinic:edit']) { history.replace('/Welcome'); return; }
     getProvincialAgentOptions().then((response) => setAgentOptions((response.data?.list || []).map((item: any) => ({ label: item.agentName, value: item.id }))));
-    if (params.id) getClinicDetail(params.id).then((response) => form.setFieldsValue(response.data || {}));
+    if (params.id) getClinicDetail(params.id).then((response) => { const data = response.data || {}; form.setFieldsValue(data); setAvatar(data.avatar || ''); setBusinessLicenseUrl(data.businessLicenseUrl || ''); });
   }, [params.id]);
   const submit = async () => {
     const values = await form.validateFields(); if (!params.id) return; setLoading(true);
@@ -23,21 +27,37 @@
   };
   return <div className="clinic-edit-page"><div className="clinic-edit-panel"><h2>编辑诊所</h2>
     <Form form={form} labelCol={{ span: 4 }} wrapperCol={{ span: 10 }}>
-      <Form.Item shouldUpdate noStyle>{() => <div style={{ marginBottom: 20 }}><div className="clinic-edit-upload" /></div>}</Form.Item>
-      <Form.Item name="avatar" label="头像"><Input placeholder="请输入头像地址" /></Form.Item>
+      <Form.Item label="头像" className="clinic-edit-upload-item">
+        <Form.Item name="avatar" rules={[{ required: true, message: "请上传头像" }]} hidden><Input /></Form.Item>
+        <Upload accept="image/*" showUploadList={false} customRequest={async (options) => {
+          setUploadingAvatar(true);
+          try { const response = await uploadClinicImage(options.file as File); const url = response.data?.url || response.data; if (!url) throw new Error("upload failed"); setAvatar(url); form.setFieldsValue({ avatar: url }); options.onSuccess?.(response); } catch (error) { message.error("头像上传失败"); options.onError?.(error as Error); } finally { setUploadingAvatar(false); }
+        }}>
+          <div className="clinic-edit-upload clinic-edit-upload--avatar">{uploadingAvatar ? <span>上传中...</span> : avatar ? <Image src={avatar} width={104} height={104} preview={false} /> : <div><PlusOutlined /><span>上传头像</span></div>}</div>
+        </Upload>
+      </Form.Item>
+
       <Form.Item name="name" label="诊所名称"><Input maxLength={100} placeholder="请输入诊所名称" /></Form.Item>
       <Form.Item name="loginAccount" label="登录账号"><Input maxLength={50} placeholder="请输入登录账号" /></Form.Item>
       <Form.Item name="regionCodes" label="诊所地址"><RegionCascader placeholder="请选择省/市/区" /></Form.Item>
       <Form.Item name="address" label="详细地址"><Input maxLength={255} placeholder="请输入详细地址" /></Form.Item>
-      <Form.Item label="营业执照" shouldUpdate>{() => <Upload accept="image/*" showUploadList={false} beforeUpload={async (file) => { const response = await uploadClinicImage(file); form.setFieldValue("businessLicenseUrl", response.data?.url || response.data); return false; }}><div className="clinic-edit-upload clinic-edit-upload--license">{form.getFieldValue("businessLicenseUrl") ? <img src={form.getFieldValue("businessLicenseUrl")} alt="营业执照" /> : <div><PlusOutlined /><span>上传营业执照</span></div>}</div></Upload>}</Form.Item>
+      <Form.Item label="营业执照" className="clinic-edit-upload-item">
+        <Form.Item name="businessLicenseUrl" rules={[{ required: true, message: "请上传营业执照" }]} hidden><Input /></Form.Item>
+        <Upload accept="image/*" showUploadList={false} customRequest={async (options) => {
+          setUploadingLicense(true);
+          try { const response = await uploadClinicImage(options.file as File); const url = response.data?.url || response.data; if (!url) throw new Error("upload failed"); setBusinessLicenseUrl(url); form.setFieldsValue({ businessLicenseUrl: url }); options.onSuccess?.(response); } catch (error) { message.error("营业执照上传失败"); options.onError?.(error as Error); } finally { setUploadingLicense(false); }
+        }}>
+          <div className="clinic-edit-upload clinic-edit-upload--license">{uploadingLicense ? <span>上传中...</span> : businessLicenseUrl ? <Image src={businessLicenseUrl} width={208} height={128} preview={false} /> : <div><PlusOutlined /><span>上传营业执照</span></div>}</div>
+        </Upload>
+      </Form.Item>
       <Form.Item name="status" label="状态"><Select options={[{ label: "启用", value: 1 }, { label: "禁用", value: 0 }]} placeholder="请选择状态" /></Form.Item>
       <Form.Item name="agentId" label="所属省区代理"><Select allowClear showSearch optionFilterProp="label" options={agentOptions} placeholder="请选择省区代理" /></Form.Item>
       <Form.Item name="bankName" label="银行名称"><Input maxLength={100} placeholder="请输入银行名称" /></Form.Item>
       <Form.Item name="accountHolder" label="账号名称"><Input maxLength={50} placeholder="请输入账号名称" /></Form.Item>
       <Form.Item name="accountNumber" label="账号"><Input maxLength={30} placeholder="请输入账号" /></Form.Item>
       <Form.Item name="branchName" label="支行名称"><Input maxLength={100} placeholder="请输入支行名称" /></Form.Item>
-      <Form.Item name="agentId" label="所属省区代理"><Select allowClear showSearch optionFilterProp="label" options={agentOptions} placeholder="请选择省区代理" /></Form.Item>
+
       <Form.Item wrapperCol={{ offset: 4 }}><Button onClick={() => history.back()}>取消</Button><Button type="primary" loading={loading} onClick={submit} style={{ marginLeft: 8 }}>保存</Button></Form.Item>
     </Form></div></div>;
 };
-export default ClinicManagePage;
\ No newline at end of file
+export default ClinicManagePage;

--
Gitblit v1.7.1