ai资源广场bug fix

This commit is contained in:
谢思 2026-05-26 14:23:47 +08:00
parent f16c9ac788
commit e74b7eedb8
12 changed files with 209 additions and 70 deletions

View File

@ -1,5 +1,5 @@
import React, { useState, useEffect, Fragment, useRef } from "react";
import { Input, Select, Upload, message, Form, Checkbox, Tree, Spin } from "antd";
import { Input, Select, Upload, message, Form, Checkbox, Tree, Spin, Alert } from "antd";
import './index.scss';
import { aiCreateSteps, aiResouceType, aiResouceTypeKey, convertToTreeData } from "../static";
import { Link } from "react-router-dom";
@ -15,7 +15,7 @@ function CreateResource(props) {
const { getFieldDecorator, getFieldsValue, setFieldsValue, validateFields } = form;
const type = params.type || aiResouceType[0].key;
const sourceId = params.id; //id
const { user_id } = current_user || {}
const { user_id, login } = current_user || {}
const { id: aiZoneId } = aiZoneInfo || {}
const [loading, setLoading] = useState(false);
@ -28,6 +28,7 @@ function CreateResource(props) {
const [cate, setCate] = useState();
const [info, setInfo] = useState();
const [uploadedFile, setUploadedFile] = useState(null);
const [remark, setRemark] = useState('');
const uploadRef = useRef(null);
const typeDetail = type && aiResouceType.find(i => i.key === type);
@ -38,6 +39,13 @@ function CreateResource(props) {
const skillTreeData = fileTreeJson && convertToTreeData(JSON.parse(fileTreeJson).children)
//
useEffect(() => {
if (!login) {
history.push(`/login?go_page=${encodeURIComponent(window.location.pathname)}`);
}
}, [login]);
// 5
useEffect(() => {
document.title = `发布新资源`;
@ -104,7 +112,11 @@ function CreateResource(props) {
if (sourceId) {
getAISourceById(sourceId).then(res => {
if (res && res.data && res.data.code === 200) {
const { fileIds, extendData, name, summary, domainId, fileList } = res.data.data || {}
const { fileIds, extendData, name, summary, domainId, fileList, auditStatus, remark: remarkText } = res.data.data || {}
// auditStatus=2
if (auditStatus === '2') {
setRemark(remarkText || "请修改后重新提交");
}
const fileInfo = {
fileId: fileList[0]?.fileId,
fileOriginName: fileList[0]?.fileOriginName,
@ -121,8 +133,8 @@ function CreateResource(props) {
file: {
response: {
fileId: fileIds,
skillMdContent: extendData.skill.content,
fileTreeJson: JSON.stringify(extendData.skill.fileTree)
skillMdContent: extendData.SKILL.content,
fileTreeJson: JSON.stringify(extendData.SKILL.fileTree)
}
}
} : fileValue.fileId = fileIds
@ -145,7 +157,7 @@ function CreateResource(props) {
const compelete = (response) => {
if (response && response.data && response.data.code === 200) {
message.success("操作成功");
history.push(`/ai/${type}`);
history.push(`/ai/${type}/my`);
}
}
validateFields((err, values) => {
@ -249,6 +261,7 @@ function CreateResource(props) {
if (!!sourceId) {
e.preventDefault();
e.stopPropagation();
return;
}
form.resetFields();
if (uploadRef.current) {
@ -290,6 +303,17 @@ function CreateResource(props) {
<div className="ai_create_form_header mb30">
<div className="font-20 font-bd mb5">基本信息</div>
<div className="font-15 color768">{typeDetail.createIntro}</div>
{remark && (
<div className="audit-rejected-wrapper mt20">
<Alert
message="审核驳回"
description={remark}
type="error"
showIcon
closable
/>
</div>
)}
</div>
<Form
form={form}
@ -331,6 +355,7 @@ function CreateResource(props) {
<span className="ml10 mr10 mt10 font-18">/</span>
<Form.Item
label="项目名称"
className="flex1"
>
{getFieldDecorator('name', {
rules: [{
@ -385,13 +410,13 @@ function CreateResource(props) {
</Form.Item>
{codeCheckBox("ignoreFlag", ".gitignore", ignoreFlag, "ignore", gitignoreList)}
{codeCheckBox("licenseFlag", "开源许可证", licenseFlag, "license", licensesList)}
<Form.Item
{/* <Form.Item
className="privatePart"
>
{getFieldDecorator('private')(
<Checkbox value="limit">将项目设为私有<span className="font-13 color-grey-9">(只有项目所有人或拥有权限的项目成员才能看到)</span></Checkbox>
)}
</Form.Item >
</Form.Item > */}
{codeCheckBox("categoreFlag", "项目类别", categoreFlag, "project_category", categoryList)}
{codeCheckBox("languageFlag", "项目语言", languageFlag, "project_language", languageList)}
</Fragment> : <Fragment>
@ -399,7 +424,7 @@ function CreateResource(props) {
{getFieldDecorator('file', {
rules: [{ required: true, message: "请上传文件" }],
})(
fileByupload ? <Spin spinning={loading}>
responseByupload ? <Spin spinning={loading}>
<div className="edu-txt-right mb10"><a onClick={() => {
form.setFieldsValue({
file: undefined
@ -408,7 +433,6 @@ function CreateResource(props) {
<Tree.DirectoryTree
className="ai_tree_file"
treeData={skillTreeData}
defaultExpandAll={true}
selectable={false}
/>
</Spin> : <Upload
@ -456,7 +480,7 @@ function CreateResource(props) {
<Form.Item label="简介">
{getFieldDecorator('summary', {
rules: [{
required: true, message: '请填写简介内容'
required: false, message: '请填写简介内容'
}],
})(
<Input.TextArea maxLength={500} placeholder="请输入简介内容不超过500字" autoSize={{ minRows: 2 }} />
@ -489,9 +513,11 @@ function CreateResource(props) {
})(
<div>
<ChunkedUpload
hierarchy={`model/${type}`}
accept=".zip"
defaultFile={uploadedFile}
ref={uploadRef}
fileType={aiResouceTypeKey[type] || 'resource'}
fileType={"ai-resource"}
disabled={loading}
maxSize={50 * 1024 * 1024 * 1024} // 50GB
onUploadComplete={(fileInfo) => {
@ -520,9 +546,9 @@ function CreateResource(props) {
</Fragment>}
{/* 提交按钮 */}
<Form.Item className="mt40">
<a className="btn-black mr25" loading={loading} onClick={submit}> </a>
<a className="btn-outline" href={sourceId ? `/ai/${type}/my` : `/ai/${type}`}> </a>
<Form.Item className="mt36">
<a className="btn-outline mr25" href={sourceId ? `/ai/${type}/my` : `/ai/${type}`}> </a>
<a className="btn-black" loading={loading} onClick={submit}> </a>
</Form.Item>
</Form>
</div>

View File

@ -50,34 +50,6 @@
}
}
// 工具类
.mb30 {
margin-bottom: 30px;
}
.mb40 {
margin-bottom: 40px;
}
.mb5 {
margin-bottom: 5px;
}
.mb8 {
margin-bottom: 8px;
}
.ml10 {
margin-left: 10px;
}
.mr20 {
margin-right: 20px;
}
.mt40 {
margin-top: 40px;
}
// 主体内容
.ai_create_content {
@ -151,6 +123,13 @@
.ant-upload.ant-upload-select {
width: 100%;
}
// 审核驳回提示样式
.audit-rejected-wrapper {
.ant-alert-error {
border-radius: 8px;
}
}
}
.ai_create_upload {

View File

@ -1,12 +1,12 @@
import React, { useState, useEffect } from "react";
import { Tabs, Tag, Spin, message, Divider, Tree, Tooltip } from "antd";
import axios from "axios";
import React, { useState, useEffect, Fragment } from "react";
import { Tabs, Tag, Spin, message, Divider, Tree, Tooltip, Button, Badge } from "antd";
import RenderHtml from "../../components/render-html";
import "./index.scss";
import { getlikedSource, getAISourceById, likeAISource } from "../../forge/Information/api";
import { aiResouceTypeKey, convertToTreeData } from "../static";
import { getlikedSource, getAISourceById, likeAISource, editModel, editDataset } from "../../forge/Information/api";
import { aiResouceStatus, aiResouceType, aiResouceTypeKey, convertToTreeData } from "../static";
import skillIcon from '../image/icon7.png';
import CommentsList from "../../components/commentsByZone";
import TpmMdEditor from "../../modules/tpm/challengesnew/tpm-md-editor";
const { TabPane } = Tabs;
@ -18,18 +18,26 @@ function Detail(props) {
//
const [reload, setReload] = useState(undefined);
const [detail, setDetail] = useState({});
const [files, setFiles] = useState(undefined);
const [comments, setComments] = useState(undefined);
const [loading, setLoading] = useState(true);
const [activeTab, setActiveTab] = useState("introduction");
const [branch, setBranch] = useState("master");
const [liked, setLiked] = useState(false);
const [isEditor, setIsEditor] = useState(false);
const [editContent, setEditContent] = useState("");
const [saving, setSaving] = useState(false);
const { extendData={}, createBy, auditStatus } = detail;
const status = aiResouceStatus[auditStatus] || {};
const typeDetail = type && aiResouceType.find(i => i.key === type);
const name = typeDetail && typeDetail.name
const { extendData={} } = detail;
const dataByType = extendData[aiResouceTypeKey[type]] || {}
const content = dataByType.content || dataByType.readmeContent
const introContent = String(content || '<div className="ai_detail_nodata_text">暂无 README 内容</div>').replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n/, '');
const introContent = String(content || `<div className="ai_detail_nodata_text">当前${name}未提供详细介绍</div>`).replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n/, '');
const treeData = dataByType && dataByType.fileTree && convertToTreeData(dataByType.fileTree.children)
//
const isPublisher = login && createBy && login === createBy;
// ID
useEffect(() => {
@ -60,6 +68,41 @@ function Detail(props) {
})
}
//
const handleToggleEdit = () => {
if (isEditor) {
//
setEditContent(content || "");
setIsEditor(false);
} else {
//
setEditContent(content || "");
setIsEditor(true);
}
}
//
const handleSave = () => {
setSaving(true);
const editFun = type === "models" ? editModel : editDataset
editFun({
id: +id,
readmeContent: editContent
}).then(response => {
if (response && response.data && response.data.code === 200) {
message.success("保存成功");
setIsEditor(false);
setReload(Date.now());
} else {
message.error(response?.data?.msg || "保存失败");
}
}).catch(() => {
message.error("保存失败,请重试");
}).finally(() => {
setSaving(false);
});
}
//
const formatDownloads = (num) => {
if (num >= 10000) {
@ -124,6 +167,7 @@ function Detail(props) {
if (!login) {
showLoginDialog();
e.stopPropagation();
e.preventDefault();
}
}}>下载</a>}
<a className="btn-outline font-15" onClick={() => history.goBack()}>返回</a>
@ -132,6 +176,10 @@ function Detail(props) {
<div>
<Divider dashed className="mt20 mb20" />
<div className="df-center color828">
{auditStatus !== "1" && <Fragment>
<Badge color={status.color} text={status.label} className='status-dot' />
<Divider type="vertical" style={{ height: '7px', margin: '0 20px' }} />
</Fragment>}
<i className="iconfont icon-yonghuming font-14 mr8"></i>
{detail.createUser && detail.createUser.userName || "未知作者"}
<Divider type="vertical" style={{ height: '7px', margin: '0 20px' }} />
@ -172,11 +220,11 @@ function Detail(props) {
centered
>
<TabPane
tab={`模型介绍`}
tab={`${name}介绍`}
key="introduction"
/>
<TabPane
tab={`模型文件`}
tab={`${name}文件`}
key="files"
/>
<TabPane
@ -189,9 +237,36 @@ function Detail(props) {
<div className="ai_detail_content ai_1600">
{activeTab === "introduction" && (
<div className="ai_detail_tab_panel">
<div className="ai_detail_readme">
<RenderHtml value={introContent} />
</div>
{/* 编辑按钮区域 - 仅发布者可见 */}
{isPublisher && ["models", "datasets"].includes(type) && !isEditor && <div className="ai_detail_edit_toolbar mb15">
{auditStatus === "1" ? (
<Tooltip getPopupContainer={trigger => trigger.parentNode} title={<span className="color-grey-98">当前资源是上架状态请前往<br/><a href={`/ai/${type}/my`} className="color-white">我的资源池</a>页面先进行下架</span>}>
<a className="btn-outline pt5 pb5" style={{ cursor: 'not-allowed', color: '#999', borderColor: '#999' }}>编辑</a>
</Tooltip>
) : (
<a className="btn-outline pt5 pb5" onClick={handleToggleEdit}>编辑</a>
)}
</div>}
{isEditor ? (
<Fragment>
<div className="ai_detail_readme_editor">
<TpmMdEditor
initValue={editContent}
onChange={(value) => setEditContent(value)}
height={600}
mdID={`ai_detail_readme_editor_${id}`}
/>
</div>
<div className="edu-txt-right mt40">
<Button className="mr10" onClick={handleToggleEdit}>取消</Button>
<Button type="primary" loading={saving} onClick={handleSave}>保存</Button>
</div>
</Fragment>
) : (
<div className="ai_detail_readme">
<RenderHtml value={introContent} />
</div>
)}
</div>
)}

View File

@ -356,5 +356,30 @@
font-size: 15px;
color:#27264d;
}
li:not(.ant-tree-treenode-switcher-close):not(.ant-tree-treenode-switcher-open) .ant-tree-node-content-wrapper{
cursor: default;
}
}
// 编辑工具栏
.ai_detail_edit_toolbar {
padding: 0 4px;
text-align: right;
}
// Markdown 编辑器容器
.ai_detail_readme_editor {
// min-height: 500px;
// 覆盖 TpmMdEditor 的默认样式使其与页面风格一致
.markdown-editor {
border-radius: 8px;
.editor-textarea {
font-family: "Courier New", monospace;
font-size: 15px;
line-height: 1.8;
}
}
}
}

View File

@ -31,20 +31,29 @@ function AI(props) {
const [aiZoneInfo, setAiZoneInfo] = useState(undefined);
const [roleByZone, setRoleByZone] = useState("Member");
const {id} = aiZoneInfo || {}
useEffect(() => {
getMainInfos("AI").then(res => {
if (res && res.data && res.data.data) {
setAiZoneInfo(res.data.data)
// login && getCurrentRole(res.data.data.id).then(res1 => {
// if (res1 && res1.data && res1.data.data) { }
// setRoleByZone(res1.data.data.role)
// })
// deptIdheader
sessionStorage.setItem('deptId', res.data.data.deptId)
}
})
}, [])
useEffect(()=>{
if(id && login){
getCurrentRole(id);
// login && getCurrentRole(res.data.data.id).then(res1 => {
// if (res1 && res1.data && res1.data.data) {
// setRoleByZone(res1.data.data.role)
// }
// })
}
}, [id, login])
return <div className="ai_resouce_page">
<Switch {...props}>
<Route

View File

@ -212,7 +212,7 @@ function List(props) {
{model.icon && type !== "skills" && <i className={`iconfont ${model.icon} font-30 mr15`}></i>}
<div className="flex1 task-hide mr10" style={{ minHeight: '40px' }}>
<div className="font-16 font-bd">{model.name}{model.isNew && <i className="iconfont-color icon-colora-zuixin1 font-20 ml10"></i>}</div>
<div className="color3f4">{model.desc}</div>
{model.desc && <div className="color3f4">{model.desc}</div>}
</div>
<a className="btn-black arrowRight" href={type === "codes" ? `/${model.url}` : `/ai/${type}/${model.id}`}>查看<i className="iconfont icon-arrright"></i></a>
</div>

View File

@ -9,9 +9,10 @@ import moment from "moment";
function MyResources(props) {
const pageSize = 10;
const { match: { params }, history, aiZoneInfo, roleByZone } = props || {};
const { match: { params }, history, aiZoneInfo, roleByZone, current_user } = props || {};
const type = params.type;
const { id: aiZoneId } = aiZoneInfo || {}
const { login } = current_user || {}
const [current, setCurrent] = useState(1);
const [total, setTotal] = useState(0);
const [reload, setReload] = useState(undefined);
@ -24,6 +25,13 @@ function MyResources(props) {
document.title = `我的资源池`
}, [])
//
useEffect(() => {
if (!login) {
history.push(`/login?go_page=${encodeURIComponent(window.location.pathname)}`);
}
}, [login]);
useEffect(() => {
if (aiZoneId && type) {
if(type === "codes") {
@ -56,7 +64,7 @@ function MyResources(props) {
}
})
}
}, [aiZoneId, type])
}, [aiZoneId, type, reload])
useEffect(() => {
if(type === "codes" && aiZoneId) {
@ -176,6 +184,12 @@ function MyResources(props) {
{category.name}
</Link>
))}
<Link
to={`/ai/${type}/create`}
className={`category-tab-add`}
>
<i className="iconfont icon-jia font-12"></i>
</Link>
</div>
{/* 统计数据 */}

View File

@ -30,6 +30,7 @@
// 分类标签
.category-tabs {
display: flex;
align-items: center;
margin-bottom: 30px;
border: 1px solid #091221;
border-radius: 24px;
@ -53,6 +54,16 @@
color: #fff;
}
}
.category-tab-add{
width:34px;
height:34px;
line-height: 34px;
text-align: center;
background:#091221;
border-radius:17px;
color: white;
margin-right: 9px;
}
}
// 统计数据

View File

@ -61,7 +61,7 @@ export const convertToTreeData = (data) => {
let title = node.name;
if (isLeaf && node.size) {
const sizeStr = formatFileSize(node.size);
title = <Fragment><span>{node.name}</span><span>{sizeStr}</span></Fragment>;
title = <Fragment><span>{node.name}</span><span className="fileSize">{sizeStr}</span></Fragment>;
}
return {
key: node.name,

View File

@ -38,7 +38,7 @@ const ChunkedUpload = forwardRef(({
hierarchy = '',
chunkSize = 10 * 1024 * 1024, // 10MB
disabled = false,
accept = '*',
accept = '', // HTML5
maxSize = 1024 * 1024 * 1024, // 1GB
children, //
defaultFile, //

View File

@ -145,6 +145,7 @@ function EditComment(props) {
action={getZoneUrl(`/api/file/common/upload`)}
beforeUpload={beforeUpload}
onChange={changeFileList}
withCredentials={true}
headers={{
Authorization: cookie.load('autologin_trustie'),
}}

View File

@ -510,10 +510,9 @@ export function takeUpSourceById(id, role="Member"){
method:"PUT"
})
}
//
export function drawSourceById(id){
return fetch({
url: `/zoneFront/resource/${id}/withdraw`,
url: `/zone/zoneFront/resource/${id}/withdraw`,
method:"PUT"
})
}