This commit is contained in:
WenC 2024-04-19 18:20:51 +08:00
parent 94d3198c1a
commit dd6dfd1b59
6 changed files with 264 additions and 16 deletions

View File

@ -8,14 +8,15 @@ import {
GlobalDataActionType,
GlobalDispatchContext,
} from "./context/GlobalContext.ts";
import {Reducer, useReducer} from "react";
import React, {Reducer, useReducer} from "react";
const App: React.FC = () => {
const globalDataReducer = (state: GlobalData, action: GlobalDataAction) => {
if (action.type == GlobalDataActionType.REFRESH_SELF) {
if(action.user) {
if (action.user) {
state.user.balance = action.user.balance;
state.user.userId = action.user.userId;
state.user.nickname = action.user.nickname;
state.user.username = action.user.username;
state.user.hasUserInfo = action.user.hasUserInfo;
@ -28,6 +29,7 @@ const App: React.FC = () => {
const [globalData, dispatch] = useReducer<Reducer<GlobalData, GlobalDataAction>>(globalDataReducer, {
user: {
balance: '0',
userId: '',
username: '',
nickname: '',
hasUserInfo: false

View File

@ -0,0 +1,211 @@
import {useContext, useEffect, useRef, useState} from "react";
import {GlobalContext} from "../../context/GlobalContext.ts";
import {put, WebSocketBaseUrl} from "../../util/AjaxUtils.ts";
import {Button, Divider, Empty, Space, Spin} from "antd";
import {CheckOutlined, ReloadOutlined} from "@ant-design/icons";
import useMessage from "antd/es/message/useMessage";
type PropsType = {
projId: string;
projIntroduction?: string;
projDesc?: string;
}
export default function AiHelper(props: PropsType) {
const globalContext = useContext(GlobalContext);
const pingTimeout = useRef(-1);
const ws = useRef<WebSocket | null>(null);
const [messageApi, messageApiHolder] = useMessage();
const [projIntroduction, setProjIntroduction] = useState<string>(props.projIntroduction ? props.projIntroduction : '');
const [newProjIntroduction, setNewProjIntroduction] = useState<string>('');
const [isProjIntroductionLoading, setIsProjIntroductionLoading] = useState(false);
const [projDesc, setProjDesc] = useState<string>(props.projDesc ? props.projDesc : '');
const [newProjDesc, setNewProjDesc] = useState<string>('');
const [isProjDescLoading, setIsProjDescLoading] = useState(false);
const ping = () => {
clearTimeout(pingTimeout.current);
pingTimeout.current = setTimeout(() => {
if (!ws.current) {
return;
}
ws.current.send("PING");
ping();
}, 3000);
}
const websocket = () => {
ws.current = new WebSocket(`${WebSocketBaseUrl}/ws/ai/${globalContext.user.userId}`);
ws.current.onopen = (event) => {
console.log('打开', event);
ping();
}
ws.current.onmessage = (event) => {
if (event.data == 'PONE') {
return;
}
const data = JSON.parse(event.data);
if (data.type == 'REFRESH_PROJ_INTRODUCTION') {
if (data.projId != props.projId) {
return;
}
setIsProjIntroductionLoading(false);
setNewProjIntroduction(data.content);
return;
}
if (data.type == 'REFRESH_PROJ_DESC') {
if (data.projId != props.projId) {
return;
}
setIsProjDescLoading(false);
setNewProjDesc(data.content);
return;
}
}
ws.current.onerror = (event) => {
console.log('error', event);
}
ws.current.onclose = (event) => {
console.log('close', event);
websocket()
}
}
const generateProjIntroduction = () => {
ws.current?.send(JSON.stringify({
type: 'REFRESH_PROJ_INTRODUCTION',
projId: props.projId
}));
ping();
setIsProjIntroductionLoading(true);
}
const generateProjDesc = () => {
ws.current?.send(JSON.stringify({
type: 'REFRESH_PROJ_DESC',
projId: props.projId
}));
ping();
setIsProjDescLoading(true);
}
/**
*
*/
const updateProjIntroduction = () => {
put<any>({
messageApi,
url: `/api/proj/update-introduction/${props.projId}`,
body: {
content: newProjIntroduction
},
onBefore() {
setIsProjIntroductionLoading(true);
},
onSuccess() {
messageApi.success('保存成功');
setProjIntroduction(newProjIntroduction);
setNewProjIntroduction('');
},
onFinally() {
setIsProjIntroductionLoading(false)
}
})
}
/**
*
*/
const updateProjDesc = () => {
put<any>({
messageApi,
url: `/api/proj/update-desc/${props.projId}`,
body: {
content: newProjDesc
},
onBefore() {
setIsProjDescLoading(true);
},
onSuccess() {
messageApi.success('保存成功').then();
setProjDesc(newProjDesc);
setNewProjDesc('');
},
onFinally() {
setIsProjDescLoading(false)
}
})
}
useEffect(() => {
if (!props.projId) {
return;
}
websocket();
}, [globalContext.user.userId, props.projId]);
return (
<>
{messageApiHolder}
<div style={{padding: '5px 0 0 0', fontWeight: 'bold'}}></div>
<Spin tip="正在处理,请稍后..." size="small" spinning={isProjIntroductionLoading}>
<div style={{padding: '5px 0 0 0'}}>
{newProjIntroduction ? <Divider orientation="right" plain></Divider> : <></>}
{projIntroduction ? <div>{projIntroduction}</div> : <Empty description="暂无内容"/>}
{
newProjIntroduction ? (
<>
<Divider orientation="right" plain></Divider>
<div>{newProjIntroduction}</div>
</>
) : <></>
}
</div>
<div style={{padding: '5px 0 0 0', textAlign: 'center'}}>
{
newProjIntroduction ? (
<Space>
<Button type="link" style={{cursor: 'pointer'}}
onClick={updateProjIntroduction}><CheckOutlined/> </Button>
<Button type="link" style={{cursor: 'pointer'}}
onClick={generateProjIntroduction}><ReloadOutlined/> </Button>
</Space>
) : <Button type="link" style={{cursor: 'pointer'}}
onClick={generateProjIntroduction}>AI生成</Button>
}
</div>
</Spin>
<Divider dashed/>
<div style={{padding: '5px 0 0 0', fontWeight: 'bold'}}></div>
<Spin tip="正在处理,请稍后..." size="small" spinning={isProjDescLoading}>
<div style={{padding: '5px 0 0 0'}}>
{newProjDesc ? <Divider orientation="right" plain></Divider> : <></>}
{projDesc ? <div>{projDesc}</div> : <Empty description="暂无内容"/>}
{
newProjDesc ? (
<>
<Divider orientation="right" plain></Divider>
<div>{newProjDesc}</div>
</>
) : <></>
}
</div>
<div style={{padding: '5px 0 0 0', textAlign: 'center'}}>
<div style={{padding: '5px 0 0 0', textAlign: 'center'}}>
{
newProjDesc ? (
<Space>
<Button type="link" style={{cursor: 'pointer'}}
onClick={updateProjDesc}><CheckOutlined/> </Button>
<Button type="link" style={{cursor: 'pointer'}}
onClick={generateProjDesc}><ReloadOutlined/> </Button>
</Space>
) : <Button type="link" style={{cursor: 'pointer'}}
onClick={generateProjDesc}>AI生成</Button>
}
</div>
</div>
</Spin>
</>
)
}

View File

@ -9,6 +9,7 @@ export enum GlobalDataActionType {
export interface User {
balance: string;
nickname: string;
userId: string;
username: string;
hasUserInfo: boolean;
}
@ -24,6 +25,7 @@ export function reloadUser(messageApi: MessageInstance, globalDispatchContext: D
user: {
balance: (Math.floor(data.accountMoney) / 100).toFixed(2),
nickname: data.nickname,
userId: data.userId,
username: data.username,
hasUserInfo: data.hasUserInfo,
}
@ -47,6 +49,7 @@ export const GlobalContext = createContext<GlobalData>({
user: {
balance: '0',
nickname: '',
userId: '',
username: '',
hasUserInfo: false
}

View File

@ -1,6 +1,6 @@
import './proj-edit.css';
import {Link, useNavigate, useParams} from "react-router-dom";
import {Breadcrumb, Button, message, Modal} from "antd";
import {Breadcrumb, 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";
@ -13,6 +13,13 @@ import {Axios, get, post} from "../../util/AjaxUtils.ts";
import {EditStepEnum, IProjEdit} from "../../interfaces/card/ICardProj.ts";
import {MAX_MOD_SIZE} from "./edit/ProjConfigModList.tsx";
import {GenerateStatus} from "../../interfaces/proj/IProj.ts";
import AiHelper from "../../components/ai/AiHelper.tsx";
type AiHelperType = {
projId: string;
projIntroduction: string;
projDesc: string;
}
export default function ProjEdit() {
const nav = useNavigate();
@ -30,6 +37,13 @@ export default function ProjEdit() {
const [previewUrl, setPreviewUrl] = useState('');
const [generateErrorModal, setGenerateErrorModal] = useState(false);
const [generateErrorMsg, setGenerateErrorMsg] = useState('');
const [aiHelperModalOpen, setAiHelperModalOpen] = useState(false);
const [aiHelper, setAiHelper] = useState<AiHelperType>({
projId: '',
projIntroduction: '',
projDesc: ''
});
const height = window.innerHeight - 240;
@ -196,6 +210,13 @@ export default function ProjEdit() {
setPreviewUrl(data.previewUrl);
setGenerateEmainingTime(data.generate.generateEmainingTime);
setGenerateErrorMsg(data.generate.generateMsg);
setAiHelper({
projId: data.projId,
projIntroduction: data.projIntroduction,
projDesc: data.projDesc
})
setAiHelperModalOpen(!data.projIntroduction || !data.projDesc)
}
})
}
@ -367,6 +388,13 @@ export default function ProjEdit() {
nav(-1);
}}></Button>
</div>
<FloatButton.Group>
<FloatButton
onClick={() => {setAiHelperModalOpen(true)}}
description={<span style={{fontWeight: 'bold'}}>AI</span>}
>AI助手</FloatButton>
</FloatButton.Group>
<Modal title="提示"
okText="确定"
cancelText="取消"
@ -398,10 +426,25 @@ export default function ProjEdit() {
backgroundColor: 'var(--color-primary)',
}
}}
onCancel={() => {setGenerateErrorModal(false)}}
onCancel={() => {
setGenerateErrorModal(false)
}}
>
<p style={{color: 'var(--color-red)'}}>{generateErrorMsg}</p>
</Modal>
<Modal open={aiHelperModalOpen}
title="AI助手"
footer={false}
onCancel={() => {
setAiHelperModalOpen(false);
}}
>
<AiHelper
projId={aiHelper.projId}
projIntroduction={aiHelper.projIntroduction}
projDesc={aiHelper.projDesc}
/>
</Modal>
</>
)
}

View File

@ -6,8 +6,6 @@ import {get, post} from "../../util/AjaxUtils.ts";
import {IProjCharge, ProjAdditionalType, ProjChargeType} from "../../interfaces/proj/IProj.ts";
import {GlobalDispatchContext, reloadUser} from "../../context/GlobalContext.ts";
const {TextArea} = Input;
type ProjInfo = {
projName: string;
projIntroduction: string;
@ -104,21 +102,12 @@ export default function ProjNew() {
autoComplete="off"
>
<Form.Item<ProjInfo>
label="系统标题"
name="projName"
rules={[{required: true, message: '请输入系统标题'}]}
>
<Input placeholder="请输入系统标题"/>
</Form.Item>
<Form.Item<ProjInfo>
label="简单描述"
name="projIntroduction"
rules={[{required: true, message: '请输入简单描述'}]}
>
<TextArea rows={6} placeholder="请用一段话简单描述系统"/>
</Form.Item>
<Form.Item>
<Flex align="center" justify="center" gap="large">
<Button type="primary" htmlType="submit"
@ -147,7 +136,6 @@ export default function ProjNew() {
url: '/api/proj/create',
body: {
projName: projInfo.projName,
projIntroduction: projInfo.projIntroduction,
projChargeType: pathParams.projChargeType,
listProjChargeAdditional: listProjChargeAdditional
},

View File

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