This commit is contained in:
xixi 2024-06-14 15:50:34 +08:00
parent f4cd0bfdf2
commit 9289522415
18 changed files with 860 additions and 232 deletions

View File

@ -1,20 +1,24 @@
import {useContext, useEffect, useRef, useState} from "react";
import {GlobalContext} from "../../context/GlobalContext.ts";
import {del, get, post, put, websocketUrl} from "../../util/AjaxUtils.ts";
import {Col, Divider, Row, Spin} from "antd";
import './ai-helper.css'
import { useContext, useEffect, useRef, useState } from "react";
import { GlobalContext } from "../../context/GlobalContext.ts";
import { del, get, post, put, websocketUrl } from "../../util/AjaxUtils.ts";
import {
// Col, Divider, Row,
Spin } from "antd";
import useMessage from "antd/es/message/useMessage";
import AiHelperText from "./text/AiHelperText.tsx";
import AiHelperMod from "./mod/AiHelperMod.tsx";
import {IProjMod} from "../../interfaces/proj/IProj.ts";
import {MAX_MOD_SIZE} from "../../route/proj/edit/ProjConfigModList.tsx";
import {useNavigate} from "react-router-dom";
import {uuid} from "../../util/CommonUtil.ts";
import { IProjMod } from "../../interfaces/proj/IProj.ts";
import { MAX_MOD_SIZE } from "../../route/proj/edit/ProjConfigModList.tsx";
import { useNavigate } from "react-router-dom";
import { uuid } from "../../util/CommonUtil.ts";
type PropsType = {
projId: string;
projIntroduction?: string;
projDesc?: string;
isFree: boolean;
}
type ProjModType = {
@ -24,10 +28,14 @@ type ProjModType = {
}
export default function AiHelper(props: PropsType) {
// const height = window.innerHeight - 280;
const globalContext = useContext(GlobalContext);
const pingTimeout = useRef(-1);
const ws = useRef<WebSocket | null>(null);
const [messageApi, messageApiHolder] = useMessage();
const [activeTab, setActiveTab] = useState('简介') //tab栏选中
const [projIntroduction, setProjIntroduction] = useState<string>(props.projIntroduction ? props.projIntroduction : '');
const [newProjIntroduction, setNewProjIntroduction] = useState<string>('');
const [isProjIntroductionLoading, setIsProjIntroductionLoading] = useState(false);
@ -98,7 +106,7 @@ export default function AiHelper(props: PropsType) {
onBefore() {
setIsProjModArrayLoading(true);
},
onSuccess({data}) {
onSuccess({ data }) {
setProjModArray(data);
},
onFinally() {
@ -191,9 +199,9 @@ export default function AiHelper(props: PropsType) {
}, [globalContext.user.userId, props.projId]);
return (
<>
<div className='aiTab'>
{messageApiHolder}
<Row>
{/* <Row>
<Col span={24}>
<Spin tip="正在处理,请稍后..." size="small" spinning={isProjIntroductionLoading}>
<AiHelperText title="项目简介"
@ -312,7 +320,159 @@ export default function AiHelper(props: PropsType) {
</Spin>
</Col>
</Row>
</>
</Row> */}
<div className="aiTabBtnBox">
<div className={activeTab == '简介' ? 'tabActive' : "aiTabBtn"}
onClick={() => {
setActiveTab('简介')
}}
>
</div>
<div className={activeTab == '详情' ? 'tabActive' : "aiTabBtn"}
onClick={() => {
setActiveTab('详情')
}}
>
</div>
<div className={activeTab == '模块' ? 'tabActive' : "aiTabBtn"}
onClick={() => {
setActiveTab('模块')
}}
>
</div>
</div>
<div className='aiTabContent'>
<div style={{ display: activeTab == '简介' ? 'block' : 'none' }}>
<Spin tip="正在处理,请稍后..." size="small" spinning={isProjIntroductionLoading}>
<AiHelperText
title="项目简介"
text={projIntroduction}
newText={newProjIntroduction}
maxLength={1500}
handleGenerate={() => {
generateProjIntroduction();
}}
handleSave={(text) => {
updateProjIntroduction(text);
}}
></AiHelperText>
</Spin>
</div>
<div style={{ display: activeTab == '详情' ? 'block' : 'none' }}>
<Spin tip="正在处理,请稍后..." size="small" spinning={isProjDescLoading}>
<AiHelperText title="项目详情"
text={projDesc}
newText={newProjDesc}
maxLength={1500}
handleGenerate={() => {
generateProjDesc();
}}
handleSave={(text) => {
updateProjDesc(text);
}}
/>
</Spin>
</div>
<div style={{ display: activeTab == '模块' ? 'block' : 'none' }}>
<Spin tip="正在处理,请稍后..." size="small" spinning={isProjModArrayLoading}>
<AiHelperMod mods={projModArray}
newMods={newProjModArray}
handleGenerate={() => {
generateProjModArray();
}}
handleSave={(index, mod) => {
if (projModArray.length > MAX_MOD_SIZE) {
messageApi.error(`模块最大数量为${MAX_MOD_SIZE}`);
return;
}
post<any>({
messageApi,
url: `/api/proj-mod/save-ai/${props.projId}`,
body: {
name: mod.name,
desc: mod.desc,
},
onBefore() {
setIsProjModArrayLoading(true);
},
onSuccess() {
messageApi.success('提交成功')
listMods();
newProjModArray.splice(index, 1);
setNewProjModArray([
...newProjModArray
]);
},
onFinally() {
setIsProjModArrayLoading(false);
}
})
}}
handleResaveField={(_index, projModId) => {
post<any>({
messageApi,
url: `/api/proj-mod/resave-ai-field/${props.projId}/${projModId}`,
onBefore() {
setIsProjModArrayLoading(true);
},
onSuccess() {
messageApi.success('提交成功')
listMods();
},
onFinally() {
setIsProjModArrayLoading(false);
}
})
}}
handleEdit={(_index, projModId, item) => {
if (item.aiFieldStatus != 'SUCCESS') {
messageApi.error('模块未处理完毕不能编辑');
return;
}
if (props.isFree) {
nav(`/proj-edit/config-mod-fedit/${props.projId}/${projModId}`)
} else {
nav(`/proj-edit/config-mod-edit/${props.projId}/${projModId}`)
}
}}
handleRemove={(_index, projModId, item) => {
if (item.aiFieldStatus != 'SUCCESS') {
messageApi.error('模块未处理完毕不能删除');
return;
}
del<any>({
messageApi,
url: `/api/proj-mod/remove/proj-id/${props.projId}/${projModId}`,
onBefore() {
setIsProjModArrayLoading(true);
},
onSuccess() {
messageApi.success('删除成功');
listMods();
},
onFinally() {
setIsProjModArrayLoading(false);
}
})
}}
/>
</Spin>
</div>
</div>
{/* <div className='proIntroduction'>
</div>
<div className='proDetail'>
</div> */}
</div>
)
}

View File

@ -0,0 +1,42 @@
.aiTab {
padding: 20px;
}
.aiTabBtnBox {
display: flex;
width: 100%;
/* background-color: red; */
/* border-bottom: 1px solid #D5D5D5; */
}
.aiTabBtn {
width: 109px;
height: 39px;
background: #F1F1F1;
font-size: 14px;
color: #5A5A5A;
text-align: center;
line-height: 39px;
cursor: pointer;
font-weight: 700;
}
.tabActive {
width: 109px;
height: 39px;
background: #FF7E00;
font-size: 14px;
color: #FFFFFF;
text-align: center;
line-height: 39px;
cursor: pointer;
font-weight: 700;
}
.aiTabContent {
width: 100%;
/* background-color: pink; */
}

View File

@ -1,5 +1,12 @@
import { Button, Divider, Dropdown, Space, Table, TableProps, Modal } from "antd";
import { CheckOutlined, LoadingOutlined, ReloadOutlined, RedoOutlined } from "@ant-design/icons";
import './ai-helper-mod.css'
import { Button,
// Divider, Dropdown, Space,
Table, TableProps, Modal } from "antd";
import {
// CheckOutlined,
LoadingOutlined,
// ReloadOutlined,
RedoOutlined } from "@ant-design/icons";
import { useEffect, useState } from "react";
import { IProjMod } from "../../../interfaces/proj/IProj.ts";
import EditModal from '../../EditModal/EditModal.tsx'
@ -7,6 +14,7 @@ import ConfigModModal from '../../ConfigModModal/ConfigModModal.tsx'
import { get } from "../../../util/AjaxUtils.ts";
import { useParams } from "react-router-dom";
import { SearchOutlined } from '@ant-design/icons';
import noTextImg from '../../../static/noText.png'
import {
message
} from "antd";
@ -26,13 +34,14 @@ type ProjModType = {
}
export default function AiHelperMod(props: PropsType) {
const height = window.innerHeight - 265;
// 菜单状态是否可以编辑
const [messageApi] = message.useMessage();
const [modArray, setModArray] = useState<IProjMod[]>([]);
const [newModArray, setNewModArray] = useState<ProjModType[]>([]);
const [id, setId] = useState('')
const[configModal,setConfigModal] = useState(false)
const [configModal, setConfigModal] = useState(false)
const [editModal, setEditModal] = useState(false)
const [updata, setUpdata] = useState<any[]>([])
const [status, setStatus] = useState('')
@ -86,10 +95,12 @@ export default function AiHelperMod(props: PropsType) {
return '处理中'
}
if (value == 'FAILED') {
return '失败'
return (
<span style={{ color: '#FF4040 ' }}></span>
)
}
if (value == 'SUCCESS') {
return '成功'
return <span style={{ color: '#00A351' }}></span>
}
}
},
@ -97,7 +108,7 @@ export default function AiHelperMod(props: PropsType) {
title: '操作',
dataIndex: 'option',
key: 'option',
width: 80,
// width: 80,
align: 'center',
render: (_value, record, index) => {
if (status == 'SUCCESS') {
@ -123,27 +134,49 @@ export default function AiHelperMod(props: PropsType) {
}}><RedoOutlined /></Button>
}
return (
<Dropdown menu={{
items: [
{
key: 'edit',
label: '编辑',
onClick: () => {
// props.handleEdit(index, record.projModId, record);
setId(record.projModId)
setEditModal(true)
}
}, {
key: 'remove',
label: '删除',
onClick: () => {
props.handleRemove(index, record.projModId, record);
}
},
]
}} placement="bottom" arrow>
<Button></Button>
</Dropdown>
<div>
<span
style={{
cursor: 'pointer'
}}
onClick={() => {
setId(record.projModId)
setEditModal(true)
}}
></span>
<span
style={{
cursor: 'pointer',
marginLeft: 19,
color: '#FF4040'
}}
onClick={() => {
props.handleRemove(index, record.projModId, record);
}}
></span>
</div>
// <Dropdown menu={{
// items: [
// {
// key: 'edit',
// label: '编辑',
// onClick: () => {
// // props.handleEdit(index, record.projModId, record);
// setId(record.projModId)
// setEditModal(true)
// }
// }, {
// key: 'remove',
// label: '删除',
// onClick: () => {
// props.handleRemove(index, record.projModId, record);
// }
// },
// ]
// }} placement="bottom" arrow>
// <Button>…</Button>
// </Dropdown>
)
}
// if (record.aiFieldStatus == 'GENERATING') {
@ -230,61 +263,163 @@ export default function AiHelperMod(props: PropsType) {
{ title: '模块名称', dataIndex: 'name', key: 'name', width: 200, align: 'center' },
{ title: '模块描述', dataIndex: 'desc', key: 'desc', align: 'center' },
{
title: '确认',
title: '操作',
dataIndex: 'option',
key: 'option',
width: 80,
align: 'center',
render: (_value, record, index) => {
return (
<Button onClick={() => {
props.handleSave(index, record);
}}>
<CheckOutlined />
</Button>
// <Button onClick={() => {
// props.handleSave(index, record);
// }}>
// 添加
// </Button>
<span style={{ cursor: 'pointer' }}
onClick={() => {
props.handleSave(index, record);
}}
>
</span>
)
}
},
];
return (
<>
<div style={{ padding: '5px 0 0 0', fontWeight: 'bold' }}></div>
<div style={{ padding: '5px 0 0 0' }}>
{newModArray.length > 0 ? <Divider orientation="right" plain></Divider> : <></>}
<Table columns={modColumnArray} dataSource={modArray} size="small" bordered={true} scroll={{ y: 240 }}
pagination={false} rowKey="projModId" />
{
newModArray.length > 0 ? (
<>
<Divider orientation="right" plain></Divider>
// <>
// <div style={{ padding: '5px 0 0 0', fontWeight: 'bold' }}>模块管理</div>
// <div style={{ padding: '5px 0 0 0' }}>
// {newModArray.length > 0 ? <Divider orientation="right" plain>原模块</Divider> : <></>}
// <Table columns={modColumnArray} dataSource={modArray} size="small" bordered={true} scroll={{ y: 240 }}
// pagination={false} rowKey="projModId" />
// {
// newModArray.length > 0 ? (
// <>
// <Divider orientation="right" plain>新模块</Divider>
// <Table columns={newModColumnArray} dataSource={newModArray} size="small" bordered={true}
// scroll={{ y: 240 }} pagination={false} rowKey="id" />
// </>
// ) : <></>
// }
// </div>
// <div style={{ padding: '5px 0 0 0', textAlign: 'center' }}>
// {
// newModArray.length > 0 ? (
// <Space>
// <Button type="link" style={{ cursor: 'pointer' }}
// onClick={() => {
// props.handleGenerate();
// }}><ReloadOutlined /> 重新生成</Button>
// </Space>
// ) : <Button type="link" disabled={status == 'SUCCESS' || status == 'GENERATING' ? true : false} style={{ cursor: 'pointer' }}
// onClick={() => {
// props.handleGenerate();
// }}>AI生成</Button>
// }
// </div>
// <Modal title="编辑"
// destroyOnClose={true}
// open={editModal} footer={null} onCancel={() => { setEditModal(false) }} width={1200} >
// < EditModal id={id} onConfirm={() => setEditModal(false)} setUpdata={setUpdata} />
// </Modal>
// <Modal title="查看"
// destroyOnClose={true}
// open={configModal} footer={null} onCancel={() => { setConfigModal(false) }} width={1200} >
// <ConfigModModal id={id} onConfirm={() => setConfigModal(false)} ></ConfigModModal>
// </Modal>
// </>
<div style={{ height: `${height}px`, border: '1px solid #D5D5D5' }}>
<div className="aiMod-top">
{modArray.length > 0 ?
<>
<div className='aiMod-top-table' >
<Table columns={modColumnArray} dataSource={modArray} size="small" bordered={true}
pagination={false}
rowKey="projModId"
/>
</div>
{/* <div className="aiMod-top-btn">
<Button type="primary"
style={{
marginLeft: 15
}}
></Button>
</div> */}
</>
:
<div style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: '14px',
}}
>
<div style={{ width: 300, height: 300, marginTop: 10, position: 'relative' }}>
<img src={noTextImg} alt="" width={'100%'} />
<div style={{ position: 'absolute', left: 90, bottom: 100 }}>
<span style={{ color: '#FF9D00', cursor: 'pointer' }}
onClick={() => {
props.handleGenerate()
}}
></span>
</div>
</div>
</div>
}
</div>
<div className="aiMod-center">
</div>
<div className="aiMod-bot">
<div className='aiMod-bot-table'>
{newModArray.length > 0 ?
<div style={{
border: '1px solid #f0f0f0',
borderRadius: '10px'
}}>
<Table columns={newModColumnArray} dataSource={newModArray} size="small" bordered={true}
scroll={{ y: 240 }} pagination={false} rowKey="id" />
</>
) : <></>
}
</div>
<div style={{ padding: '5px 0 0 0', textAlign: 'center' }}>
{
newModArray.length > 0 ? (
<Space>
<Button type="link" style={{ cursor: 'pointer' }}
onClick={() => {
props.handleGenerate();
}}><ReloadOutlined /> </Button>
</Space>
) : <Button type="link" disabled={status == 'SUCCESS' || status == 'GENERATING' ? true : false} style={{ cursor: 'pointer' }}
pagination={false} rowKey="id" />
</div>
:
<>
<span style={{
color: '#ABABAB',
fontSize:16
}}> AI自动生成后可自动生成模块管理...</span>
</>}
</div>
<div className="aiMod-bot-btn">
<Button type="primary"
style={{
marginLeft: 15,
width: 109,
}}
disabled={status == 'SUCCESS' || status == 'GENERATING' ? true : false}
onClick={() => {
props.handleGenerate();
}}>AI生成</Button>
}
props.handleGenerate()
}}
>AI生成</Button>
</div>
</div>
<Modal title="编辑" open={editModal} footer={null} onCancel={() => { setEditModal(false) }} width={1200} >
<Modal title="编辑"
destroyOnClose={true}
open={editModal} footer={null} onCancel={() => { setEditModal(false) }} width={1200} >
< EditModal id={id} onConfirm={() => setEditModal(false)} setUpdata={setUpdata} />
</Modal>
<Modal title="查看" open={configModal} footer={null} onCancel={() => { setConfigModal(false) }} width={1200} >
<Modal title="查看"
destroyOnClose={true}
open={configModal} footer={null} onCancel={() => { setConfigModal(false) }} width={1200} >
<ConfigModModal id={id} onConfirm={() => setConfigModal(false)} ></ConfigModModal>
</Modal>
</>
</div>
)
}

View File

@ -0,0 +1,35 @@
.aiMod-top{
height: calc(50% - 21px);
padding: 21px;
box-sizing: border-box;
}
.aiMod-top-table{
height: 100%;
/* background-color: pink; */
overflow: auto;
border: 1px solid #f0f0f0;
border-radius: 10px;
}
.aiMod-bot-table{
/* border: 1px solid #f0f0f0;
border-radius: 10px; */
height: 88%;
/* background-color: pink; */
overflow: auto;
}
.aiMod-bot-btn{
margin-top: 10px;
display: flex;
justify-content: flex-end;
}
.aiMod-center{
height: 42px;
background: #EFEFEF;
}
.aiMod-bot{
/* background-color: red; */
height: calc(50% - 21px);
padding: 21px;
box-sizing: border-box;
}

View File

@ -1,9 +1,14 @@
import {Button, Divider, Empty, Space} from "antd";
import {
Button,
// Divider, Empty, Space
} from "antd";
import './ai-helper-text.css'
import TextArea from "antd/es/input/TextArea";
import {useEffect, useState} from "react";
import {CheckOutlined, EditOutlined, ReloadOutlined} from "@ant-design/icons";
import { useEffect, useState } from "react";
// import { CheckOutlined, EditOutlined, ReloadOutlined } from "@ant-design/icons";
import { get } from "../../../util/AjaxUtils.ts";
import { useParams } from "react-router-dom";
import noTextImg from '../../../static/noText.png'
import {
message
} from "antd";
@ -17,6 +22,7 @@ type PropsType = {
}
export default function AiHelperText(props: PropsType) {
const height = window.innerHeight - 265;
const pathParams = useParams();
const [status, setStatus] = useState('')
@ -37,108 +43,141 @@ export default function AiHelperText(props: PropsType) {
const [text, setText] = useState('');
const [newText, setNewText] = useState('');
const [isTextEdit, setIsTextEdit] = useState(false);
const [isNewTextEdit, setIsNewTextEdit] = useState(false);
// const [isNewTextEdit, setIsNewTextEdit] = useState(false);
useEffect(() => {
setText(props.text);
setNewText(props.newText);
}, [props.text, props.newText])
const renderTextBtn = () => {
if (!newText) {
if (!isTextEdit) {
return (
<Space>
<Button type="link" style={{cursor: 'pointer'}}
disabled={status=='SUCCESS' || status=='GENERATING' ? true : false}
onClick={() => {
props.handleGenerate();
}}>AI生成</Button>
{
text ? (
<Button type="link" style={{cursor: 'pointer'}}
disabled={status=='SUCCESS' || status=='GENERATING' ? true : false}
onClick={() => {
setIsTextEdit(true);
}}><EditOutlined/> </Button>
) : <></>
}
</Space>
)
}
return (
<Space>
<Button type="link" style={{cursor: 'pointer'}} onClick={() => {
props.handleSave(text);
setIsTextEdit(false);
}}><CheckOutlined/> </Button>
</Space>
)
}
if (!isNewTextEdit) {
return (
<Space>
<Button type="link" style={{cursor: 'pointer'}} onClick={() => {
props.handleSave(newText);
setIsNewTextEdit(false);
}}><CheckOutlined/> </Button>
<Button type="link" style={{cursor: 'pointer'}} onClick={() => {
props.handleGenerate();
}}><ReloadOutlined/> </Button>
<Button type="link" style={{cursor: 'pointer'}} onClick={() => {
setIsNewTextEdit(true);
}}><EditOutlined/> </Button>
</Space>
)
}
return (
<Space>
<Button type="link" style={{cursor: 'pointer'}} onClick={() => {
setIsNewTextEdit(false);
}}><CheckOutlined/> </Button>
</Space>
)
}
// const renderTextBtn = () => {
// if (!newText) {
// if (!isTextEdit) {
// return (
// <Space>
// <Button type="link" style={{ cursor: 'pointer' }}
// disabled={status == 'SUCCESS' || status == 'GENERATING' ? true : false}
// onClick={() => {
// props.handleGenerate();
// }}>AI生成</Button>
// {
// text ? (
// <Button type="link" style={{ cursor: 'pointer' }}
// disabled={status == 'SUCCESS' || status == 'GENERATING' ? true : false}
// onClick={() => {
// setIsTextEdit(true);
// }}><EditOutlined /> 编辑</Button>
// ) : <></>
// }
// </Space>
// )
// }
// return (
// <Space>
// <Button type="link" style={{ cursor: 'pointer' }} onClick={() => {
// props.handleSave(text);
// setIsTextEdit(false);
// }}><CheckOutlined /> 保存结果</Button>
// </Space>
// )
// }
// if (!isNewTextEdit) {
// return (
// <Space>
// <Button type="link" style={{ cursor: 'pointer' }} onClick={() => {
// props.handleSave(newText);
// setIsNewTextEdit(false);
// }}><CheckOutlined /> 保存结果</Button>
// <Button type="link" style={{ cursor: 'pointer' }} onClick={() => {
// props.handleGenerate();
// }}><ReloadOutlined /> 重新生成</Button>
// <Button type="link" style={{ cursor: 'pointer' }} onClick={() => {
// setIsNewTextEdit(true);
// }}><EditOutlined /> 编辑</Button>
// </Space>
// )
// }
// return (
// <Space>
// <Button type="link" style={{ cursor: 'pointer' }} onClick={() => {
// setIsNewTextEdit(false);
// }}><CheckOutlined /> 确定</Button>
// </Space>
// )
// }
// const renderTextDom = () => {
// if (!text) {
// return <Empty description="暂无内容" />
// }
// if (!isTextEdit) {
// return (<div>{text}</div>)
// }
// return (
// <TextArea autoSize={true} value={text} placeholder={`请编辑${props.title}`} maxLength={props.maxLength} onChange={(e) => {
// setText(e.currentTarget.value);
// }} />
// )
// }
const renderTextDom = () => {
if(!text) {
return <Empty description="暂无内容"/>
}
if(!isTextEdit) {
return <div>{text}</div>
}
return (
<TextArea autoSize={true} value={text} placeholder={`请编辑${props.title}`} maxLength={props.maxLength} onChange={(e) => {
setText(e.currentTarget.value);
}}/>
)
}
const renderNewTextDom = () => {
if(!newText) {
return <></>
}
if(!isNewTextEdit) {
if (!text) {
return (
<>
<Divider orientation="right" plain>{props.title}</Divider>
<div>{newText}</div>
</>
<div className="aiText-top-notext">
{/* <Empty description="暂无内容" /> */}
<div className="aiNotext">
<img src={noTextImg} alt="" width={'100%'} />
<div className="aiNotext-text">
,
<span style={{ color: '#FF9D00', cursor: 'pointer' }}
onClick={() => {
props.handleGenerate();
}}
></span>
</div>
</div>
</div>
)
}
if (!isTextEdit) {
return (
<div className="aiText-top-text">
{text}
</div>
)
}
return (
<>
<Divider orientation="right" plain>{props.title}</Divider>
<TextArea autoSize={true} value={newText} placeholder={`请编辑${props.title}`} maxLength={props.maxLength} onChange={(e) => {
setNewText(e.currentTarget.value);
}}/>
</>
<TextArea style={{ height: ' 77%', resize: 'none', fontSize: 16, textIndent: 32 }} value={text} onChange={(e) => {
setText(e.currentTarget.value);
}}></TextArea>
)
}
// const renderNewTextDom = () => {
// if (!newText) {
// return <></>
// }
// if (!isNewTextEdit) {
// return (
// <>
// <Divider orientation="right" plain>新{props.title}</Divider>
// <div>{newText}</div>
// </>
// )
// }
// return (
// <>
// <Divider orientation="right" plain>新{props.title}</Divider>
// <TextArea autoSize={true} value={newText} placeholder={`请编辑${props.title}`} maxLength={props.maxLength} onChange={(e) => {
// setNewText(e.currentTarget.value);
// }} />
// </>
// )
// }
return (
<>
<div style={{padding: '5px 0 0 0', fontWeight: 'bold'}}>{props.title}</div>
{/* <div style={{padding: '5px 0 0 0', fontWeight: 'bold'}}>{props.title}</div>
<div style={{padding: '5px 0 0 0'}}>
{newText ? <Divider orientation="right" plain>{props.title}</Divider> : <></>}
{renderTextDom()}
@ -146,6 +185,71 @@ export default function AiHelperText(props: PropsType) {
</div>
<div style={{padding: '5px 0 0 0', textAlign: 'center'}}>
{renderTextBtn()}
</div> */}
<div className="aiText" style={{ height: `${height}px` }}>
<div className="aiText-top">
{renderTextDom()}
<div className="aiText-top-btn" style={{ display: text ? 'block' : 'none' }}>
<div style={{ display: "flex", justifyContent: 'flex-end' }}>
<Button disabled={status == 'SUCCESS' || status == 'GENERATING' ? true : false}
onClick={() => {
setIsTextEdit(true);
}}></Button>
<Button
disabled={status == 'SUCCESS' || status == 'GENERATING' ? true : false}
// size="large"
style={{
marginLeft: 15,
}}
type="primary" onClick={() => {
props.handleSave(text);
setIsTextEdit(false);
}}></Button>
</div>
</div>
</div>
<div style={{height:42,background:" #EFEFEF"}}></div>
<div className="aiText-bot">
<div className="aiText-bot-text">
{newText ? <>
{newText}
</> : <>
<span style={{
color: '#ABABAB',
fontSize: 16
}}> AI自动生成后可自动生{props.title}...</span> </>}
</div>
<div style={{ display: "flex", justifyContent: 'flex-end', marginTop: 10 }}>
<Button
// size="large"
style={{
width: 109,
display: newText ? 'block' : 'none'
}}
onClick={() => {
setText(newText)
// setNewText('')
}}
>使</Button>
<Button
disabled={status == 'SUCCESS' || status == 'GENERATING' ? true : false}
// size="large"
type="primary" style={{
width: 109,
marginLeft: 15,
}}
onClick={() => {
props.handleGenerate();
}}
>AI生成</Button>
</div>
</div>
</div>
</>
)

View File

@ -0,0 +1,79 @@
.aiText {
/* background: #EFEFEF; */
border: 1px solid #D5D5D5;
}
.aiText-top {
width: 100%;
height: 62%;
background-color: rgb(255, 255, 255);
padding: 27px;
box-sizing: border-box;
font-size: 16px;
color: #1D1D1D;
/* border: 1px solid #D5D5D5; */
}
.aiText-top-text {
width: 100%;
/* height: 77%; */
height: 77%;
/* background: pink; */
font-weight: 400;
font-size: 16px;
color: #1D1D1D;
overflow-y: auto;
text-indent: 32px;
text-align: justify;
/* background-color: pink; */
}
.aiText-top-notext {
width: 100%;
height: 100%;
/* background-color: pink; */
display: flex;
align-items: center;
justify-content: center;
}
.aiNotext {
width: 382px;
height: 303px;
font-size: 14px;
color: #99A5B7;
position: relative;
margin-top: 50px;
}
.aiNotext-text {
position: absolute;
left: 134px;
bottom: 66px;
}
.aiText-top-btn {
margin-top: 20px;
}
.aiText-bot {
/* margin-top: 42px; */
width: 100%;
height: 31.5%;
background-color: rgb(255, 255, 255);
padding: 21px 27px 21px 27px;
box-sizing: border-box;
font-size: 16px;
}
.aiText-bot-text{
height: 75%;
width: 100%;
/* background-color: pink; */
overflow-y: auto;
text-indent: 32px;
text-align: justify;
}

View File

@ -31,7 +31,7 @@ export default function CardProjType(props: ICardProj) {
const renderLines = () => {
return props.bodyLineArray.map((line, index) => {
return (
<div className="line" key={`line_${index}`}>
<div className="line" key={`line_${index}`} >
{/* <div className="line-title">{line.title}</div> */}
<img src={serveImg} className='seruseImg' alt="" />
<div className="line-content">
@ -109,7 +109,12 @@ export default function CardProjType(props: ICardProj) {
pkg: pkg,
videoDemo: videoDemo
});
}}></button>
}}
style={{
cursor:props.isClickable==1?'pointer':'not-allowed'
}}
disabled={props.isClickable==1?false:true}
></button>
</div>
</div>
)
@ -172,7 +177,7 @@ export default function CardProjType(props: ICardProj) {
return (
<div className="proj">
<div className="proj" style={{display:props.isShow == 1?'block':'none'}}>
<div className='projTop'>
{/* <div className="proj-head" style={{ backgroundImage: `url(${ProjCardHeadBg})` }}> */}
<div className="proj-head">

View File

@ -71,7 +71,7 @@
.proj .proj-body .line .line-content ul li {
display: flex;
align-items: center;
margin-top: 20px;
margin-top: 15px;
font-family: PingFang SC;
font-weight: 500;
font-size: 18px;

View File

@ -27,4 +27,6 @@ export interface ICardProj {
chargeLineArray?: ICardProjChargeLine[];
buyArray: ICardProjBuy[];
newArray:ICardProjBodyLine[];
isShow:number
isClickable:number
}

View File

@ -41,6 +41,7 @@ export interface IProjCharge {
material: number;
free: number;
},
projTypes:[]
additional: {
pkg: number;
videoDemo: number;

View File

@ -260,15 +260,15 @@ export default function Index() {
setNow('首页')
setPathArray([{ title: '首页' }])
// /config-mod-edit /product-release /transaction-order
}else if (location.pathname.includes(' /transaction-order')) {
} else if (location.pathname.includes(' /transaction-order')) {
setNow('首页')
setPathArray([{ title: '首页' }])
// /config-mod-edit /product-release /transaction-order
}else if (location.pathname.includes(' /copyright-goods')) {
} else if (location.pathname.includes(' /copyright-goods')) {
setNow('首页')
setPathArray([{ title: '首页' }])
// /config-mod-edit /product-release /transaction-order
}else if (location.pathname.includes(' /trading-goods')) {
} else if (location.pathname.includes(' /trading-goods')) {
setNow('首页')
setPathArray([{ title: '首页' }])
// /config-mod-edit /product-release /transaction-order
@ -335,8 +335,8 @@ export default function Index() {
button={buyMenu.button}
list={buyMenu.list}
handleListItem={buyMenu.handleListItem}
/> */}
</div>
/> */}
</div>
</div>
<div className="right">
<div>
@ -347,12 +347,29 @@ export default function Index() {
<img src={gpsImg} alt="" />
<div>:{now}</div>
</div>
<div className='line'></div>
<div className='line' style={{
display: location.pathname.includes('/home') ? 'block' : 'none',
}} ></div>
<div
style={{
width: 1,
height: 23,
marginLeft: 33,
marginRight: 31,
display: location.pathname.includes('/home') ? 'none' : 'block'
}}
></div>
<Search placeholder="输入项目名称" onSearch={handleSearch} style={{
width: '253px',
height: '31px',
display:location.pathname.includes('/home')?'block':'none'
display: location.pathname.includes('/home') ? 'block' : 'none'
}} />
<div style={{
width: '253px',
height: '31px',
display: location.pathname.includes('/home') ? 'none' : 'block'
}}></div>
<div className='nowPosition'>
<img src={backImg} alt="" />
<div>

View File

@ -2,26 +2,35 @@ import './proj-create.css'
import { useNavigate } from "react-router-dom";
import { message } from "antd";
import CardProjType from "../../components/card/CardProjType.tsx";
import { IProjCharge, ProjAdditionalType, ProjChargeType } from "../../interfaces/proj/IProj.ts";
import {
// IProjCharge,
ProjAdditionalType, ProjChargeType } from "../../interfaces/proj/IProj.ts";
import { useEffect, useState } from "react";
import { get } from "../../util/AjaxUtils.ts";
// import { Link } from "react-router-dom";
export default function ProjCreate() {
const [messageApi, contextHolder] = message.useMessage();
const [charge, setCharge] = useState<IProjCharge>({
proj: {
all: 0,
materialAgent: 0,
materialAgentUrgent: 0,
material: 0,
free: 0,
},
additional: {
pkg: 0,
videoDemo: 0
}
});
// const [charge, setCharge] = useState<IProjCharge>({
// proj: {
// all: 0,
// materialAgent: 0,
// materialAgentUrgent: 0,
// material: 0,
// free: 0,
// },
// additional: {
// pkg: 0,
// videoDemo: 0
// },
// projTypes:[
// {}
// ]
// });
const [charge, setCharge] = useState<any>({}) //安装包信息
const [allInfo, setAllInfo] = useState<any>({}) //全部托管信息
const [materialInfo, setMaterialInfo] = useState<any>({}) //写材料信息
const [freeInfo, setFreeInfo] = useState<any>({}) //免费托管信息
const nav = useNavigate();
const height = window.innerHeight - 170;
useEffect(() => {
@ -29,21 +38,28 @@ export default function ProjCreate() {
messageApi: messageApi,
url: '/api/proj/charge/get',
onSuccess({ data }) {
setCharge(data);
setCharge(data.additional);
console.log("创建数据", data);
// console.log(charge.projTypes[0].price);
setAllInfo(data.projTypes[0])
setMaterialInfo(data.projTypes[1])
setFreeInfo(data.projTypes[2])
// console.log('后面的price',data.proj.materialAgent);
}
})
// sessionStorage.setItem('pathArray', JSON.stringify([ { title: <Link to={'/home'}>首页</Link> },{ title: '创建项目' }]));
// sessionStorage.setItem('now', '创建');
console.log(allInfo);
}, [])
return (
< >
{contextHolder}
<div style={{ }}>
<div className="proj-create" style={{height: `${height}px`, overflow: 'auto' }}>
{/* <CardProjType
<div style={{}}>
<div className="proj-create" style={{ height: `${height}px`, overflow: 'auto' }}>
<CardProjType
head={'全托'}
bodyLineArray={[
{
@ -58,7 +74,7 @@ export default function ProjCreate() {
'提供系统安装包'
]
},
]}
newArray={[
{
@ -72,13 +88,15 @@ export default function ProjCreate() {
buyArray={[
{
id: ProjChargeType.ALL,
price: charge.proj.all,
price: allInfo.price,
handleClick: () => {
nav(`/proj-new/${ProjChargeType.ALL}`)
}
}
]}
/> */}
isShow = {allInfo.isShow}
isClickable = {allInfo.isClickable}
/>
{/* <CardProjType
head={'写材料+代理'}
bodyLineArray={[
@ -135,7 +153,7 @@ export default function ProjCreate() {
}
]}
/> */}
<CardProjType
<CardProjType
head={'写材料'}
bodyLineArray={[
{
@ -162,26 +180,30 @@ export default function ProjCreate() {
chargeLineArray={[
{
id: ProjAdditionalType.PKG,
price: charge.additional.pkg,
title: `安装包 ${charge.additional.pkg / 100}`
price: charge.pkg,
title: `安装包 ${charge.pkg / 100}`
},
{
id: ProjAdditionalType.VIDEO_DEMO,
price: charge.additional.videoDemo,
title: `系统演示视频文件 ${charge.additional.videoDemo / 100}`
price: charge.videoDemo,
title: `系统演示视频文件 ${charge.videoDemo / 100}`
}
]}
buyArray={[
{
id: ProjChargeType.MATERIAL,
price: charge.proj.material,
price: materialInfo.price,
handleClick: (_title, additional) => {
nav(`/proj-new/${ProjChargeType.MATERIAL}?${additional.pkg ? 'pkg=true' : 'pkg='}&${additional.videoDemo ? 'videoDemo=true' : 'videoDemo='}`);
}
}
]}
isShow = {materialInfo.isShow}
isClickable = {materialInfo.isClickable}
/>
{/* <CardProjType
<CardProjType
head={'免费试用'}
bodyLineArray={[
{
@ -205,13 +227,15 @@ export default function ProjCreate() {
buyArray={[
{
id: ProjChargeType.FREE,
price: charge.proj.free,
price: freeInfo.price,
handleClick: (_title, additional) => {
nav(`/proj-new/${ProjChargeType.FREE}?${additional.pkg ? 'pkg=true' : 'pkg='}&${additional.videoDemo ? 'videoDemo=true' : 'videoDemo='}`)
}
}
]}
/> */}
isShow = {freeInfo.isShow}
isClickable = {freeInfo.isClickable}
/>
</div>
</div>
</>

View File

@ -1,7 +1,11 @@
import './proj-edit.css'; 1
// import {Link, useNavigate, useParams} from "react-router-dom";
import { useNavigate, useParams } from "react-router-dom";
import { Button, FloatButton, message, Modal } from "antd";
import {
Button,
// FloatButton,
message, Modal
} from "antd";
import StepProjEdit from "../../components/step/StepProjEdit.tsx";
import CardProjEdit from "../../components/card/CardProjEdit.tsx";
import { Process } from "../../interfaces/step/IStepProj.ts";
@ -27,6 +31,7 @@ import SoftwareManagement from '../../route/proj/edit/ProjConfigModList.tsx'
import SoftwareManagementShow from '../../route/proj/edit/ProjConfigModListShow.tsx'
import DisplayOrder from '../../route/proj/edit/ProjConfigMenuList.tsx'
import DisplayOrderShow from '../../route/proj/edit/ProjConfigMenuListShow.tsx'
import aiGif from '../../static/ai.gif'
type AiHelperType = {
projId: string;
projIntroduction: string;
@ -40,6 +45,7 @@ type ProjModType = {
}
export default function ProjEdit() {
//第一步 标题简介弹窗
const [titleIntroductionOpen, setTitleIntroductionOpen] = useState(false)
const [titleIntroductionShowOpen, setTitleIntroductionShowOpen] = useState(false)
@ -510,13 +516,16 @@ export default function ProjEdit() {
nav(-1);
}}></Button>
</div>
<FloatButton.Group>
{/* <FloatButton.Group>
<FloatButton
shape="square" //报错处理 lyp
shape="square" //报错处理
onClick={() => { setAiHelperModalOpen(true) }}
description={<span style={{ fontWeight: 'bold' }}>AI</span>}
>AI助手</FloatButton>
</FloatButton.Group>
</FloatButton.Group> */}
<div className='aiImg' onClick={() => { setAiHelperModalOpen(true) }}>
<img src={aiGif} alt="" width={'100%'} height={'100%'} />
</div>
<Modal title="提示"
okText="确定"
@ -559,11 +568,11 @@ export default function ProjEdit() {
<p style={{ color: 'var(--color-red)' }}>{generateErrorMsg}</p>
</Modal>
<Modal open={aiHelperModalOpen}
title="AI助手"
title=""
width={1200}
footer={false}
maskClosable={false}
destroyOnClose
destroyOnClose = {true}
onCancel={() => {
setAiHelperModalOpen(false);
@ -572,13 +581,16 @@ export default function ProjEdit() {
// console.log(123);
}}
>
<AiHelper
projId={aiHelper.projId}
projIntroduction={aiHelper.projIntroduction}
projDesc={aiHelper.projDesc}
isFree={false}
/>
<div style={{ height: `${height - 20}px`, overflow: 'hidden',}}>
<AiHelper
projId={aiHelper.projId}
projIntroduction={aiHelper.projIntroduction}
projDesc={aiHelper.projDesc}
isFree={false}
/>
</div>
</Modal>
{/* 第1步设置 */}
<Modal open={titleIntroductionOpen}

View File

@ -3,7 +3,9 @@ import { useNavigate, useParams, useSearchParams } from "react-router-dom";
import { Button, Flex, Form, Input, message, Modal, Spin, Checkbox } from "antd";
import { useContext, useEffect, useState } from "react";
import { get, post } from "../../util/AjaxUtils.ts";
import { IProjCharge, ProjAdditionalType, ProjChargeType } from "../../interfaces/proj/IProj.ts";
import {
// IProjCharge,
ProjAdditionalType, ProjChargeType } from "../../interfaces/proj/IProj.ts";
import { GlobalDispatchContext, reloadUser } from "../../context/GlobalContext.ts";
// const { TextArea } = Input;
type ProjInfo = {
@ -36,26 +38,28 @@ export default function ProjNew() {
messageApi: messageApi,
url: '/api/proj/charge/get',
onSuccess({ data }) {
const charge = data as IProjCharge;
console.log(data);
const charge = data as any;
// console.log('创建页price',charge.proj.materialAgent);
let price = 0;
switch (pathParams.projChargeType) {
case ProjChargeType.ALL:
price = charge.proj.all;
break;
case ProjChargeType.MATERIAL_AGENT:
price = charge.proj.materialAgent;
break;
case ProjChargeType.MATERIAL_AGENT_URGENT:
price = charge.proj.materialAgentUrgent;
// price = charge.proj.all;
price = charge.projTypes[0].price;
break;
// case ProjChargeType.MATERIAL_AGENT:
// price = charge.proj.materialAgent;
// break;
// case ProjChargeType.MATERIAL_AGENT_URGENT:
// price = charge.proj.materialAgentUrgent;
// break;
case ProjChargeType.MATERIAL:
price = charge.proj.material;
price = charge.projTypes[1].price;
break;
case ProjChargeType.FREE:
price = charge.proj.free;
price = charge.projTypes[2].price;
break;
default:
messageApi.error({

View File

@ -3,7 +3,7 @@
/* overflow: auto; */
/* background-color: red; */
background-color: var(--color-light);
position: relative;
}
@ -20,3 +20,11 @@
text-align: center;
background-color: var(--color-light);
}
.aiImg{
width: 100px;
height: 100px;
position: fixed;
right: 25px;
bottom: 50px;
cursor: pointer;
}

BIN
src/static/ai.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 602 KiB

BIN
src/static/noText.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 KiB

View File

@ -7,7 +7,7 @@ export const Axios = axios;
axios.defaults.baseURL = 'http://192.168.0.15:7025/copyright';
// axios.defaults.baseURL = 'https://www.aimzhu.com/copyright';
// axios.defaults.baseURL = '/copyright';
export const WebSocketBaseUrl: string = 'wss://www.aimzhu.com/copyright';
export const WebSocketBaseUrl: string = 'ws://192.168.0.15:7025/copyright';
// export const WebSocketBaseUrl: string = '/copyright';
export const DevUserId: string = '80d3365e-0597-4988-979e-18ef1c3ec671'; // 18634604067
// export const DevUserId: string = 'c2438eb8-2685-49a9-bf02-5111a5192d96'; // 18647109157