add upload resource
Change-Id: I35f0f32f053f424c8dc03629e833e3f984389865
diff --git a/src/app/user/page.tsx b/src/app/user/page.tsx
index 0af92a4..7a1b631 100644
--- a/src/app/user/page.tsx
+++ b/src/app/user/page.tsx
@@ -6,6 +6,15 @@
import { Button } from 'primereact/button';
import { Card } from 'primereact/card';
import {Image} from "primereact/image";
+// 发布资源
+import { Dialog } from 'primereact/dialog';
+import {InputText} from "primereact/inputtext";
+import {InputTextarea} from "primereact/inputtextarea";
+import {FileUpload} from "primereact/fileupload";
+// 资源分类
+import { RadioButton, RadioButtonChangeEvent } from "primereact/radiobutton";
+// 资源标签
+import { MultiSelect, MultiSelectChangeEvent } from 'primereact/multiselect';
// 浮动按钮
import { SpeedDial } from 'primereact/speeddial';
// 评分图标
@@ -14,15 +23,13 @@
import { Toast } from 'primereact/toast';
// 页面跳转
import { useRouter } from "next/navigation";
-// import Link from 'next/link';
// 接口传输
import axios from "axios";
// 样式
import './user.scss';
-
-
+import {toNumber} from "lodash";
// 用户信息
interface UserInfo {
@@ -46,7 +53,7 @@
seedPercentageList: number[]; // 上传资源类型百分比列表,按材质包、模组、整合包、地图的顺序返回
}
-// 用户发布的资源
+// 用户发布过的资源
interface Resource {
resourceId: number;
resourceName: string;
@@ -64,11 +71,34 @@
classify: string; // 资源分类(材质包:resourcePack,模组:mod,整合包:modPack ,地图:map
}
-// 用户发布的资源列表
+// 用户发布过的资源列表
interface ResourceList {
records: Resource[];
}
+// 资源标签
+interface GameplayOption {
+ name: string;
+ code: number;
+}
+
+// 资源标签选项
+const gameplayOptions: GameplayOption[] = [
+ { name: '科技', code: 1 },
+ { name: '魔法', code: 2 },
+ { name: '建筑', code: 3 },
+ { name: '风景', code: 4 },
+ { name: '竞技', code: 5 },
+ { name: '生存', code: 6 },
+ { name: '冒险', code: 7 },
+ { name: '跑酷', code: 8 },
+ { name: '艺术', code: 9 },
+ { name: '剧情', code: 10 },
+ { name: '社交', code: 11 },
+ { name: '策略', code: 12 },
+ { name: '极限', code: 13 }
+];
+
export default function UserPage() {
// 路由
const router = useRouter();
@@ -80,7 +110,10 @@
const [userData, setUserData] = useState<UserData>();
// 消息提醒
const toast = useRef<Toast>(null);
-
+ // 资源标签
+ const [selectedGameplay, setSelectedGameplay] = useState<GameplayOption[]>([]);
+ // 判断用户是否上传资源封面
+ const [isUploadPicture, setIsUploadPicture] = useState<boolean>(false);
useEffect(() => {
fetchUserInfo();
@@ -165,7 +198,7 @@
},
{
template: () => (
- <Button label="发布资源" onClick={() => router.push(`/user/manage/resources/`)}/>
+ <Button label="发布资源" onClick={() => setVisible(true)}/>
)
},
{
@@ -175,12 +208,125 @@
}
];
+ // 发布资源弹窗
+ const [visible, setVisible] = useState(false);
+ const [resourceFormData, setResourceFormData] = useState({
+ resource: {
+ resourceName: '',
+ resourcePicture: '',
+ resourceSummary: '',
+ resourceDetail: '',
+ uploadTime: '',
+ lastUpdateTime: '',
+ price: '',
+ classify: '',
+ },
+ gameplayList: [''],
+ completeRewardId: null,
+ userId: 0,
+ });
+ const [ingredient, setIngredient] = useState<string>('');
+
+
+ // 图片上传消息通知
+ const onUpload = () => {
+ setIsUploadPicture(true);
+ toast.current?.show({ severity: 'info', summary: 'Success', detail: 'File Uploaded' });
+ };
+
+ // 上传资源接口
+ const handleSubmit = async () => {
+ try {
+ // 规定用户必须输入的内容
+ if (resourceFormData.resource.resourceName == '') {
+ toast.current?.show({ severity: 'info', summary: 'error', detail: '缺少资源名称' });
+ return;
+ }
+ if (resourceFormData.resource.resourceSummary == '') {
+ toast.current?.show({ severity: 'info', summary: 'error', detail: '缺少资源简介' });
+ return;
+ }
+ if (resourceFormData.resource.price == '') {
+ toast.current?.show({ severity: 'info', summary: 'error', detail: '缺少资源价格' });
+ return;
+ }
+ if (resourceFormData.resource.classify == '') {
+ toast.current?.show({ severity: 'info', summary: 'error', detail: '缺少资源分类' });
+ return;
+ }
+ if (
+ resourceFormData.gameplayList.length === 1 &&
+ resourceFormData.gameplayList[0] === ''
+ ) {
+ toast.current?.show({ severity: 'info', summary: 'error', detail: '缺少资源标签' });
+ return;
+ }
+ if (!isUploadPicture) {
+ toast.current?.show({ severity: 'info', summary: 'error', detail: '缺少资源封面' });
+ return;
+ }
+
+ const currentDate = new Date().toISOString().split('T')[0];
+ const postData = {
+ resource: {
+ resourceName: resourceFormData.resource.resourceName,
+ resourcePicture: resourceFormData.resource.resourcePicture,
+ resourceSummary: resourceFormData.resource.resourceSummary,
+ resourceDetail: resourceFormData.resource.resourceDetail,
+ uploadTime: currentDate,
+ lastUpdateTime: currentDate,
+ price: toNumber(resourceFormData.resource.price),
+ classify: resourceFormData.resource.classify,
+ },
+ gameplayList: resourceFormData.gameplayList,
+ completeRewardId: null,
+ userId: 22301010, // 记得用户登录状态获取
+ };
+ // 发送POST请求
+ const response = await axios.post(process.env.PUBLIC_URL + '/resource', postData);
+ console.log("上传资源的信息:", postData);
+
+ if (response.status === 200) {
+ toast.current?.show({ severity: 'success', summary: 'Success', detail: '资源上传成功' });
+ // 上传成功
+ setVisible(false);
+ // 重置表单
+ setResourceFormData({
+ resource: {
+ resourceName: '',
+ resourcePicture: '',
+ resourceSummary: '',
+ resourceDetail: '',
+ uploadTime: '',
+ lastUpdateTime: '',
+ price: '',
+ classify: '',
+ },
+ gameplayList: [],
+ completeRewardId: null,
+ userId: 0,
+ });
+ // 重置资源分类
+ setIngredient("");
+ // 重置资源标签
+ setSelectedGameplay([]);
+ // 重置上传封面状态
+ setIsUploadPicture(false);
+ // 可以刷新资源列表
+ // fetchResourceList();
+ }
+ } catch (error) {
+ console.error('资源上传失败:', error);
+ toast.current?.show({ severity: 'error', summary: 'error', detail: '资源上传失败' });
+ }
+ };
+
return (
<div className="user-container">
-
+ <Toast ref={toast}></Toast>
{/*个人信息*/}
<div className="user-profile-card">
<Avatar
@@ -350,6 +496,234 @@
/>
</div>
+ {/*发布资源弹窗*/}
+ <Dialog
+ header="发布资源"
+ visible={visible}
+ onHide={() => setVisible(false)}
+ className="publish-dialog"
+ modal
+ footer={
+ <div className="dialog-footer">
+ <Button label="发布" icon="pi pi-check" onClick={handleSubmit} autoFocus />
+ <Button label="取消" icon="pi pi-times" onClick={() => setVisible(false)} className="p-button-text" />
+ </div>
+ }
+ >
+ <div className="publish-form">
+ <div className="form-field">
+ <div className="form-field-header">
+ <span className="form-field-sign">*</span>
+ <label htmlFor="name">资源名称</label>
+ </div>
+ <InputText
+ id="name"
+ value={resourceFormData.resource.resourceName}
+ onChange={(e) => setResourceFormData(prev => ({
+ ...prev, // 复制顶层所有属性
+ resource: {
+ ...prev.resource, // 复制resource对象的所有属性
+ resourceName: e.target.value // 只更新resourceName
+ }
+ }))}
+ placeholder="请输入资源名称"
+ className="w-full"
+ required
+ />
+ </div>
+ <div className="form-field">
+ <div className="form-field-header">
+ <span className="form-field-sign">*</span>
+ <label htmlFor="summary">资源简介</label>
+ </div>
+ <InputText
+ id="summary"
+ value={resourceFormData.resource.resourceSummary}
+ onChange={(e) => setResourceFormData(prev => ({
+ ...prev, // 复制顶层所有属性
+ resource: {
+ ...prev.resource, // 复制resource对象的所有属性
+ resourceSummary: e.target.value
+ }
+ }))}
+ placeholder="请输入资源简介(一句话)"
+ className="w-full"
+ required
+ />
+ </div>
+ <div className="form-field">
+ <div className="form-field-header">
+ <label htmlFor="detail">资源介绍</label>
+ </div>
+ <InputTextarea
+ id="detail"
+ value={resourceFormData.resource.resourceDetail}
+ onChange={(e) => setResourceFormData(prev => ({
+ ...prev, // 复制顶层所有属性
+ resource: {
+ ...prev.resource, // 复制resource对象的所有属性
+ resourceDetail: e.target.value
+ }
+ }))}
+ rows={5}
+ placeholder="请输入资源介绍"
+ className="w-full"
+ required
+ />
+ </div>
+ <div className="form-field">
+ <div className="form-field-header">
+ <span className="form-field-sign">*</span>
+ <label htmlFor="price">价格</label>
+ </div>
+ <InputText
+ id="price"
+ value={resourceFormData.resource.price}
+ onChange={(e) => setResourceFormData(prev => ({
+ ...prev, // 复制顶层所有属性
+ resource: {
+ ...prev.resource, // 复制resource对象的所有属性
+ price: e.target.value
+ }
+ }))}
+ placeholder="请输入资源价格"
+ className="w-full"
+ />
+ </div>
+ <div className="form-field">
+ <div className="form-field-header">
+ <span className="form-field-sign">*</span>
+ <label htmlFor="classify">资源分类(请选择一项)</label>
+ </div>
+ <div className="form-field-classify">
+ <div className="flex align-items-center">
+ <RadioButton
+ inputId="ingredient1"
+ name="pizza"
+ value="resourcePack"
+ onChange={(e: RadioButtonChangeEvent) => {
+ setResourceFormData(prev => ({
+ ...prev, // 复制顶层所有属性
+ resource: {
+ ...prev.resource, // 复制resource对象的所有属性
+ classify: e.target.value
+ }
+ }));
+ setIngredient(e.value);
+ console.log(ingredient);
+ // console.log(resourceFormData.resource.classify);
+ }}
+ checked={ingredient === 'resourcePack'}
+ />
+ <label htmlFor="ingredient1" className="ml-2">材质包</label>
+ </div>
+ <div className="flex align-items-center">
+ <RadioButton
+ inputId="ingredient2"
+ name="pizza"
+ value="modPack"
+ onChange={(e: RadioButtonChangeEvent) => {
+ setResourceFormData(prev => ({
+ ...prev, // 复制顶层所有属性
+ resource: {
+ ...prev.resource, // 复制resource对象的所有属性
+ classify: e.target.value
+ }
+ }));
+ setIngredient(e.value);
+ }}
+ // onChange={(e: RadioButtonChangeEvent) => setIngredient(e.value)}
+ checked={ingredient === 'modPack'}
+ />
+ <label htmlFor="ingredient2" className="ml-2">整合包</label>
+ </div>
+ <div className="flex align-items-center">
+ <RadioButton
+ inputId="ingredient3"
+ name="pizza"
+ value="mod"
+ onChange={(e: RadioButtonChangeEvent) => {
+ setResourceFormData(prev => ({
+ ...prev, // 复制顶层所有属性
+ resource: {
+ ...prev.resource, // 复制resource对象的所有属性
+ classify: e.target.value
+ }
+ }));
+ setIngredient(e.value);
+ }}
+ // onChange={(e: RadioButtonChangeEvent) => setIngredient(e.value)}
+ checked={ingredient === 'mod'}
+ />
+ <label htmlFor="ingredient3" className="ml-2">模组</label>
+ </div>
+ <div className="flex align-items-center">
+ <RadioButton
+ inputId="ingredient4"
+ name="pizza"
+ value="map"
+ onChange={(e: RadioButtonChangeEvent) => {
+ setResourceFormData(prev => ({
+ ...prev, // 复制顶层所有属性
+ resource: {
+ ...prev.resource, // 复制resource对象的所有属性
+ classify: e.target.value
+ }
+ }));
+ setIngredient(e.value);
+ }}
+ // onChange={(e: RadioButtonChangeEvent) => setIngredient(e.value)}
+ checked={ingredient === 'map'}
+ />
+ <label htmlFor="ingredient4" className="ml-2">地图</label>
+ </div>
+ </div>
+ </div>
+ <div className="form-field">
+ <div className="form-field-header">
+ <span className="form-field-sign">*</span>
+ <label htmlFor="gameplayList">资源标签</label>
+ </div>
+ <MultiSelect
+ value={selectedGameplay}
+ onChange={(e: MultiSelectChangeEvent) => {
+ const selectedOptions = e.value as GameplayOption[];
+ // 提取选中项的 name 属性组成字符串数组
+ const selectedNames = selectedOptions.map(item => item.name);
+
+ setResourceFormData(prev => ({
+ ...prev,
+ gameplayList: selectedNames
+ }));
+ setSelectedGameplay(selectedOptions);
+ }}
+ options={gameplayOptions}
+ display="chip"
+ optionLabel="name"
+ placeholder="请选择资源标签"
+ // maxSelectedLabels={3}
+ className="w-full md:w-20rem"
+ />
+ </div>
+ <div className="form-field">
+ <div className="form-field-header">
+ <span className="form-field-sign">*</span>
+ <label>封面图片</label>
+ </div>
+ <FileUpload
+ mode="basic"
+ name="resource-image"
+ url={process.env.PUBLIC_URL +"/file"} // 与后端交互的URL
+ accept="image/*"
+ maxFileSize={10000000000}
+ chooseLabel="选择资源封面"
+ className="w-full"
+ onUpload={onUpload}
+ />
+ </div>
+ </div>
+ </Dialog>
+
</div>
);
};