feat: 本次更新项目为:

样式做的很好
This commit is contained in:
超级老白兔
2025-07-29 14:59:10 +08:00
parent 6d768f3a97
commit 8a06e6504d
2 changed files with 445 additions and 44 deletions

View File

@@ -1,7 +1,5 @@
.materials-page {
padding: 16px;
background: #f5f5f5;
min-height: 100vh;
}
.search-bar {
@@ -338,3 +336,268 @@
background: white;
border-top: 1px solid #f0f0f0;
}
// 内容类型标签样式
.content-type-tag {
display: inline-flex;
align-items: center;
padding: 4px 8px;
border-radius: 12px;
font-size: 12px;
font-weight: 500;
border: 1px solid currentColor;
}
// 图片类型预览样式
.material-image-preview {
margin: 12px 0;
.image-grid {
display: grid;
gap: 8px;
width: 100%;
// 1张图片宽度拉伸高度自适应
&.single {
grid-template-columns: 1fr;
img {
width: 100%;
height: auto;
object-fit: cover;
border-radius: 8px;
}
}
// 2张图片左右并列
&.double {
grid-template-columns: 1fr 1fr;
img {
width: 100%;
height: 120px;
object-fit: cover;
border-radius: 8px;
}
}
// 3张图片三张并列
&.triple {
grid-template-columns: 1fr 1fr 1fr;
img {
width: 100%;
height: 100px;
object-fit: cover;
border-radius: 8px;
}
}
// 4张及以上网格布局
&.grid {
grid-template-columns: repeat(3, 1fr);
img {
width: 100%;
height: 80px;
object-fit: cover;
border-radius: 8px;
}
.image-more {
display: flex;
align-items: center;
justify-content: center;
background: #f5f5f5;
border-radius: 8px;
color: #666;
font-size: 12px;
font-weight: 500;
height: 80px;
}
}
}
.no-image {
display: flex;
align-items: center;
justify-content: center;
height: 80px;
background: #f5f5f5;
border-radius: 8px;
color: #999;
font-size: 14px;
}
}
// 链接类型预览样式
.material-link-preview {
margin: 12px 0;
.link-card {
display: flex;
background: #e9f8ff;
border-radius: 8px;
padding: 12px;
cursor: pointer;
transition: all 0.2s;
border: 1px solid #cde6ff;
&:hover {
background: #cde6ff;
}
.link-image {
width: 60px;
height: 60px;
margin-right: 12px;
flex-shrink: 0;
img {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 6px;
}
}
.link-content {
flex: 1;
min-width: 0;
.link-title {
font-weight: 500;
margin-bottom: 4px;
color: #333;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.link-url {
font-size: 12px;
color: #666;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
}
// 视频类型预览样式
.material-video-preview {
margin: 12px 0;
.video-thumbnail {
video {
width: 100%;
max-height: 200px;
border-radius: 8px;
}
}
.no-video {
display: flex;
align-items: center;
justify-content: center;
height: 120px;
background: #f5f5f5;
border-radius: 8px;
color: #999;
font-size: 14px;
}
}
// 文本类型预览样式
.material-text-preview {
margin: 12px 0;
.text-content {
background: #f8f9fa;
padding: 12px;
border-radius: 8px;
line-height: 1.6;
color: #333;
font-size: 14px;
}
}
// 小程序类型预览样式
.material-miniprogram-preview {
margin: 12px 0;
.miniprogram-card {
display: flex;
background: #f8f9fa;
border-radius: 8px;
padding: 12px;
width: 100%;
img {
width: 60px;
height: 60px;
border-radius: 8px;
margin-right: 12px;
flex-shrink: 0;
object-fit: cover;
}
.miniprogram-info {
flex: 1;
min-width: 0;
.miniprogram-title {
font-weight: 500;
color: #333;
margin-bottom: 4px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
}
// 图文类型预览样式
.material-article-preview {
margin: 12px 0;
.article-image {
margin-bottom: 12px;
img {
width: 100%;
height: 120px;
object-fit: cover;
border-radius: 8px;
}
}
.article-content {
.article-title {
font-weight: 500;
color: #333;
margin-bottom: 8px;
font-size: 16px;
}
.article-text {
color: #666;
line-height: 1.6;
font-size: 14px;
}
}
}
// 默认预览样式
.material-default-preview {
margin: 12px 0;
.default-content {
background: #f8f9fa;
padding: 12px;
border-radius: 8px;
color: #333;
line-height: 1.6;
}
}

View File

@@ -10,6 +10,11 @@ import {
DeleteOutlined,
UserOutlined,
BarChartOutlined,
PictureOutlined,
LinkOutlined,
VideoCameraOutlined,
FileTextOutlined,
AppstoreOutlined,
} from "@ant-design/icons";
import Layout from "@/components/Layout/Layout";
import NavCommon from "@/components/NavCommon";
@@ -17,6 +22,16 @@ import { getContentItemList, deleteContentItem } from "./api";
import { ContentItem } from "./data";
import style from "./index.module.scss";
// 内容类型配置
const contentTypeConfig = {
1: { label: "图片", icon: PictureOutlined, color: "#52c41a" },
2: { label: "链接", icon: LinkOutlined, color: "#1890ff" },
3: { label: "视频", icon: VideoCameraOutlined, color: "#722ed1" },
4: { label: "文本", icon: FileTextOutlined, color: "#fa8c16" },
5: { label: "小程序", icon: AppstoreOutlined, color: "#eb2f96" },
6: { label: "图文", icon: PictureOutlined, color: "#13c2c2" },
};
const MaterialsList: React.FC = () => {
const navigate = useNavigate();
const { id } = useParams<{ id: string }>();
@@ -73,19 +88,12 @@ const MaterialsList: React.FC = () => {
if (result) {
try {
const response = await deleteContentItem(materialId);
if (response.code === 200) {
Toast.show({
content: "删除成功",
position: "top",
});
fetchMaterials();
} else {
Toast.show({
content: response.msg || "删除失败",
position: "top",
});
}
await deleteContentItem(materialId.toString());
Toast.show({
content: "删除成功",
position: "top",
});
fetchMaterials();
} catch (error: unknown) {
console.error("删除素材失败:", error);
Toast.show({
@@ -114,6 +122,159 @@ const MaterialsList: React.FC = () => {
setCurrentPage(page);
};
// 渲染内容类型标签
const renderContentTypeTag = (contentType: number) => {
const config =
contentTypeConfig[contentType as keyof typeof contentTypeConfig];
if (!config) return null;
const IconComponent = config.icon;
return (
<div
className={style["content-type-tag"]}
style={{ backgroundColor: config.color + "20", color: config.color }}
>
<IconComponent style={{ fontSize: 12, marginRight: 4 }} />
{config.label}
</div>
);
};
// 渲染素材内容预览
const renderContentPreview = (material: ContentItem) => {
const { contentType, content, resUrls, urls, coverImage } = material;
switch (contentType) {
case 1: // 图片
return (
<div className={style["material-image-preview"]}>
{resUrls && resUrls.length > 0 ? (
<div
className={`${style["image-grid"]} ${
resUrls.length === 1
? style.single
: resUrls.length === 2
? style.double
: resUrls.length === 3
? style.triple
: style.grid
}`}
>
{resUrls.slice(0, 9).map((url, index) => (
<img key={index} src={url} alt={`图片${index + 1}`} />
))}
{resUrls.length > 9 && (
<div className={style["image-more"]}>
+{resUrls.length - 9}
</div>
)}
</div>
) : coverImage ? (
<div className={`${style["image-grid"]} ${style.single}`}>
<img src={coverImage} alt="封面图" />
</div>
) : (
<div className={style["no-image"]}></div>
)}
</div>
);
case 2: // 链接
return (
<div className={style["material-link-preview"]}>
{urls && urls.length > 0 && (
<div
className={style["link-card"]}
onClick={() => {
window.open(urls[0].url, "_blank");
}}
>
{urls[0].image && (
<div className={style["link-image"]}>
<img src={urls[0].image} alt="链接预览" />
</div>
)}
<div className={style["link-content"]}>
<div className={style["link-title"]}>
{urls[0].desc || "链接"}
</div>
<div className={style["link-url"]}>{urls[0].url}</div>
</div>
</div>
)}
</div>
);
case 3: // 视频
return (
<div className={style["material-video-preview"]}>
{resUrls && resUrls.length > 0 ? (
<div className={style["video-thumbnail"]}>
<video src={resUrls[0]} controls />
</div>
) : (
<div className={style["no-video"]}></div>
)}
</div>
);
case 4: // 文本
return (
<div className={style["material-text-preview"]}>
<div className={style["text-content"]}>
{content.length > 100
? `${content.substring(0, 100)}...`
: content}
</div>
</div>
);
case 5: // 小程序
return (
<div className={style["material-miniprogram-preview"]}>
{resUrls && resUrls.length > 0 && (
<div className={style["miniprogram-card"]}>
<img src={resUrls[0]} alt="小程序封面" />
<div className={style["miniprogram-info"]}>
<div className={style["miniprogram-title"]}>
{material.title || "小程序"}
</div>
</div>
</div>
)}
</div>
);
case 6: // 图文
return (
<div className={style["material-article-preview"]}>
{coverImage && (
<div className={style["article-image"]}>
<img src={coverImage} alt="文章封面" />
</div>
)}
<div className={style["article-content"]}>
<div className={style["article-title"]}>
{material.title || "图文内容"}
</div>
<div className={style["article-text"]}>
{content.length > 80
? `${content.substring(0, 80)}...`
: content}
</div>
</div>
</div>
);
default:
return (
<div className={style["material-default-preview"]}>
<div className={style["default-content"]}>{content}</div>
</div>
);
}
};
return (
<Layout
header={
@@ -130,7 +291,7 @@ const MaterialsList: React.FC = () => {
<div className="search-bar">
<div className="search-input-wrapper">
<Input
placeholder="搜索计划名称"
placeholder="搜索素材内容"
value={searchQuery}
onChange={e => setSearchQuery(e.target.value)}
prefix={<SearchOutlined />}
@@ -185,7 +346,7 @@ const MaterialsList: React.FC = () => {
<>
{materials.map(material => (
<Card key={material.id} className={style["material-card"]}>
{/* 顶部头像+系统创建+ID */}
{/* 顶部信息 */}
<div className={style["card-header"]}>
<div className={style["avatar-section"]}>
<div className={style["avatar"]}>
@@ -193,41 +354,18 @@ const MaterialsList: React.FC = () => {
</div>
<div className={style["header-info"]}>
<span className={style["creator-name"]}>
{material.senderNickname}
{material.senderNickname || "系统创建"}
</span>
<span className={style["material-id"]}>
ID: {material.id}
</span>
</div>
</div>
{renderContentTypeTag(material.contentType)}
</div>
{/* 主标题 */}
<div className={style["material-title"]}>
{material.content}
</div>
{/* 链接预览 */}
{material.urls && material.urls.length > 0 && (
<div
className={style["link-preview"]}
onClick={() => {
window.open(material.urls[0].url, "_blank");
}}
>
<div className={style["link-icon"]}>
<img src={material.urls[0].image} />
</div>
<div className={style["link-content"]}>
<div className={style["link-title"]}>
{material.urls[0].desc}
</div>
<div className={style["link-url"]}>
{material.urls[0].url}
</div>
</div>
</div>
)}
{/* 内容预览 */}
{renderContentPreview(material)}
{/* 操作按钮区 */}
<div className={style["action-buttons"]}>