303 lines
9.4 KiB
TypeScript
303 lines
9.4 KiB
TypeScript
import React, { useState, useEffect } from "react";
|
|
import { SearchOutlined, DeleteOutlined } from "@ant-design/icons";
|
|
import { Button, Input } from "antd";
|
|
import { Popup, Checkbox } from "antd-mobile";
|
|
import style from "./index.module.scss";
|
|
import Layout from "@/components/Layout/Layout";
|
|
import PopupHeader from "@/components/PopuLayout/header";
|
|
import PopupFooter from "@/components/PopuLayout/footer";
|
|
import { getContentLibraryList } from "./api";
|
|
import { ContentItem, ContentSelectionProps } from "./data";
|
|
|
|
// 类型标签文本
|
|
const getTypeText = (type?: number) => {
|
|
if (type === 1) return "文本";
|
|
if (type === 2) return "图片";
|
|
if (type === 3) return "视频";
|
|
return "未知";
|
|
};
|
|
|
|
// 时间格式化
|
|
const formatDate = (dateStr?: string) => {
|
|
if (!dateStr) return "-";
|
|
const d = new Date(dateStr);
|
|
if (isNaN(d.getTime())) return "-";
|
|
return `${d.getFullYear()}/${(d.getMonth() + 1)
|
|
.toString()
|
|
.padStart(2, "0")}/${d.getDate().toString().padStart(2, "0")} ${d
|
|
.getHours()
|
|
.toString()
|
|
.padStart(2, "0")}:${d.getMinutes().toString().padStart(2, "0")}:${d
|
|
.getSeconds()
|
|
.toString()
|
|
.padStart(2, "0")}`;
|
|
};
|
|
|
|
export default function ContentSelection({
|
|
selectedOptions,
|
|
onSelect,
|
|
placeholder = "选择内容库",
|
|
className = "",
|
|
visible,
|
|
onVisibleChange,
|
|
selectedListMaxHeight = 300,
|
|
showInput = true,
|
|
showSelectedList = true,
|
|
readonly = false,
|
|
onConfirm,
|
|
}: ContentSelectionProps) {
|
|
const [popupVisible, setPopupVisible] = useState(false);
|
|
const [libraries, setLibraries] = useState<ContentItem[]>([]);
|
|
const [searchQuery, setSearchQuery] = useState("");
|
|
const [currentPage, setCurrentPage] = useState(1);
|
|
const [totalPages, setTotalPages] = useState(1);
|
|
const [totalLibraries, setTotalLibraries] = useState(0);
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
// 删除已选内容库
|
|
const handleRemoveLibrary = (id: number) => {
|
|
if (readonly) return;
|
|
onSelect(selectedOptions.filter(c => c.id !== id));
|
|
};
|
|
|
|
// 受控弹窗逻辑
|
|
const realVisible = visible !== undefined ? visible : popupVisible;
|
|
const setRealVisible = (v: boolean) => {
|
|
if (onVisibleChange) onVisibleChange(v);
|
|
if (visible === undefined) setPopupVisible(v);
|
|
};
|
|
|
|
// 打开弹窗
|
|
const openPopup = () => {
|
|
if (readonly) return;
|
|
setCurrentPage(1);
|
|
setSearchQuery("");
|
|
setRealVisible(true);
|
|
fetchLibraries(1, "");
|
|
};
|
|
|
|
// 当页码变化时,拉取对应页数据(弹窗已打开时)
|
|
useEffect(() => {
|
|
if (realVisible && currentPage !== 1) {
|
|
fetchLibraries(currentPage, searchQuery);
|
|
}
|
|
}, [currentPage, realVisible, searchQuery]);
|
|
|
|
// 搜索防抖
|
|
useEffect(() => {
|
|
if (!realVisible) return;
|
|
const timer = setTimeout(() => {
|
|
setCurrentPage(1);
|
|
fetchLibraries(1, searchQuery);
|
|
}, 500);
|
|
return () => clearTimeout(timer);
|
|
}, [searchQuery, realVisible]);
|
|
|
|
// 获取内容库列表API
|
|
const fetchLibraries = async (page: number, keyword: string = "") => {
|
|
setLoading(true);
|
|
try {
|
|
const params: any = {
|
|
page,
|
|
limit: 20,
|
|
};
|
|
if (keyword.trim()) {
|
|
params.keyword = keyword.trim();
|
|
}
|
|
const response = await getContentLibraryList(params);
|
|
if (response && response.list) {
|
|
setLibraries(response.list);
|
|
setTotalLibraries(response.total || 0);
|
|
setTotalPages(Math.ceil((response.total || 0) / 20));
|
|
}
|
|
} catch (error) {
|
|
console.error("获取内容库列表失败:", error);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
// 处理内容库选择
|
|
const handleLibraryToggle = (library: ContentItem) => {
|
|
if (readonly) return;
|
|
const newSelected = selectedOptions.some(c => c.id === library.id)
|
|
? selectedOptions.filter(c => c.id !== library.id)
|
|
: [...selectedOptions, library];
|
|
onSelect(newSelected);
|
|
};
|
|
|
|
// 获取显示文本
|
|
const getDisplayText = () => {
|
|
if (selectedOptions.length === 0) return "";
|
|
return `已选择 ${selectedOptions.length} 个内容库`;
|
|
};
|
|
|
|
// 确认选择
|
|
const handleConfirm = () => {
|
|
if (onConfirm) {
|
|
onConfirm(selectedOptions);
|
|
}
|
|
setRealVisible(false);
|
|
};
|
|
|
|
return (
|
|
<>
|
|
{/* 输入框 */}
|
|
{showInput && (
|
|
<div className={`${style.inputWrapper} ${className}`}>
|
|
<Input
|
|
placeholder={placeholder}
|
|
value={getDisplayText()}
|
|
onClick={openPopup}
|
|
prefix={<SearchOutlined />}
|
|
allowClear={!readonly}
|
|
size="large"
|
|
readOnly={readonly}
|
|
disabled={readonly}
|
|
style={
|
|
readonly ? { background: "#f5f5f5", cursor: "not-allowed" } : {}
|
|
}
|
|
/>
|
|
</div>
|
|
)}
|
|
{/* 已选内容库列表窗口 */}
|
|
{showSelectedList && selectedOptions.length > 0 && (
|
|
<div
|
|
className={style.selectedListWindow}
|
|
style={{
|
|
maxHeight: selectedListMaxHeight,
|
|
overflowY: "auto",
|
|
marginTop: 8,
|
|
border: "1px solid #e5e6eb",
|
|
borderRadius: 8,
|
|
background: "#fff",
|
|
}}
|
|
>
|
|
{selectedOptions.map(item => (
|
|
<div
|
|
key={item.id}
|
|
className={style.selectedListRow}
|
|
style={{
|
|
display: "flex",
|
|
alignItems: "center",
|
|
padding: "4px 8px",
|
|
borderBottom: "1px solid #f0f0f0",
|
|
fontSize: 14,
|
|
}}
|
|
>
|
|
<div
|
|
style={{
|
|
flex: 1,
|
|
minWidth: 0,
|
|
whiteSpace: "nowrap",
|
|
overflow: "hidden",
|
|
textOverflow: "ellipsis",
|
|
}}
|
|
>
|
|
{item.name || item.id}
|
|
</div>
|
|
{!readonly && (
|
|
<Button
|
|
type="text"
|
|
icon={<DeleteOutlined />}
|
|
size="small"
|
|
style={{
|
|
marginLeft: 4,
|
|
color: "#ff4d4f",
|
|
border: "none",
|
|
background: "none",
|
|
minWidth: 24,
|
|
height: 24,
|
|
display: "flex",
|
|
alignItems: "center",
|
|
justifyContent: "center",
|
|
}}
|
|
onClick={() => handleRemoveLibrary(item.id)}
|
|
/>
|
|
)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
{/* 弹窗 */}
|
|
<Popup
|
|
visible={realVisible && !readonly}
|
|
onMaskClick={() => setRealVisible(false)}
|
|
position="bottom"
|
|
bodyStyle={{ height: "100vh" }}
|
|
>
|
|
<Layout
|
|
header={
|
|
<PopupHeader
|
|
title="选择内容库"
|
|
searchQuery={searchQuery}
|
|
setSearchQuery={setSearchQuery}
|
|
searchPlaceholder="搜索内容库"
|
|
loading={loading}
|
|
onRefresh={() => fetchLibraries(currentPage, searchQuery)}
|
|
/>
|
|
}
|
|
footer={
|
|
<PopupFooter
|
|
total={totalLibraries}
|
|
currentPage={currentPage}
|
|
totalPages={totalPages}
|
|
loading={loading}
|
|
selectedCount={selectedOptions.length}
|
|
onPageChange={setCurrentPage}
|
|
onCancel={() => setRealVisible(false)}
|
|
onConfirm={handleConfirm}
|
|
/>
|
|
}
|
|
>
|
|
<div className={style.libraryList}>
|
|
{loading ? (
|
|
<div className={style.loadingBox}>
|
|
<div className={style.loadingText}>加载中...</div>
|
|
</div>
|
|
) : libraries.length > 0 ? (
|
|
<div className={style.libraryListInner}>
|
|
{libraries.map(item => (
|
|
<label key={item.id} className={style.libraryItem}>
|
|
<Checkbox
|
|
checked={selectedOptions.map(c => c.id).includes(item.id)}
|
|
onChange={() => !readonly && handleLibraryToggle(item)}
|
|
disabled={readonly}
|
|
className={style.checkboxWrapper}
|
|
/>
|
|
<div className={style.libraryInfo}>
|
|
<div className={style.libraryHeader}>
|
|
<span className={style.libraryName}>{item.name}</span>
|
|
<span className={style.typeTag}>
|
|
{getTypeText(item.sourceType)}
|
|
</span>
|
|
</div>
|
|
<div className={style.libraryMeta}>
|
|
<div>创建人: {item.creatorName || "-"}</div>
|
|
<div>更新时间: {formatDate(item.updateTime)}</div>
|
|
</div>
|
|
{item.description && (
|
|
<div className={style.libraryDesc}>
|
|
{item.description}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</label>
|
|
))}
|
|
</div>
|
|
) : (
|
|
<div className={style.emptyBox}>
|
|
<div className={style.emptyText}>
|
|
{searchQuery
|
|
? `没有找到包含"${searchQuery}"的内容库`
|
|
: "没有找到内容库"}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</Layout>
|
|
</Popup>
|
|
</>
|
|
);
|
|
}
|