hejianhao
2026-08-18 50c40264084f032c5d28b07e247ed4d7f84c55df
feat0806: 诊所管理部分功能
3个文件已添加
12个文件已修改
236 ■■■■■ 已修改文件
tcm-training-admin-backend/sql/20260806_admin.sql 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tcm-training-admin-backend/src/main/java/com/tcm/training/admin/agent/excel/ClinicAgentImportRow.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tcm-training-admin-backend/src/main/java/com/tcm/training/admin/agent/service/ProvincialAgentAppService.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tcm-training-admin-backend/src/main/java/com/tcm/training/admin/bootstrap/DataInitializer.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tcm-training-admin-backend/src/main/java/com/tcm/training/admin/member/controller/ClinicController.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tcm-training-admin-backend/src/main/java/com/tcm/training/admin/member/dto/ClinicBankAccountUpdateRequest.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tcm-training-admin-backend/src/main/java/com/tcm/training/admin/member/row/ClinicDetailRow.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tcm-training-admin-backend/src/main/java/com/tcm/training/admin/member/service/ClinicAppService.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tcm-training-admin-backend/src/main/java/com/tcm/training/admin/member/vo/ClinicDetailVO.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tcm-training-admin-backend/src/main/resources/mapper/ClinicQueryMapper.xml 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tcm-training-admin/config/routes.ts 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tcm-training-admin/src/pages/member/clinic/detail.tsx 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tcm-training-admin/src/pages/member/clinic/index.tsx 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tcm-training-admin/src/pages/member/clinic/manage.tsx 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tcm-training-admin/src/pages/member/clinic/service.ts 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tcm-training-admin-backend/sql/20260806_admin.sql
@@ -33,12 +33,13 @@
ALTER TABLE tcm_clinic
ADD COLUMN agent_id BIGINT COMMENT '负责该诊所的省代理ID,tcm_provincial_agent.id'
AFTER name;
INSERT INTO sys_menu (id, parent_id, name, menu_code, type, path, component, sort, status, visible, permission_key, remark, deleted) VALUES
(2119, 2100, '省代理管理', 'member_agent_manage', 'C', '/member/user/agent', 'Member/AgentList', 12, 1, 1, 'member:agent:view', '省代理管理菜单', 0),
(2120, 2119, '新增省代理', 'member_agent_add', 'B', NULL, NULL, 1, 1, 1, 'member:agent:add', '新增省代理权限', 0),
(2121, 2119, '编辑省代理', 'member_agent_edit', 'B', NULL, NULL, 2, 1, 1, 'member:agent:edit', '编辑省代理权限', 0),
(2122, 2119, '删除省代理', 'member_agent_delete', 'B', NULL, NULL, 3, 1, 1, 'member:agent:delete', '删除省代理权限', 0),
(2123, 2119, '省代理详情', 'member_agent_detail', 'B', NULL, NULL, 4, 1, 1, 'member:agent:detail', '省代理详情权限', 0);
(5046, 2100, '省代理管理', 'member_agent_manage', 'C', '/member/user/agent', 'Member/AgentList', 12, 1, 1, 'member:agent:view', '省代理管理菜单', 0),
(5047, 5046, '新增省代理', 'member_agent_add', 'B', NULL, NULL, 1, 1, 1, 'member:agent:add', '新增省代理权限', 0),
(5048, 5046, '编辑省代理', 'member_agent_edit', 'B', NULL, NULL, 2, 1, 1, 'member:agent:edit', '编辑省代理权限', 0),
(5049, 5046, '删除省代理', 'member_agent_delete', 'B', NULL, NULL, 3, 1, 1, 'member:agent:delete', '删除省代理权限', 0),
(5050, 5046, '省代理详情', 'member_agent_detail', 'B', NULL, NULL, 4, 1, 1, 'member:agent:detail', '省代理详情权限', 0);
-- 养生直播:新增设置直播间密码按钮权限
@@ -55,3 +56,7 @@
ADD COLUMN account_number VARCHAR(30) COMMENT '银行账号' AFTER bank_name,
ADD COLUMN account_holder VARCHAR(50) COMMENT '账户名称/户名(即开户人姓名或公司名称)' AFTER account_number,
ADD COLUMN branch_name VARCHAR(100) COMMENT '开户支行名称(如:成都高新支行)' AFTER account_holder;
INSERT INTO sys_menu (id, parent_id, name, menu_code, type, sort, status, visible, permission_key, remark, deleted)
VALUES (5045, 2118, '编辑诊所', 'member_clinic_edit', 'B', 1, 1, 1, 'member:clinic:edit', '编辑诊所按钮', 0)
ON DUPLICATE KEY UPDATE permission_key = VALUES(permission_key), deleted = 0;
tcm-training-admin-backend/src/main/java/com/tcm/training/admin/agent/excel/ClinicAgentImportRow.java
New file
@@ -0,0 +1,22 @@
package com.tcm.training.admin.agent.excel;
import cn.idev.excel.annotation.ExcelProperty;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class ClinicAgentImportRow {
    @ExcelProperty("诊所name")
    private String clinicName;
    @ExcelProperty("诊所电话contact_phone")
    private String clinicContactPhone;
    @ExcelProperty("省区代理名称agent_name")
    private String agentName;
    @ExcelProperty("省区代理电话contact_info")
    private String agentContactInfo;
}
tcm-training-admin-backend/src/main/java/com/tcm/training/admin/agent/service/ProvincialAgentAppService.java
@@ -1,5 +1,6 @@
package com.tcm.training.admin.agent.service;
import cn.idev.excel.FastExcel;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
@@ -19,7 +20,11 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@Service
@@ -47,8 +52,11 @@
        if (trim(regionCode) != null) {
            List<Long> ids = new ArrayList<Long>();
            for (AgentRegion region : regionMapper.selectList(new LambdaQueryWrapper<AgentRegion>()
                    .eq(AgentRegion::getRegionCode, trim(regionCode)))) ids.add(region.getAgentId());
            if (ids.isEmpty()) return emptyPage(page, pageSize);
                    .eq(AgentRegion::getRegionCode, trim(regionCode))))
                ids.add(region.getAgentId());
            if (ids.isEmpty()) {
                return emptyPage(page, pageSize);
            }
            query.in(ProvincialAgent::getId, ids);
        }
        if (trim(clinicName) != null) {
@@ -57,7 +65,9 @@
                    .like(Clinic::getName, trim(clinicName)))) {
                if (clinic.getAgentId() != null && !ids.contains(clinic.getAgentId())) ids.add(clinic.getAgentId());
            }
            if (ids.isEmpty()) return emptyPage(page, pageSize);
            if (ids.isEmpty()) {
                return emptyPage(page, pageSize);
            }
            query.in(ProvincialAgent::getId, ids);
        }
        IPage<ProvincialAgent> result = agentMapper.selectPage(new Page<ProvincialAgent>(page, pageSize), query);
@@ -69,6 +79,7 @@
    private PageResponse<ProvincialAgentListItemVO> emptyPage(int page, int pageSize) {
        return PageResponse.of(new ArrayList<ProvincialAgentListItemVO>(), 0, page, pageSize);
    }
    public ProvincialAgentDetailVO detail(Long id) {
        return toDetail(require(id));
    }
tcm-training-admin-backend/src/main/java/com/tcm/training/admin/bootstrap/DataInitializer.java
@@ -201,6 +201,8 @@
                "/member/user/manage", "Member/UserList", null, 10, "member:user:view", "用户管理菜单"));
        menuIds.add(saveMenu(2118L, 2100L, "诊所管理", "member_clinic_manage", "C",
                "/member/user/clinic", "Member/ClinicList", null, 11, "member:clinic:view", "诊所管理菜单"));
        menuIds.add(saveMenu(5045L, 2118L, "编辑诊所", "member_clinic_edit", "B",
                null, null, null, 1, "member:clinic:edit", "编辑诊所按钮"));
        menuIds.add(saveMenu(2119L, 2100L, "会员用户", "member_only_user_manage", "C",
                "/member/user/member-users", "Member/MemberUserList", null, 12, "member:user:member:view", "会员用户菜单"));
        menuIds.add(saveMenu(2200L, 2000L, "会员", "member_vip_group", "M",
@@ -227,15 +229,15 @@
                null, null, null, 6, "member:user:export", "导出用户按钮"));
        menuIds.add(saveMenu(2117L, 2110L, "解绑微信", "member_user_unbind_wechat", "B",
                null, null, null, 7, "member:user:unbind-wechat", "解绑微信按钮"));
        menuIds.add(saveMenu(2119L, 2100L, "省代理管理", "member_agent_manage", "C",
        menuIds.add(saveMenu(5046L, 2100L, "省代理管理", "member_agent_manage", "C",
                "/member/user/agent", "Member/AgentList", null, 12, "member:agent:view", "省代理管理菜单"));
        menuIds.add(saveMenu(2120L, 2119L, "新增省代理", "member_agent_add", "B",
        menuIds.add(saveMenu(5047L, 5046L, "新增省代理", "member_agent_add", "B",
                null, null, null, 1, "member:agent:add", "新增省代理按钮"));
        menuIds.add(saveMenu(2121L, 2119L, "编辑省代理", "member_agent_edit", "B",
        menuIds.add(saveMenu(5048L, 5046L, "编辑省代理", "member_agent_edit", "B",
                null, null, null, 2, "member:agent:edit", "编辑省代理按钮"));
        menuIds.add(saveMenu(2122L, 2119L, "删除省代理", "member_agent_delete", "B",
        menuIds.add(saveMenu(5049L, 5046L, "删除省代理", "member_agent_delete", "B",
                null, null, null, 3, "member:agent:delete", "删除省代理按钮"));
        menuIds.add(saveMenu(2123L, 2119L, "省代理详情", "member_agent_detail", "B",
        menuIds.add(saveMenu(5050L, 5046L, "省代理详情", "member_agent_detail", "B",
                null, null, null, 4, "member:agent:detail", "省代理详情按钮"));
        menuIds.add(saveMenu(3000L, 0L, "课程与学习", "live_root", "M",
                "/live", null, "read", 300, null, "课程与学习模块"));
tcm-training-admin-backend/src/main/java/com/tcm/training/admin/member/controller/ClinicController.java
@@ -3,6 +3,7 @@
import com.tcm.training.admin.common.ApiResponse;
import com.tcm.training.admin.common.PageResponse;
import com.tcm.training.admin.member.service.ClinicAppService;
import com.tcm.training.admin.member.dto.ClinicBankAccountUpdateRequest;
import com.tcm.training.admin.member.vo.ClinicBoundUserItemVO;
import com.tcm.training.admin.member.vo.ClinicDetailVO;
import com.tcm.training.admin.member.vo.ClinicListItemVO;
@@ -14,6 +15,9 @@
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import javax.validation.Valid;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@@ -44,6 +48,11 @@
        return ApiResponse.ok(clinicAppService.page(page, pageSize, regionCode, name, phone, createdFrom, createdTo, status));
    }
    @GetMapping("/clinics/clinic-import-template")
    public void downloadClinicImportTemplate(HttpServletResponse response) throws IOException {
        clinicAppService.downloadClinicImportTemplate(response);
    }
    @GetMapping("/clinics/export")
    public void export(HttpServletResponse response,
                       @RequestParam(required = false) String regionCode,
@@ -65,6 +74,11 @@
        return ApiResponse.ok(clinicAppService.detail(id));
    }
    @PutMapping("/clinics/{id}")
    public ApiResponse<ClinicDetailVO> update(@PathVariable Long id,
                                               @Valid @RequestBody ClinicBankAccountUpdateRequest request) {
        return ApiResponse.ok(clinicAppService.updateBankAccount(id, request));
    }
    @GetMapping("/clinics/{id}/users")
    public ApiResponse<PageResponse<ClinicBoundUserItemVO>> clinicUsers(
            @PathVariable Long id,
tcm-training-admin-backend/src/main/java/com/tcm/training/admin/member/dto/ClinicBankAccountUpdateRequest.java
New file
@@ -0,0 +1,14 @@
package com.tcm.training.admin.member.dto;
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.Size;
@Getter
@Setter
public class ClinicBankAccountUpdateRequest {
    @Size(max = 100, message = "银行名称不能超过100个字") private String bankName;
    @Size(max = 50, message = "账号名称不能超过50个字") private String accountHolder;
    @Size(max = 30, message = "账号不能超过30个字") private String accountNumber;
    @Size(max = 100, message = "支行名称不能超过100个字") private String branchName;
}
tcm-training-admin-backend/src/main/java/com/tcm/training/admin/member/row/ClinicDetailRow.java
@@ -15,4 +15,8 @@
    private String address;
    private String businessLicenseUrl;
    private Integer userCount;
    private String bankName;
    private String accountHolder;
    private String accountNumber;
    private String branchName;
}
tcm-training-admin-backend/src/main/java/com/tcm/training/admin/member/service/ClinicAppService.java
@@ -3,10 +3,15 @@
import cn.idev.excel.FastExcel;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tcm.training.admin.agent.excel.ClinicAgentImportRow;
import com.tcm.training.admin.common.DateTimes;
import com.tcm.training.admin.common.PageResponse;
import com.tcm.training.admin.common.PhoneMasker;
import com.tcm.training.admin.member.excel.ClinicExcelRow;
import com.tcm.training.admin.member.dto.ClinicBankAccountUpdateRequest;
import com.tcm.training.admin.live.entity.Clinic;
import com.tcm.training.admin.live.mapper.ClinicMapper;
import java.util.Collections;
import com.tcm.training.admin.member.row.ClinicBoundUserRow;
import com.tcm.training.admin.member.row.ClinicDetailRow;
import com.tcm.training.admin.member.mapper.ClinicQueryMapper;
@@ -29,9 +34,11 @@
public class ClinicAppService {
    private final ClinicQueryMapper clinicQueryMapper;
    private final ClinicMapper clinicMapper;
    public ClinicAppService(ClinicQueryMapper clinicQueryMapper) {
    public ClinicAppService(ClinicQueryMapper clinicQueryMapper, ClinicMapper clinicMapper) {
        this.clinicQueryMapper = clinicQueryMapper;
        this.clinicMapper = clinicMapper;
    }
    public PageResponse<ClinicListItemVO> page(int page,
@@ -56,6 +63,16 @@
            list.add(toListItem(row));
        }
        return PageResponse.of(list, result.getTotal(), result.getCurrent(), result.getSize());
    }
    public void downloadClinicImportTemplate(HttpServletResponse response) throws IOException {
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        String filename = URLEncoder.encode("诊所省区代理导入模板", "UTF-8").replace("+", "%20");
        response.setHeader("Content-Disposition", "attachment;filename*=utf-8''" + filename + ".xlsx");
        FastExcel.write(response.getOutputStream(), ClinicAgentImportRow.class)
                .sheet("诊所省区代理导入模板")
                .doWrite(Collections.emptyList());
    }
    public void export(String regionCode,
@@ -117,9 +134,25 @@
        vo.setAddress(defaultText(row.getAddress()));
        vo.setBusinessLicenseUrl(trimToNull(row.getBusinessLicenseUrl()));
        vo.setUserCount(row.getUserCount() == null ? 0 : row.getUserCount());
        vo.setBankName(defaultText(row.getBankName()));
        vo.setAccountHolder(defaultText(row.getAccountHolder()));
        vo.setAccountNumber(defaultText(row.getAccountNumber()));
        vo.setBranchName(defaultText(row.getBranchName()));
        return vo;
    }
    public ClinicDetailVO updateBankAccount(Long id, ClinicBankAccountUpdateRequest request) {
        Clinic clinic = clinicMapper.selectById(id);
        if (clinic == null || (clinic.getDeleted() != null && clinic.getDeleted() == 1)) {
            throw new IllegalArgumentException("诊所不存在");
        }
        clinic.setBankName(trimToNull(request.getBankName()));
        clinic.setAccountHolder(trimToNull(request.getAccountHolder()));
        clinic.setAccountNumber(trimToNull(request.getAccountNumber()));
        clinic.setBranchName(trimToNull(request.getBranchName()));
        clinicMapper.updateById(clinic);
        return detail(id);
    }
    public PageResponse<ClinicBoundUserItemVO> clinicUsers(Long clinicId,
                                                           int page,
                                                           int pageSize,
tcm-training-admin-backend/src/main/java/com/tcm/training/admin/member/vo/ClinicDetailVO.java
@@ -15,4 +15,8 @@
    private String address;
    private String businessLicenseUrl;
    private Integer userCount;
    private String bankName;
    private String accountHolder;
    private String accountNumber;
    private String branchName;
}
tcm-training-admin-backend/src/main/resources/mapper/ClinicQueryMapper.xml
@@ -121,23 +121,16 @@
    </select>
    <select id="selectClinicDetail" resultType="com.tcm.training.admin.member.row.ClinicDetailRow">
        SELECT
            c.id,
            c.name,
            c.contact_phone AS contactPhone,
            c.address,
        SELECT c.id, c.name, c.contact_phone AS contactPhone, c.address,
            c.business_license_url AS businessLicenseUrl,
               c.bank_name AS bankName, c.account_holder AS accountHolder,
               c.account_number AS accountNumber, c.branch_name AS branchName,
            COUNT(u.id) AS userCount
        FROM tcm_clinic c
        LEFT JOIN tcm_app_user u ON u.clinic_id = c.id AND u.deleted = 0
        WHERE c.deleted = 0
          AND c.id = #{id}
        GROUP BY
            c.id,
            c.name,
            c.contact_phone,
            c.address,
            c.business_license_url
        WHERE c.deleted = 0 AND c.id = #{id}
        GROUP BY c.id, c.name, c.contact_phone, c.address, c.business_license_url,
                 c.bank_name, c.account_holder, c.account_number, c.branch_name
    </select>
    <select id="selectClinicUserPage" resultType="com.tcm.training.admin.member.row.ClinicBoundUserRow">
tcm-training-admin/config/routes.ts
@@ -210,6 +210,13 @@
            component: './member/agent',
          },
          {
            name: '编辑诊所',
            path: '/member/user/clinic/:id/edit',
            hideInMenu: true,
            access: 'member:clinic:edit',
            component: './member/clinic/manage',
          },
          {
            name: '诊所详情',
            path: '/member/user/clinic/:id',
            hideInMenu: true,
tcm-training-admin/src/pages/member/clinic/detail.tsx
@@ -21,6 +21,10 @@
  address?: string;
  businessLicenseUrl?: string;
  userCount: number;
  bankName?: string;
  accountHolder?: string;
  accountNumber?: string;
  branchName?: string;
};
type UserItem = {
@@ -58,6 +62,7 @@
  const canView = access['member:clinic:view'];
  const canUserDetail = access['member:user:detail'];
  const canEdit = access['member:clinic:edit'];
  const loadDetail = async () => {
    if (!params.id) {
@@ -178,11 +183,15 @@
        <div className="member-clinic-detail-card__header">
          <div className="member-clinic-detail-card__avatar-placeholder">{detail.name?.slice(0, 1) || '诊'}</div>
          <div className="member-clinic-detail-card__summary">
            <div className="member-clinic-detail-card__name">{detail.name}</div>
            <div className="member-clinic-detail-card__name">{detail.name} {canEdit ? <Button type="link" onClick={() => history.push(`/member/user/clinic/${detail.id}/edit`)}>编辑</Button> : null}</div>
            <div className="member-clinic-detail-card__meta">
              <span>电话: {detail.phone || '-'}</span>
              <span>诊所地址: {detail.address || '-'}</span>
              <span>绑定用户数: {detail.userCount ?? 0}</span>
              <span>银行名称: {detail.bankName || '-'}</span>
              <span>账号名称: {detail.accountHolder || '-'}</span>
              <span>账号: {detail.accountNumber || '-'}</span>
              <span>支行名称: {detail.branchName || '-'}</span>
              <span>
                营业执照:
                {detail.businessLicenseUrl ? (
tcm-training-admin/src/pages/member/clinic/index.tsx
@@ -5,7 +5,7 @@
import React, { useEffect, useState } from 'react';
import { downloadBlob } from '../../trade/points-exchange/shared';
import { clinicRegionOptions } from './regionOptions';
import { exportClinics, getClinicPage } from './service';
import { downloadClinicImportTemplate, exportClinics, getClinicPage } from './service';
import './style.less';
type SearchValues = {
@@ -48,6 +48,7 @@
  const [searchForm] = Form.useForm<SearchValues>();
  const [list, setList] = useState<ClinicItem[]>([]);
  const [loading, setLoading] = useState(false);
  const [templateDownloading, setTemplateDownloading] = useState(false);
  const [pagination, setPagination] = useState({ page: 1, pageSize: 10, total: 0 });
  const canView = access['member:clinic:view'];
@@ -82,6 +83,16 @@
  const handleExport = async () => {
    const blob = await exportClinics(buildSearchParams(searchForm.getFieldsValue()));
    downloadBlob(blob, '诊所列表.xlsx');
  };
  const handleTemplateDownload = async () => {
    setTemplateDownloading(true);
    try {
      const blob = await downloadClinicImportTemplate();
      downloadBlob(blob, '诊所省区代理导入模板.xlsx');
    } finally {
      setTemplateDownloading(false);
    }
  };
  const columns: ColumnsType<ClinicItem> = [
@@ -190,6 +201,9 @@
        </div>
        <div className="member-clinic-toolbar">
          <Button loading={templateDownloading} onClick={handleTemplateDownload}>
            下载模板
          </Button>
          <Button onClick={handleExport}>导出</Button>
        </div>
tcm-training-admin/src/pages/member/clinic/manage.tsx
New file
@@ -0,0 +1,28 @@
import { history, useAccess, useParams } from '@umijs/max';
import { Button, Form, Input, message } from 'antd';
import React, { useEffect, useState } from 'react';
import { getClinicDetail, updateClinicBankAccount } from './service';
type FormValues = { bankName?: string; accountHolder?: string; accountNumber?: string; branchName?: string };
const ClinicManagePage: React.FC = () => {
  const access = useAccess(); const params = useParams<{ id: string }>();
  const [form] = Form.useForm<FormValues>(); const [loading, setLoading] = useState(false);
  useEffect(() => {
    if (!access['member:clinic:edit']) { history.replace('/Welcome'); return; }
    if (params.id) getClinicDetail(params.id).then((response) => form.setFieldsValue(response.data || {}));
  }, [params.id]);
  const submit = async () => {
    const values = await form.validateFields(); if (!params.id) return; setLoading(true);
    try { await updateClinicBankAccount(params.id, values); message.success('保存成功'); history.push(`/member/user/clinic/${params.id}`); }
    finally { setLoading(false); }
  };
  return <div style={{ padding: 24, background: '#fff' }}><h2>编辑诊所</h2>
    <Form form={form} labelCol={{ span: 4 }} wrapperCol={{ span: 10 }}>
      <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 wrapperCol={{ offset: 4 }}><Button onClick={() => history.back()}>取消</Button><Button type="primary" loading={loading} onClick={submit} style={{ marginLeft: 8 }}>保存</Button></Form.Item>
    </Form></div>;
};
export default ClinicManagePage;
tcm-training-admin/src/pages/member/clinic/service.ts
@@ -29,6 +29,20 @@
  return response.blob();
}
export async function downloadClinicImportTemplate() {
  const authorization = decryptData(localStorage.getItem('token')) || '';
  const response = await fetch(`${BASE_URL}/clinics/clinic-import-template`, {
    method: 'GET',
    headers: {
      Authorization: authorization,
    },
  });
  if (!response.ok) {
    throw new Error('模板下载失败');
  }
  return response.blob();
}
export async function getClinicDetail(id: number | string) {
  return request(`/clinics/${id}`, {
    method: 'GET',
@@ -41,3 +55,7 @@
    params,
  });
}
export async function updateClinicBankAccount(id: number | string, data: Record<string, any>) {
  return request(`/clinics/${id}`, { method: 'PUT', data });
}