董国庆
2025-12-26 c3dfb73503d7dbe30e7057897327193dce26cba2
Merge branch 'main' of http://120.76.84.145:10101/gitblit/r/H5/JiaShiDa
17个文件已修改
254 ■■■■■ 已修改文件
management/.gitignore 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/config/config.ts 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/config/env.ts 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/package.json 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/public/logo/logo.png 补丁 | 查看 | 原始文档 | blame | 历史
management/src/app.tsx 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/src/pages/bigGiftPackage/index.jsx 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/src/pages/company/detail/components/qrCodeModal.jsx 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/src/pages/company/detail/components/userManagement.jsx 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/src/pages/company/detail/service.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/src/pages/companyInfo/components/CompanyUserManagement.jsx 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/src/pages/companyInfo/service.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/src/pages/order/orderTransfer/components/TransferDetailModal.jsx 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/src/pages/order/physicalGoodsOrder/index.jsx 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/src/pages/system/basicInfoManagement/agreementManagement/index.jsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/src/pages/user/list/index.jsx 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/src/utils/utils.js 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/.gitignore
@@ -8,7 +8,7 @@
# production
/dist
/admin
/management
# misc
.DS_Store
management/config/config.ts
@@ -8,7 +8,7 @@
const { REACT_APP_ENV = 'dev' } = process.env;
export default defineConfig({
  outputPath: 'admin', // 设置打包后的文件名为 screen
  outputPath: 'management',
  /**
   * @name 开启 hash 模式
   * @description 让 build 之后的产物包含 hash 后缀。通常用于增量发布和避免浏览器加载缓存。
@@ -146,7 +146,7 @@
  mfsu: {
    strategy: 'normal',
  },
  favicons: [],
  favicons: ['https://jiashikang.cn/images/2025-12-26/30ae8143-dc90-49b7-bc1f-ac49b74ae092.png'],
  requestRecord: {},
  publicPath: REACT_APP_ENV === 'prod' ? '/manage' : '/',
  publicPath: '/',
});
management/config/env.ts
@@ -1,14 +1,14 @@
export default {
  dev: {
    SERVER_URL: 'http://192.168.110.111:9908',
    IMAGE_URL: 'http://221.182.45.100:11223/images/', // 图片地址
    SERVER_URL: 'https://jiashikang.cn/manage',
    IMAGE_URL: 'https://jiashikang.cn/images/', // 图片地址
  },
  test: {
    SERVER_URL: 'http://221.182.45.100:8686',
    IMAGE_URL: 'https://jiashikang.cn/images/', // 图片地址
  },
  prod: {
    SERVER_URL: 'http://47.108.162.242:8090',
    SERVER_URL: 'https://jiashikang.cn/manage',
    IMAGE_URL: 'https://jiashikang.cn/images/', // 图片地址
  },
};
management/package.json
@@ -4,10 +4,14 @@
  "private": true,
  "description": "An out-of-box UI solution for enterprise applications",
  "scripts": {
    "start:dev": "cross-env PORT=8888 REACT_APP_ENV=dev MOCK=none UMI_ENV=dev max dev",
    "start:test": "cross-env PORT=8888 REACT_APP_ENV=test MOCK=none UMI_ENV=dev max dev",
    "start:prod": "cross-env PORT=8888 REACT_APP_ENV=prod MOCK=none UMI_ENV=prod max dev",
    "build:dev": "cross-env REACT_APP_ENV=dev MOCK=none UMI_ENV=dev max build",
    "build:test": "cross-env REACT_APP_ENV=test MOCK=none UMI_ENV=test max build",
    "build:prod": "cross-env REACT_APP_ENV=prod MOCK=none UMI_ENV=prod max build",
    "analyze": "cross-env ANALYZE=1 max build",
    "build": "cross-env REACT_APP_ENV=prod MOCK=none UMI_ENV=prod max build",
    "deploy": "npm run build && npm run gh-pages",
    "dev": "npm run start:dev --debug",
    "gh-pages": "gh-pages -d dist",
    "postinstall": "max setup",
    "jest": "jest",
@@ -16,11 +20,6 @@
    "preview": "npm run build && max preview --port 8000",
    "record": "cross-env NODE_ENV=development REACT_APP_ENV=test max record --scene=login",
    "serve": "umi-serve",
    "start": "cross-env PORT=8888 UMI_ENV=dev max dev",
    "start:dev": "cross-env PORT=8888 REACT_APP_ENV=dev MOCK=none UMI_ENV=dev max dev",
    "start:test": "cross-env PORT=8888 REACT_APP_ENV=test MOCK=none UMI_ENV=dev max dev",
    "start:pre": "cross-env PORT=8888 REACT_APP_ENV=pre UMI_ENV=dev max dev",
    "start:prod": "cross-env PORT=8888 REACT_APP_ENV=prod MOCK=none UMI_ENV=prod max dev",
    "test": "jest",
    "test:coverage": "npm run jest -- --coverage",
    "test:update": "npm run jest -- -u",
management/public/logo/logo.png

management/src/app.tsx
@@ -41,22 +41,15 @@
  loading?: boolean;
}> {
  // 如果不是登录页面,执行
  let permission = [];
  if (
    JSON.parse(localStorage.getItem('userInfo') || '').userId &&
    window.location.pathname !== loginPath
  ) {
    const roleInfo = await getRoleInfoFromUserId(
      JSON.parse(localStorage.getItem('userInfo') || '').userId,
    );
    const allMenuIds = getAllMenuIds(roleInfo.data || []);
  const { location } = history;
  if (location.pathname !== loginPath && !localStorage.getItem('userInfo')) {
    history.replace(loginPath)
    // 将 menuId 转为权限对象
    const accessObj: any = {};
    allMenuIds.forEach((id) => {
      accessObj[id] = true;
    });
    permission = accessObj;
    return {
      permission: JSON.parse(localStorage.getItem('access') || '') || [],
      currentUser: JSON.parse(localStorage.getItem('userInfo') || '') || {},
      settings: { ...defaultSettings, fixedHeader: true } as Partial<LayoutSettings>,
    };
  }
  return {
management/src/pages/bigGiftPackage/index.jsx
@@ -1,6 +1,6 @@
import { ProTable, Modal, message } from '@ant-design/pro-components';
import { ProTable, Modal } from '@ant-design/pro-components';
import { useRef, useState } from 'react';
import { Button, Space } from 'antd';
import { Button, Space, message } from 'antd';
import { getRefundOrderList } from './service';
import OrderDetailModal from '../order/bigGiftPackageOrder/components/OrderDetailModal';
import RefundApplyModal from './components/RefundApplyModal';
management/src/pages/company/detail/components/qrCodeModal.jsx
@@ -1,34 +1,60 @@
import { Button, Modal, Space, Typography } from 'antd';
import { Button, Modal, Space, Typography, Spin } from 'antd';
import { forwardRef, useImperativeHandle, useState } from 'react';
import { DownloadOutlined } from '@ant-design/icons';
import { request } from '@umijs/max';
import { getUserQrcode } from '../service';
const { Title, Paragraph } = Typography;
const QrCodeModal = ({ visible, onCancel, userInfo }, ref) => {
const QrCodeModal = ({ visible, onCancel }, ref) => {
  const [loading, setLoading] = useState(false);
  const [qrCodeUrl, setQrCodeUrl] = useState('');
  const [qrCodeLoading, setQrCodeLoading] = useState(false);
  const [userInfoData, setUserInfoData] = useState({});
  useImperativeHandle(ref, () => {
    return {
      showQrCode: (url) => {
        setQrCodeUrl(url);
        setVisible(true);
      showQrCode: (userInfo) => {
        setUserInfoData(userInfo)
        fetchQrCode(userInfo);
      },
    };
  });
  // 获取二维码
  const fetchQrCode = async (userInfo) => {
    if (!userInfo?.userId) return;
    setQrCodeLoading(true);
    try {
      const response = await getUserQrcode({ userId: userInfo.userId });
      if (response && response.code === 200) {
        setQrCodeUrl(response.data);
      }
    } catch (error) {
      console.error('获取二维码失败:', error);
      message.error('获取二维码失败');
    } finally {
      setQrCodeLoading(false);
    }
  };
  const downloadQrCode = async () => {
    if (!qrCodeUrl) return;
    setLoading(true);
    try {
      const response = await request(qrCodeUrl, {
        method: 'GET',
        responseType: 'blob',
      });
      // 将base64转换为blob并下载
      const byteCharacters = atob(qrCodeUrl);
      const byteNumbers = new Array(byteCharacters.length);
      const url = window.URL.createObjectURL(new Blob([response]));
      for (let i = 0; i < byteCharacters.length; i++) {
        byteNumbers[i] = byteCharacters.charCodeAt(i);
      }
      const byteArray = new Uint8Array(byteNumbers);
      const blob = new Blob([byteArray], { type: 'image/png' });
      const url = window.URL.createObjectURL(blob);
      const link = document.createElement('a');
      link.href = url;
      link.setAttribute('download', `推广二维码_${userInfo.nickName || userInfo.code || '未命名'}.png`);
@@ -38,6 +64,7 @@
      window.URL.revokeObjectURL(url);
    } catch (error) {
      console.error('下载二维码失败:', error);
      message.error('下载二维码失败');
    } finally {
      setLoading(false);
    }
@@ -67,11 +94,15 @@
      }
    >
      <div style={{ textAlign: 'center', padding: '20px 0' }}>
        <Title level={4}>{userInfo.nickName || userInfo.code} 的推广二维码</Title>
        {qrCodeUrl ? (
        <Title level={4}>{userInfoData.nickName || userInfoData.code} 的推广二维码</Title>
        {qrCodeLoading ? (
          <div style={{ marginTop: 20 }}>
            <Spin />
          </div>
        ) : qrCodeUrl ? (
          <div style={{ marginTop: 20 }}>
            <img
              src={qrCodeUrl}
              src={`data:image/png;base64,${qrCodeUrl}`}
              alt="推广二维码"
              style={{ maxWidth: '100%', height: 'auto', border: '1px solid #d9d9d9', borderRadius: 4 }}
            />
management/src/pages/company/detail/components/userManagement.jsx
@@ -1,11 +1,10 @@
import {
  buildProTableDataSource,
  sendRequest,
  showConfirm,
  showDelConfirm,
} from '@/utils/antdUtils';
import { importFile } from '@/utils/utils';
import { PageContainer, ProTable } from '@ant-design/pro-components';
import { ProTable } from '@ant-design/pro-components';
import { Button, Space, Tag, Upload, message } from 'antd';
import { useRef, useState } from 'react';
import UserModal from './userModal';
@@ -18,7 +17,6 @@
  const qrCodeModalRef = useRef();
  const [userModalVisible, setUserModalVisible] = useState(false);
  const [qrCodeModalVisible, setQrCodeModalVisible] = useState(false);
  const [currentUser, setCurrentUser] = useState({});
  const [importLoading, setImportLoading] = useState(false);
  const [fileList, setFileList] = useState([]);
@@ -120,7 +118,6 @@
              size="small"
              type="link"
              onClick={() => {
                setCurrentUser(record);
                userModalRef.current.setViewMode('view');
                userModalRef.current.refreshData(record);
                setUserModalVisible(true);
@@ -132,7 +129,6 @@
              size="small"
              type="link"
              onClick={() => {
                setCurrentUser(record);
                userModalRef.current.setViewMode('edit');
                userModalRef.current.refreshData(record);
                setUserModalVisible(true);
@@ -144,7 +140,7 @@
              size="small"
              type="link"
              onClick={() => {
                setCurrentUser(record);
                qrCodeModalRef.current.showQrCode(record);
                setQrCodeModalVisible(true);
              }}
            >
@@ -210,7 +206,6 @@
            <Button
              type='primary'
              onClick={() => {
                setCurrentUser({});
                userModalRef.current.setViewMode('add');
                userModalRef.current.refreshData({});
                setUserModalVisible(true);
@@ -249,7 +244,6 @@
        ref={qrCodeModalRef}
        visible={qrCodeModalVisible}
        onCancel={() => setQrCodeModalVisible(false)}
        userInfo={currentUser}
      />
    </div>
  );
management/src/pages/company/detail/service.js
@@ -52,4 +52,12 @@
  return request(`/t-company/system/user/deleteById/${id}`, {
    method: 'DELETE',
  });
};
// 管理人员获取二维码
export const getUserQrcode = async (params) => {
  return request('/t-company/getQrcode', {
    method: 'GET',
    params,
  });
};
management/src/pages/companyInfo/components/CompanyUserManagement.jsx
@@ -1,7 +1,7 @@
import { ProTable, Modal } from '@ant-design/pro-components';
import { ProTable } from '@ant-design/pro-components';
import { useRef, useState } from 'react';
import { Button, Space, DownloadOutlined } from 'antd';
import { getCompanyUserList } from '../service';
import { Button, Space, Modal, Spin } from 'antd';
import { getCompanyUserList, getUserQrcode } from '../service';
import { buildProTableDataSource } from '@/utils/antdUtils';
import { request } from '@umijs/max';
@@ -11,6 +11,26 @@
  const [qrCodeUrl, setQrCodeUrl] = useState('');
  const [currentRecord, setCurrentRecord] = useState(null);
  const [loading, setLoading] = useState(false);
  const [qrCodeLoading, setQrCodeLoading] = useState(false);
  // 获取二维码
  const fetchQrCode = async (record) => {
    setCurrentRecord(record);
    if (!record?.userId) return;
    setQrCodeLoading(true);
    try {
      const response = await getUserQrcode({ userId: record.userId });
      if (response && response.code === 200) {
        setQrCodeUrl(response.data);
      }
    } catch (error) {
      console.error('获取二维码失败:', error);
      message.error('获取二维码失败');
    } finally {
      setQrCodeLoading(false);
    }
  };
  // 下载二维码
  const downloadQrCode = async () => {
@@ -18,12 +38,18 @@
    setLoading(true);
    try {
      const response = await request(qrCodeUrl, {
        method: 'GET',
        responseType: 'blob',
      });
      // 将base64转换为blob并下载
      const byteCharacters = atob(qrCodeUrl);
      const byteNumbers = new Array(byteCharacters.length);
      const url = window.URL.createObjectURL(new Blob([response]));
      for (let i = 0; i < byteCharacters.length; i++) {
        byteNumbers[i] = byteCharacters.charCodeAt(i);
      }
      const byteArray = new Uint8Array(byteNumbers);
      const blob = new Blob([byteArray], { type: 'image/png' });
      const url = window.URL.createObjectURL(blob);
      const link = document.createElement('a');
      link.href = url;
      link.setAttribute('download', `推广二维码_${currentRecord?.nickName || currentRecord?.code || '未命名'}.png`);
@@ -33,6 +59,7 @@
      window.URL.revokeObjectURL(url);
    } catch (error) {
      console.error('下载二维码失败:', error);
      message.error('下载二维码失败');
    } finally {
      setLoading(false);
    }
@@ -84,9 +111,9 @@
            type="link"
            size="small"
            onClick={() => {
              setCurrentRecord(record);
              setQrCodeUrl(`/t-company-user/qrCode/${record.id}`);
              setQrCodeUrl('');
              setQrVisible(true);
              fetchQrCode(record)
            }}
          >
            推广二维码
@@ -99,7 +126,7 @@
  return (
    <>
      <ProTable
        rowKey="id"
        rowKey="userId"
        columns={columns}
        actionRef={actionRef}
        cardBordered
@@ -115,7 +142,7 @@
      <Modal
        width={500}
        centered
        destroyOnClose
        destroyOnHidden
        title="推广二维码"
        open={qrVisible}
        onCancel={() => setQrVisible(false)}
@@ -125,7 +152,6 @@
            {qrCodeUrl && (
              <Button
                type="primary"
                icon={<DownloadOutlined />}
                loading={loading}
                onClick={downloadQrCode}
              >
@@ -137,10 +163,14 @@
      >
        <div style={{ textAlign: 'center', padding: '20px 0' }}>
          <h3>{currentRecord?.nickName || currentRecord?.code} 的推广二维码</h3>
          {qrCodeUrl ? (
          {qrCodeLoading ? (
            <div style={{ marginTop: 20 }}>
              <Spin />
            </div>
          ) : qrCodeUrl ? (
            <div style={{ marginTop: 20 }}>
              <img
                src={qrCodeUrl}
                src={`data:image/png;base64,${qrCodeUrl}`}
                alt="推广二维码"
                style={{ maxWidth: '100%', height: 'auto', border: '1px solid #d9d9d9', borderRadius: 4 }}
              />
management/src/pages/companyInfo/service.js
@@ -29,4 +29,12 @@
    method: 'POST',
    data,
  });
};
// 管理人员获取二维码
export const getUserQrcode = async (params) => {
  return request('/t-company/getQrcode', {
    method: 'GET',
    params,
  });
};
management/src/pages/order/orderTransfer/components/TransferDetailModal.jsx
@@ -174,7 +174,7 @@
      onCancel={onCancel}
      footer={null}
      width={1200}
      destroyOnClose
      destroyOnHidden
    >
      {loading ? (
        <div style={{ textAlign: 'center', padding: '50px' }}>加载中...</div>
management/src/pages/order/physicalGoodsOrder/index.jsx
@@ -54,6 +54,7 @@
    {
      title: '订单编号',
      dataIndex: 'code',
      hideInSearch: true,
      key: 'code',
    },
    {
@@ -66,11 +67,13 @@
    {
      title: '下单用户',
      dataIndex: 'userName',
      hideInSearch: true,
      key: 'userName',
    },
    {
      title: '用户手机号',
      dataIndex: 'phone',
      hideInSearch: true,
      key: 'phone',
    },
    {
management/src/pages/system/basicInfoManagement/agreementManagement/index.jsx
@@ -44,6 +44,14 @@
            break;
          // 启动页图片
          case 9:
            // let imageUrl = '';
            // if (item.content.startsWith('http://') || item.content.startsWith('https://')) {
            //   imageUrl = item.content;
            // } else {
            //   // 确保路径格式正确(去除开头的斜杠,因为IMAGE_URL已经包含斜杠)
            //   item.content = item.content.replace(/^\/+/, '');
            //   imageUrl = item.content;
            // }
            setSplashScreenImage(item.content || '');
            break;
        }
@@ -121,7 +129,7 @@
  // 图片上传成功回调
  const handleUploadSuccess = (response) => {
    setSplashScreenImage(response);
    setSplashScreenImage(IMAGE_URL + response);
  };
  // 图片移除回调
@@ -268,7 +276,7 @@
            >
              {splashScreenImage ? (
                <img
                  src={IMAGE_URL + splashScreenImage}
                  src={splashScreenImage}
                  alt="启动页图片"
                  style={{ width: '100%', height: '100%', objectFit: 'cover' }}
                />
management/src/pages/user/list/index.jsx
@@ -133,13 +133,6 @@
    exportExcell('用户列表.xlsx', searchParams, '/t-app-user/exportAppUser');
  };
  // 角色映射
  const userTypeMap = {
    1: '游客',
    2: '营销员',
    3: '合伙人',
  };
  // 状态映射
  const statusMap = {
    1: { text: '使用中', color: 'success' },
@@ -219,7 +212,6 @@
        2: { text: '营销员' },
        3: { text: '合伙人' },
      },
      render: (text) => userTypeMap[text] || '-',
    },
    {
      title: '状态',
management/src/utils/utils.js
@@ -1,5 +1,4 @@
// import { getToken } from '@/utils/authority';
import pathRegexp from 'path-to-regexp';
import { parse } from 'querystring';
import { uploadFile } from './service';
/* eslint no-useless-escape:0 import/prefer-default-export:0 */
@@ -172,35 +171,6 @@
      document.body.removeChild(link);
    })
    .catch((error) => console.error('Error downloading file:', error));
};
export const getAuthorityFromRouter = (router = [], pathname) => {
  const authority = router.find(
    ({ routes, path = '/' }) =>
      (path && pathRegexp(path).exec(pathname)) ||
      (routes && getAuthorityFromRouter(routes, pathname)),
  );
  if (authority) return authority;
  return undefined;
};
export const getRouteAuthority = (path, routeData) => {
  let authorities;
  routeData.forEach((route) => {
    // match prefix
    if (pathRegexp(`${route.path}/(.*)`).test(`${path}/`)) {
      if (route.authority) {
        authorities = route.authority;
      } // exact match
      if (route.path === path) {
        authorities = route.authority || authorities;
      } // get children authority recursively
      if (route.routes) {
        authorities = getRouteAuthority(path, route.routes) || authorities;
      }
    }
  });
  return authorities;
};
// export const upload2oss = (file) => {