友情链接管理;封装上传接口

This commit is contained in:
linxd
2025-06-18 18:46:32 +08:00
parent 2a0532f775
commit 73db059e7d
24 changed files with 562 additions and 385 deletions

View File

@ -1,45 +0,0 @@
import { defineConfig } from 'umi';
import proxy from './proxy';
export default defineConfig({
proxy: proxy['UAT'],
define:{
//商城2.0跳转地址
REACT_APP_MALL_V2_URL:'http://111.198.162.67/zglt/index_hzf.jsp?id=dev',
//各系统跳转参数
REACT_APP_CLIENT_KEY : 'KgPEkttG',
// REACT_APP_CLIENT_SECRET :'ae5bdb183c502355d2055b3de73300aa73cbfdf3',
//密码加密参数
REACT_APP_PASSWORD_CIPHERMODE:'1',
REACT_APP_PASSWORD_PUBLICKEY:'0428D625CEEB71CE823BD7D78DFEE7B122F2DA5C4D21E32253AD684D0FE21810394A799639C0CDFBFEB535A1DFD6A366A637E582CE0B1466A5FE7858841135DE6B',
//当前环境
START_ENV:'UAT',
//询价查看报价跳转地址
// REACT_APP_XUNJIA_REDIRECT: 'http://10.0.204.215:8280/provider_uat',
//询价-查看报价详情-虚拟用户uid
REACT_APP_XUNJIA_UID: 'admin_entrance',
// //智慧客服ws地址
// REACT_APP_CUSTOMERSERVICE_WS_REDIRECT: 'ws://10.242.31.158:18022/api/api/biz-customer-service',
//智慧客服用户中心地址
REACT_APP_CUSTOMERSERVICE_USERCENTER: 'http://10.242.31.158:18022/auth/oauth/authorize?response_type=code',
//智慧客服client_id
REACT_APP_CUSTOMERSERVICE_CLIENT_ID: 'COsHJydx',
//智慧客服地址
REACT_APP_CUSTOMERSERVICE_REDIRECT: 'http://10.242.31.158:8632',
// //智慧客服文档中心查看图片地址
// REACT_APP_CUSTOMERSERVICE_DOC_REDIRECT: 'http://cos.gz-tst.cos.tg.unicom.local/349553515466:mall/',
// //智慧客服加密公钥私钥ciphercode
// REACT_APP_CUSTOMERSERVICE_PUBLICKEY : '0428D625CEEB71CE823BD7D78DFEE7B122F2DA5C4D21E32253AD684D0FE21810394A799639C0CDFBFEB535A1DFD6A366A637E582CE0B1466A5FE7858841135DE6B',
// REACT_APP_CUSTOMERSERVICE_PRIVATEKEY : '4F7144028D4DCF88FA50F0E2B3FFDDCF63BBE17D1700537DCE037687D3AA3DA7',
// REACT_APP_CUSTOMERSERVICE_CIPHERCODE : 1,
},
});

View File

@ -3,43 +3,7 @@ import proxy from './proxy';
export default defineConfig({
proxy: proxy['dev'],
define: {
//商城2.0跳转地址
REACT_APP_MALL_V2_URL: 'http://111.198.162.67/zglt/index_hzf.jsp?id=dev',
//各系统跳转参数
REACT_APP_CLIENT_KEY: 'KgPEkttG',
// REACT_APP_CLIENT_SECRET: 'ae5bdb183c502355d2055b3de73300aa73cbfdf3',
//密码加密参数
REACT_APP_PASSWORD_CIPHERMODE: '1',
REACT_APP_PASSWORD_PUBLICKEY: '0428D625CEEB71CE823BD7D78DFEE7B122F2DA5C4D21E32253AD684D0FE21810394A799639C0CDFBFEB535A1DFD6A366A637E582CE0B1466A5FE7858841135DE6B',
//当前环境
START_ENV: 'DEV',
//询价查看报价跳转地址
// REACT_APP_XUNJIA_REDIRECT: 'http://10.0.204.215:8080/provider_dev',
//询价-查看报价详情-虚拟用户uid
REACT_APP_XUNJIA_UID: 'admin_entrance',
//智慧客服用户中心地址
REACT_APP_CUSTOMERSERVICE_USERCENTER: 'http://10.242.31.158:8100/auth/oauth/authorize?response_type=code',
//智慧客服client_id
REACT_APP_CUSTOMERSERVICE_CLIENT_ID: 'COsHJydx',
//智慧客服地址
REACT_APP_CUSTOMERSERVICE_REDIRECT: 'http://10.242.31.158:8632',
// //智慧客服ws地址
// REACT_APP_CUSTOMERSERVICE_WS_REDIRECT: 'ws://10.242.37.148:18022/api/api/biz-customer-service',
// //智慧客服文档中心查看图片地址
// REACT_APP_CUSTOMERSERVICE_DOC_REDIRECT: 'http://cos.gz-tst.cos.tg.unicom.local/349553515466:mall/',
// //智慧客服加密公钥私钥ciphercode
// REACT_APP_CUSTOMERSERVICE_PUBLICKEY : '0428D625CEEB71CE823BD7D78DFEE7B122F2DA5C4D21E32253AD684D0FE21810394A799639C0CDFBFEB535A1DFD6A366A637E582CE0B1466A5FE7858841135DE6B',
// REACT_APP_CUSTOMERSERVICE_PRIVATEKEY : '4F7144028D4DCF88FA50F0E2B3FFDDCF63BBE17D1700537DCE037687D3AA3DA7',
// REACT_APP_CUSTOMERSERVICE_CIPHERCODE : 1,
UPLOAD_URL: '/upload',
REQUEST_BASE: '/api',
},
});

View File

@ -3,41 +3,7 @@ import proxy from './proxy';
export default defineConfig({
proxy: proxy['prod'],
define:{
//商城2.0跳转地址
REACT_APP_MALL_V2_URL:'http://111.198.162.67/zglt/index_hzf.jsp?id=dev',
//各系统跳转参数
REACT_APP_CLIENT_KEY : 'KgPEkttG',
// REACT_APP_CLIENT_SECRET :'ae5bdb183c502355d2055b3de73300aa73cbfdf3',
//密码加密参数
REACT_APP_PASSWORD_CIPHERMODE:'1',
REACT_APP_PASSWORD_PUBLICKEY:'04819CF427F9150FEEBD91E8D2346F203FC47312D212022A967D8372EA30B9581CCEEFCE2670BDDAF2E8DA1620EA73948126078ED9FF9773AA3A94EE6C80035A18',
//当前环境
START_ENV:'PROD',
//询价查看报价跳转地址
// REACT_APP_XUNJIA_REDIRECT: 'https://60.10.26.178/provider',
//询价-查看报价详情-虚拟用户uid
REACT_APP_XUNJIA_UID: 'admin_entrance',
//智慧客服用户中心地址
REACT_APP_CUSTOMERSERVICE_USERCENTER: 'https://uscm.chinaunicom.cn:18023/auth/oauth/authorize?response_type=code',
//智慧客服client_id
REACT_APP_CUSTOMERSERVICE_CLIENT_ID: 'COsHJydx',
//智慧客服地址
REACT_APP_CUSTOMERSERVICE_REDIRECT: 'https://uscm.chinaunicom.cn:18011',
// //智慧客服ws地址
// REACT_APP_CUSTOMERSERVICE_WS_REDIRECT: 'ws://uscm.unicom.local:18022/api/api/biz-customer-service',
// //智慧客服文档中心查看图片地址
// REACT_APP_CUSTOMERSERVICE_DOC_REDIRECT: 'http://cos.xx-pbc.cos.tg.unicom.local/349553515466:mall/',
// //智慧客服加密公钥私钥ciphercode
// REACT_APP_CUSTOMERSERVICE_PUBLICKEY : '0428D625CEEB71CE823BD7D78DFEE7B122F2DA5C4D21E32253AD684D0FE21810394A799639C0CDFBFEB535A1DFD6A366A637E582CE0B1466A5FE7858841135DE6B',
// REACT_APP_CUSTOMERSERVICE_PRIVATEKEY : '4F7144028D4DCF88FA50F0E2B3FFDDCF63BBE17D1700537DCE037687D3AA3DA7',
// REACT_APP_CUSTOMERSERVICE_CIPHERCODE : 1,
UPLOAD_URL: '/upload',
REQUEST_BASE: '/api',
},
});

View File

@ -1,43 +0,0 @@
import { defineConfig } from 'umi';
import proxy from './proxy';
export default defineConfig({
proxy: proxy['sim'],
define: {
//商城2.0跳转地址
REACT_APP_MALL_V2_URL: 'http://111.198.162.67/zglt/index_hzf.jsp?id=dev',
//各系统跳转参数
REACT_APP_CLIENT_KEY: 'KgPEkttG',
// REACT_APP_CLIENT_SECRET: 'ae5bdb183c502355d2055b3de73300aa73cbfdf3',
//密码加密参数
REACT_APP_PASSWORD_CIPHERMODE: '1',
REACT_APP_PASSWORD_PUBLICKEY: '0428D625CEEB71CE823BD7D78DFEE7B122F2DA5C4D21E32253AD684D0FE21810394A799639C0CDFBFEB535A1DFD6A366A637E582CE0B1466A5FE7858841135DE6B',
//当前环境
START_ENV: 'sim',
//询价查看报价跳转地址
// REACT_APP_XUNJIA_REDIRECT: 'http://10.0.204.215:8080/provider',
//询价-查看报价详情-虚拟用户uid
REACT_APP_XUNJIA_UID: 'admin_entrance',
//智慧客服用户中心地址
REACT_APP_CUSTOMERSERVICE_USERCENTER: 'http://10.242.31.158:8100/auth/oauth/authorize?response_type=code',
//智慧客服client_id
REACT_APP_CUSTOMERSERVICE_CLIENT_ID: 'COsHJydx',
//智慧客服地址
REACT_APP_CUSTOMERSERVICE_REDIRECT: 'http://10.242.31.158:8632',
// //智慧客服ws地址
// REACT_APP_CUSTOMERSERVICE_WS_REDIRECT: 'ws://10.242.31.54:18022/api/api/biz-customer-service',
// //智慧客服文档中心查看图片地址
// REACT_APP_CUSTOMERSERVICE_DOC_REDIRECT: 'http://cos.xx-pbc.cos.tg.unicom.local/349553515466:mall/',
// //智慧客服加密公钥私钥ciphercode
// REACT_APP_CUSTOMERSERVICE_PUBLICKEY : '0428D625CEEB71CE823BD7D78DFEE7B122F2DA5C4D21E32253AD684D0FE21810394A799639C0CDFBFEB535A1DFD6A366A637E582CE0B1466A5FE7858841135DE6B',
// REACT_APP_CUSTOMERSERVICE_PRIVATEKEY : '4F7144028D4DCF88FA50F0E2B3FFDDCF63BBE17D1700537DCE037687D3AA3DA7',
// REACT_APP_CUSTOMERSERVICE_CIPHERCODE : 1,
},
});

View File

@ -1,8 +1,6 @@
import { defineConfig } from 'umi';
import defaultSettings,{antdTheme} from './defaultSettings';
// import proxy from './proxy';
import PageRoutes from './router.config'
const { REACT_APP_ENV } = process.env;
export default defineConfig({
hash: true,
antd: {

View File

@ -1,96 +1,20 @@
export default {
dev: {
// '/api/wfap/v1/audit/bill/find/by/procid': {
// target: 'http://10.242.37.148:8891/',//审批单 dev环境自动审批暂时用不到
// changeOrigin: true,
// pathRewrite: { '^': '' },
// },
'/api': {
// target: 'http://10.242.37.148:18022',//连接天宫的ng
target: 'http://10.0.0.10:18013',//连接天宫的ng
// target: 'http://10.242.37.148:18022',//
target: 'http://10.0.0.10:18013',//
changeOrigin: true,
pathRewrite: { '^/api': '' },
},
},
UAT: {
'/api/core-service-ebtp-userinfo': {
target: 'http://localhost:18023',
'/upload': {
target: 'http://10.0.0.10:18013',//
changeOrigin: true,
pathRewrite: { '/api/core-service-ebtp-userinfo': '' },
},
'/api/biz-service-ebtp-bid': {
target: 'http://localhost:18003',
changeOrigin: true,
pathRewrite: { 'biz-service-ebtp-bid': '' },
},
'/api/biz-service-ebtp-extend': {
target: 'http://localhost:18018',
changeOrigin: true,
pathRewrite: { '/api/biz-service-ebtp-extend': '' },
},
'/api/biz-service-ebtp-project': {
target: 'http://localhost:18012',
changeOrigin: true,
pathRewrite: { '/api/biz-service-ebtp-project': '' },
},
'/api/sys-manager-ebtp-project': {
target: 'http://localhost:18030',
changeOrigin: true,
pathRewrite: { '/api/sys-manager-ebtp-project': '' },
},
'/api/biz-service-ebtp-rsms': {
target: 'http://localhost:18014',
changeOrigin: true,
pathRewrite: { '/api/biz-service-ebtp-rsms': '' },
},
'/api/biz-service-ebtp-agency': {
target: 'http://localhost:18099',
changeOrigin: true,
pathRewrite: { '/api/biz-service-ebtp-agency': '' },
},
'/api/biz-supplier-manage': {
target: 'http://localhost:18096',
changeOrigin: true,
pathRewrite: { '/api/biz-supplier-manage': '' },
}
// '/api/wfap/v1/audit/bill/find/by/procid': {
// target: 'http://10.242.31.158:8891/',//审批单 uat环境自动审批暂时用不到
// changeOrigin: true,
// pathRewrite: { '^': '' },
// },
// '/living/api/*': {
// // target: 'https://ai.cubigdata.cn:5001',//连接天宫的ng
// target: 'http://localhost:3000',//连接天宫的ng
// changeOrigin: true,
// pathRewrite: { '/living/api': '' },
// },
// '/api/*': {
// target: 'http://10.242.31.158:18022',//连接天宫的ng
// changeOrigin: true,
// pathRewrite: { '^': '' },
// },
// '/doc/v1.0/*': {
// target: 'http://10.242.31.158:8806',//连接天宫的ng
// changeOrigin: true,
// pathRewrite: { '^': '' },
// },
},
sim: {
// '/api/wfap/v1/audit/bill/find/by/procid': {
// target: 'http://10.242.31.158:8891/',//审批单 uat环境自动审批暂时用不到
// changeOrigin: true,
// pathRewrite: { '^': '' },
// },
'/api/*': {
target: 'http://10.242.31.54:18022',//连接天宫的ng
changeOrigin: true,
pathRewrite: { '^': '' },
pathRewrite: { '^/upload': '' },
},
},
prod: {
'/api/*': {
target: 'http://uscm.unicom.local:18022',//连接天宫的ng
target: 'http://uscm.unicom.local:18022',//
changeOrigin: true,
pathRewrite: { '^': '' },
},

View File

@ -0,0 +1,51 @@
.file-upload-wrapper {
.ant-upload-list {
margin-top: 8px;
}
.ant-upload-list-item-name {
max-width: 300px;
overflow: hidden;
text-overflow: ellipsis;
}
.ant-upload-select {
margin-bottom: 8px;
}
.file-upload-tip {
margin-top: 8px;
color: rgba(0, 0, 0, 0.45);
font-size: 12px;
line-height: 1.5;
span {
cursor: help;
text-decoration: underline dotted;
}
}
.ant-upload-drag {
padding: 16px;
.ant-upload-drag-icon {
margin-bottom: 8px;
.anticon {
color: #40a9ff;
font-size: 48px;
}
}
.ant-upload-text {
margin: 0 0 4px;
color: rgba(0, 0, 0, 0.85);
font-size: 16px;
}
.ant-upload-hint {
color: rgba(0, 0, 0, 0.45);
font-size: 14px;
}
}
}

View File

@ -0,0 +1,226 @@
import React, { useState, useEffect } from 'react';
import { Upload, Button, message, Tooltip } from 'antd';
import { UploadOutlined, InboxOutlined } from '@ant-design/icons';
import type { UploadFile, UploadProps } from 'antd/es/upload/interface';
import { validateFileSize } from '@/utils/utils';
import { useIntl } from 'umi';
import './FileUpload.less';
export interface FileUploadProps {
value?: UploadFile[];
onChange?: (fileList: UploadFile[]) => void;
maxCount?: number;
maxSize?: number;
allowedTypes?: string[];
listType?: 'text' | 'picture' | 'picture-card';
buttonText?: string;
disabled?: boolean;
accept?: string;
showUploadList?:
| boolean
| { showPreviewIcon?: boolean; showRemoveIcon?: boolean; showDownloadIcon?: boolean };
isDragger?: boolean;
tip?: string;
action?: string;
}
// 上传接口返回数据格式
interface UploadResponseData {
fileName: string;
fileSize: string;
filePath: string;
fileType: string;
url: string;
}
// API响应格式
interface ApiResponse<T> {
code: number;
success: boolean;
message: string;
data: T;
}
const FileUpload: React.FC<FileUploadProps> = ({
value,
onChange,
maxCount = 1,
maxSize = 5,
allowedTypes = ['*'],
listType = 'text',
buttonText,
disabled = false,
accept,
showUploadList = true,
isDragger = false,
tip,
action,
}) => {
const actionUrl = action ? `${UPLOAD_URL}${action}` : `${UPLOAD_URL}/api/files/upload`;
const intl = useIntl();
const [fileList, setFileList] = useState<UploadFile[]>([]);
// 监听value变化
useEffect(() => {
// 处理字符串URL值这是关键修复
if (typeof value === 'string' && value) {
const file: Partial<UploadFile> = {
uid: '-1',
name: 'image.jpg',
status: 'done',
url: value,
size: 0,
type: 'image/jpeg',
};
setFileList([file as UploadFile]);
return;
}
// 处理空值
if (!value || (Array.isArray(value) && value.length === 0)) {
setFileList([]);
return;
}
// 处理正常的文件数组
if (Array.isArray(value)) {
setFileList(value);
return;
}
// 处理对象格式
if (typeof value === 'object') {
// 处理fileList属性
if ('fileList' in value && Array.isArray((value as any).fileList)) {
const files = (value as { fileList: UploadFile[] }).fileList;
setFileList(files);
return;
}
// 尝试作为单个文件对象处理
if ('uid' in value && 'status' in value) {
setFileList([value as unknown as UploadFile]);
return;
}
console.warn('FileUpload: Unrecognized object format', value);
setFileList([]);
return;
}
// 无法识别的格式
console.warn('FileUpload component: unrecognized value format:', value);
setFileList([]);
}, [value]);
const defaultButtonText = intl.formatMessage({
id: 'component.fileUpload.buttonText',
defaultMessage: '上传文件',
});
// 处理上传接口返回的数据格式
const getFileUrl = (file: UploadFile) => {
// 如果response是API的标准响应格式
if (file.response && typeof file.response === 'object') {
// 首先检查是否是标准API响应格式
if ('success' in file.response && 'data' in file.response) {
const response = file.response as ApiResponse<UploadResponseData>;
if (response.success && response.data) {
return response.data.url;
}
}
// 如果response直接包含url属性
else if ('url' in file.response) {
return (file.response as UploadResponseData).url;
}
}
// 回退到文件的url或thumbUrl
return file.url || file.thumbUrl;
};
const handleChange: UploadProps['onChange'] = ({ fileList: newFileList }) => {
const filteredList = newFileList.filter((file) => file.status !== 'error');
// 为每个文件添加正确的url
const processedList = filteredList.map(file => {
if (file.status === 'done' && file.response && !file.url) {
return {
...file,
url: getFileUrl(file)
};
}
return file;
});
setFileList(processedList);
if (onChange) {
onChange(processedList);
}
};
const beforeUpload = (file: File) => {
return validateFileSize(file, maxSize, allowedTypes);
};
const UploadComponent = isDragger ? Upload.Dragger : Upload;
const uploadProps = {
fileList,
onChange: handleChange,
beforeUpload,
listType,
maxCount,
disabled,
accept,
showUploadList,
action: actionUrl,
};
const renderUploadButton = () => {
if (fileList.length >= maxCount) {
return null;
}
if (isDragger) {
return (
<>
<p className="ant-upload-drag-icon">
<InboxOutlined />
</p>
<p className="ant-upload-text">
{buttonText ||
intl.formatMessage({
id: 'component.fileUpload.dragText',
defaultMessage: '点击或拖拽文件到此区域上传',
})}
</p>
{tip && <p className="ant-upload-hint">{tip}</p>}
</>
);
}
return (
<Button icon={<UploadOutlined />} disabled={disabled}>
{buttonText || defaultButtonText}
</Button>
);
};
return (
<div className="file-upload-wrapper">
<UploadComponent {...uploadProps}>{renderUploadButton()}</UploadComponent>
{!isDragger && tip && (
<div className="file-upload-tip">
<Tooltip title={tip}>
<span>{tip}</span>
</Tooltip>
</div>
)}
</div>
);
};
export default FileUpload;

View File

@ -0,0 +1,127 @@
# FileUpload 文件上传组件
基于Ant Design Upload组件封装的文件上传组件支持文件类型和大小校验。
## 组件特性
- 支持文件类型校验
- 支持文件大小限制
- 支持拖拽上传
- 支持上传多个文件
- 支持国际化
- 支持自定义提示信息
- 支持表单受控模式
## 使用示例
### 基本使用
```tsx
import React from 'react';
import { Form } from 'antd';
import FileUpload from '@/components/FileUpload';
const App: React.FC = () => {
const [form] = Form.useForm();
return (
<Form form={form}>
<Form.Item
name="files"
label="上传文件"
rules={[{ required: true, message: '请上传文件' }]}
>
<FileUpload maxSize={5} allowedTypes={['pdf', 'doc', 'docx']} />
</Form.Item>
</Form>
);
};
export default App;
```
### 拖拽上传
```tsx
import React from 'react';
import { Form } from 'antd';
import FileUpload from '@/components/FileUpload';
import { useIntl } from 'umi';
const App: React.FC = () => {
const [form] = Form.useForm();
const intl = useIntl();
const fileTip = intl.formatMessage(
{ id: 'component.fileUpload.fileTypeTip' },
{ types: 'PDF, Word' }
);
return (
<Form form={form}>
<Form.Item
name="files"
label="上传文件"
rules={[{ required: true, message: '请上传文件' }]}
>
<FileUpload
maxSize={10}
allowedTypes={['pdf', 'doc', 'docx']}
isDragger={true}
tip={fileTip}
/>
</Form.Item>
</Form>
);
};
export default App;
```
### 图片上传
```tsx
import React from 'react';
import { Form } from 'antd';
import FileUpload from '@/components/FileUpload';
const App: React.FC = () => {
const [form] = Form.useForm();
return (
<Form form={form}>
<Form.Item
name="images"
label="上传图片"
rules={[{ required: true, message: '请上传图片' }]}
>
<FileUpload
maxSize={2}
allowedTypes={['jpg', 'png']}
listType="picture-card"
tip="支持jpg, png格式大小不超过2MB"
/>
</Form.Item>
</Form>
);
};
export default App;
```
## API
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| value | 文件列表 | UploadFile[] | [] |
| onChange | 文件列表改变时的回调 | (fileList: UploadFile[]) => void | - |
| maxCount | 最大上传数量 | number | 1 |
| maxSize | 文件大小限制MB | number | 5 |
| allowedTypes | 允许的文件类型 | string[] | ['*'] |
| listType | 上传列表的样式 | 'text' \| 'picture' \| 'picture-card' | 'text' |
| buttonText | 上传按钮文字 | string | '上传文件' |
| disabled | 是否禁用 | boolean | false |
| accept | 接受的文件类型 | string | - |
| showUploadList | 是否展示文件列表 | boolean \| { showPreviewIcon?: boolean; showRemoveIcon?: boolean; showDownloadIcon?: boolean } | true |
| isDragger | 是否启用拖拽上传 | boolean | false |
| tip | 提示文字 | string | - |

View File

@ -0,0 +1,4 @@
import FileUpload from './FileUpload';
export default FileUpload;
export type { FileUploadProps } from './FileUpload';

View File

@ -1,5 +1,9 @@
import { useIntl } from 'umi';
export enum LinkStatusMap {
ENABLED = '0',
DISABLED = '1',
}
export const useFriendLinkDict = () => {
const intl = useIntl();
@ -7,11 +11,11 @@ export const useFriendLinkDict = () => {
const linkStatusOptions = [
{
label: intl.formatMessage({ id: 'friendLink.enabled' }),
value: '1',
value: LinkStatusMap.ENABLED,
},
{
label: intl.formatMessage({ id: 'friendLink.disabled' }),
value: '0',
value: LinkStatusMap.DISABLED,
},
];

View File

@ -31,4 +31,13 @@ export default {
// Buttons
"common.confirm": "Confirm",
"common.cancel": "Cancel",
// FileUpload component
'component.fileUpload.buttonText': 'Upload File',
'component.fileUpload.dragText': 'Click or drag file to this area to upload',
'component.fileUpload.fileTypeTip': 'Supported file types: {types}',
'component.fileUpload.fileSizeTip': 'File size should not exceed {size}MB',
'component.fileUpload.uploadFailed': 'Upload failed',
'component.fileUpload.parseError': 'Failed to parse response',
'component.fileUpload.networkError': 'Network error',
};

View File

@ -51,6 +51,7 @@ export default {
'friendLink.enable.confirm.content': 'Are you sure you want to enable this link?',
'friendLink.disable.confirm.title': 'Disable Confirmation',
'friendLink.disable.confirm.content': 'Are you sure you want to disable this link?',
'friendLink.detail.failed': 'Failed to get link details',
// Friendly Link Category Management
'friendLink.category.title': 'Friendly Link Category Management',

View File

@ -22,6 +22,7 @@ export default {
'helpManage.cancel': 'Cancel',
'helpManage.confirmDelete': 'Are you sure you want to delete this help item?',
'helpManage.batchDelete': 'Are you sure you want to delete the selected help items?',
'helpManage.batchDeleteButton': 'Batch Delete',
'helpManage.confirmPublish': 'Are you sure you want to publish this help item?',
'helpManage.confirmUnpublish': 'Are you sure you want to unpublish this help item?',
'helpManage.confirmTop': 'Are you sure you want to top this help item?',

View File

@ -23,9 +23,10 @@ export default {
'policyManage.confirmDeleteBatch': 'Are you sure you want to delete the selected policies?',
'policyManage.confirmPublish': 'Are you sure you want to publish this policy?',
'policyManage.confirmUnpublish': 'Are you sure you want to unpublish this policy?',
'policyManage.batchDelete': 'Batch Delete',
'policyManage.deleteSuccess': 'Delete successful',
'policyManage.deleteFailed': 'Delete failed',
'policyManage.batchDeleteSuccess': 'Delete successful',
'policyManage.batchDeleteSuccess': 'Batch delete successful',
'policyManage.batchDeletePartialFailed': 'Some deletions failed, please refresh and try again',
'policyManage.batchDeleteFailed': 'Batch delete failed',
'policyManage.publishSuccess': 'Publish successful',

View File

@ -62,7 +62,7 @@ export default {
'readQuestion.search': 'Search',
'readQuestion.reset': 'Reset',
'readQuestion.confirmDelete': 'Are you sure you want to delete this question?',
'readQuestion.batchDelete': 'Delete Selected',
'readQuestion.batchDelete': 'Batch Delete',
'readQuestion.viewDetails': 'View Question Details',
'readQuestion.questionDetails': 'Question Details',
'readQuestion.answerDetails': 'Reply Details',
@ -103,7 +103,7 @@ export default {
'unreadQuestion.search': 'Search',
'unreadQuestion.reset': 'Reset',
'unreadQuestion.confirmDelete': 'Are you sure you want to delete this question?',
'unreadQuestion.batchDelete': 'Delete Selected',
'unreadQuestion.batchDelete': 'Batch Delete',
'unreadQuestion.replyModal': 'Reply to Question',
'unreadQuestion.questionContent': 'Question Content',
'unreadQuestion.answerContent': 'Reply Content',

View File

@ -31,4 +31,13 @@ export default {
// 按钮
"common.confirm": "确定",
"common.cancel": "取消",
// FileUpload组件
'component.fileUpload.buttonText': '上传文件',
'component.fileUpload.dragText': '点击或拖拽文件到此区域上传',
'component.fileUpload.fileTypeTip': '支持的文件类型:{types}',
'component.fileUpload.fileSizeTip': '文件大小不超过{size}MB',
'component.fileUpload.uploadFailed': '上传失败',
'component.fileUpload.parseError': '解析响应失败',
'component.fileUpload.networkError': '网络错误',
};

View File

@ -51,6 +51,7 @@ export default {
'friendLink.enable.confirm.content': '确定要启用该友情链接吗?',
'friendLink.disable.confirm.title': '禁用确认',
'friendLink.disable.confirm.content': '确定要禁用该友情链接吗?',
'friendLink.detail.failed': '获取友情链接详情失败',
// 友情链接分类管理
'friendLink.category.title': '友情链接分类管理',

View File

@ -1,12 +1,7 @@
import React, { useState, useEffect } from 'react';
import { useIntl } from 'umi';
import { Table, Button, Modal, Form, Input, Space, message, Upload, Tag, TreeSelect } from 'antd';
import {
PlusOutlined,
DeleteOutlined,
ExclamationCircleOutlined,
UploadOutlined,
} from '@ant-design/icons';
import { Table, Button, Modal, Form, Input, Space, message, Tag, TreeSelect } from 'antd';
import { PlusOutlined, DeleteOutlined, ExclamationCircleOutlined } from '@ant-design/icons';
import type { UploadFile } from 'antd/es/upload/interface';
import {
getLinkList,
@ -16,8 +11,10 @@ import {
enableLink,
disableLink,
getAllCategories,
getLinkDetail,
} from '@/servers/api/friendLink';
import { useFriendLinkDict } from '@/dicts/friendLinkDict';
import { LinkStatusMap } from '@/dicts/friendLinkDict';
import FileUpload from '@/components/FileUpload';
import './friendLinkManage.less';
const FriendLinkManage: React.FC = () => {
@ -35,7 +32,6 @@ const FriendLinkManage: React.FC = () => {
total: 0,
});
const [form] = Form.useForm();
const [fileList, setFileList] = useState<UploadFile[]>([]);
const [categoryMap, setCategoryMap] = useState<Record<string, string>>({});
// 获取友情链接列表
@ -50,12 +46,7 @@ const FriendLinkManage: React.FC = () => {
});
if (res.data && res.success) {
// 处理分类名称
const links = res.data.records.map((item: API.LinkItem) => ({
...item,
categoryName: categoryMap[item.classificationId] || '',
}));
setLinkData(links);
setLinkData(res.data.records);
setPagination({
current: res.data.current,
pageSize: res.data.size,
@ -74,28 +65,7 @@ const FriendLinkManage: React.FC = () => {
try {
const res = await getAllCategories();
if (res.data && res.success) {
const flattenCategories = (
categories: API.CategoryType[],
result: API.CategoryType[] = [],
): API.CategoryType[] => {
categories.forEach((category) => {
result.push(category);
if (category.children && category.children.length > 0) {
flattenCategories(category.children, result);
}
});
return result;
};
const flatCategories = flattenCategories(res.data);
setCategoryData(res.data);
// 创建分类ID到名称的映射
const categoryNameMap: Record<string, string> = {};
flatCategories.forEach((category) => {
categoryNameMap[category.id] = category.name;
});
setCategoryMap(categoryNameMap);
}
} catch (error) {
console.error('Failed to fetch category list', error);
@ -119,7 +89,6 @@ const FriendLinkManage: React.FC = () => {
setIsEdit(false);
setCurrentId('');
form.resetFields();
setFileList([]);
form.setFieldsValue({
orderBy: 1,
});
@ -132,30 +101,34 @@ const FriendLinkManage: React.FC = () => {
setCurrentId(record.id);
setModalVisible(true);
// 设置表单初始值
form.setFieldsValue({
name: record.name,
nameEn: record.nameEn,
url: record.url,
orderBy: parseInt(record.orderBy),
classificationId: record.classificationId,
});
// 清空表单
form.resetFields();
// 设置上传文件列表
if (record.thumbnail) {
setFileList([
{
uid: '-1',
name: 'thumbnail.png',
status: 'done',
url: record.thumbnail,
type: 'image/png',
size: 0,
} as UploadFile,
]);
} else {
setFileList([]);
}
// 调用详情接口获取完整数据
getLinkDetail(record.id)
.then((res: API.Response<API.LinkType>) => {
if (res.success && res.data) {
const detail = res.data;
console.log('Detail data:', detail);
// 设置表单初始值包括缩略图URL
form.setFieldsValue({
name: detail.name,
nameEn: detail.nameEn,
url: detail.url,
orderBy: parseInt(detail.orderBy),
classificationId: detail.classificationId,
// 设置为URL字符串组件会自动处理
thumbnail: detail.thumbnail,
});
} else {
message.error(intl.formatMessage({ id: 'friendLink.detail.failed' }));
}
})
.catch((error: any) => {
console.error('Failed to get link detail', error);
message.error(intl.formatMessage({ id: 'friendLink.detail.failed' }));
});
};
// 处理删除
@ -220,22 +193,24 @@ const FriendLinkManage: React.FC = () => {
// 添加确认对话框
Modal.confirm({
title: intl.formatMessage({
id: record.status === '1'
? 'friendLink.disable.confirm.title'
: 'friendLink.enable.confirm.title'
id:
record.status === LinkStatusMap.ENABLED
? 'friendLink.disable.confirm.title'
: 'friendLink.enable.confirm.title',
}),
icon: <ExclamationCircleOutlined />,
content: intl.formatMessage({
id: record.status === '1'
? 'friendLink.disable.confirm.content'
: 'friendLink.enable.confirm.content'
id:
record.status === LinkStatusMap.ENABLED
? 'friendLink.disable.confirm.content'
: 'friendLink.enable.confirm.content',
}),
okText: intl.formatMessage({ id: 'common.confirm' }),
cancelText: intl.formatMessage({ id: 'common.cancel' }),
onOk: async () => {
try {
let res;
if (record.status === '1') {
if (record.status === LinkStatusMap.ENABLED) {
// 当前启用,需要禁用
res = await disableLink(record.id);
} else {
@ -245,7 +220,7 @@ const FriendLinkManage: React.FC = () => {
if (res.success) {
message.success(
record.status === '1'
record.status === LinkStatusMap.ENABLED
? intl.formatMessage({ id: 'friendLink.disable.success' })
: intl.formatMessage({ id: 'friendLink.enable.success' }),
);
@ -261,19 +236,15 @@ const FriendLinkManage: React.FC = () => {
// 处理表单提交
const handleModalSubmit = () => {
form.validateFields().then(async (values) => {
console.log('Form values:', values);
try {
// 确保thumbnail有值
if (fileList.length === 0) {
message.error(intl.formatMessage({ id: 'friendLink.form.thumbnail.required' }));
return;
}
// 准备提交数据
// 现在thumbnail字段已经直接是URL字符串不需要额外处理
const formData = {
...values,
thumbnail: fileList[0].url || fileList[0].thumbUrl || '',
};
console.log('Submitting form data:', formData);
let res;
if (isEdit) {
// 编辑模式
@ -301,11 +272,6 @@ const FriendLinkManage: React.FC = () => {
});
};
// 处理文件上传变化
const handleFileChange = (info: { fileList: UploadFile[] }) => {
setFileList(info.fileList);
};
// 表格列定义
const columns = [
{
@ -345,7 +311,7 @@ const FriendLinkManage: React.FC = () => {
dataIndex: 'status',
key: 'status',
render: (text: string) =>
text === '1' ? (
text === LinkStatusMap.ENABLED ? (
<Tag color="green">{intl.formatMessage({ id: 'friendLink.enabled' })}</Tag>
) : (
<Tag color="red">{intl.formatMessage({ id: 'friendLink.disabled' })}</Tag>
@ -361,7 +327,7 @@ const FriendLinkManage: React.FC = () => {
{intl.formatMessage({ id: 'friendLink.edit' })}
</Button>
<Button type="link" onClick={() => handleToggleStatus(record)}>
{record.status === '1'
{record.status === LinkStatusMap.ENABLED
? intl.formatMessage({ id: 'friendLink.disable' })
: intl.formatMessage({ id: 'friendLink.enable' })}
</Button>
@ -381,26 +347,28 @@ const FriendLinkManage: React.FC = () => {
return (
<div className="friend-link-manage-container common-container">
<div className="action-bar">
<div className="filter-action-row">
<Button type="primary" icon={<PlusOutlined />} onClick={handleAdd}>
{intl.formatMessage({ id: 'friendLink.add' })}
</Button>
<Button
danger
icon={<DeleteOutlined />}
onClick={handleBatchDelete}
disabled={selectedRowKeys.length === 0}
>
{intl.formatMessage({ id: 'friendLink.batchDelete' })}
</Button>
{selectedRowKeys.length > 0 && (
<span className="selected-count">
{intl.formatMessage(
{ id: 'friendLink.selectedCount' },
{ count: selectedRowKeys.length },
)}
</span>
)}
<div className="right-buttons">
<Button
danger
icon={<DeleteOutlined />}
onClick={handleBatchDelete}
disabled={selectedRowKeys.length === 0}
>
{intl.formatMessage({ id: 'friendLink.batchDelete' })}
</Button>
{selectedRowKeys.length > 0 && (
<span className="selected-count">
{intl.formatMessage(
{ id: 'friendLink.selectedCount' },
{ count: selectedRowKeys.length },
)}
</span>
)}
</div>
</div>
<div className="content-area">
@ -496,18 +464,30 @@ const FriendLinkManage: React.FC = () => {
<Input placeholder={intl.formatMessage({ id: 'friendLink.form.url.placeholder' })} />
</Form.Item>
<Form.Item name="thumbnail" label={intl.formatMessage({ id: 'friendLink.thumbnail' })}>
<Upload
listType="picture"
<Form.Item
name="thumbnail"
label={intl.formatMessage({ id: 'friendLink.thumbnail' })}
getValueFromEvent={(e) => {
return e[0].url;
}}
rules={[
{
required: true,
message: intl.formatMessage({ id: 'friendLink.form.thumbnail.required' }),
},
]}
>
<FileUpload
maxCount={1}
fileList={fileList}
onChange={handleFileChange}
beforeUpload={() => false}
>
<Button icon={<UploadOutlined />}>
{intl.formatMessage({ id: 'friendLink.form.thumbnail.upload' })}
</Button>
</Upload>
maxSize={2}
allowedTypes={['jpg', 'png']}
listType="picture"
tip={intl.formatMessage(
{ id: 'component.fileUpload.fileTypeTip' },
{ types: 'JPG, PNG' },
)}
buttonText={intl.formatMessage({ id: 'friendLink.form.thumbnail.upload' })}
/>
</Form.Item>
<Form.Item

View File

@ -96,3 +96,10 @@ export async function disableLink(id: string) {
method: 'POST',
});
}
// 获取友情链接详情
export async function getLinkDetail(id: string) {
return request(`/portals/links/${id}`, {
method: 'GET',
});
}

View File

@ -439,6 +439,18 @@ declare namespace API {
createTime?: string;
updateTime?: string;
categoryName?: string; // 用于显示
// 以下是详情接口返回的额外字段
createBy?: string;
createDate?: string | null;
updateBy?: string | null;
updateDate?: string | null;
tenantId?: string | null;
tenantName?: string | null;
deleteFlag?: string | null;
lastUpdateTime?: string | null;
delFlag?: string;
remark?: string | null;
basePageRequest?: null;
}
// 友情链接分类请求参数
export interface CategoryRequest {

23
src/typings.d.ts vendored
View File

@ -40,25 +40,6 @@ declare interface Window {
declare let ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION: 'site' | undefined;
declare const REACT_APP_ENV: 'test' | 'dev' | 'pre' | false;
/**ENV-商城2.0跳转地址 */
declare const REACT_APP_MALL_V2_URL: string
/**ENV-系统跳转参数KEY */
declare const REACT_APP_CLIENT_KEY: string
/**ENV-系统跳转参数SECRET */
// declare const REACT_APP_CLIENT_SECRET: string
/**ENV-密码加密参数CIPHERMODE */
declare const REACT_APP_PASSWORD_CIPHERMODE: string
/**ENV-密码加密参数PUBLICKEY */
declare const REACT_APP_PASSWORD_PUBLICKEY: string
/**ENV-环境参数 */
declare const START_ENV: string
/**ENV-询价查看报价跳转地址 */
// declare const REACT_APP_XUNJIA_REDIRECT: string
/**ENV-询价查看报价跳转地址 */
declare const REACT_APP_XUNJIA_UID: string
//智慧客服
declare const REACT_APP_CUSTOMERSERVICE_USERCENTER: string
declare const REACT_APP_CUSTOMERSERVICE_CLIENT_ID: string
declare const REACT_APP_CUSTOMERSERVICE_REDIRECT: string
declare const REQUEST_BASE: string;
declare const UPLOAD_URL: string;

View File

@ -52,7 +52,7 @@ const codeMessage = {
* 配置request请求时的默认参数
*/
const request = extend({
prefix: '/api',
prefix: REQUEST_BASE,
// errorHandler, // 默认错误处理
credentials: 'include' // 默认请求是否带上cookie
});

View File

@ -19,7 +19,6 @@ const FileTypeMap = {
}
export const validateFileSize = (file: File, maxSize: number,type: string[]) => {
console.log(file)
const { LIST_IGNORE } = Upload;
const isLtMaxSize = file.size / 1024 / 1024 < maxSize;
if (!isLtMaxSize) {