提交 fac449c9 authored 作者: 王鹏飞's avatar 王鹏飞

refactor(admin): migrate books sale module

上级 632a69a5
...@@ -10,11 +10,7 @@ const ChapterManagement = lazy(() => import("@/pages/books/section/index")); ...@@ -10,11 +10,7 @@ const ChapterManagement = lazy(() => import("@/pages/books/section/index"));
const AuditRoutes = lazy(() => import("./audit/routes")); const AuditRoutes = lazy(() => import("./audit/routes"));
const ClassifyRoutes = lazy(() => import("./classify/routes")); const ClassifyRoutes = lazy(() => import("./classify/routes"));
const LabelRoutes = lazy(() => import("./label/routes")); const LabelRoutes = lazy(() => import("./label/routes"));
const SaleLayout = lazy(() => import("@/pages/books/sale/index")); const SaleRoutes = lazy(() => import("./sale/routes"));
const SaleTable = lazy(() => import("@/pages/books/sale/table"));
const SaleEdit = lazy(() => import("@/pages/books/sale/edit"));
const DiscussList = lazy(() => import("@/pages/books/sale/discussList/index"));
const DiscussDetail = lazy(() => import("@/pages/books/sale/discussDetail/index"));
const QuestionBankRoutes = lazy(() => import("./question-bank/routes")); const QuestionBankRoutes = lazy(() => import("./question-bank/routes"));
const DiscussionRoutes = lazy(() => import("./discussion/routes")); const DiscussionRoutes = lazy(() => import("./discussion/routes"));
const OrderManagementLayout = lazy(() => import("@/pages/books/order-management/index")); const OrderManagementLayout = lazy(() => import("@/pages/books/order-management/index"));
...@@ -41,17 +37,7 @@ export default function BooksRoutes() { ...@@ -41,17 +37,7 @@ export default function BooksRoutes() {
{ path: "audit/*", Component: AuditRoutes }, { path: "audit/*", Component: AuditRoutes },
{ path: "classify/*", Component: ClassifyRoutes }, { path: "classify/*", Component: ClassifyRoutes },
{ path: "label/*", Component: LabelRoutes }, { path: "label/*", Component: LabelRoutes },
{ { path: "sale/*", Component: SaleRoutes },
path: "sale",
Component: SaleLayout,
children: [
{ index: true, Component: SaleTable },
{ path: "dataset", Component: SaleTable },
{ path: "detail", Component: SaleEdit },
{ path: "discussP", Component: DiscussList },
{ path: "discuss-detail", Component: DiscussDetail },
],
},
{ path: "question-bank/*", Component: QuestionBankRoutes }, { path: "question-bank/*", Component: QuestionBankRoutes },
{ path: "discussion/*", Component: DiscussionRoutes }, { path: "discussion/*", Component: DiscussionRoutes },
{ {
......
import { message } from "antd";
import request from "@/utils/request";
import type { SaleDiscussionQuery, SaleQuery } from "./query";
async function post(path: string, data: Record<string, unknown>, notify = false) {
try { const response = (await request.post(path, new URLSearchParams(data as Record<string, string>).toString())) as { data?: { code?: number; data?: any; message?: string } }; if (response.data?.code === 200) { if (notify) message.success(response.data.message); return response.data.data; } message.error(response.data?.message || "数据获取失败!"); } catch {}
return false;
}
export const getSaleList = (query: SaleQuery) => post("/book/book/getList", query);
export const changeSaleStatus = (id: string | number, status: string) => post("/book/book/changeStatus", { id, status }, true);
export const getSaleDiscussionList = (query: SaleDiscussionQuery) => post("/comment/comment/getCommentList", query);
export { default } from "@/pages/books/sale/discussDetail";
export { default } from "@/pages/books/sale/edit";
import type { Dayjs } from "dayjs";
export interface SaleFilters { name?: string; authors?: string; status?: string | number; time?: [Dayjs | null, Dayjs | null] | null; }
export interface SaleQuery { page: number; page_size: number; name?: string; authors?: string; status?: string | number; start_time?: number; end_time?: number; }
export interface SaleDiscussionFilters { title?: string; user_name?: string; time?: [Dayjs | null, Dayjs | null] | null; }
export interface SaleDiscussionQuery { page: number; page_size: number; book_id: string | number; title?: string; user_name?: string; start_time?: number; end_time?: number; }
function addTime(query: any, time: any) { if (time?.[0] && time[1]) { query.start_time = time[0].startOf("day").unix(); query.end_time = time[1].endOf("day").unix(); } return query; }
export const buildSaleQuery = (page: number, pageSize: number, filters: SaleFilters): SaleQuery => addTime({ page, page_size: pageSize, ...(filters.name ? { name: filters.name } : {}), ...(filters.authors ? { authors: filters.authors } : {}), ...(filters.status !== undefined && filters.status !== null ? { status: filters.status } : {}) }, filters.time);
export const buildSaleDiscussionQuery = (page: number, pageSize: number, bookId: string | number, filters: SaleDiscussionFilters): SaleDiscussionQuery => addTime({ page, page_size: pageSize, book_id: bookId, ...(filters.title ? { title: filters.title } : {}), ...(filters.user_name ? { user_name: filters.user_name } : {}) }, filters.time);
import { Navigate, Outlet, useRoutes } from "react-router";
import SaleDiscussionDetail from "./components/SaleDiscussionDetail";
import SaleEdit from "./components/SaleEdit";
import SaleDiscussionListView from "./views/SaleDiscussionListView";
import SaleListView from "./views/SaleListView";
function Layout() { return <Outlet />; }
export default function SaleRoutes() { return useRoutes([{ Component: Layout, children: [{ index: true, Component: SaleListView }, { path: "dataset", Component: SaleListView }, { path: "detail", Component: SaleEdit }, { path: "discussP", Component: SaleDiscussionListView }, { path: "discuss-detail", Component: SaleDiscussionDetail }, { path: "*", element: <Navigate to="." replace /> }] }]); }
import { useCallback, useEffect, useRef, useState } from "react";
import { getSaleDiscussionList } from "./api";
import { buildSaleDiscussionQuery, type SaleDiscussionFilters, type SaleDiscussionQuery } from "./query";
export function useSaleDiscussionList(bookId?: string | number) { const [filters, setFilters] = useState<SaleDiscussionFilters>({}); const [applied, setApplied] = useState<SaleDiscussionFilters>({}); const [page, setPage] = useState(1); const [pageSize, setPageSize] = useState(10); const [rows, setRows] = useState<any[]>([]); const [total, setTotal] = useState(0); const [loading, setLoading] = useState(true); const [error, setError] = useState<Error | null>(null); const last = useRef<SaleDiscussionQuery | null>(null); const load = useCallback(async (query: SaleDiscussionQuery) => { last.current = query; setLoading(true); try { const result: any = await getSaleDiscussionList(query); if (!result) throw new Error("请求失败"); setRows(result.list || []); setTotal(result.total || 0); setError(null); } catch (e) { setError(e instanceof Error ? e : new Error("请求失败")); } finally { setLoading(false); } }, []); useEffect(() => { if (bookId) void load(buildSaleDiscussionQuery(1, 10, bookId, {})); else setLoading(false); }, [bookId, load]); const run = (p: number, s: number, f: SaleDiscussionFilters) => bookId && void load(buildSaleDiscussionQuery(p, s, bookId, f)); const search = () => { setApplied(filters); setPage(1); run(1, pageSize, filters); }; return { filters, setFilters, page, pageSize, rows, total, loading, error, search, reset: () => { setFilters({}); setApplied({}); setPage(1); run(1, pageSize, {}); }, refresh: () => run(page, pageSize, filters), retry: () => last.current && void load(last.current), changePage: (p: number) => { setPage(p); run(p, pageSize, applied); }, changePageSize: (s: number) => { setPage(1); setPageSize(s); run(1, s, applied); } }; }
import { useCallback, useEffect, useRef, useState } from "react";
import { getSaleList } from "./api";
import { buildSaleQuery, type SaleFilters, type SaleQuery } from "./query";
export function useSaleList() { const [filters, setFilters] = useState<SaleFilters>({}); const [applied, setApplied] = useState<SaleFilters>({}); const [page, setPage] = useState(1); const [pageSize, setPageSize] = useState(10); const [rows, setRows] = useState<any[]>([]); const [total, setTotal] = useState(0); const [loading, setLoading] = useState(true); const [error, setError] = useState<Error | null>(null); const last = useRef<SaleQuery>(buildSaleQuery(1, 10, {})); const load = useCallback(async (query: SaleQuery) => { last.current = query; setLoading(true); try { const result: any = await getSaleList(query); if (!result) throw new Error("请求失败"); setRows(result.list || []); setTotal(result.total || 0); setError(null); } catch (e) { setError(e instanceof Error ? e : new Error("请求失败")); } finally { setLoading(false); } }, []); useEffect(() => { void load(last.current); }, [load]); const run = (p: number, s: number, f: SaleFilters) => void load(buildSaleQuery(p, s, f)); const search = () => { setApplied(filters); setPage(1); run(1, pageSize, filters); }; return { filters, setFilters, page, pageSize, rows, total, loading, error, search, reset: () => { setFilters({}); setApplied({}); setPage(1); run(1, pageSize, {}); }, refresh: () => { setApplied(filters); run(page, pageSize, filters); }, retry: () => void load(last.current), changePage: (p: number) => { setPage(p); run(p, pageSize, applied); }, changePageSize: (s: number) => { setPage(1); setPageSize(s); run(1, s, applied); } }; }
import { DatePicker, Input, Button, Space } from "antd";
import type { ColumnsType } from "antd/es/table";
import { useLocation, useNavigate } from "react-router";
import { AppList, type AppListFilterItem } from "@/shared/components/AppList";
import type { SaleDiscussionFilters } from "../query";
import { useSaleDiscussionList } from "../useSaleDiscussionList";
const { RangePicker } = DatePicker;
export default function SaleDiscussionListView() { const location = useLocation(); const navigate = useNavigate(); const state = (location.state || {}) as any; const list = useSaleDiscussionList(state.id); const filters: AppListFilterItem<SaleDiscussionFilters>[] = [{ name: "title", label: "话题", render: (field) => <Input value={field.value || ""} onChange={(event) => field.onChange(event.target.value)} placeholder="请输入话题" /> }, { name: "user_name", label: "发起人", render: (field) => <Input value={field.value || ""} onChange={(event) => field.onChange(event.target.value)} placeholder="请输入发起人" /> }, { name: "time", label: "发起时间", render: (field) => <RangePicker value={field.value} onChange={(value) => field.onChange(value)} /> }]; const columns: ColumnsType<any> = [{ title: "话题", dataIndex: "title", width: 200 }, { title: "发起人", dataIndex: "name" }, { title: "发起时间", dataIndex: "create_time" }, { title: "操作", render: (_, row) => <Button onClick={() => navigate("../discuss-detail", { state: { id: row.id, name: state.name, book_id: state.id } })}>查看详情</Button> }]; if (!state.id) return <div>缺少书籍信息,请返回销售列表。</div>; return <AppList rowKey="id" columns={columns} data={{ rows: list.rows, total: list.total, loading: list.loading, error: list.error }} filters={{ value: list.filters, items: filters, onChange: list.setFilters, onSearch: list.search, onReset: list.reset, onRefresh: list.refresh }} pagination={{ page: list.page, pageSize: list.pageSize, pageSizeOptions: [10, 20, 30, 40], onChange: list.changePage, onPageSizeChange: list.changePageSize }} onRetry={list.retry} />; }
import { App, Button, DatePicker, Image, Input, Modal, Select, Space } from "antd";
import type { ColumnsType } from "antd/es/table";
import { useState } from "react";
import { useNavigate } from "react-router";
import { useSelector } from "react-redux";
import { AppList, type AppListFilterItem } from "@/shared/components/AppList";
import { changeSaleStatus } from "../api";
import type { SaleFilters } from "../query";
import { useSaleList } from "../useSaleList";
const { RangePicker } = DatePicker;
export default function SaleListView() { const list = useSaleList(); const navigate = useNavigate(); const { modal } = App.useApp(); const { operationPermissionsList = [] } = useSelector((state: any) => state.user); const filters: AppListFilterItem<SaleFilters>[] = [{ name: "name", label: "书籍名称", render: (field) => <Input allowClear value={field.value || ""} onChange={(event) => field.onChange(event.target.value)} placeholder="请输入书籍名称" /> }, { name: "authors", label: "作者", render: (field) => <Input allowClear value={field.value || ""} onChange={(event) => field.onChange(event.target.value)} placeholder="请输入作者" /> }, { name: "status", label: "上架状态", render: (field) => <Select allowClear value={field.value} onChange={field.onChange} placeholder="请选择上架状态" style={{ width: 160 }} options={[{ label: "上架", value: 1 }, { label: "下架", value: "0" }]} /> }, { name: "time", label: "创建时间", render: (field) => <RangePicker value={field.value} onChange={(value) => field.onChange(value)} /> }]; const columns: ColumnsType<any> = [{ title: "ID", dataIndex: "id", align: "center" }, { title: "书籍图片", dataIndex: "img", align: "center", render: (src) => <Image width={80} height={80} src={src} /> }, { title: "书籍名称", dataIndex: "name", align: "center" }, { title: "作者", dataIndex: "authors", align: "center" }, { title: "类型", dataIndex: "status", align: "center", render: (status) => status === 0 ? "下架" : status === 1 ? "上架" : "" }, { title: "零售价", dataIndex: "price", align: "center" }, { title: "会员价", dataIndex: "vip_price", align: "center" }, { title: "创建时间", dataIndex: "create_time", align: "center" }, { title: "上架时间", dataIndex: "onsale_time", align: "center", render: (value) => value || "--" }, { title: "下架时间", dataIndex: "offshelves_time", align: "center", render: (value) => value || "--" }, { title: "操作", render: (_, row) => <Space>{row.status && operationPermissionsList.includes("/books/sale/getCommentList") && <Button onClick={() => navigate("discussP", { state: { id: row.id, name: row.name } })}>讨论</Button>}{!row.status && operationPermissionsList.includes("/books/sale/getInfoById") && <Button onClick={() => navigate("detail", { state: { id: row.id } })}>编辑</Button>}{operationPermissionsList.includes("/books/sale/changeStatus") && <Button onClick={() => modal.confirm({ title: "提示", content: `书籍将${row.status ? "下架" : "上架"},是否继续?`, onOk: async () => { if (await changeSaleStatus(row.id, row.status ? "0" : "1")) list.refresh(); } })}>{row.status ? "下架" : "上架"}</Button>}</Space> }]; return <AppList rowKey="id" columns={columns} data={{ rows: list.rows, total: list.total, loading: list.loading, error: list.error }} filters={{ value: list.filters, items: filters, onChange: list.setFilters, onSearch: list.search, onReset: list.reset, onRefresh: list.refresh }} pagination={{ page: list.page, pageSize: list.pageSize, pageSizeOptions: [10, 20, 30, 40], onChange: list.changePage, onPageSizeChange: list.changePageSize }} onRetry={list.retry} />; }
Markdown 格式
0% 或
您添加了 0 人 到此讨论。请谨慎行事。
请先完成此评论的编辑!
请 注册 或者 后发表评论