add resource manage
Change-Id: Ib26c45ca71347e1a314d053b7a3c62cf90bb78ae
diff --git "a/src/app/user/manage/resources/data/\133resourceId\135/page.tsx" "b/src/app/user/manage/resources/data/\133resourceId\135/page.tsx"
deleted file mode 100644
index c48b626..0000000
--- "a/src/app/user/manage/resources/data/\133resourceId\135/page.tsx"
+++ /dev/null
@@ -1,12 +0,0 @@
-'use client';
-import React from 'react';
-
-const EmptyPage: React.FC = () => {
- return (
- <div className="p-d-flex p-jc-center p-ai-center" style={{ height: '100vh' }}>
- {"一个空页面"}
- </div>
- );
-};
-
-export default EmptyPage;
diff --git a/src/app/user/manage/resources/page.tsx b/src/app/user/manage/resources/page.tsx
index c48b626..6c18cf0 100644
--- a/src/app/user/manage/resources/page.tsx
+++ b/src/app/user/manage/resources/page.tsx
@@ -1,12 +1,384 @@
'use client';
-import React from 'react';
+import React, {useEffect, useRef, useState} from 'react';
+// import {TabView, TabPanel} from "primereact/tabview";
+import {Card} from "primereact/card";
+import {Image} from "primereact/image";
+import {Button} from "primereact/button";
+// 弹窗
+import { Dialog } from 'primereact/dialog';
+import {InputText} from "primereact/inputtext";
+import {InputTextarea} from "primereact/inputtextarea";
+import {FileUpload} from "primereact/fileupload";
+// 类型转换
+import {toNumber} from "lodash";
+// 消息提醒
+import {Toast} from 'primereact/toast';
+// 接口传输
+import axios from "axios";
+// 页面跳转
+import {useRouter} from "next/navigation";
+// 分页
+import { Paginator, type PaginatorPageChangeEvent } from 'primereact/paginator';
+// 密码
+import { Password } from 'primereact/password';
+// 样式
+import './resources.scss';
-const EmptyPage: React.FC = () => {
- return (
- <div className="p-d-flex p-jc-center p-ai-center" style={{ height: '100vh' }}>
- {"一个空页面"}
- </div>
- );
+
+
+// 用户发布的资源
+interface Resource {
+ resourceId: number;
+ resourceName: string;
+ resourcePicture: string;
+ resourceSummary: string; // 资源简介(一句话)
+ resourceDetail: string; // 资源介绍
+ uploadTime: string; // 上传时间
+ lastUpdateTime: string; // 最近更新时间
+ price: number;
+ downloads: number;
+ likes: number;
+ collections: number;
+ comments: number;
+ seeds: number; // 种子数
+ classify: string; // 资源分类(材质包:resourcePack,模组:mod,整合包:modPack ,地图:map
+}
+
+// 用户发布的资源列表
+interface ResourceList {
+ records: Resource[];
+ total: number; // 总记录数
+ pages: number; // 总页数
+ current: number; // 当前页码
+ sizes: number; // 每页大小
+}
+
+export default function UserManageResources() {
+ // 路由
+ const router = useRouter();
+ // 发布资源列表
+ const [resourceList, setResourceList] = useState<Resource[]>([]);
+ // 要删除资源的id
+ const [deleteResourceId, setDeleteResourceId] = useState<number>(0);
+ // 资源封面路径
+ const [resourcePictureUrl, setResourcePictureUrl] = useState<string>('');
+ // 编辑资源的分类
+ const [resourceClassify, setResourceClassify] = useState<string>('');
+ // 消息提醒
+ const toast = useRef<Toast>(null);
+ // 分页
+ const [first, setFirst] = useState(0);
+ const [rows, setRows] = useState(5);
+ const [totalResources, setTotalResources] = useState<number>(0);
+ const onPageChange = (event: PaginatorPageChangeEvent) => {
+ setFirst(event.first);
+ setRows(event.rows);
+ };
+ // 删除资源弹窗
+ const [deleteVisible, setDeleteVisible] = useState(false);
+ const [deleteResourceFormData, setDeleteResourceFormData] = useState({
+ password: '',
+ });
+
+ // 编辑资源弹窗
+ const [editVisible, setEditVisible] = useState(false);
+ const [editResourceFormData, setEditResourceFormData] = useState({
+ resourceId: 0,
+ resourceName: '',
+ resourceSummary: '',
+ resourceDetail: '',
+ price: '',
+ });
+
+ useEffect(() => {
+ fetchResourceList();
+ }, [first, rows]);
+
+
+ // 获取用户发布资源
+ const fetchResourceList = async () => {
+ try {
+ const pageNumber = first / rows + 1;
+ const response = await axios.get<ResourceList>(process.env.PUBLIC_URL + `/user/upload`, {
+ params: {userId: 22301010, pageNumber: pageNumber, rows: rows}
+ });
+
+ console.log('获取发布资源列表:', response.data.records);
+ setResourceList(response.data.records);
+ setTotalResources(response.data.total);
+ } catch (err) {
+ console.error('获取发布资源失败', err);
+ toast.current?.show({severity: 'error', summary: 'error', detail: '获取发布资源失败'});
+ }
+ };
+
+ // 处理删除资源接口
+ const handleDeleteSubmit = async () => {
+ try {
+ const deleteData = {
+ resourceId: deleteResourceId,
+ userId: 223010100,
+ password: deleteResourceFormData.password,
+ };
+ // 发送DELETE请求
+ const response = await axios.delete(process.env.PUBLIC_URL + `/resource`, {
+ params: {resourceId: deleteData.resourceId, userId: deleteData.userId, password: deleteData.password},
+ });
+ console.log("用户" + 22301010 + "要删除" + deleteData.resourceId + "号资源");
+ console.log(deleteData);
+ console.log(response);
+ console.log(response.status)
+
+ if (response.status === 204) {
+ console.log("用户成功删除资源");
+ // setIsDeleteResource(true);
+ toast.current?.show({severity: 'success', summary: 'Success', detail: '删除资源成功'});
+ setDeleteVisible(false);
+ // 重置表单
+ setDeleteResourceFormData({
+ password: '',
+ });
+ // 重新拉取资源列表
+ fetchResourceList();
+ } else {
+ console.log('用户密码错误');
+ toast.current?.show({severity: 'error', summary: 'Error', detail: '密码错误,删除资源失败'});
+ }
+
+ } catch (error) {
+ console.error('资源删除失败:', error);
+ toast.current?.show({ severity: 'error', summary: 'error', detail: '密码错误,资源删除失败' });
+ }
+ };
+
+ // 处理编辑资源接口
+ const handleEditSubmit = async () => {
+ try {
+ const postData = {
+ resourceId: editResourceFormData.resourceId,
+ resourceName: editResourceFormData.resourceName,
+ resourcePicture: resourcePictureUrl,
+ resourceSummary: editResourceFormData.resourceSummary,
+ resourceDetail: editResourceFormData.resourceDetail,
+ price: toNumber(editResourceFormData.price),
+ classify: resourceClassify,
+ };
+ // 发送POST请求
+ const response = await axios.put(process.env.PUBLIC_URL + '/resource/info', postData);
+ console.log("编辑资源的信息:", postData);
+
+ if (response.status === 200) {
+ toast.current?.show({ severity: 'success', summary: 'Success', detail: '资源编辑成功' });
+ // 编辑成功
+ setEditVisible(false);
+ fetchResourceList();
+ }
+ } catch (error) {
+ console.error('资源上传失败:', error);
+ toast.current?.show({ severity: 'error', summary: 'error', detail: '资源上传失败' });
+ }
+ };
+
+ return (
+ <div className="resources-container">
+ <Toast ref={toast}></Toast>
+ <div className="resources-title">
+ <h1>我上传的资源</h1>
+ </div>
+
+ <div className="resource-list">
+ {resourceList.map((resourceList) => (
+ <Card key={resourceList.resourceId} className="resources-list-card"
+ onClick={() => router.push(`/resource/resource-detail/${resourceList.resourceId}`)}>
+ <Image alt="avatar"
+ src={process.env.NEXT_PUBLIC_NGINX_URL + "resource/" + resourceList.resourcePicture}
+ className="resource-avatar" width="250" height="140"/>
+ <div className="resource-header">
+ <div className="resource-content">
+ <h3>{resourceList.resourceName}</h3>
+ </div>
+
+ <div className="resource-operation">
+ <Button
+ label="编辑"
+ onClick={(e) => {
+ e.stopPropagation(); // 关键修复:阻止事件冒泡,避免触发Card的点击事件
+ setEditVisible(true);
+
+ setResourceClassify(resourceList.classify);
+ setEditResourceFormData({
+ resourceId: resourceList.resourceId,
+ resourceName: resourceList.resourceName,
+ resourceSummary: resourceList.resourceSummary,
+ resourceDetail: resourceList.resourceDetail,
+ price: resourceList.price.toString(),
+ });
+ console.log("用户编辑资源");
+ }}
+ />
+ <Button
+ label="删除"
+ onClick={(e) => {
+ e.stopPropagation(); // 关键修复:阻止事件冒泡,避免触发Card的点击事件
+ setDeleteResourceId(resourceList.resourceId);
+ setDeleteVisible(true);
+ }}
+ style={{backgroundColor: "rgba(255, 87, 51, 1)"}}
+ />
+ </div>
+ </div>
+ </Card>
+ ))}
+ {totalResources > 5 && <Paginator
+ className="Paginator"
+ first={first}
+ rows={rows}
+ totalRecords={totalResources}
+ rowsPerPageOptions={[5, 10]}
+ onPageChange={onPageChange}
+ />}
+ </div>
+
+ {/*删除资源时,获取用户密码弹窗*/}
+ <Dialog
+ header="删除资源"
+ visible={deleteVisible}
+ onHide={() => setDeleteVisible(false)}
+ className="resource-delete-dialog"
+ modal
+ footer={
+ <div className="dialog-footer">
+ <Button label="确认" icon="pi pi-check" onClick={handleDeleteSubmit} autoFocus />
+ <Button label="取消" icon="pi pi-times" onClick={() => setDeleteVisible(false)} className="p-button-text" />
+ </div>
+ }
+ >
+ <div className="dialog-form">
+ <div className="form-field">
+ <div className="form-field-header">
+ <label htmlFor="name">密码</label>
+ </div>
+ <Password
+ id="passwrod"
+ value={deleteResourceFormData.password}
+ onChange={(e) => setDeleteResourceFormData(prev => ({
+ ...prev,
+ password: e.target.value
+ }))}
+ placeholder="请输入密码"
+ className="w-full"
+ toggleMask
+ />
+ </div>
+ </div>
+ </Dialog>
+
+ {/*编辑资源弹窗*/}
+ <Dialog
+ header="编辑资源"
+ visible={editVisible}
+ onHide={() => setEditVisible(false)}
+ className="resource-edit-dialog"
+ modal
+ footer={
+ <div className="dialog-footer">
+ <Button label="确认" icon="pi pi-check" onClick={handleEditSubmit} autoFocus />
+ <Button label="取消" icon="pi pi-times" onClick={() => setEditVisible(false)} className="p-button-text" />
+ </div>
+ }
+ >
+ <div className="dialog-form">
+ <div className="form-field">
+ <div className="form-field-header">
+ <label htmlFor="name">资源名称</label>
+ </div>
+ <InputText
+ id="name"
+ value={editResourceFormData.resourceName}
+ onChange={(e) => setEditResourceFormData(prev => ({
+ ...prev,
+ resourceName: e.target.value
+ }))}
+ className="w-full"
+ />
+ </div>
+ <div className="form-field">
+ <div className="form-field-header">
+ <label htmlFor="summary">资源简介</label>
+ </div>
+ <InputText
+ id="summary"
+ value={editResourceFormData.resourceSummary}
+ onChange={(e) => setEditResourceFormData(prev => ({
+ ...prev,
+ resourceSummary: e.target.value
+ }))}
+ className="w-full"
+ />
+ </div>
+ <div className="form-field">
+ <div className="form-field-header">
+ <label htmlFor="detail">资源介绍</label>
+ </div>
+ <InputTextarea
+ id="detail"
+ value={editResourceFormData.resourceDetail}
+ onChange={(e) => setEditResourceFormData(prev => ({
+ ...prev,
+ resourceDetail: e.target.value
+ }))}
+ rows={5}
+ className="w-full"
+ />
+ </div>
+ <div className="form-field">
+ <div className="form-field-header">
+ <label htmlFor="price">价格</label>
+ </div>
+ <InputText
+ id="price"
+ value={editResourceFormData?.price.toString()}
+ onChange={(e) => setEditResourceFormData(prev => ({
+ ...prev,
+ price: e.target.value
+ }))}
+ className="w-full"
+ />
+ </div>
+ <div className="form-field">
+ <div className="form-field-header">
+ <span className="form-field-sign">*</span>
+ <label>封面图片</label>
+ </div>
+
+ <FileUpload
+ mode="advanced"
+ name="resource-image"
+ customUpload
+
+ uploadHandler={async (e) => {
+ const formData = new FormData();
+ formData.append("file", e.files[0]);
+
+ try {
+ const res = await axios.post(`${process.env.PUBLIC_URL}/file`, formData);
+
+ const fileUrl = res.data.url;
+ console.log(fileUrl);
+ setResourcePictureUrl(fileUrl);
+ toast.current?.show({ severity: 'success', summary: '上传成功' });
+ } catch (error) {
+ console.log(error);
+ toast.current?.show({ severity: 'error', summary: '上传失败' });
+ }
+ }}
+ auto
+ accept="image/*"
+ chooseLabel="选择资源封面"
+ />
+ </div>
+ </div>
+ </Dialog>
+ </div>
+ )
};
-
-export default EmptyPage;
diff --git a/src/app/user/manage/resources/resources.scss b/src/app/user/manage/resources/resources.scss
new file mode 100644
index 0000000..93f82c4
--- /dev/null
+++ b/src/app/user/manage/resources/resources.scss
@@ -0,0 +1,202 @@
+@import '../../../globals.scss';
+
+//全局容器样式
+.resources-container {
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 0 2rem;
+
+ .resource-title {
+
+ }
+
+ .resources-list {
+ width: 100%;
+ padding: 1rem;
+
+ &-card {
+ height: 140px;
+ padding: 1.5rem;
+ margin-bottom: 1rem;
+ border-radius: 0.5rem;
+ transition: transform 0.3s ease;
+ box-shadow: none !important; // 取消阴影
+
+ //填充卡片
+ &.p-card.p-component {
+ padding: 0;
+ }
+
+ .p-card-body {
+ padding: 0;
+ }
+
+ &:hover {
+ transform: translateY(-3px);
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+ }
+
+ .p-card-content {
+ height: 140px;
+ display: flex;
+ justify-content: space-between;
+ padding: 0;
+ }
+
+ img {
+ border-radius: 0.5rem 0 0 0.5rem;
+ object-fit: cover;
+ }
+
+
+ .resource-avatar {
+ width: 80px;
+ height: 80px;
+ border-radius: 8px;
+ object-fit: cover;
+ }
+
+ .resource-header {
+ display: flex;
+ flex: 1;
+ max-width: 850px;
+ padding-left: 20px;
+ padding-right: 20px;
+ margin-bottom: 20px;
+ }
+
+ .resource-content {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+
+ h3 {
+ font-size: 1.5rem;
+ font-weight: bold;
+ color: #2c3e50;
+ }
+
+
+ }
+
+ .resource-operation {
+ min-width: 240px;
+ display: flex;
+ flex-direction: row;
+ justify-content: flex-end;
+ align-items: flex-end;
+ gap: 1rem;
+ }
+ }
+ }
+}
+
+//删除资源弹窗样式
+.resource-delete-dialog {
+ width: 350px !important;
+ max-width: 90vw;
+
+ .dialog-footer {
+ margin-top: 10px;
+ display: flex;
+ justify-content: center;
+ }
+
+ .p-dialog-header {
+ font-size: 1.5rem;
+ font-weight: bold;
+ color: $heading-color;
+ padding-bottom: 0.5rem;
+ }
+
+ .p-dialog-content {
+ padding-top: 0;
+ padding-bottom: 0;
+
+ .dialog-form {
+ display: flex;
+ flex-direction: column;
+ gap: 1.5rem;
+ margin-top: 1rem;
+
+ .form-field {
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+
+ label {
+ font-weight: 600;
+ color: $heading-color;
+ }
+
+ input,
+ textarea{
+ padding: 0.75rem 1rem;
+ border-radius: 8px;
+ font-size: 1rem;
+ color: #2d3748;
+ }
+
+ }
+ }
+ }
+}
+
+//资源编辑弹窗样式
+.resource-edit-dialog {
+ width: 600px !important;
+ max-width: 90vw;
+
+ .dialog-footer {
+ margin-top: 10px;
+ display: flex;
+ justify-content: center;
+ }
+
+ .p-dialog-header {
+ font-size: 1.5rem;
+ font-weight: bold;
+ color: $heading-color;
+ padding-bottom: 0.5rem;
+ }
+
+ .p-dialog-content {
+ padding-top: 0;
+ padding-bottom: 0;
+
+ .dialog-form {
+ display: flex;
+ flex-direction: column;
+ gap: 1.5rem;
+ margin-top: 1rem;
+
+ .form-field {
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+
+ label {
+ font-weight: 600;
+ color: $heading-color;
+ }
+
+ input,
+ textarea{
+ padding: 0.75rem 1rem;
+ border-radius: 8px;
+ font-size: 1rem;
+ color: #2d3748;
+ }
+
+ .p-fileupload {
+ .p-button {
+ width: 100%;
+ justify-content: center;
+ border: none;
+ margin-bottom: 1rem;
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file