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

refactor(admin): 移除无用文件

上级 3a0c40f0
...@@ -84,7 +84,6 @@ const EditorReader = (props) => { ...@@ -84,7 +84,6 @@ const EditorReader = (props) => {
const url = await uploadFile(file, { filenamePrefix: "banner" }); const url = await uploadFile(file, { filenamePrefix: "banner" });
insertFn(url, "题库图片"); insertFn(url, "题库图片");
// const { url } = await uploadFiles({ file, file_type: 'question' });
// insertFn(url, '题库图片'); // insertFn(url, '题库图片');
setLoading(false); setLoading(false);
...@@ -105,7 +104,6 @@ const EditorReader = (props) => { ...@@ -105,7 +104,6 @@ const EditorReader = (props) => {
setLoading(true); setLoading(true);
const url = await uploadFile(file, { filenamePrefix: "banner" }); const url = await uploadFile(file, { filenamePrefix: "banner" });
insertFn(url, "题库视频"); insertFn(url, "题库视频");
// const { url } = await uploadFiles({ file, file_type: 'question' });
// insertFn(url, '题库视频'); // insertFn(url, '题库视频');
setLoading(false); setLoading(false);
}, },
......
...@@ -109,7 +109,6 @@ const CustomerTopic = (props, ref) => { ...@@ -109,7 +109,6 @@ const CustomerTopic = (props, ref) => {
const url = await uploadFile(file, { filenamePrefix: "practice" }); const url = await uploadFile(file, { filenamePrefix: "practice" });
insertFn(url, "题库图片"); insertFn(url, "题库图片");
// const { url } = await uploadFiles({ file, file_type: 'question' });
// insertFn(url, '题库图片'); // insertFn(url, '题库图片');
}, },
}, },
......
import axios from "@/utils/request.js";
import qs from "qs";
import { message } from "antd";
import { get } from "lodash-es";
// 仅为尚未删除的旧 pages 代码保留的兼容接口;现行模块统一使用 utils/oss。
const getAliOSSSTSToken = async (obj) => {
try {
const res = await axios.post("/common/OssUpload/getUploadToken", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "获取OSS令牌失败!");
if (code === 200) {
return data;
}
message.error(msg);
return false;
} catch {
return false;
}
};
export { getAliOSSSTSToken };
...@@ -116,7 +116,6 @@ const HelpAddEdit = () => { ...@@ -116,7 +116,6 @@ const HelpAddEdit = () => {
const url = await uploadFile(file, { filenamePrefix: "help" }); const url = await uploadFile(file, { filenamePrefix: "help" });
insertFn(url, "题库图片"); insertFn(url, "题库图片");
// const { url } = await uploadFiles({ file, file_type: 'question' });
// insertFn(url, '题库图片'); // insertFn(url, '题库图片');
setLoading(false); setLoading(false);
......
import axios from "@/utils/request.js";
import qs from "qs";
import { get } from "lodash-es";
import { message } from "antd";
// 广告位列表
const getPositionList = async (obj) => {
try {
const res = await axios.post("/ad/position/getList", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 修改状态
const changeStatus = async (obj) => {
try {
const res = await axios.post("/ad/position/changeStatus", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
} catch (error) {
return error;
}
};
export { getPositionList, changeStatus };
import React, { useEffect, useState, useRef } from "react";
import {
Table,
Input,
Popover,
Tooltip,
Button,
Row,
Col,
Space,
DatePicker,
Drawer,
Image,
Form,
Modal,
Switch,
Select,
Upload,
Spin,
message,
} from "antd";
import { useNavigate } from "react-router";
import { useSelector } from "react-redux";
const { RangePicker } = DatePicker;
import PaginationCom from "@/common/Pagination";
import reload from "@/assets/images/icon/reload.png";
import reset from "@/assets/images/icon/reset.png";
import filter from "@/assets/images/icon/filter.png";
import add from "@/assets/images/icon/add.png";
import { addAd, getList, InfoAd, editAd, delAd, changeStatus } from "./request";
import { uploadFiles } from "@/utils/upload";
import { getPositionList } from "../adsense/request";
import dayjs from "dayjs";
import AliOSS from "ali-oss";
import { getAliOSSSTSToken } from "@/pages/setting/help/addedit/requet";
import TableCom from "@/common/TableCom/index";
const Advertising = () => {
const [data, setData] = useState([]);
const navigate = useNavigate();
const [page_size, setpage_size] = useState(10);
const [page, setPage] = useState(1);
const [total, setTotal] = useState(0);
const [loading, setLoading] = useState(true);
const [showModal, setshowModal] = useState(false);
const [isAdd, setIsAdd] = useState(false);
const [delMoadal, setDelModal] = useState(false);
const [form] = Form.useForm();
const flag = useRef(true);
// 获取操作权限
const { operationPermissionsList } = useSelector((state) => state.user);
// 广告位数据
const [positionData, setPositionData] = useState([]);
const [filterObj, setfilterObj] = useState({
name: "",
position_id: null,
start_time: "",
end_time: "",
});
const [dateVal, setDateVal] = useState("");
const [ossClient, setOssClient] = useState(null); // oss 操作
const [STSToken, setSTSToken] = useState(null); // oss 过期设置
const [file, setFile] = useState({});
let ossClientTemp;
const getStsAuthToken = () => {
return new Promise(async (resolve) => {
const data = await getAliOSSSTSToken();
if (data) {
window.sessionStorage.setItem("sts", JSON.stringify(data));
setSTSToken(data);
ossClientTemp = await new AliOSS({
accessKeyId: data.AccessKeyId,
accessKeySecret: data.AccessKeySecret,
stsToken: data.SecurityToken,
endpoint: data.endpoint,
bucket: "zxts-ad-file",
timeout: 180000,
refreshSTSToken: async () => {
const info = await getAliOSSSTSToken();
return {
AccessKeyId: info.AccessKeyId,
AccessKeySecret: info.AccessKeySecret,
SecurityToken: info.SecurityToken,
};
},
refreshSTSTokenInterval: 14 * 60 * 1000,
});
setOssClient(ossClientTemp);
}
resolve();
});
};
// 过滤
const handleFilter = (flag, val) => {
if (flag == "startandend") {
let val2 = val || [];
filterObj["start_time"] = parseInt(
new Date(dayjs(val2[0]).format("YYYY-MM-DD") + " 00:00:00").getTime() / 1000,
);
const endDate = new Date(dayjs(val2[1])).getDate();
const endTime = parseInt(
new Date(dayjs(val2[1]).format("YYYY-MM-") + endDate + " 23:59:59").getTime() / 1000,
);
filterObj["end_time"] = endTime;
setDateVal(val);
if (!val2.length) {
delete filterObj["start_time"];
delete filterObj["end_time"];
}
} else {
filterObj[flag] = val;
}
for (const key in filterObj) {
if (!filterObj[key]) {
delete filterObj[key];
}
}
setfilterObj({ ...filterObj });
if (!val) init(filterObj);
};
// 重置
const handleReset = () => {
setfilterObj({ name: "", position_id: "" });
setDateVal("");
init();
};
const handleStatus = async (val, id) => {
const bool = await changeStatus({ id, status: val ? "1" : "0" });
bool && init();
};
const init = async (obj = {}) => {
setLoading(true);
const { total, list } = await getList({ page, page_size, ...obj });
setTotal(total);
setData(list);
setLoading(false);
};
const [id, setId] = useState(0);
const handleEdit = async (obj) => {
const data = await InfoAd({ id: obj.id });
let { start_validity, end_validity, id } = data;
setIsAdd(false);
setshowModal(true);
form.setFieldsValue({ ...data, validity: [dayjs(start_validity), dayjs(end_validity)] });
setUploadimg(data.pic);
setId(id);
};
const onClose = () => {
setshowModal(false);
};
const submitForm = async (obj) => {
let bool;
let { validity } = obj;
let start_validity =
new Date(dayjs(validity[0]).format("YYYY-MM-DD") + " 00:00:00").getTime() / 1000;
let end_validity =
new Date(dayjs(validity[1]).format("YYYY-MM-DD") + " 23:59:59").getTime() / 1000;
let uploadObj = { ...obj };
delete uploadObj.validity;
if (isAdd) {
bool = await addAd({ ...uploadObj, start_validity, end_validity, pic: uploadimg });
} else {
bool = await editAd({ ...uploadObj, id, start_validity, end_validity, pic: uploadimg });
}
if (!bool) return;
init();
setshowModal(false);
};
const delSuccess = async () => {
setDelModal(false);
const bool = await delAd({ id });
bool && init();
};
const getPosition = async () => {
const { list } = await getPositionList();
setPositionData(list);
};
useEffect(() => {
// if(!flag.current){
getPosition();
getStsAuthToken();
// }else{
// flag.current=false
// }
}, []);
const columns = [
{
title: "ID",
key: "id",
dataIndex: "id",
},
{
title: "所属广告位",
key: "position_name",
align: "center",
dataIndex: "position_name",
},
{
title: "广告名称",
key: "name",
dataIndex: "name",
width: 200,
align: "center",
render: (text) => (
<Popover content={text} overlayStyle={{ maxWidth: "400px" }} visible={false}>
<div
style={{
display: "-webkit-box",
WebkitLineClamp: "3",
WebkitBoxOrient: "vertical",
overflow: "hidden",
textOverflow: "ellipsis",
textAlign: "center",
}}
>
{text}
</div>
</Popover>
),
},
{
title: "广告图片",
key: "pic",
dataIndex: "pic",
align: "center",
render: (_, { pic }) => (
<Image
width={80}
height={80}
src={pic}
fallback="data:image/png;base64,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"
/>
),
},
{
title: "广告链接地址",
key: "url",
dataIndex: "url",
align: "center",
width: 200,
render: (text) => (
<Popover
content={text}
overlayStyle={{
maxWidth: "400px",
whiteSpace: "pre-wrap",
wordBreak: "breal-all",
wordWrap: "break-word",
}}
>
<div
style={{
textAlign: "left",
maxWidth: "300px",
}}
>
<a href={text} target="_blank" rel="noreferrer">
{text.length > 80 ? `${text.substring(0, 80)}...` : text}
</a>
</div>
</Popover>
),
},
{
title: "状态",
key: "status",
align: "center",
dataIndex: "status",
render(_, { id, status }) {
return (
<Switch
defaultChecked={status == "1" ? true : false}
onChange={(ev) => handleStatus(ev, id)}
disabled={!operationPermissionsList.includes("/advertisement/advert/changeStatus")}
/>
);
},
},
{
title: "有效期开始时间",
key: "start_validity",
dataIndex: "start_validity",
align: "center",
},
{
title: "有效期结束时间",
key: "end_validity",
dataIndex: "end_validity",
align: "center",
},
{
title: "操作",
key: "handle",
dataIndex: "handle",
align: "center",
render: (_, record) => {
return (
<Space>
{operationPermissionsList.includes("/advertisement/advert/getInfoById") && (
<Button onClick={() => handleEdit(record)}>编辑</Button>
)}
{operationPermissionsList.includes("/advertisement/advert/del") && (
<Button
onClick={() => {
setId(record.id);
setDelModal(true);
}}
>
删除
</Button>
)}
</Space>
);
},
},
];
const [formload, setFormLoad] = useState(false);
const [uploadimg, setUploadimg] = useState("");
// 图片上传
const uploadImg = async (file) => {
setFormLoad(true);
const { url } = await uploadFiles({ file, file_type: "ad" });
setUploadimg(url);
setFormLoad(false);
};
const props = {
name: "file",
accept: ".jpg,.png",
showUploadList: false,
beforeUpload(file) {
const suffixArr = ["jpg", "png"];
const suffix = file.name.substring(file.name.lastIndexOf(".") + 1);
if (!suffixArr.includes(suffix)) {
message.error(`请上传以${suffixArr.join("、")}格式的书籍`);
return false;
}
setFile(file);
},
customRequest: async () => {
if (!ossClient) {
message.error("ossClient还未初始化", ossClient);
return false;
}
try {
const fileExt = file.name.substring(file.name.lastIndexOf("."));
const fileName = `ad-${dayjs().valueOf()}-${Math.random().toString(36).substring(2)}${fileExt}`;
const filePath = `${dayjs().format("YYYY/MM/DD")}/${fileName}`;
const result = await ossClient.put(filePath, file);
console.log("result", result);
// 假设 result 中包含了文件的 URL
const imageUrl = result.url;
// 更新图片 URL 状态
setUploadimg(imageUrl);
form.setFieldsValue({ pic: imageUrl });
// 上传成功,返回 true 表示可以继续上传
return true;
} catch (error) {
// 上传失败,处理错误
console.error("上传文件失败:", error);
message.error("上传文件失败,请重试");
return false;
}
},
};
const [initialValues, setinitialValues] = useState({
position_id: null,
name: "",
pic: "",
validity: "",
url: "",
});
useEffect(() => {
if (!showModal) {
form.resetFields();
form.setFieldsValue(initialValues);
setUploadimg("");
}
}, [showModal]);
return (
<div>
<Row justify={"space-between"}>
<Col className="form-devices-inline">
<Form layout="inline">
<Form.Item label="所属广告位">
<Select
style={{ width: 160 }}
value={filterObj.position_id}
allowClear
id="position_id"
placeholder="请选择所属广告位"
onChange={(ev) => handleFilter("position_id", ev)}
>
{positionData.length &&
positionData.map((item) => (
<Select.Option key={item.id} value={item.id}>
{item.position_name}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item label="广告名称">
<Input
autoComplete="off"
allowClear
value={filterObj.name}
onChange={(ev) => handleFilter("name", ev.target.value)}
placeholder="请输入广告名称"
id="name"
></Input>
</Form.Item>
<Form.Item label="有效期">
<RangePicker
value={dateVal}
onChange={(ev) => handleFilter("startandend", ev)}
id="createTime"
></RangePicker>
</Form.Item>
<Space>
<Button
type="primary"
ghost
icon={
<span
style={{
display: "inline-block",
width: "11px",
height: "10px",
pointerEvents: "none",
}}
>
<Image src={reset} />
</span>
}
onClick={handleReset}
>
重置
</Button>
<Button
type="primary"
ghost
icon={
<span
style={{
display: "inline-block",
width: "13px",
height: "12px",
pointerEvents: "none",
}}
>
<Image src={filter} />
</span>
}
onClick={() => {
setPage(1);
Object.values(filterObj).some((item) => item) ? init(filterObj) : init();
}}
>
筛选
</Button>
<Button
type="primary"
ghost
icon={
<span
style={{
display: "inline-block",
width: "10px",
height: "12px",
pointerEvents: "none",
}}
>
<Image src={reload} />
</span>
}
onClick={() =>
Object.values(filterObj).some((item) => item) ? init(filterObj) : init()
}
>
刷新
</Button>
</Space>
</Form>
</Col>
<Col>
<Space>
{operationPermissionsList.includes("/advertisement/advert/add") && (
<Button
type="primary"
ghost
onClick={() => {
setIsAdd(true);
setshowModal(true);
}}
icon={
<span
style={{
display: "inline-block",
width: "13px",
height: "12px",
pointerEvents: "none",
}}
>
<Image src={add} />
</span>
}
>
添加
</Button>
)}
</Space>
</Col>
</Row>
<br />
{TableCom({ columns, loading, data })}
<Modal
mask={false}
centered
open={delMoadal}
onOk={delSuccess}
onCancel={() => setDelModal(false)}
footer={(_, { OkBtn, CancelBtn }) => {
return (
<Row justify={"center"} className="delModal" style={{ marginBottom: "30px" }}>
<Space size={20}>
<CancelBtn />
<OkBtn />
</Space>
</Row>
);
}}
okText="确认"
>
<p style={{ textAlign: "center", padding: "30px 0 18px", fontSize: 16 }}>
广告将永久删除,是否继续?
</p>
</Modal>
<br />
<PaginationCom
total={total}
page_size={page_size}
setpage_size={setpage_size}
page={page}
setPage={setPage}
init={init}
filterObj={filterObj}
/>
<Drawer placement="right" onClose={onClose} open={showModal} mask={false}>
<Spin spinning={formload}>
<Form form={form} onFinish={submitForm} labelCol={{ span: 7 }}>
<Form.Item
label="所属广告位"
rules={[{ required: true, message: "请选择所属广告位" }]}
name="position_id"
>
<Select placeholder="请选择所属广告位">
{positionData.length &&
positionData.map((item) => (
<Select.Option key={item.id} value={item.id}>
{item.position_name}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label="广告名称"
rules={[
{ required: true, message: "请输入广告名称" },
{ max: 80, message: "最大80个字符" },
]}
name="name"
>
<Input autoComplete="off" placeholder="请输入广告名称"></Input>
</Form.Item>
<Form.Item
name="pic"
label="广告图片"
rules={[{ required: true, message: "请选择广告图片" }]}
>
<div>
<Upload {...props}>
<Button ghost type="primary">
点击上传
</Button>
</Upload>
{uploadimg && (
<div className="img" style={{ marginTop: 10 }}>
<Image src={uploadimg} width={80} height={80} />
</div>
)}
</div>
</Form.Item>
<Form.Item
name="validity"
label="广告有效期"
rules={[
{ required: true, message: "请选择广告有效期" },
{
validator(_, val) {
if (!val || val.length !== 2) {
return Promise.reject("");
}
const today = new Date();
const startDate = new Date(
today.getFullYear(),
today.getMonth(),
today.getDate(),
);
const endDate = new Date(
today.getFullYear(),
today.getMonth(),
today.getDate(),
23,
59,
);
if (endDate.getFullYear() > 2101) {
return Promise.reject("有效期结束时间最大限制2100-12-31");
} else if (startDate <= endDate) {
return Promise.resolve();
} else {
return Promise.reject("开始时间要小于结束时间");
}
},
},
]}
>
<RangePicker />
</Form.Item>
<Form.Item
name="url"
label="广告链接地址"
rules={[{ max: 500, message: "最大长度500" }]}
>
<Input autoComplete="off" placeholder="请输入广告链接地址" />
</Form.Item>
<Form.Item wrapperCol={{ offset: 11, span: 16 }}>
<Space size={20}>
<Button onClick={() => setshowModal(false)}>取消</Button>
<Button type="primary" htmlType="submit">
提交
</Button>
</Space>
</Form.Item>
</Form>
</Spin>
</Drawer>
</div>
);
};
export default Advertising;
import axios from "@/utils/request.js";
import qs from "qs";
import { get } from "lodash-es";
import { message } from "antd";
// 广告列表
const getList = async (obj) => {
try {
const res = await axios.post("/ad/ad/getList", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 添加
const addAd = async (obj) => {
try {
const res = await axios.post("/ad/ad/add", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 详情
const InfoAd = async (obj) => {
try {
const res = await axios.post("/ad/ad/getInfoById", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 修改
const editAd = async (obj) => {
try {
const res = await axios.post("/ad/ad/edit", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 删除
const delAd = async (obj) => {
try {
const res = await axios.post("/ad/ad/del", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 启用停用
const changeStatus = async (obj) => {
try {
const res = await axios.post("/ad/ad/changeStatus", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
export { getList, addAd, InfoAd, editAd, delAd, changeStatus };
export { default } from '@/modules/advertisements/Layout'
import React, { useState, useEffect } from "react";
import { Card, Input, Space, Form, Select, Button, Steps, Modal, Spin, message, Row } from "antd";
const { TextArea } = Input;
import { getExamineList, addExamine, getUserInfo, getPowerByRoleId } from "./request";
import { useLocation, useNavigate } from "react-router";
import user from "@/assets/images/user.png";
import userStatus1 from "@/assets/images/user_status1.png";
import userStatus2 from "@/assets/images/user_status2.png";
import userStatus3 from "@/assets/images/user_status3.png";
import defaultUser from "@/assets/images/defaultUser.png";
import { useSelector } from "react-redux";
const AuditDetail = (props) => {
const location = useLocation();
const {
state: { id },
} = location;
const navigate = useNavigate();
const [current, setCurrent] = useState(0);
const [examineList, setExamineList] = useState([]);
const [quanXian, setQuanXian] = useState(true);
// 获取操作权限
const { operationPermissionsList } = useSelector((state) => state.user);
const ExamineList = async () => {
// const data1 = await getUserInfo();
// const data2 = await getPowerByRoleId({ role_id: data1.id });
// if (!data2.includes(154)) {
// setQuanXian(true);
// }
const data = await getExamineList({ id });
const totalObjects = data.length; // 计算对象个数
let arr = data.map((item, index) => {
const isLastObject = index === totalObjects - 1; // 判断是否为最后一个对象
return {
description: <StepCont info={item} />,
icon: <ImageEl info={item} isLast={isLastObject} />, // 传递 isLast 属性
};
});
setExamineList(arr);
};
useEffect(() => {
ExamineList();
}, []);
const StepCont = (itemStep) => {
const { info } = itemStep;
let statusColor = "";
if (info.audit_status === 1) {
statusColor = "green";
} else if (info.audit_status === 2) {
statusColor = "red";
}
const statusStyle = {
color: statusColor,
};
return (
<div className="">
{quanXian ? (
<div>
{info.audit_status === 0 ? (
<div className="">
<strong>{info.user_name ? info.user_name : ""}(提交人)</strong>
<div className="date">{info.create_time}</div>
<div className="status" style={statusStyle}>
{info.content}
</div>
<div className="infor">提交信息: {info.release_mess ? info.release_mess : ""}</div>
</div>
) : (
<div className="audit">
<strong>{info.examine_name ? info.examine_name : ""}(审核人)</strong>
<div className="date">{info.create_time}</div>
<div className="status" style={statusStyle}>
{info.audit_status_name}
</div>
<div className="infor">审核信息: {info.content ? info.content : ""}</div>
</div>
)}
{/* <div style={{ marginTop: 40 }}>
<strong>{info.examine_name ? info.examine_name : '空'}(审核人)</strong>
<div className='infor'>
审核意见: {info.audit_status_name ? info.audit_status_name : '空'}
</div>
</div> */}
</div>
) : null}
</div>
);
};
const ImageEl = (itemStep) => {
const { info, isLast } = itemStep;
let imageSrc;
let connectorColor;
if (info.audit_status === 1) {
imageSrc = userStatus2;
connectorColor = "green";
} else if (info.audit_status === 2) {
imageSrc = userStatus3;
connectorColor = "red";
} else {
imageSrc = userStatus1;
connectorColor = "gray";
}
const containerStyle = {
position: "relative", // 相对定位
height: "100px",
};
const connectorStyle = {
backgroundColor: connectorColor,
border: `1px solid ${connectorColor}`,
display: "flex",
justifyContent: "center",
content: "''",
position: "absolute",
top: "75%",
left: "50%",
transform: "translateY(-50%)",
height: "50px",
width: "1px",
zIndex: "2",
};
return (
<div className="">
{quanXian ? (
<div className="imgel" style={containerStyle}>
{info.audit_status === 0 ? (
<div
className="user"
style={{
backgroundImage: info.user_pic ? `url(${info.user_pic}) ` : `url(${defaultUser})`,
backgroundRepeat: "no-repeat",
backgroundSize: "50px 50px",
borderRadius: "50%",
marginBottom: 50,
}}
>
<img src={imageSrc} className="status"></img>
</div>
) : (
<div
className="user"
style={{
backgroundImage: info.examine_pic
? `url(${info.examine_pic}) `
: `url(${defaultUser})`,
backgroundRepeat: "no-repeat",
backgroundSize: "50px 50px",
borderRadius: "50%",
marginBottom: 50,
}}
>
<img src={imageSrc} className="status"></img>
</div>
)}
{isLast ? null : <div className="connector" style={connectorStyle}></div>}
{/* <div
className='user'
style={{
backgroundImage: info.examine_pic ? `url(${info.examine_pic}) ` : `url(${defaultUser})`,
backgroundRepeat: 'no-repeat',
backgroundSize: '50px 50px',
borderRadius: '50%',
}}
>
<img src={imageSrc} className='status'></img>
</div> */}
{isLast ? null : <div className="connector" style={connectorStyle}></div>}
</div>
) : null}
</div>
);
};
const onChange = (value) => {
console.log("onChange:", value);
setCurrent(value);
};
const [form] = Form.useForm();
// 获取历史审核
// const ExamineList = async () => {
// const {
// state: { id },
// } = location;
// const data = await getExamineList({ id });
// let arr = data.map((item) => ({
// description: <StepCont info={item} />,
// icon: <ImageEl info={item} />,
// }));
// setExamineList(arr);
// };
const [loading, setLoading] = useState(false);
const [showModal, setShowModal] = useState(false);
const [modalMsg, setModalMsg] = useState("");
const ExamineSure = async (obj) => {
setLoading(true);
const {
data: { check },
msg,
} = await addExamine({ id, ...obj });
setModalMsg(msg);
let bool = true;
if (check === 1) {
setShowModal(true);
bool = false;
} else {
message.success(msg);
}
setLoading(false);
if (!bool) return;
navigate(-1);
};
return (
<div style={{ padding: 20 }} className="auditdetail">
<Modal
mask={false}
centered
open={showModal}
onOk={() => {
ExamineSure({ ...form.getFieldsValue(), check: 1 });
setShowModal(false);
}}
onCancel={() => setShowModal(false)}
footer={(_, { OkBtn, CancelBtn }) => {
return (
<Row justify={"center"} className="delModal" style={{ marginBottom: "30px" }}>
<Space size={20}>
<CancelBtn className="cancel" />
<OkBtn className="true" />
</Space>
</Row>
);
}}
okText="确认"
>
<p style={{ textAlign: "center", padding: "30px 0 18px", fontSize: 16 }}>{modalMsg}</p>
</Modal>
<Spin spinning={loading}>
<Form onFinish={ExamineSure} form={form} initialValues={{ audit_status: 1 }}>
<Form.Item
label="审核状态"
name="audit_status"
rules={[{ required: true, message: "请选择审核状态" }]}
>
<Select
placeholder="请选择审核状态"
style={{ width: 260 }}
options={[
{ label: "通过", value: 1 },
{ label: "不通过", value: 2 },
]}
></Select>
</Form.Item>
<Form.Item label="审核意见" name="content">
<TextArea
placeholder="请输入审核意见"
style={{ width: 700 }}
autoSize={{
minRows: 6,
maxRows: 8,
}}
></TextArea>
</Form.Item>
<Form.Item wrapperCol={{ offset: 4 }}>
<Space size={20} className="footer">
<Button
className="reset"
onClick={() => {
form.setFieldsValue({ audit_status: null, content: "" });
form.resetFields();
}}
>
重置
</Button>
<Button className="as" htmlType="submit" type="primary">
提交
</Button>
</Space>
</Form.Item>
</Form>
{operationPermissionsList &&
operationPermissionsList.includes("/books/audit/getExamineList") && (
<Card
className="auditPanel"
style={{ marginTop: 100, height: "350px", overflow: "auto" }}
>
<Steps
current={current}
onChange={onChange}
direction="vertical"
items={examineList}
></Steps>
</Card>
)}
</Spin>
</div>
);
};
export default AuditDetail;
import React from "react";
import { Outlet } from "react-router";
import "./index.less";
const Audit = () => {
return (
<div>
<Outlet />
</div>
);
};
export default Audit;
@import "@/modules/books/audit/audit.less";
export { default } from "@/modules/books/audit/components/PreviewModal";
import axios from "@/utils/request.js";
import qs from "qs";
import { get } from "lodash-es";
import { message } from "antd";
// 用户信息
const getUserInfo = async (obj) => {
try {
const res = await axios.post("/user/user/getUserInfo", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 获取角色已选权限
const getPowerByRoleId = async (obj) => {
try {
const res = await axios.post("/user/role/getPowerByRoleId", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 审核列表
const getList = async (obj) => {
try {
const res = await axios.post("/book/teacher/getList/examine", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 历史
const getExamineList = async (obj) => {
try {
const res = await axios.post("/book/teacher/getExamineList", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 审核书籍
const addExamine = async (obj) => {
try {
const res = await axios.post("/book/examine/add", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return { data, msg };
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 详情
const infoGrade = async (obj) => {
try {
const res = await axios.post("/members/grade/getInfoById", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 添加
const addGrade = async (obj) => {
try {
const res = await axios.post("/members/grade/add", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 修改
const editGrade = async (obj) => {
try {
const res = await axios.post("/members/grade/edit", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 删除
const delGrade = async (obj) => {
try {
const res = await axios.post("/members/grade/del", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
export {
getList,
getExamineList,
addExamine,
infoGrade,
addGrade,
editGrade,
delGrade,
getUserInfo,
getPowerByRoleId,
};
import React, { useEffect, useState, useRef } from "react";
import TableCom from "@/common/TableCom/index";
import {
Table,
Input,
Button,
Row,
Col,
Space,
DatePicker,
Image,
Drawer,
Form,
Tree,
Modal,
} from "antd";
const { RangePicker } = DatePicker;
import { useNavigate } from "react-router";
import PaginationCom from "@/common/Pagination";
import { getList, getExamineList, addExamine } from "./request";
import { getAllList, getInfoByChapterId } from "../section/request";
import reload from "@/assets/images/icon/reload.png";
import reset from "@/assets/images/icon/reset.png";
import filter from "@/assets/images/icon/filter.png";
import add from "@/assets/images/icon/add.png";
import { CloseOutlined } from "@ant-design/icons";
import dayjs from "dayjs";
import { useSelector } from "react-redux";
import PreviewModal from "./preview";
const Audit = () => {
const navigate = useNavigate();
const [data, setData] = useState([]);
const [page_size, setpage_size] = useState(10);
const [page, setPage] = useState(1);
const [total, setTotal] = useState(0);
const [ids, setIds] = useState([]);
const [chapterId, setChapterId] = useState([]);
const [priviewVisible, setPriviewVisible] = useState(false);
const previewRef = useRef();
const [selectedId, setSelectedId] = useState(null);
const [loading, setLoading] = useState(true);
const [isEdit, setIsEdit] = useState(false);
const [form] = Form.useForm();
// 获取操作权限
const { operationPermissionsList } = useSelector((state) => state.user);
const [filterObj, setfilterObj] = useState({
name: "",
authors: "",
start_time: "",
end_time: "",
});
const [dateVal, setDateVal] = useState("");
const [showPhone, setShowPhone] = useState(false);
const [initialValues, setinitialValues] = useState(
JSON.parse(sessionStorage.getItem("editData")) || {},
);
const [editor, setEditor] = useState(null);
// 初始化 html 状态值
const [html, setHtml] = useState(initialValues.help_content || "");
const [shouldPreview, setShouldPreview] = useState(false);
const handleRowClick = (record) => {
setSelectedId(record.id);
setShouldPreview(true);
};
useEffect(() => {
if (shouldPreview) {
setShowPhone(true);
setShouldPreview(false);
prev({ book_id: selectedId });
}
}, [selectedId, shouldPreview, data]);
const Com = () => {
const [h, setH] = useState(0);
useEffect(() => {
let height = document.querySelector(".ant-table-tbody").clientHeight;
if (height > 600) {
setH(600);
}
}, [data]);
return (
<Table
scroll={{
y: h,
x: true,
}}
columns={columns}
// rowKey={(record) => record.id}
dataSource={data}
loading={loading}
pagination={false}
></Table>
);
};
// 过滤
const handleFilter = (flag, val) => {
if (flag == "startandend") {
let val2 = val || [];
filterObj["start_time"] = parseInt(
new Date(dayjs(val2[0]).format("YYYY-MM-DD") + " 00:00:00").getTime() / 1000,
);
const endDate = new Date(dayjs(val2[1])).getDate();
const endTime = parseInt(
new Date(dayjs(val2[1]).format("YYYY-MM-") + endDate + " 23:59:59").getTime() / 1000,
);
filterObj["end_time"] = endTime;
setDateVal(val);
if (!val2.length) {
delete filterObj["start_time"];
delete filterObj["end_time"];
}
} else {
filterObj[flag] = val;
}
for (const key in filterObj) {
if (!filterObj[key]) {
delete filterObj[key];
}
}
setfilterObj({ ...filterObj });
if (!val) init(filterObj);
};
// 重置
const handleReset = () => {
setfilterObj({ real_name: "", tel: "" });
setDateVal("");
init();
};
const init = async (obj = {}) => {
setLoading(true);
const { total, list } = await getList({ page, page_size, ...obj, audit_status: 2 });
setTotal(total);
setData(list);
setIds(list.map((item) => item.id));
console.log("setID", ids);
setLoading(false);
};
const prev = async (obj = {}) => {
setLoading(true);
const res = await getAllList({ ...obj, book_id: selectedId });
setChapterId(res.data.map((item) => item.id));
setHtml(res.img);
// if (res.length > 0) {
// const rowData = res.find((item) => item.id === id); // 找到对应id的行数据
// if (rowData) {
// // setHtml(rowData.img);
// }
// }
setLoading(false);
};
const submitForm = (obj) => {
console.log(obj);
};
const columns = [
{
title: "ID",
key: "id",
align: "center",
dataIndex: "id",
},
{
title: "书籍图片",
key: "img",
align: "center",
dataIndex: "img",
render: (_, { img }) => (
<Image
width={80}
height={80}
src={img}
fallback="data:image/png;base64,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"
/>
),
},
{
title: "书籍名称",
key: "name",
align: "center",
dataIndex: "name",
},
{
title: "作者",
key: "authors",
align: "center",
dataIndex: "authors",
},
{
title: "创建时间",
key: "create_time",
align: "center",
dataIndex: "create_time",
},
{
title: "更新时间",
key: "update_time",
align: "center",
dataIndex: "update_time",
},
{
title: "操作",
key: "handle",
dataIndex: "handle",
render: (_, { id }) => {
return (
<Space>
{operationPermissionsList.includes("/books/audit/getInfoByChapterId") && (
<Button
onClick={() => {
// setShowPhone(true);
// setShouldPreview(true);
setPriviewVisible(true);
}}
>
预览
</Button>
)}
{operationPermissionsList.includes("/books/audit/add") && (
<Button
onClick={() => {
navigate("/books/audit/detail", {
state: { id },
replace: true, // hyy
});
}}
>
审核
</Button>
)}
</Space>
);
},
},
];
return (
<div>
<Row justify={"space-between"}>
<Col span={21} className="form-devices-inline">
<Form layout="inline">
<Form.Item label="书籍名称">
<Input
autoComplete="off"
allowClear
value={filterObj.name}
onChange={(ev) => handleFilter("name", ev.target.value)}
placeholder="请输入书籍名称"
id="name"
></Input>
</Form.Item>
<Form.Item label="作者">
<Input
autoComplete="off"
allowClear
value={filterObj.authors}
onChange={(ev) => handleFilter("authors", ev.target.value)}
placeholder="请输入作者"
id="author"
></Input>
</Form.Item>
<Form.Item label="创建时间">
<RangePicker
value={dateVal}
onChange={(ev) => handleFilter("startandend", ev)}
id="createTime"
></RangePicker>
</Form.Item>
<Space>
<Button
type="primary"
ghost
icon={
<span
style={{
display: "inline-block",
width: "11px",
height: "10px",
pointerEvents: "none",
}}
>
<Image src={reset} />
</span>
}
onClick={handleReset}
>
重置
</Button>
<Button
type="primary"
icon={
<span
style={{
display: "inline-block",
width: "13px",
height: "12px",
pointerEvents: "none",
}}
>
<Image src={filter} />
</span>
}
ghost
onClick={() => {
setPage(1);
Object.values(filterObj).some((item) => item) ? init(filterObj) : init();
}}
>
筛选
</Button>
<Button
icon={
<span
style={{
display: "inline-block",
width: "10px",
height: "12px",
pointerEvents: "none",
}}
>
<Image src={reload} />
</span>
}
onClick={() =>
Object.values(filterObj).some((item) => item) ? init(filterObj) : init()
}
type="primary"
ghost
>
刷新
</Button>
</Space>
</Form>
</Col>
<Col>
<Space></Space>
</Col>
</Row>
<br />
{/* {TableCom({ columns, loading, data })} */}
<Table
dataSource={data}
columns={columns}
loading={loading}
pagination={false}
rowKey="id"
onRow={(record) => ({
onClick: () => handleRowClick(record),
})}
/>
<br />
<PaginationCom
total={total}
page_size={page_size}
setpage_size={setpage_size}
page={page}
setPage={setPage}
filterObj={filterObj}
init={init}
/>
<Modal
open={priviewVisible}
header={null}
footer={null}
destroyOnHidden
keyboard={false}
closeIcon={<CloseOutlined style={{ fontSize: 20, color: "#fff" }} />}
maskClosable={false}
classNames={{ body: "phone-body", wrapper: "phone-wrapper" }}
wrapClassName="wrap-phone-privew"
width="494px"
centered
onCancel={() => setPriviewVisible(false)}
>
<PreviewModal ref={previewRef} bookId={selectedId} />
</Modal>
</div>
);
};
export default Audit;
import React, { useEffect, useState } from "react";
import {
Table,
Input,
Button,
Row,
Col,
Space,
DatePicker,
Drawer,
Form,
Modal,
Image,
} from "antd";
const { RangePicker } = DatePicker;
import PaginationCom from "@/common/Pagination";
import "./index.less";
import {
getList,
categoryAdd,
getInfoById,
categoryEdit,
categoryDel,
changeSort,
} from "./request";
import reset from "@/assets/images/icon/reset.png";
import filter from "@/assets/images/icon/filter.png";
import add from "@/assets/images/icon/add.png";
import imports from "@/assets/images/icon/import.png";
import reload from "@/assets/images/icon/reload.png";
import TableCom from "@/common/TableCom/index";
import dayjs from "dayjs";
import { useSelector } from "react-redux";
const Classify = () => {
const [data, setData] = useState([]);
const [page_size, setpage_size] = useState(10);
const [page, setPage] = useState(1);
const [total, setTotal] = useState(0);
const [loading, setLoading] = useState(true);
const [isEdit, setIsEdit] = useState(false);
const [showModal, setShowModal] = useState(false);
const [delMoadal, setDelModal] = useState(false);
const [isadd, setisadd] = useState(true);
const [form] = Form.useForm();
const [id, setId] = useState(0);
// 获取操作权限
const { operationPermissionsList } = useSelector((state) => state.user);
const [filterObj, setfilterObj] = useState({
name: "",
authors: "",
status: null,
start_time: "",
end_time: "",
});
const [dateVal, setDateVal] = useState("");
// 重置
const handleReset = () => {
setfilterObj({ name: "", authors: "", status: null });
setDateVal("");
init();
};
const init = async (obj = {}) => {
setLoading(true);
const { total, list } = await getList({ page, page_size, ...obj });
setTotal(total);
setData(list);
setLoading(false);
};
// 过滤
const handleFilter = (flag, val) => {
if (flag === "startandend") {
let val2 = val || [];
filterObj["start_time"] = parseInt(
new Date(dayjs(val2[0]).format("YYYY-MM-DD") + " 00:00:00").getTime() / 1000,
);
const endDate = new Date(dayjs(val2[1])).getDate();
const endTime = parseInt(
new Date(dayjs(val2[1]).format("YYYY-MM-") + endDate + " 23:59:59").getTime() / 1000,
);
filterObj["end_time"] = endTime;
setDateVal(val);
if (!val2.length) {
delete filterObj["start_time"];
delete filterObj["end_time"];
}
} else {
filterObj[flag] = val;
}
for (const key in filterObj) {
if (!filterObj[key]) {
delete filterObj[key];
}
}
setfilterObj({ ...filterObj });
if (!val) init(filterObj);
};
const handleEdit = async (id) => {
setisadd(false);
setShowModal(true);
const data = await getInfoById({ id });
form.setFieldsValue(data);
setId(id);
};
const onClose = () => {
setShowModal(false);
};
const submitForm = async (obj) => {
let bool;
if (isadd) {
bool = await categoryAdd(obj);
} else {
bool = await categoryEdit({
...obj,
id,
});
}
if (!bool) return;
init();
setShowModal(false);
};
const delSuccess = async () => {
const bool = await categoryDel({ id });
if (!bool) return;
init();
setDelModal(false);
};
useEffect(() => {
if (!showModal) {
form.setFieldsValue({ name: "", sort: "" });
}
}, [showModal]);
const columns = [
{
title: "ID",
key: "id",
dataIndex: "id",
align: "center",
},
{
title: "分类名称",
key: "name",
dataIndex: "name",
align: "center",
},
{
title: "创建时间",
key: "create_time",
dataIndex: "create_time",
align: "center",
},
{
title: "更新时间",
key: "update_time",
dataIndex: "update_time",
align: "center",
},
{
title: "排序",
key: "sort",
dataIndex: "sort",
align: "center",
},
{
title: "操作",
key: "handle",
dataIndex: "handle",
render: (_, { id }) => {
return (
<Space>
{operationPermissionsList.includes("/books/classify/getInfoById") && (
<Button
style={{
letterSpacing: "-2px", // 设置负值来减小字母之间的间距
}}
onClick={() => handleEdit(id)}
>
编辑
</Button>
)}
{operationPermissionsList.includes("/books/classify/del") && (
<Button
style={{
letterSpacing: "-2px", // 设置负值来减小字母之间的间距
}}
onClick={() => {
setId(id);
setDelModal(true);
}}
>
删除
</Button>
)}
</Space>
);
},
},
];
return (
<div className="classify">
<Row justify={"space-between"}>
<Col span={21} className="form-devices-inline">
<Form layout="inline">
<Form.Item label="分类名称">
<Input
autoComplete="off"
allowClear
placeholder="请输入分类名称"
id="name"
value={filterObj.name}
onChange={(ev) => handleFilter("name", ev.target.value)}
></Input>
</Form.Item>
<Form.Item label="创建时间">
<RangePicker
value={dateVal}
onChange={(ev) => handleFilter("startandend", ev)}
id="createTime"
></RangePicker>
</Form.Item>
<Space>
<Button
type="primary"
ghost
icon={
<span
style={{
display: "inline-block",
width: "11px",
height: "10px",
pointerEvents: "none",
}}
>
<Image src={reset} />
</span>
}
onClick={handleReset}
>
重置
</Button>
<Button
type="primary"
icon={
<span
style={{
display: "inline-block",
width: "13px",
height: "12px",
pointerEvents: "none",
}}
>
<Image src={filter} />
</span>
}
onClick={() => {
setPage(1);
Object.values(filterObj).some((item) => item) ? init(filterObj) : init();
}}
ghost
>
筛选
</Button>
<Button
type="primary"
onClick={() =>
Object.values(filterObj).some((item) => item) ? init(filterObj) : init()
}
icon={
<span
style={{
display: "inline-block",
width: "10px",
height: "12px",
pointerEvents: "none",
}}
>
<Image src={reload} />
</span>
}
ghost
>
刷新
</Button>
</Space>
</Form>
</Col>
<Col>
<Space>
{operationPermissionsList.includes("/books/classify/add") && (
<Button
type="primary"
ghost
onClick={() => {
setIsEdit(false);
setShowModal(true);
}}
icon={
<span
style={{
display: "inline-block",
width: "13px",
height: "12px",
pointerEvents: "none",
}}
>
<Image src={add} />
</span>
}
>
添加
</Button>
)}
</Space>
</Col>
</Row>
<br />
{TableCom({ columns, loading, data })}
<Modal
mask={false}
centered
open={delMoadal}
onOk={delSuccess}
onCancel={() => setDelModal(false)}
footer={(_, { OkBtn, CancelBtn }) => {
return (
<Row justify={"center"} className="delModal" style={{ marginBottom: "30px" }}>
<Space size={20}>
<CancelBtn />
<OkBtn />
</Space>
</Row>
);
}}
okText="确认"
style={{
position: "absolute",
top: "33%",
left: "36%",
}}
>
<p style={{ textAlign: "center", padding: "30px 0 18px", fontSize: 16 }}>
分类将永久删除,是否继续?
</p>
</Modal>
<br />
<PaginationCom
total={total}
page_size={page_size}
setpage_size={setpage_size}
page={page}
setPage={setPage}
init={init}
filterObj={filterObj}
/>
<Drawer placement="right" onClose={onClose} open={showModal} mask={false}>
<Form form={form} onFinish={submitForm} labelCol={{ span: 6 }}>
<Form.Item
name="name"
label="分类名称"
rules={[{ required: true, message: "请输入分类名称" }]}
>
<Input autoComplete="off" placeholder="请输入分类名称"></Input>
</Form.Item>
<Form.Item name="sort" label="排序" rules={[{ required: true, message: "请输入排序" }]}>
<Input autoComplete="off" placeholder="请输入排序"></Input>
</Form.Item>
<Form.Item wrapperCol={{ offset: 6, span: 16 }} style={{ textAlign: "center" }}>
<Space size={20}>
{operationPermissionsList.includes("/books/classify/edit") && (
<Button onClick={() => setShowModal(false)}>取消</Button>
)}
{operationPermissionsList.includes("/books/classify/edit") && (
<Button type="primary" htmlType="submit">
提交
</Button>
)}
</Space>
</Form.Item>
</Form>
</Drawer>
</div>
);
};
export default Classify;
.submit {
background: #aa1941;
border-radius: 4px;
color: #ffffff;
font-size: 14px;
&:hover {
opacity: 0.5;
color: #ffffff !important;
// background:#aa194283 !important;
}
}
// .cancel{
// background: #FFFFFF;
// border-radius: 4px;
// border: 1px solid #AA1941;
// font-size: 14px;
// color: #AA1941;
// &:hover{
// background:#ffffff7c !important;
// border-color: #aa194283 !important;
// color: #aa194283 !important;
// }
// }
import axios from "@/utils/request.js";
import qs from "qs";
import { get } from "lodash-es";
import { message } from "antd";
// 分类列表
const getList = async (obj) => {
const res = await axios.post("/book/category/getList", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
};
// add
const categoryAdd = async (obj) => {
const res = await axios.post("/book/category/add", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
};
// info
const getInfoById = async (obj) => {
const res = await axios.post("/book/category/getInfoById", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
};
// edit
const categoryEdit = async (obj) => {
const res = await axios.post("/book/category/edit", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
};
// del
const categoryDel = async (obj) => {
const res = await axios.post("/book/category/del", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
};
// 修改排序
const changeSort = async (obj) => {
const res = await axios.post("/book/category/changeSort", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
};
export { getList, categoryAdd, getInfoById, categoryEdit, categoryDel, changeSort };
.salesTop {
background-color: #fff;
}
.salesTop .ant-segmented-item {
background-color: #fff;
}
.salesTop .ant-segmented-item.ant-segmented-item-selected {
background-color: #f8edf0;
}
.salesTop .ant-segmented-item.ant-segmented-item-selected .ant-segmented-item-label {
color: #ab1941;
font-weight: bold;
}
.salesTop .sales {
width: 1400px;
margin: auto;
}
.salesTop .sales .items {
display: flex;
justify-content: space-between;
align-items: center;
}
.salesTop .sales .items .item {
flex: 0 0 300px;
border-radius: 4px;
border: 1px solid #e4e4e4;
display: flex;
padding: 30px;
}
.salesTop .sales .items .item .img {
width: 40px;
margin-right: 40px;
}
.salesTop .sales .items .item .img img {
width: 100%;
}
.salesTop .sales .items .item .cont .title {
font-size: 12px;
font-weight: 400;
color: #999999;
}
.salesTop .sales .items .item .cont .total {
font-size: 22px;
font-weight: 500;
color: #333333;
line-height: 30px;
}
.salesTop .sales .items .item .unit {
margin-left: 5px;
}
.salesTop .sales .echarts {
display: flex;
margin-top: 90px;
}
.salesTop .sales .echarts .echarts-item {
width: 800px;
height: 400px;
}
import md5 from "js-md5";
import CktDesign from "@chuangkit/chuangkit-design";
import { useSelector } from "react-redux";
import { useEffect } from "react";
export default function Design() {
/**
* 构建签名
* @param obj 参数对象,对象中的所有属性全部参与签名的生成
* @returns {string} 签名
*/
const buildSign = (obj) => {
let signParameterArray = [];
for (let key in obj) {
signParameterArray.push(`${key}=${obj[key]}`);
}
let signPlaintext = signParameterArray.sort().join("&");
return md5(signPlaintext).toUpperCase();
};
/**
* 构建2.0版本签名
* @param appId 第三方企业id
* @param expireTime 时间戳,取当前时间即可
* @param userFlag 用户标记
* @param appSecret 企业密钥
* @returns {string} 签名
*/
const buildVersion2Sign = (appId, expireTime, userFlag, appSecret) => {
let signParameterObj = {
app_id: appId,
expire_time: expireTime,
user_flag: userFlag,
app_secret: appSecret,
};
return buildSign(signParameterObj);
};
const { userInfo } = useSelector((state) => state.user);
let cktInstance;
const openDesignPage = () => {
const appId = "54d9adec77d0402794018d166110f3dd";
const appSecret = "08097010E0EF4B85EE2B8CE438328249";
const userFlag = userInfo.id;
const expireTime = Date.now();
const sign = buildVersion2Sign(appId, expireTime, userFlag, appSecret);
let params = {
app_id: appId,
expire_time: expireTime,
user_flag: userFlag,
device_type: 1,
kind_id: 438,
version: "2.0",
sign: sign,
enable_authorize: "1",
taxpayer_name: "chuangkit",
taxpayer_phone: "13820659475",
taxpayer_number: "91120116636067462H",
render: "ckt-design-page",
};
cktInstance = new CktDesign(params);
cktInstance.open();
console.log(cktInstance);
};
const closeDesignPage = () => {
cktInstance && cktInstance.close();
};
useEffect(() => {
openDesignPage();
return () => {
closeDesignPage();
};
}, []);
window.chuangkitComplete = (data) => {
console.log(data);
};
return <div id="ckt-design-page" style={{ width: "100%", height: "100%" }}></div>;
}
import React, { useEffect, useState, useRef } from "react";
import {
Table,
Input,
Button,
Row,
Col,
Space,
DatePicker,
Pagination,
Drawer,
Form,
Modal,
Switch,
Image,
Select,
Radio,
} from "antd";
const { RangePicker } = DatePicker;
import { useNavigate } from "react-router";
import PaginationCom from "@/common/Pagination/index.jsx";
import { getList, changeStatus, delComment, getSystem, systemEdit, getBooklist } from "./requset";
import reload from "@/assets/images/icon/reload.png";
import reset from "@/assets/images/icon/reset.png";
import filter from "@/assets/images/icon/filter.png";
import add from "@/assets/images/icon/add.png";
import TableCom from "@/common/TableCom/index";
import { getList as getBooks } from "../management/request";
import { useSelector } from "react-redux";
import dayjs from "dayjs";
const Discuss = () => {
const navigate = useNavigate();
const [data, setData] = useState([]);
const [page_size, setpage_size] = useState(10);
const [page, setPage] = useState(1);
const [total, setTotal] = useState(0);
const [loading, setLoading] = useState(true);
const [isEdit, setIsEdit] = useState(false);
const [delMoadal, setDelModal] = useState(false);
const [form] = Form.useForm();
const [form1] = Form.useForm();
const [books, setBooks] = useState([]);
// 获取操作权限
const { operationPermissionsList } = useSelector((state) => state.user);
const [filterObj, setfilterObj] = useState({
book_id: null,
title: "",
start_time: "",
end_time: "",
});
const [id, setId] = useState(0);
const [dateVal, setDateVal] = useState("");
const [isEditValidity, setIsEditValidity] = useState(false);
const [isAdd, setIsAdd] = useState(false);
const flag = useRef(true);
// 过滤
const handleFilter = (flag, val) => {
if (flag == "startandend") {
let val2 = val || [];
filterObj["start_time"] = parseInt(
new Date(dayjs(val2[0]).format("YYYY-MM-DD") + " 00:00:00").getTime() / 1000,
);
const endDate = new Date(dayjs(val2[1])).getDate();
const endTime = parseInt(
new Date(dayjs(val2[1]).format("YYYY-MM-") + endDate + " 23:59:59").getTime() / 1000,
);
filterObj["end_time"] = endTime;
setDateVal(val);
if (!val2.length) {
delete filterObj["start_time"];
delete filterObj["end_time"];
}
} else {
filterObj[flag] = val;
}
for (const key in filterObj) {
if (!filterObj[key]) {
delete filterObj[key];
}
}
setfilterObj({ ...filterObj });
if (!val) init(filterObj);
// else init({ ...filterObj, book_id: val }); // 在此处传入包含 book_id 的 filterObj,hyy
};
//改变状态
const changeState = async (obj) => {
let status = obj.status === 1 ? 2 : 1;
const bool = await changeStatus({ ...obj, status, book_id: filterObj.book_id });
let obj2 = { ...filterObj };
for (let key in obj2) {
if (!obj2[key]) {
delete obj2[key];
}
}
bool && init(obj2);
};
// 重置
const handleReset = () => {
setfilterObj({ title: "", book_id: books[0].id });
setDateVal("");
init({ book_id: books[0].id });
};
// 获取书籍名称
const getBookOption = async () => {
const res = await getBooklist();
filterObj.book_id = res[0].id;
setfilterObj(filterObj);
setBooks(res);
return res[0].id;
};
const init = async (obj = {}) => {
setLoading(true);
let bookid;
if (flag.current) {
bookid = await getBookOption();
flag.current = false;
}
const { total, list } = await getList({
page,
page_size,
book_id: bookid,
...obj,
});
setTotal(total);
setData(list);
setLoading(false);
};
const onClose = () => {
setIsEdit(false);
};
const submitForm = (obj) => {
console.log(obj);
};
const delSuccess = async () => {
const bool = await delComment({ id, book_id: filterObj.book_id });
if (!bool) return;
init(filterObj);
setDelModal(false);
};
// 获取信息
const getInfo = async () => {
const data = await getSystem({ type: 3 });
console.log("data", data);
form1.setFieldsValue({
set_value: data.set_value,
});
};
// 设置个性化信息
const handleSystemEdit = async (values) => {
const { set_value } = values; // 获取输入框的内容
const data = { type: 3, set_value }; // 构造传递给systemEdit的参数对象
const bool = await systemEdit(data);
console.log("bool", bool);
bool && getInfo();
};
useEffect(() => {
if (operationPermissionsList.includes("/books/discussion/getSystem")) {
getInfo();
}
}, [operationPermissionsList]);
const columns = [
{
title: "ID",
key: "id",
dataIndex: "id",
align: "center",
},
{
title: "书籍名称",
key: "name",
dataIndex: "name",
align: "center",
},
{
title: "话题",
key: "title",
dataIndex: "title",
align: "center",
width: 200,
},
{
title: "讨论人",
key: "person_name",
dataIndex: "person_name",
align: "center",
},
{
title: "讨论时间",
key: "create_time",
dataIndex: "create_time",
align: "center",
},
{
title: "讨论内容",
key: "content",
dataIndex: "content",
align: "center",
render: (contentList) => {
// 如果contentList不存在或者为空,则返回null或自定义提示
if (!contentList || contentList.length === 0) {
return <span>暂无内容</span>;
}
return contentList.map((item, index) => {
const type = item.types;
const value = item.content;
if (type === "audio") {
return (
<div key={index}>
<span>
<audio controls>
<source src={value} type="audio/mp3" />
Your browser does not support the audio element.
</audio>
</span>
</div>
);
} else if (type === "text") {
return <span key={index}>{value}</span>;
} else if (type === "image") {
// return <img height={100} src={value}></img>;
return (
<Image
height={100}
src={value}
fallback="data:image/png;base64,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"
/>
);
} else {
return <span key={index}>其他类型</span>;
}
});
},
},
{
title: "审核",
key: "status",
dataIndex: "status",
align: "center",
render(_, { status, id }) {
let bool = status == 1 ? true : false;
return <Switch checked={bool} onChange={() => changeState({ status, id })} />;
},
},
{
title: "操作",
key: "handle",
dataIndex: "handle",
align: "center",
render: (_, { id }) => {
return (
<Space>
{operationPermissionsList.includes("/books/discussion/delComment") && (
<Button
onClick={() => {
setDelModal(true);
setId(id);
}}
style={{
letterSpacing: "-2px", // 设置负值来减小字母之间的间距
}}
>
删除
</Button>
)}
</Space>
);
},
},
];
return (
<div>
<Row justify={"space-between"}>
<Col className="form-devices-inline">
<Form layout="inline">
<Form.Item label="书籍名称">
<Select
showSearch //下拉框出现搜素,并可以输入
optionFilterProp="children" // 使搜索匹配子节点的文本内容
value={filterObj.book_id}
onChange={(ev) => handleFilter("book_id", ev)}
placeholder="请选择书籍名称"
autoComplete="off"
style={{ width: 160 }}
id="name"
>
{books &&
books.map((item) => (
<Select.Option value={item.id} key={item.id}>
{item.name}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item label="话题">
<Input
value={filterObj.title}
onChange={(ev) => handleFilter("title", ev.target.value)}
autoComplete="off"
placeholder="请输入话题"
allowClear
id="title"
></Input>
</Form.Item>
<Form.Item label="讨论时间">
<RangePicker
value={dateVal}
allowClear
onChange={(ev) => handleFilter("startandend", ev)}
id="createTime"
></RangePicker>
</Form.Item>
<Space>
<Button
type="primary"
ghost
icon={
<span
style={{
display: "inline-block",
width: "11px",
height: "10px",
pointerEvents: "none",
}}
>
<Image src={reset} />
</span>
}
onClick={handleReset}
>
重置
</Button>
<Button
type="primary"
icon={
<span
style={{
display: "inline-block",
width: "13px",
height: "12px",
pointerEvents: "none",
}}
>
<Image src={filter} />
</span>
}
ghost
onClick={() => {
setPage(1);
Object.values(filterObj).some((item) => item) ? init(filterObj) : init();
}}
>
筛选
</Button>
<Button
icon={
<span
style={{
display: "inline-block",
width: "10px",
height: "12px",
pointerEvents: "none",
}}
>
<Image src={reload} />
</span>
}
type="primary"
ghost
onClick={() =>
Object.values(filterObj).some((item) => item) ? init(filterObj) : init()
}
>
刷新
</Button>
</Space>
</Form>
</Col>
<Col>
<Space>
{operationPermissionsList.includes("/books/discussion/getSystem") && (
<Button
type="primary"
ghost
onClick={() => {
setIsAdd(true);
setIsEditValidity(true);
}}
>
审核设置
</Button>
)}
</Space>
</Col>
</Row>
<br />
<Modal
mask={false}
centered
open={delMoadal}
onOk={delSuccess}
onCancel={() => setDelModal(false)}
footer={(_, { OkBtn, CancelBtn }) => {
return (
<Row justify={"center"} className="delModal" style={{ marginBottom: "30px" }}>
<Space size={20}>
<CancelBtn />
<OkBtn />
</Space>
</Row>
);
}}
okText="确认"
>
<p style={{ textAlign: "center", padding: "30px 0 6px", fontSize: 16 }}>
讨论将永久删除,是否继续?
</p>
</Modal>
{TableCom({ columns, loading, data })}
<br />
<PaginationCom
total={total}
page_size={page_size}
setpage_size={setpage_size}
page={page}
setPage={setPage}
filterObj={filterObj}
init={init}
/>
<Drawer placement="right" onClose={onClose} open={isEdit} mask={false}>
<Form form={form} onFinish={submitForm} labelCol={{ span: 6 }}>
<Form.Item label="分类名称" rules={[{ required: true, message: "请输入分类名称" }]}>
<Input autoComplete="off" placeholder="请输入分类名称"></Input>
</Form.Item>
<Form.Item label="排序" rules={[{ required: true, message: "请输入排序" }]}>
<Input autoComplete="off" placeholder="请输入排序"></Input>
</Form.Item>
<Form.Item wrapperCol={{ offset: 6, span: 16 }} style={{ textAlign: "center" }}>
<Space>
<Button>取消</Button>
<Button type="primary" htmlType="submit">
提交
</Button>
</Space>
</Form.Item>
</Form>
</Drawer>
<Drawer
onClose={() => setIsEditValidity(false)}
open={isEditValidity}
footer={null}
mask={false}
keyboard={false}
onCancel={() => setIsEditValidity(false)}
>
<Form form={form1} onFinish={handleSystemEdit}>
<Form.Item
name="set_value"
label="评论是否需要审核"
labelCol={{ span: 12 }}
wrapperCol={{ span: 16 }}
>
<Radio.Group style={{ textAlign: "center" }}>
<Radio value={"1"}>是</Radio>
<Radio value={"0"}>否</Radio>
</Radio.Group>
</Form.Item>
<Form.Item wrapperCol={{ offset: 8, span: 26 }}>
<Space size={20}>
<Button onClick={() => setIsEditValidity(false)}>取消</Button>
<Button type="primary" htmlType="submit" onClick={() => setIsEditValidity(false)}>
提交
</Button>
</Space>
</Form.Item>
</Form>
</Drawer>
</div>
);
};
export default Discuss;
import axios from "@/utils/request.js";
import qs from "qs";
import { get } from "lodash-es";
import { message } from "antd";
// 讨论列表
const getList = async (obj) => {
try {
const res = await axios.post("/comment/comment/getAllCommentList", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 删除
const delComment = async (obj) => {
try {
const res = await axios.post("/comment/comment/delComment", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 状态
const changeStatus = async (obj) => {
try {
const res = await axios.post("/comment/comment/changeStatus", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
//书籍名称下拉框的内容
const getSelectList = async (obj) => {
try {
const res = await axios.post("/book/book/getAllList", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 获取设置信息
const getSystem = async (obj) => {
try {
const res = await axios.post("/system/config/getSystem", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
} catch (error) {
return error;
}
};
// 修改设置
const systemEdit = async (obj) => {
try {
const res = await axios.post("/system/config/systemEdit", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
} catch (error) {
return error;
}
};
// 所有上架书籍名称列表
const getBooklist = async (obj) => {
try {
const res = await axios.post("/book/book/getAllList", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
} catch (error) {
return error;
}
};
export { getList, delComment, changeStatus, getSystem, systemEdit, getBooklist };
export { default } from "@/modules/books/BooksLayout";
.delModal,
.footer {
.ant-btn-primary {
background: #aa1941;
border-radius: 4px;
color: #ffffff;
font-size: 14px;
&:hover {
background: #aa194283 !important;
}
}
.ant-btn-default {
background: #ffffff;
border-radius: 4px;
border: 1px solid #aa1941;
font-size: 14px;
color: #aa1941;
&:hover {
background: #ffffff7c !important;
border-color: #aa194283 !important;
color: #aa194283 !important;
}
}
}
import React, { useEffect, useState, useRef } from "react";
import {
Table,
Input,
Button,
Row,
Col,
Space,
DatePicker,
Drawer,
Form,
Modal,
Image,
} from "antd";
const { RangePicker } = DatePicker;
import "./index.less";
import PaginationCom from "@/common/Pagination";
import { getList, getInfo, addLabel, editLabel, delLabel, getAllBooks } from "./request";
import reload from "@/assets/images/icon/reload.png";
import reset from "@/assets/images/icon/reset.png";
import filter from "@/assets/images/icon/filter.png";
import add from "@/assets/images/icon/add.png";
import dayjs from "dayjs";
import TableCom from "@/common/TableCom/index";
import { useSelector } from "react-redux";
const Audit = () => {
const [data, setData] = useState([]);
const [page_size, setpage_size] = useState(10);
const [page, setPage] = useState(1);
const [total, setTotal] = useState(0);
const [loading, setLoading] = useState(true);
const [isEdit, setIsEdit] = useState(false);
const [delMoadal, setDelModal] = useState(false);
const [form] = Form.useForm();
const [isAdd, setIsAdd] = useState(false);
const [id, setId] = useState(0);
// 获取操作权限
const { operationPermissionsList } = useSelector((state) => state.user);
const [initialValues, setinitialValues] = useState({
name: "",
phone: "",
sex: "1",
birthday_time: "",
});
const [labelObj, setLabelObj] = useState({});
const [filterObj, setfilterObj] = useState({
name: "",
start_time: "",
end_time: "",
});
const [dateVal, setDateVal] = useState("");
// 全部标签数组
const [bookLabels, setBookLabels] = useState([]);
//选中id数组
const [book_ids, setBook_ids] = useState([]);
// 过滤
const handleFilter = (flag, val) => {
if (flag == "startandend") {
let val2 = val || [];
filterObj["start_time"] = parseInt(
new Date(dayjs(val2[0]).format("YYYY-MM-DD") + " 00:00:00").getTime() / 1000,
);
const endDate = new Date(dayjs(val2[1])).getDate();
const endTime = parseInt(
new Date(dayjs(val2[1]).format("YYYY-MM-") + endDate + " 23:59:59").getTime() / 1000,
);
filterObj["end_time"] = endTime;
setDateVal(val);
console.log(" setDateVal(val);", setDateVal(val));
if (!val2.length) {
delete filterObj["start_time"];
delete filterObj["end_time"];
}
} else {
filterObj[flag] = val;
}
for (const key in filterObj) {
if (!filterObj[key]) {
delete filterObj[key];
}
}
setfilterObj({ ...filterObj });
if (!val) init(filterObj);
};
useEffect(() => {
if (!isEdit) {
form.setFieldsValue({ ...initialValues });
}
}, [isEdit]);
// 选择标签
const handlChangeLabel = async (obj) => {
const { flag = false, id } = obj;
// 全选
if (flag) {
if (!book_ids.length) {
let arr = [];
let obj = {};
bookLabels.length &&
bookLabels.forEach((item) => {
arr.push(item.id);
obj[item.id.toString()] = item.id;
});
setBook_ids(arr);
setLabelObj(obj);
} else if (book_ids.length === bookLabels.length) {
setBook_ids([]);
setLabelObj({});
} else {
let arr = [];
let obj = {};
bookLabels.length &&
bookLabels.forEach((item) => {
arr.push(item.id);
obj[item.id.toString()] = item.id;
});
setBook_ids(arr);
setLabelObj(obj);
}
// 单选
} else {
// 有
if (id == labelObj[id]) {
labelObj[id] = 0;
setLabelObj({ ...labelObj });
} else {
labelObj[id] = id;
setLabelObj({ ...labelObj });
}
let arr = Object.values(labelObj).filter((item) => item !== 0);
setBook_ids(arr);
}
};
// 重置
const handleReset = () => {
setfilterObj({ name: "" });
setDateVal("");
init();
};
const init = async (obj) => {
setLoading(true);
const { total, list } = await getList({ page, page_size, ...obj });
setTotal(total);
setData(list);
setLoading(false);
};
const handleEdit = async (obj) => {
console.log("edit obj", obj);
const { books, id, name } = await getInfo({ id: obj.id });
setIsAdd(false);
setIsEdit(true);
console.log("我是点击编辑以后得obj", id);
setId(id);
let copyObj = {};
books.forEach((item) => {
copyObj[item.toString()] = item;
});
setLabelObj({ ...copyObj });
setBook_ids(books);
form.setFieldsValue({ name });
};
const onClose = () => {
setIsEdit(false);
};
const submitForm = async (obj) => {
let bool;
let arr = Object.values(labelObj);
console.log(arr);
// setBook_ids(arr)
if (isAdd) {
bool = await addLabel({ ...obj, book_ids });
} else {
bool = await editLabel({ ...obj, id, book_ids });
}
if (!bool) return;
init();
setIsEdit(false);
};
const delSuccess = async () => {
const bool = await delLabel({ id });
bool && init();
setDelModal(false);
};
const columns = [
{
title: "ID",
key: "id",
dataIndex: "id",
align: "center",
},
{
title: "标签名称",
key: "name",
dataIndex: "name",
align: "center",
},
{
title: "创建时间",
key: "create_time",
dataIndex: "create_time",
align: "center",
},
{
title: "更新时间",
key: "update_time",
dataIndex: "update_time",
align: "center",
},
{
title: "操作",
key: "handle",
dataIndex: "handle",
render: (_, record) => {
return (
<Space>
{operationPermissionsList.includes("/books/label/getInfoById") && (
<Button onClick={() => handleEdit(record)}>编辑</Button>
)}
{operationPermissionsList.includes("/books/label/del") && (
<Button
onClick={() => {
setDelModal(true);
setId(record.id);
}}
>
删除
</Button>
)}
</Space>
);
},
},
];
const getBookList = async () => {
const data = await getAllBooks();
setBookLabels(data);
};
useEffect(() => {
getBookList();
}, []);
return (
<div className="label">
<Row justify={"space-between"}>
<Col span={21} className="form-devices-inline">
<Form layout="inline">
<Form.Item label="标签名称">
{" "}
<Input
autoComplete="off"
allowClear
value={filterObj.name}
onChange={(ev) => handleFilter("name", ev.target.value)}
placeholder="请输入标签名称"
id="name"
></Input>
</Form.Item>
<Form.Item label="创建时间">
{" "}
<RangePicker
value={dateVal}
onChange={(ev) => handleFilter("startandend", ev)}
id="createTime"
></RangePicker>
</Form.Item>
<Space>
<Button
type="primary"
ghost
icon={
<span
style={{
display: "inline-block",
width: "11px",
height: "10px",
pointerEvents: "none",
}}
>
<Image src={reset} />
</span>
}
onClick={handleReset}
>
重置
</Button>
<Button
icon={
<span
style={{
display: "inline-block",
width: "13px",
height: "12px",
pointerEvents: "none",
}}
>
<Image src={filter} />
</span>
}
onClick={() => {
setPage(1);
Object.values(filterObj).some((item) => item) ? init(filterObj) : init();
}}
type="primary"
ghost
>
筛选
</Button>
<Button
icon={
<span
style={{
display: "inline-block",
width: "10px",
height: "12px",
pointerEvents: "none",
}}
>
<Image src={reload} />
</span>
}
onClick={() =>
Object.values(filterObj).some((item) => item) ? init(filterObj) : init()
}
type="primary"
ghost
>
刷新
</Button>
</Space>
</Form>
</Col>
<Col>
<Space>
{operationPermissionsList.includes("/books/label/add") && (
<Button
icon={
<span
style={{
display: "inline-block",
width: "13px",
height: "12px",
pointerEvents: "none",
}}
>
<Image src={add} />
</span>
}
onClick={() => {
setIsAdd(true);
setIsEdit(true);
}}
type="primary"
ghost
>
添加
</Button>
)}
</Space>
</Col>
</Row>
<br />
<Modal
mask={false}
centered
open={delMoadal}
onOk={delSuccess}
onCancel={() => setDelModal(false)}
footer={(_, { OkBtn, CancelBtn }) => {
return (
<Row justify={"center"} className="delModal" style={{ marginBottom: "30px" }}>
<Space size={20}>
<CancelBtn />
<OkBtn />
</Space>
</Row>
);
}}
okText="确认"
>
<p style={{ textAlign: "center", padding: "30px 0 18px", fontSize: 16 }}>
标签将永久删除,是否继续?
</p>
</Modal>
{TableCom({ columns, loading, data })}
<br />
<PaginationCom
total={total}
page_size={page_size}
setpage_size={setpage_size}
page={page}
setPage={setPage}
init={init}
filterObj={filterObj}
/>
<Drawer placement="right" onClose={onClose} open={isEdit} mask={false}>
<Form form={form} onFinish={submitForm} labelCol={{ span: 5 }}>
<Form.Item
label="标签名称"
name="name"
rules={[{ required: true, message: "请输入标签名称" }]}
>
<Input autoComplete="off" placeholder="请输入标签名称"></Input>
</Form.Item>
<Form.Item label="书籍" name="name" rules={[{ required: true, message: "请选择书籍" }]}>
<Button
onClick={() => handlChangeLabel({ flag: true })}
style={{
background: "rgba(170, 25, 65, 0.05)",
marginRight: 0, // 取消右侧间距
fontWeight: "bold",
letterSpacing: "-2px", // 设置负值来减小字母之间的间距
}}
type="primary"
ghost
>
全选
</Button>
<br />
<br />
<Row gutter={20}>
{bookLabels.length &&
bookLabels.map((item) => {
return (
<Col style={{ marginBottom: 15 }} flex={"0 0 70px"} key={item.id}>
<Button
type={item.id == labelObj[item.id] ? "primary" : "default"}
ghost={item.id == labelObj[item.id]}
onClick={() => handlChangeLabel({ id: item.id })}
>
{item.name}
</Button>
</Col>
);
})}
</Row>
</Form.Item>
<Form.Item wrapperCol={{ offset: 5, span: 16 }}>
<Row justify={"center"}>
<Space size={20}>
<Button onClick={() => setIsEdit(false)}>取消</Button>
<Button type="primary" htmlType="submit">
提交
</Button>
</Space>
</Row>
</Form.Item>
</Form>
</Drawer>
</div>
);
};
export default Audit;
.buttonContainer {
display: flex;
justify-content: space-between;
}
import axios from "@/utils/request.js";
import qs from "qs";
import { get } from "lodash-es";
import { message } from "antd";
// 标签列表
const getList = async (obj) => {
try {
const res = await axios.post("/book/label/getList", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 获取所有书籍列表
const getAllBooks = async (obj) => {
try {
const res = await axios.post("/book/label/getAllBooks", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 详情
const getInfo = async (obj) => {
try {
const res = await axios.post("/book/label/getInfoById", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 添加
const addLabel = async (obj) => {
try {
const res = await axios.post("/book/label/add", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 修改
const editLabel = async (obj) => {
try {
const res = await axios.post("/book/label/edit", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 删除
const delLabel = async (obj) => {
try {
const res = await axios.post("/book/label/del", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
export { getList, getInfo, addLabel, editLabel, delLabel, getAllBooks };
export { default } from "@/modules/books/management/components/ManagementAddEdit";
import axios from "@/utils/axios";
// 获取教师书籍列表
export function getBookList(data) {
return axios.post("/api/book/teacher/getList", data);
}
// 获取教师书籍详情
export function getBook(data) {
return axios.post("/api/book/teacher/getInfoById", data);
}
// 添加教师书籍
export function addBook(data) {
return axios.post("/api/book/teacher/add", data);
}
// 添加教师书籍
export function updateBook(data) {
return axios.post("/api/book/teacher/edit", data);
}
// 删除教师书籍
export function deleteBook(data) {
return axios.post("/api/book/teacher/del", data);
}
.upload {
background: rgba(170, 25, 65, 0.03);
border-radius: 4px;
border: 1px solid #aa1941;
color: #aa1941;
font-size: 12px;
}
.upload:hover {
background: rgba(170, 25, 65, 0.03) !important;
border-radius: 4px !important;
opacity: 0.7;
border: 1px solid #aa1941 !important;
color: #aa1941 !important;
font-size: 12px !important;
}
.uploadPar .descBox {
display: flex;
align-items: flex-end;
/* 让子元素底部对齐 */
}
.uploadPar .descBox .desc {
font-size: 10px;
font-family: "PingFangSC, PingFang SC";
font-weight: 400;
color: #999999;
line-height: 14px;
font-size: 12px;
margin-left: 6px;
margin-bottom: 2px;
}
.back-icon {
position: absolute;
top: 10px;
left: 10px;
cursor: pointer;
display: none;
}
.ant-select
.ant-select-outlined
.ant-pagination-options-size-changer
.css-dev-only-do-not-override-1scgnrh
.ant-select-single
.ant-select-show-arrow
.ant-select-show-search {
display: none !important;
}
import { Outlet } from "react-router";
const BookManagement = () => {
return <Outlet />;
};
export default BookManagement;
import { useState } from "react";
import {
Input,
Button,
Row,
Col,
Space,
DatePicker,
Image,
Drawer,
Form,
Select,
Modal,
Upload,
Spin,
} from "antd";
const { RangePicker } = DatePicker;
const { TextArea } = Input;
import { useNavigate } from "react-router";
import PaginationCom from "@/common/Pagination";
import { getList, delUser, exportBook, releaseExamine, importBook } from "../request";
import { exportHtmlToWord } from "@/utils/exportWordHtmlDocx";
import reset from "@/assets/images/icon/reset.png";
import filter from "@/assets/images/icon/filter.png";
import add from "@/assets/images/icon/add.png";
import imports from "@/assets/images/icon/import.png";
import reload from "@/assets/images/icon/reload.png";
import dayjs from "dayjs";
import TableCom from "@/common/TableCom/index";
import { useSelector } from "react-redux";
import { downloadFile } from "@/utils/common";
const Audit = () => {
const [data, setData] = useState([]);
const navigate = useNavigate();
const [page_size, setpage_size] = useState(10);
const [page, setPage] = useState(1);
const [total, setTotal] = useState(0);
const [loading, setLoading] = useState(true);
const [delModal, setDelModal] = useState(false);
const [exportModal, setExportModal] = useState(false);
const [importModal, setImportModal] = useState(false);
const [bookFile, setBookFile] = useState("");
const [showAudit, setShowAudit] = useState(false);
const [id, setId] = useState(0);
const [filterObj, setFilterObj] = useState({
name: "",
authors: "",
audit_status: null,
start_time: "",
end_time: "",
});
const [dateVal, setDateVal] = useState("");
// 获取操作权限
const { operationPermissionsList } = useSelector((state) => state.user);
const { userInfo } = useSelector((state) => state.user);
// 重置
const handleReset = () => {
setFilterObj({ name: "", authors: "", audit_status: null });
setDateVal("");
init();
};
const init = async (obj = {}) => {
setLoading(true);
const { total, list } = await getList({ page, page_size, ...obj });
setTotal(total);
setData(list);
setLoading(false);
};
// 过滤
const handleFilter = (flag, val) => {
if (flag == "startandend") {
let val2 = val || [];
filterObj["start_time"] = parseInt(
new Date(dayjs(val2[0]).format("YYYY-MM-DD") + " 00:00:00").getTime() / 1000,
);
const endDate = new Date(dayjs(val2[1])).getDate();
const endTime = parseInt(
new Date(dayjs(val2[1]).format("YYYY-MM-") + endDate + " 23:59:59").getTime() / 1000,
);
filterObj["end_time"] = endTime;
setDateVal(val);
if (!val2.length) {
delete filterObj["start_time"];
delete filterObj["end_time"];
}
} else {
filterObj[flag] = val;
}
for (const key in filterObj) {
if (!filterObj[key]) {
delete filterObj[key];
}
}
setFilterObj({ ...filterObj });
if (!val) init(filterObj);
};
const delSuccess = async () => {
const bool = await delUser({ id });
bool && init();
setDelModal(false);
};
const exportSuccess = async () => {
setExportModal(false);
setLoading(true);
try {
const { name, content } = await exportBook({ id });
if (!name) return;
await exportHtmlToWord(name, content, [
".chapter-gallery-container",
".chapter-expand",
".chapter-practice",
".chapter-item-link",
".chapter-item-tooltip",
"div[data-w-e-type='video']",
]);
} catch (e) {
console.error("导出Word失败", e);
} finally {
setLoading(false);
}
};
const importSuccess = async (obj) => {
setUploading(true);
const bool = await importBook({ ...obj, file: bookFile });
// if (!bool) return;
if (bool) {
window.location.replace(window.location.href);
}
setImportModal(false);
setUploading(false);
};
const UploadProps = {
name: "file",
accept: ".doc , .docx",
showUploadList: false,
beforeUpload(file) {
const suffixArr = ["doc", "docx"];
const suffix = file.name.substring(file.name.lastIndexOf(".") + 1);
if (!suffixArr.includes(suffix)) {
message.error(`请上传以${suffixArr.join("、")}格式的书籍`);
return false;
}
setBookFile(file);
return false;
},
};
const handleReleaseExamine = async (obj) => {
const bool = await releaseExamine({ ...obj, id });
if (bool) {
window.location.replace(window.location.href); // 几乎完美
}
if (!bool) return;
setShowAudit(false);
init();
};
// 下载模板
const handleDownload = async () => {
downloadFile("书籍导入示例.docx");
};
const [uploading, setUploading] = useState(false);
const columns = [
{
title: "书籍名称",
key: "name",
align: "center",
dataIndex: "name",
},
{
title: "书籍图片",
key: "img",
align: "center",
dataIndex: "img",
render: (_, { img }) => (
<Image
width={80}
height={80}
src={img}
fallback="data:image/png;base64,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"
/>
),
},
{
title: "作者",
key: "authors",
align: "center",
dataIndex: "authors",
},
{
title: "审核状态",
key: "audit_status",
align: "center",
dataIndex: "audit_status",
render: (_, { audit_status }) => {
switch (audit_status) {
case 1:
return (
<Button type="link" style={{ color: "#000", cursor: "initial" }}>
待发布
</Button>
);
case 2:
return (
<Button type="link" style={{ color: "#000", cursor: "initial" }}>
审核中
</Button>
);
case 3:
return (
<Button type="link" style={{ cursor: "initial" }} danger>
审核未通过
</Button>
);
case 4:
return (
<Button type="link" style={{ color: "#000", cursor: "initial" }}>
审核通过
</Button>
);
}
},
},
{
title: "发布次数",
key: "release_num",
dataIndex: "release_num",
width: 150,
align: "center",
},
{
title: "创建时间",
key: "create_time",
align: "center",
dataIndex: "create_time",
},
{
title: "更新时间",
key: "update_time",
align: "center",
dataIndex: "update_time",
},
{
title: "操作",
key: "handle",
dataIndex: "handle",
width: 400,
render: (_, { audit_status, id, user_id }) => {
// 审核中
// if (audit_status === 2) return
// 创建者或者管理员
const isAdminOrCreator = userInfo.id === user_id || userInfo.type === 1;
return (
<Space>
{isAdminOrCreator &&
operationPermissionsList.includes("/books/management/getAllList") && (
<Button
onClick={() => {
navigate("/books/management/add-edit", { state: { id: id } });
}}
type="primary"
ghost
>
基本信息
</Button>
)}
{operationPermissionsList.includes("/books/management/getInfoById") && (
<Button
onClick={() => {
navigate("/books/management/chapter", {
state: { id, isAdminOrCreator: isAdminOrCreator },
});
}}
>
编辑
</Button>
)}
{isAdminOrCreator && operationPermissionsList.includes("/books/management/export") && (
<Button
onClick={() => {
setId(id);
setExportModal(true);
}}
>
导出
</Button>
)}
{isAdminOrCreator && operationPermissionsList.includes("/books/management/del") && (
<Button
onClick={() => {
setId(id);
setDelModal(true);
}}
>
删除
</Button>
)}
{isAdminOrCreator &&
operationPermissionsList.includes("/books/management/release") &&
audit_status === 1 && (
<Button
onClick={() => {
setId(id);
setShowAudit(true);
}}
>
发布审核
</Button>
)}
</Space>
);
},
},
];
return (
<>
<Row justify={"space-between"}>
<Col span={21} className="form-devices-inline">
<Form layout="inline">
<Form.Item label="书籍名称">
<Input
autoComplete="off"
allowClear
value={filterObj.name}
onChange={(ev) => handleFilter("name", ev.target.value)}
placeholder="请输入书籍名称"
id="name"
></Input>
</Form.Item>
<Form.Item label="作者">
<Input
autoComplete="off"
allowClear
value={filterObj.authors}
onChange={(ev) => handleFilter("authors", ev.target.value)}
placeholder="请输入作者"
id="author"
></Input>
</Form.Item>
<Form.Item label="审核状态">
<Select
style={{ width: 160 }}
allowClear
value={filterObj.audit_status}
onChange={(ev) => handleFilter("audit_status", ev)}
placeholder="请选择审核状态"
id="status"
>
<Select.Option value="1">待发布</Select.Option>
<Select.Option value="2">审核中</Select.Option>
<Select.Option value="3">审核未通过</Select.Option>
<Select.Option value="4">审核通过</Select.Option>
</Select>
</Form.Item>
<Form.Item label="创建时间">
<RangePicker
value={dateVal}
onChange={(ev) => handleFilter("startandend", ev)}
id="createTime"
></RangePicker>
</Form.Item>
<Space>
<Button
type="primary"
// ghost={flag}
ghost
icon={
<span
style={{
display: "inline-block",
width: "11px",
height: "10px",
pointerEvents: "none",
}}
>
<Image src={reset} />
</span>
}
onClick={handleReset}
>
重置
</Button>
<Button
type="primary"
icon={
<span
style={{
display: "inline-block",
width: "13px",
height: "12px",
pointerEvents: "none",
}}
>
<Image src={filter} />
</span>
}
ghost
onClick={() => {
setPage(1);
console.log(filterObj);
Object.values(filterObj).some((item) => item) ? init(filterObj) : init();
}}
>
筛选
</Button>
<Button
type="primary"
ghost
icon={
<span
style={{
display: "inline-block",
width: "10px",
height: "12px",
pointerEvents: "none",
}}
>
<Image src={reload} />
</span>
}
onClick={() => {
Object.values(filterObj).some((item) => item) ? init(filterObj) : init();
}}
>
刷新
</Button>
</Space>
</Form>
</Col>
<Col span={2} style={{ marginLeft: 20, display: "flex", justifyContent: "flex-end" }}>
<Space>
{operationPermissionsList.includes("/books/management/import") && (
<Button
type="primary"
icon={
<span
style={{
display: "inline-block",
width: "11px",
height: "12px",
pointerEvents: "none",
}}
>
<Image src={imports} />
</span>
}
ghost
onClick={() => setImportModal(true)}
>
导入
</Button>
)}
{operationPermissionsList.includes("/books/management/add") && (
<Button
type="primary"
icon={
<span
style={{
display: "inline-block",
width: "13px",
height: "12px",
pointerEvents: "none",
}}
>
<Image src={add} />
</span>
}
ghost
onClick={() => navigate("/books/management/add-edit")}
>
添加
</Button>
)}
</Space>
</Col>
</Row>
<Modal
mask={false}
centered
open={delModal}
onOk={delSuccess}
onCancel={() => setDelModal(false)}
footer={(_, { OkBtn, CancelBtn }) => {
return (
<Row justify={"center"} className="delModal" style={{ marginBottom: "30px" }}>
<Space size={20}>
<CancelBtn />
<OkBtn />
</Space>
</Row>
);
}}
okText="确认"
ghost
>
<p style={{ textAlign: "center", padding: "30px 0 18px", fontSize: 16 }}>确认删除书籍?</p>
</Modal>
<Modal
mask={false}
centered
open={exportModal}
onOk={exportSuccess}
onCancel={() => setExportModal(false)}
footer={(_, { OkBtn, CancelBtn }) => {
return (
<Row justify={"center"} className="delModal" style={{ marginBottom: "30px" }}>
<Space size={20}>
<CancelBtn />
<OkBtn />
</Space>
</Row>
);
}}
okText="确认"
>
<p style={{ textAlign: "center", padding: "30px 0 18px", fontSize: 16 }}>
只能导出文本部分,画廊、音频、视频不支持
</p>
</Modal>
<Drawer
mask={false}
centered
open={importModal}
onCancel={() => setImportModal(false)}
onClose={() => setImportModal(false)}
footer={null}
okText="确认"
>
<Spin spinning={uploading}>
<Form labelCol={{ span: 8 }} onFinish={importSuccess}>
<Form.Item label="下载模板" style={{ marginBottom: 10 }}>
<Button ghost type="primary" onClick={handleDownload}>
下载模板
</Button>
</Form.Item>
<p
className="linetext"
style={{
fontWeight: 400,
color: "#999999",
fontSize: 12,
marginBottom: 30,
paddingLeft: 75,
}}
>
目录需设置为word中的标题样式或按照示例编写,防止导入失败
</p>
<Form.Item label="选择书籍" required>
<Form.Item name="file" rules={[{ required: true, message: "请上传书籍" }]} noStyle>
<Upload {...UploadProps}>
<Button>点击上传</Button>
</Upload>
</Form.Item>
<p style={{ color: "#999999" }}>{bookFile?.name}</p>
</Form.Item>
<Form.Item
label="目录结构等级"
name="catalog_level"
rules={[{ required: true, message: "请选择目录结构等级" }]}
>
<Select placeholder="请选择目录结构的等级" style={{ width: 260 }}>
<Select.Option value={1}>标题1</Select.Option>
<Select.Option value={2}>标题2</Select.Option>
<Select.Option value={3}>标题3</Select.Option>
<Select.Option value={4}>标题4</Select.Option>
<Select.Option value={5}>标题5</Select.Option>
</Select>
</Form.Item>
<Form.Item>
<Row
justify={"center"}
className="delModal"
style={{ marginBottom: "30px", marginTop: "0px" }}
>
<Space size={20}>
<Button onClick={() => setImportModal(false)}>取消</Button>
<Button type="primary" htmlType="submit">
确认
</Button>
</Space>
</Row>
</Form.Item>
</Form>
</Spin>
</Drawer>
<Drawer placement="right" onClose={() => setShowAudit(false)} open={showAudit} mask={false}>
<Form labelCol={{ span: 5 }} onFinish={handleReleaseExamine}>
<Form.Item
label="发布信息"
name="release_mess"
rules={[{ max: 255, message: "最大255字符" }]}
>
<TextArea
placeholder="请输入发布信息"
autoSize={{
minRows: 5,
maxRows: 8,
}}
/>
</Form.Item>
<Form.Item wrapperCol={{ offset: 10, span: 16 }}>
<Space size={20}>
<Button
onClick={() => {
setShowAudit(false);
}}
>
取消
</Button>
<Button type="primary" htmlType="submit">
发布
</Button>
</Space>
</Form.Item>
</Form>
</Drawer>
<br />
{TableCom({ columns, loading, data })}
<br />
<PaginationCom
total={total}
page_size={page_size}
setpage_size={setpage_size}
page={page}
setPage={setPage}
init={init}
filterObj={filterObj}
/>
</>
);
};
export default Audit;
import axios from "@/utils/request.js";
import qs from "qs";
import { get } from "lodash-es";
import { message } from "antd";
// 书籍列表
const getList = async (obj) => {
try {
const res = await axios.post("/book/teacher/getList", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 详情
const getInfo = async (obj) => {
try {
const res = await axios.post("/book/teacher/getInfoById", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 添加
const addUser = async (obj) => {
try {
const res = await axios.post("/book/teacher/add", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 修改
const editUser = async (obj) => {
try {
const res = await axios.post("/book/teacher/edit", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 删除
const delUser = async (obj) => {
try {
const res = await axios.post("/book/teacher/del", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 导出
const exportBook = async (obj) => {
try {
const res = await axios.post("/book/teacher/export", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 导出
const exportToPdf = async (obj) => {
try {
const res = await axios.post("book/teacher/exportPdf", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 文件直传
const getUploadToken = async (obj) => {
try {
const res = await axios.post("/common/OssUpload/getUploadToken", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return data;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 发布审核
const releaseExamine = async (obj) => {
try {
const res = await axios.post("/book/teacher/release", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 导入
const importBook = async (obj) => {
try {
const res = await axios.post("/book/teacher/import", obj, {
headers: {
"Content-Type": "multipart/form-data",
},
});
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
export {
getList,
getInfo,
addUser,
editUser,
delUser,
exportBook,
exportToPdf,
releaseExamine,
importBook,
};
import React, { useState, useEffect } from "react";
import { Segmented } from "antd";
import { Outlet, NavLink, useNavigate, useLocation } from "react-router";
import "./index.less";
import { useSelector } from "react-redux";
// import styled from 'styled-components';
const OrderTab = () => {
const { operationPermissionsList } = useSelector((state) => state.user);
const navigate = useNavigate();
// const [selectedOption, setSelectedOption] = useState(1);
const [selectedOption, setSelectedOption] = useState(() => {
return parseInt(localStorage.getItem("selectedOption"), 10) || 1;
});
useEffect(() => {
if (selectedOption !== parseInt(localStorage.getItem("selectedOption"), 10)) {
localStorage.setItem("selectedOption", selectedOption.toString());
}
}, [selectedOption]);
const location = useLocation();
useEffect(() => {
// 当路由改变时,根据当前路径设置selectedOption
if (location.pathname === "/books/order-management/dataset") {
setSelectedOption(1);
localStorage.setItem("selectedOption", "1");
} else if (location.pathname === "/books/order-management/sale-statistics") {
setSelectedOption(2);
localStorage.setItem("selectedOption", "2");
}
}, [location.pathname, setSelectedOption]);
let options = [];
if (
operationPermissionsList.includes("/books/order-management/getOrderList") &&
operationPermissionsList.includes("/books/order-management/salesStatistics")
) {
options = [
{
label: "订单列表",
value: 1,
},
{
label: "销售统计",
value: 2,
},
];
} else if (operationPermissionsList.includes("/books/order-management/getOrderList1")) {
options = [
{
label: "订单列表",
value: 1,
},
];
} else if (operationPermissionsList.includes("/books/order-management/salesStatistics")) {
options = [
{
label: "销售统计",
value: 2,
},
];
}
const onChange = (value) => {
setSelectedOption(value);
localStorage.setItem("selectedOption", value.toString());
switch (value) {
case 1: {
navigate("/books/order-management/dataset");
break;
}
case 2: {
navigate("/books/order-management/sale-statistics");
break;
}
}
};
return (
<div style={{ maxHeight: 790 }} className="salesTop">
<Segmented
options={options}
onChange={onChange}
value={selectedOption}
style={{ marginBottom: 20 }}
></Segmented>
<Outlet />
</div>
);
};
export default OrderTab;
.salesTop {
background-color: #fff;
.ant-segmented-item {
background-color: #fff;
}
.ant-segmented-item.ant-segmented-item-selected {
background-color: #f8edf0;
.ant-segmented-item-label {
color: #ab1941;
font-weight: bold;
}
}
.sales {
width: 1400px;
margin: auto;
.items {
display: flex;
justify-content: space-between;
align-items: center;
.item {
flex: 0 0 300px;
border-radius: 4px;
border: 1px solid #e4e4e4;
display: flex;
padding: 30px;
.img {
width: 40px;
margin-right: 40px;
img {
width: 100%;
}
}
.cont {
.title {
font-size: 12px;
font-weight: 400;
color: #999999;
}
.total {
font-size: 22px;
font-weight: 500;
color: #333333;
line-height: 30px;
}
}
.unit {
margin-left: 5px;
}
}
}
.echarts {
display: flex;
margin-top: 90px;
.echarts-item {
width: 800px;
height: 400px;
}
}
}
}
import axios from "@/utils/request.js";
import qs from "qs";
import { get } from "lodash-es";
import { message } from "antd";
// 订单列表
const getList = async (obj) => {
try {
const res = await axios.post("/order/getOrderList", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 数据统计
const salesStatistics = async (obj) => {
try {
const res = await axios.post("/order/salesStatistics", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 退款
const refund = async (obj) => {
try {
const res = await axios.post("/order/refund", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
export { getList, salesStatistics, refund };
import React, { useEffect, useState, useRef } from "react";
// import aa from '@/assets/images/icon/形状.png';
import aa from "@/assets/images/图标.png";
import bb from "@/assets/images/图标1.png";
import cc from "@/assets/images/图标2.png";
import dd from "@/assets/images/图标3.png";
import * as echarts from "echarts";
import { salesStatistics } from "./request";
const OrderSalesStatistics = () => {
// 在组件内部定义 refs
const totalSalesAmountRef = useRef(null);
const teacherPerformanceRef = useRef(null);
const bookPerformanceRef = useRef(null);
const sale = (data) => {
const xAxisData = data.map((item) => item.month);
const seriesData = data.map((item) => item.price);
const saleOption = {
title: {
text: "销售总金额(元)/月",
left: "left",
},
tooltip: {
trigger: "axis",
axisPointer: {
type: "shadow",
},
// formatter: function (params) {
// console.log('params', params);
// if (params[0].data == '0.00') {
// return '暂无数据';
// } else {
// return params[0].name + '月' + '<br/>' + params[0].value.toFixed(2) + '元';
// }
// },
formatter: function (params) {
console.log("params", params);
if (typeof params[0].data === "string" && params[0].data === "0.00") {
return "暂无数据";
} else if (typeof params[0].value === "number") {
return params[0].name + "月" + "<br/>" + params[0].value.toFixed(2) + "元";
} else if (typeof params[0].value === "string") {
const valueAsNumber = parseFloat(params[0].value);
if (isNaN(valueAsNumber)) {
return "数据异常";
} else {
return params[0].name + "月" + "<br/>" + valueAsNumber.toFixed(2) + "元";
}
} else {
return "数据格式错误";
}
},
},
grid: {
left: "3%",
right: "4%",
bottom: "3%",
containLabel: true,
},
xAxis: [
{
type: "category",
data: xAxisData,
axisTick: {
alignWithLabel: true,
},
axisLabel: {
interval: 0, // 强制显示所有标签
formatter: function (value, index) {
// 仅显示 1、3、5、7、9、11 月的标签
if ((index + 1) % 2 !== 0) {
return value + "月";
} else {
return "";
}
},
},
},
],
yAxis: [
{
type: "value",
splitLine: {
show: false, // 取消 y 轴的横线
},
},
],
series: [
{
name: "Direct",
type: "bar",
barWidth: "60%",
data: seriesData,
itemStyle: {
color: "#d48ca0",
},
},
],
};
const saleEchart = echarts.init(document.getElementById("totalSalesAmount"));
saleEchart.setOption(saleOption);
};
const teachers = (data) => {
const seriesData = data.map((item) => ({
name: item.teacher_name,
value: item.price.replace(",", ""),
}));
const option = {
title: {
text: "老师业绩分布TOP10",
left: "left",
},
tooltip: {
trigger: "item",
formatter: function (params) {
return `<div style="display: flex; align-items: center;">
<div style="width: 10px; height: 10px; border-radius: 50%; background-color: ${params.color}; margin-right: 10px;"></div>
<span style="color: ${params.color}">老师名称</span> : ${params.name}。 <br/>
</div>
<div style="display: flex; align-items: center;">
<div style="width: 10px; height: 10px; border-radius: 50%; background-color: ${params.color}; margin-right: 10px;"></div>
<span style="color: ${params.color}">老师业绩</span> : ${params.value} 元。
</div>`;
},
},
legend: {
orient: "vertical", // 将图例位置纵向展示
top: "5%", // 调整 top 属性的值,控制图例位置
itemStyle: {
color: seriesData.length === 0 ? "#d3d3d3" : "auto", // 根据seriesData的长度设置图例颜色
},
itemHeight: 20, // 设置图例的高度
height: 20,
},
series: [
{
name: "暂无数据",
type: "pie",
radius: "50%",
data: [...seriesData],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: "rgba(211, 211, 211)",
},
},
labelLine: {
show: false,
},
label: {
// 添加label属性,在formatter函数中获取price和ratio
// formatter: function (params) {
// const { price, ratio } = data[params.dataIndex];
// return `${params.name} ${(ratio * 100).toFixed(2)}%`;
// },
formatter: function (params) {
const { price, ratio } = data[params.dataIndex];
const name =
params.name.length > 10
? `${params.name.slice(0, 10)}\n${params.name.slice(10)}`
: params.name; // 控制名字长度,超过10个字符换行
return `${name} ${(ratio * 100).toFixed(2)}%`;
},
// rich: {
// name: {
// lineHeight: 20, // 控制每行的行高
// },
// },
},
},
],
};
const teacherEchart = echarts.init(document.getElementById("teacherPerformance"));
teacherEchart.setOption(option);
};
const books = (data) => {
const seriesData = data.map((item) => ({
name: item.book_name,
value: item.price,
}));
const option = {
title: {
text: "书籍业绩分布TOP10",
left: "left",
},
tooltip: {
trigger: "item",
formatter: function (params) {
return `<div style="display: flex; align-items: center;">
<div style="width: 10px; height: 10px; border-radius: 50%; background-color: ${params.color}; margin-right: 10px;"></div>
<span style="color: ${params.color}">书籍名称</span> : ${params.name}。 <br/>
</div>
<div style="display: flex; align-items: center;">
<div style="width: 10px; height: 10px; border-radius: 50%; background-color: ${params.color}; margin-right: 10px;"></div>
<span style="color: ${params.color}">书籍价格</span> : ${params.value} 元。
</div>`;
},
},
grid: {
left: "center",
right: "center",
top: "center",
bottom: "center",
},
legend: {
orient: "vertical", // 将 orient 设置为 'horizontal'
top: "5%", // 调整 top 属性的值,控制图例位置
itemStyle: {
color: seriesData.length === 0 ? "#d3d3d3" : "auto", // 根据seriesData的长度设置图例颜色
},
itemHeight: 20, // 设置图例的高度
height: 20,
},
series: [
{
name: "暂无数据",
type: "pie",
radius: "68%",
center: ["50%", "50%"],
clockwise: false,
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: "rgba(211, 211, 211)",
},
},
data: seriesData,
label: {
// 添加label属性,在formatter函数中获取price和ratio
// formatter: function (params) {
// const { price, ratio } = data[params.dataIndex];
// return `${params.name} ${(ratio * 100).toFixed(2)}%`;
// },
formatter: function (params) {
const { price, ratio } = data[params.dataIndex];
const name =
params.name.length > 10
? `${params.name.slice(0, 10)}\n${params.name.slice(10)}`
: params.name; // 控制名字长度,超过10个字符换行
return `${name} ${(ratio * 100).toFixed(2)}%`;
},
},
labelLine: {
//取消选中饼图后往外指的小横线
normal: {
show: false,
},
},
itemStyle: {
//饼图之间的间距
normal: {
borderWidth: 4,
borderColor: "#ffffff",
},
emphasis: {
borderWidth: 0,
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: "rgba(0, 0, 0, 0.5)",
},
},
},
],
color: ["#00acee", "#52cdd5", "#79d9f1", "#a7e7ff", "#c8efff"],
backgroundColor: "#fff",
};
const bookEchart = echarts.init(document.getElementById("bookPerformance"));
bookEchart.setOption(option);
};
const [tabArr, setTabArr] = useState([]);
const [type, setType] = useState("");
const initAllEcharts = async () => {
const data = await salesStatistics();
let { salesAmount, teacher, book, order_num, buy_num, average, income } = data;
setType(data.type);
// 添加延迟来等待DOM元素准备就绪
await new Promise((resolve) => setTimeout(resolve, 100));
if (data.type === 2) {
sale(salesAmount);
books(book);
} else if (data.type === 1) {
sale(salesAmount);
teachers(teacher);
}
setTabArr([
{ name: "预计总收入", value: income },
{ name: "总订单数", value: order_num },
{ name: "总购买人数", value: buy_num },
{ name: "平均客单价", value: average },
]);
};
useEffect(() => {
(async () => {
console.log(type);
await new Promise((resolve) => setTimeout(resolve, 100));
await initAllEcharts();
})();
}, [totalSalesAmountRef, teacherPerformanceRef, bookPerformanceRef]);
// useEffect(() => {
// const initCharts = async () => {
// const data = await salesStatistics();
// let { salesAmount, teacher, book, order_num, buy_num, average, income } = data;
// setType(data.type);
// if (data.type === 2) {
// if (totalSalesAmountRef.current && bookPerformanceRef.current) {
// sale(salesAmount, totalSalesAmountRef.current);
// books(book, bookPerformanceRef.current);
// }
// } else if (data.type === 1) {
// if (totalSalesAmountRef.current && teacherPerformanceRef.current) {
// sale(salesAmount, totalSalesAmountRef.current);
// teachers(teacher, teacherPerformanceRef.current);
// }
// }
// console.log('buy_num', buy_num);
// setTabArr([
// { name: '预计总收入', value: income },
// { name: '总订单数1', value: order_num },
// { name: '总购买人数', value: buy_num },
// { name: '平均客单价', value: average },
// ]);
// };
// initCharts();
// }, []);
return (
<div className="sales">
<div className="items">
{tabArr.map((item, index) => (
<div className="item" key={Math.random()}>
<div className="img">
{index === 0 && <img src={aa} alt="" />}
{index === 1 && <img src={bb} alt="" />}
{index === 2 && <img src={cc} alt="" />}
{index === 3 && <img src={dd} alt="" />}
</div>
<div className="cont">
<div className="title">{item.name}</div>
<div className="total">
{index === 0 && (item.value - 0).toFixed(2)}
{index === 1 && item.value - 0}
{index === 2 && item.value - 0}
{index === 3 && (item.value - 0).toFixed(2)}
<span className="unit">
{index === 0 && "元"}
{index === 1 && "单"}
{index === 2 && "人"}
{index === 3 && "元"}
</span>
</div>
</div>
</div>
))}
</div>
{/* <div className='echarts'>
{type === 1 ? (
<>
<div className='echarts-item' id='totalSalesAmount'></div>
<div className='echarts-item' id='teacherPerformance'></div>
</>
) : type === 2 ? (
<>
<div className='echarts-item' id='totalSalesAmount'></div>
<div className='echarts-item' id='bookPerformance'></div>
</>
) : null}
{/* <div className='echarts-item' id='totalSalesAmount'></div>
<div className='echarts-item' id='teacherPerformance'></div>
<div className='echarts-item' id='bookPerformance'></div>
</div> */}
<div className="echarts">
{type === 1 ? (
<>
<div className="echarts-item" id="totalSalesAmount" ref={totalSalesAmountRef}></div>
<div className="echarts-item" id="teacherPerformance" ref={teacherPerformanceRef}></div>
</>
) : type === 2 ? (
<>
<div className="echarts-item" id="totalSalesAmount" ref={totalSalesAmountRef}></div>
<div className="echarts-item" id="bookPerformance" ref={bookPerformanceRef}></div>
</>
) : null}
</div>
</div>
);
};
export default OrderSalesStatistics;
import React, { useEffect, useState } from "react";
import {
Table,
Input,
Button,
Row,
Col,
Space,
DatePicker,
Pagination,
Drawer,
Form,
Select,
Image,
Rate,
Modal,
} from "antd";
const { TextArea } = Input;
const { RangePicker } = DatePicker;
import PaginationCom from "@/common/Pagination";
import { getList, refund } from "./request";
import TableCom from "@/common/TableCom/index";
import reset from "@/assets/images/icon/reset.png";
import filter from "@/assets/images/icon/filter.png";
import add from "@/assets/images/icon/add.png";
import imports from "@/assets/images/icon/import.png";
import reload from "@/assets/images/icon/reload.png";
import wx from "@/assets/images/vx.png";
import zhifu from "@/assets/images/zhifu.png";
import money from "@/assets/images/money.png";
import dayjs from "dayjs";
import { useSelector } from "react-redux";
const Audit = () => {
const [data, setData] = useState([]);
const [page_size, setpage_size] = useState(10);
const [page, setPage] = useState(1);
const [total, setTotal] = useState(0);
const [loading, setLoading] = useState(true);
const [isEdit, setIsEdit] = useState(false);
const [id, setId] = useState(0);
const [filterObj, setfilterObj] = useState({
book_name: "",
status: null,
pay_type: null,
start_pay: "",
end_pay: "",
});
const [dateVal, setDateVal] = useState("");
// 获取操作权限
const { operationPermissionsList } = useSelector((state) => state.user);
// 过滤
const handleFilter = (flag, val) => {
if (flag == "startandend") {
let val2 = val || [];
filterObj["start_pay"] = parseInt(
new Date(dayjs(val2[0]).format("YYYY-MM-DD") + " 00:00:00").getTime() / 1000,
);
const endDate = new Date(dayjs(val2[1])).getDate() + 1;
const endTime = parseInt(
new Date(dayjs(val2[1]).format("YYYY-MM-DD") + " 23:59:59").getTime() / 1000,
);
filterObj["end_pay"] = endTime;
setDateVal(val);
if (!val2.length) {
delete filterObj["start_pay"];
delete filterObj["end_pay"];
}
} else {
filterObj[flag] = val;
}
for (const key in filterObj) {
if (!filterObj[key]) {
delete filterObj[key];
}
}
setfilterObj({ ...filterObj });
if (!val) init(filterObj);
};
// 退款
const handleRefund = async (obj = {}) => {
const bool = await refund({ id, ...obj });
if (!bool) return;
init();
setShowReply(false);
};
const [form] = Form.useForm();
const onChange = (pageNumber) => {
setLoading(true);
setPage(pageNumber);
setLoading(false);
};
const handleEdit = () => {
setIsEdit(true);
};
const onClose = () => {
setIsEdit(false);
};
const submitForm = (obj) => {
console.log(obj);
};
const init = async (obj = {}) => {
setLoading(true);
const { total, list } = await getList({ ...obj, page, page_size });
setTotal(total);
setData(list || []);
setLoading(false);
};
const [showReply, setShowReply] = useState(false);
const columns = [
{
title: "订单编号",
key: "id",
dataIndex: "id",
align: "center",
},
{
title: "书籍名称",
key: "book_name",
dataIndex: "book_name",
align: "center",
width: 150,
},
{
title: "购买人",
key: "members_name",
dataIndex: "members_name",
align: "center",
},
{
title: "总金额",
key: "total_price",
dataIndex: "total_price",
align: "center",
},
{
title: "优惠券抵扣金额",
key: "coupon_price",
dataIndex: "coupon_price",
align: "center",
width: 140,
},
{
title: "积分抵扣金额",
key: "integral_price",
dataIndex: "integral_price",
align: "center",
width: 140,
},
{
title: "实际付款金额",
key: "final_total_price",
dataIndex: "final_total_price",
align: "center",
width: 140,
},
{
title: "下单时间",
key: "create_time",
dataIndex: "create_time",
align: "center",
},
{
title: "支付时间",
key: "pay_time",
dataIndex: "pay_time",
align: "center",
},
{
title: "结算状态",
key: "status_name",
dataIndex: "status_name",
align: "center",
},
{
title: "支付方式",
key: "pay_type_name",
dataIndex: "pay_type_name",
align: "center",
width: 100,
render(_, { pay_type_name }) {
if (pay_type_name == "微信") {
return <img src={wx} width={30}></img>;
} else if (pay_type_name == "支付宝") {
return <img width={30} src={zhifu}></img>;
} else {
return <img src={money} width={30} />;
}
},
},
{
title: "评价",
key: "rating",
dataIndex: "rating",
align: "center",
width: 135,
render(_, { rating }) {
return <Rate disabled allowHalf style={{ width: 135 }} value={rating}></Rate>;
},
},
{
title: "操作",
key: "handle",
dataIndex: "handle",
align: "center",
render(_, { status, id }) {
return (
operationPermissionsList.includes("/books/order-management/refund") &&
status == 3 && (
<Button
onClick={() => {
setShowReply(true);
setId(id);
}}
>
退款
</Button>
)
);
},
},
];
const handleReset = () => {
setfilterObj({ book_name: "", status: null, pay_type: null });
setDateVal("");
init();
};
return (
<div>
<Row justify={"space-between"}>
<Col className="form-devices-inline">
<Form layout="inline">
<Form.Item label="书籍名称">
<Input
allowClear
autoComplete="off"
placeholder="请输入书籍名称"
id="name"
value={filterObj.book_name}
onChange={(ev) => handleFilter("book_name", ev.target.value)}
></Input>
</Form.Item>
<Form.Item label="支付状态">
<Select
allowClear
placeholder="请选择支付状态"
value={filterObj.status}
onChange={(ev) => handleFilter("status", ev)}
style={{ width: 160 }}
>
<Select.Option value="1">待支付</Select.Option>
<Select.Option value="2">取消支付</Select.Option>
<Select.Option value="3">已支付</Select.Option>
<Select.Option value="4">已退款</Select.Option>
</Select>
</Form.Item>
<Form.Item label="支付方式">
<Select
allowClear
placeholder="请选择支付方式"
value={filterObj.pay_type}
onChange={(ev) => handleFilter("pay_type", ev)}
style={{ width: 160 }}
>
<Select.Option value="1">微信</Select.Option>
<Select.Option value="2">支付宝</Select.Option>
<Select.Option value="3">紫荆币</Select.Option>
</Select>
</Form.Item>
<Form.Item label="支付时间">
<RangePicker
id="createTime"
allowClear
autoComplete="off"
value={dateVal}
onChange={(ev) => handleFilter("startandend", ev)}
></RangePicker>
</Form.Item>
<Space>
<Button
type="primary"
ghost
icon={
<span
style={{
display: "inline-block",
width: "11px",
height: "10px",
pointerEvents: "none",
}}
>
<Image src={reset} />
</span>
}
onClick={handleReset}
>
重置
</Button>
<Button
type="primary"
icon={
<span
style={{
display: "inline-block",
width: "13px",
height: "12px",
pointerEvents: "none",
}}
>
<Image src={filter} />
</span>
}
ghost
onClick={() => {
setPage(1);
Object.values(filterObj).some((item) => item) ? init(filterObj) : init();
}}
>
筛选
</Button>
<Button
type="primary"
ghost
icon={
<span
style={{
display: "inline-block",
width: "10px",
height: "12px",
pointerEvents: "none",
}}
>
<Image src={reload} />
</span>
}
onClick={() => {
Object.values(filterObj).some((item) => item) ? init(filterObj) : init();
}}
>
刷新
</Button>
</Space>
</Form>
</Col>
<Col></Col>
</Row>
<br />
{TableCom({ columns, loading, data })}
<br />
<PaginationCom
total={total}
page_size={page_size}
setpage_size={setpage_size}
page={page}
setPage={setPage}
init={init}
filterObj={filterObj}
/>
<Drawer
open={showReply}
onCancel={() => {
setShowReply(false);
}}
maskClosable={false}
footer={null}
centered
mask={false}
onClose={() => {
setShowReply(false);
}}
keyboard={false}
>
<Form
form={form}
onFinish={handleRefund}
labelCol={{ span: 4 }}
style={{ textAlign: "center", padding: "10px 0 18px", fontSize: 16 }}
>
<Form.Item label="退款原因" name="refund_reason">
<TextArea
placeholder="请输入退款原因"
autoSize={{
minRows: 3,
maxRows: 5,
}}
></TextArea>
</Form.Item>
<Form.Item wrapperCol={{ offset: 3 }}>
<Row gutter={20} justify={"center"}>
<Col>
<Button onClick={() => setShowReply(false)}>取消</Button>
</Col>
<Col>
<Button type="primary" htmlType="submit">
确认
</Button>
</Col>
</Row>
</Form.Item>
</Form>
</Drawer>
</div>
);
};
export default Audit;
import { useState, useEffect, forwardRef, useImperativeHandle } from "react";
import { Form, Space, Input, Select, Button, Row, Col, Checkbox } from "antd";
import { MinusOutlined, PlusOutlined } from "@ant-design/icons";
import { Editor, Toolbar } from "@wangeditor/editor-for-react";
import { DomEditor } from "@wangeditor/editor";
import { questionAdd, questionAddType, questionEdit } from "@/pages/books/question-bank/request";
import QuestionEditr from "@/pages/books/question-bank/questionEditr";
import "./index.less";
import dayjs from "dayjs";
import AliOSS from "ali-oss";
import { getAliOSSSTSToken } from "@/pages/setting/help/addedit/requet";
const CustomerTopic = (props, ref) => {
const { topicData, isadd, init, getTypeList, typeDate, setShowModal, bookIdList, typeId } = props;
// oss
const [ossClient, setOssClient] = useState(null); // oss 操作
const [STSToken, setSTSToken] = useState(null); // oss 过期设置
let ossClientTemp;
const getStsAuthToken = async () => {
const data = await getAliOSSSTSToken();
if (data) {
window.sessionStorage.setItem("sts", JSON.stringify(data));
setSTSToken(data);
ossClientTemp = await new AliOSS({
accessKeyId: data.AccessKeyId,
accessKeySecret: data.AccessKeySecret,
stsToken: data.SecurityToken,
endpoint: data.endpoint,
bucket: "zxts-book-file",
timeout: 180000,
refreshSTSToken: async () => {
const info = await getAliOSSSTSToken();
return {
AccessKeyId: info.AccessKeyId,
AccessKeySecret: info.AccessKeySecret,
SecurityToken: info.SecurityToken,
};
},
refreshSTSTokenInterval: 14 * 60 * 1000,
});
setOssClient(ossClientTemp);
}
};
useEffect(() => {
(async () => {
await getStsAuthToken();
})();
}, []);
useImperativeHandle(ref, () => {
return {};
});
const [form] = Form.useForm();
const questionType = Form.useWatch("question_style", form);
// 题干名称的编辑器实例
const [titlesEditor, setTitlesEditor] = useState(null);
const [quesList, setQuesList] = useState([]);
const [initialValues, setInitialValues] = useState({
titles: "",
type_id: null,
question_style: "",
analysis: "",
answers: "",
question_list: [],
book_id: "",
});
const [selectedTypeId, setSelectedTypeId] = useState([]);
useEffect(() => {
const getTypeNameById = (id) => {
const selectedType = props.typeDate.find((item) => item.id === id);
return selectedType ? selectedType.typename : "";
};
const matchedTypename = getTypeNameById(typeId);
setSelectedTypeId(matchedTypename);
}, [props.typeDate, typeId]);
useEffect(() => {
let obj = {
titles: "",
type_id: null,
question_style: "",
analysis: "",
answers: "",
question_list: [],
book_id: "",
};
if (!isadd && Object.entries(topicData)) {
obj = {
...topicData,
};
setQuestionStyle(obj.question_style);
if (topicData.question_style === 4 || topicData.question_style === 5) {
setedierVal(topicData.answers);
setQuesList([]);
} else {
if (topicData.question_list && topicData.question_list.length) {
try {
obj.question_list = JSON.parse(topicData.question_list);
setQuesList(JSON.parse(topicData.question_list));
} catch (e) {
obj.question_list = [];
setQuesList([]);
}
} else {
obj.question_list = [];
setQuesList([]);
}
}
setInitialValues({ ...obj });
form.setFieldsValue({ ...obj });
} else {
setInitialValues(obj);
form.setFieldsValue({ ...obj });
}
}, [topicData, isadd]);
// 显示不同类型的选项函数
const [edierVal, setedierVal] = useState("");
// 是否显示选项
const [showAddClass, setShowAddClass] = useState(false);
const [addClassVal, setAddClassVal] = useState(""); // 添加分类
const [questionStyle, setQuestionStyle] = useState("");
// 分类
const sureFn = async () => {
const bool = await questionAddType({ typename: addClassVal });
if (bool) {
await getTypeList();
form.setFieldsValue({ type_id: bool.type_id });
setInitialValues({ ...initialValues, type_id: [bool.type_id] });
setAddClassVal("");
setShowAddClass(false);
}
};
// 工具栏配置
const toolbarConfig = {};
toolbarConfig.toolbarKeys = ["uploadImage"];
// 编辑器配置
const editorConfig = {
// JS 语法
placeholder: "请输入内容...",
hoverbarKeys: {
text: {
menuKeys: [],
},
},
MENU_CONF: {
// 配置默认字号
// 配置上传图片
uploadImage: {
timeout: 5 * 1000, // 5s
fieldName: "image",
headers: {
"Content-Type": "multipart/form-data",
Authorization: "Bearer " + localStorage.getItem("token"),
},
maxFileSize: 10 * 1024 * 1024, // 10M
base64LimitSize: 5 * 1024, // 5kb 以下插入 base64
customUpload: async (file, insertFn) => {
if (!ossClient) {
console.error("ossClient还未初始化", ossClient);
return false;
}
const fileExt = file.name.substring(file.name.lastIndexOf("."));
const fileName = `ad-${dayjs().valueOf()}-${Math.random().toString(36).substring(2)}${fileExt}`;
const filePath = `${dayjs().format("YYYY/MM/DD")}/${fileName}`;
const result = await ossClient.put(filePath, file);
console.log("result", result);
insertFn(result.url, "题库图片");
},
},
},
customPaste: (editor, event) => {
// const html = event.clipboardData.getData('text/html') // 获取粘贴的 html
const text = event.clipboardData.getData("text/plain"); // 获取粘贴的纯文本
// const rtf = event.clipboardData.getData('text/rtf') // 获取 rtf 数据(如从 word wsp 复制粘贴)
// 异步
setTimeout(() => {
editor.insertText(text);
}, 500);
// 阻止默认的粘贴行为
event.preventDefault();
return false;
},
};
// 及时销毁 editor ,重要!
useEffect(() => {
if (titlesEditor !== null) {
const toolbar = DomEditor.getToolbar(titlesEditor);
}
return () => {
if (titlesEditor === null) return;
titlesEditor.destroy();
setTitlesEditor(null);
};
}, [titlesEditor]);
const submitForm = async (obj) => {
let ilen = 0;
let obj2 = { ...obj };
if ([1, 2, 3].includes(parseInt(obj.question_style))) {
obj.question_list.forEach((item, index) => {
if (item.correct && item.correct === true) {
ilen++;
}
});
if ([1, 3].includes(parseInt(obj.question_style))) {
if (ilen > 1 || ilen === 0) {
form.setFields([
{
name: ["question_list", obj.question_list.length - 1, "correct"],
errors: [
`${parseInt(obj.question_style) === 3 ? "判断题" : "单选题"}有且仅有一个正确答案!`,
],
},
]);
return false;
} else {
form.setFields([
{ name: ["question_list", obj.question_list.length - 1, "correct"], errors: [""] },
]);
}
} else {
if (ilen <= 1) {
form.setFields([
{
name: ["question_list", obj.question_list.length - 1, "correct"],
errors: ["多选题需要2个及以上正确答案!"],
},
]);
return false;
} else {
form.setFields([
{ name: ["question_list", obj.question_list.length - 1, "correct"], errors: [""] },
]);
}
}
obj2["question_list"] = JSON.stringify(quesList);
} else {
obj2["answers"] = edierVal;
}
setShowAddClass(true);
let bool = null;
if (isadd) {
bool = await questionAdd({
...obj2,
});
} else {
bool = await questionEdit({
...obj2,
id: topicData.id,
});
}
setShowAddClass(false);
if (!bool) return;
setShowModal(false);
init();
};
// 校验
const vaildaterHtml = (_, value) => {
if (value === "<p><br></p>") {
return Promise.reject("问题内容不能为空");
} else if (!value.trim()) {
return Promise.reject(new Error("请输入正确的内容"));
} else {
return Promise.resolve();
}
};
return (
<div className="practice_customer">
<Form
form={form}
initialValues={initialValues}
onFinish={submitForm}
labelCol={{ span: 5 }}
wrapperCol={{ span: 19 }}
>
<Form.Item
name="book_id"
label="书籍名称"
rules={[{ required: true, message: "请选择书籍名称" }]}
>
<Select
style={{ width: 260 }}
placeholder="请选择书籍名称"
showSearch //下拉框出现搜素,并可以输入
optionFilterProp="children" // 使搜索匹配子节点的文本内容
>
{bookIdList &&
bookIdList.length &&
bookIdList.map((item, index) => (
<Select.Option key={index} value={item.book_id} data={item}>
{item.name}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
name="question_style"
label="题目类型"
rules={[{ required: true, message: "请选择题目类型" }]}
>
<Select
onChange={(ev) => {
setQuestionStyle(ev);
}}
style={{ width: 260 }}
placeholder="请选择题目类型"
>
<Select.Option value={1}>单选题</Select.Option>
<Select.Option value={2}>多选题</Select.Option>
<Select.Option value={3}>判断题</Select.Option>
<Select.Option value={4}>填空题</Select.Option>
<Select.Option value={5}>简答题</Select.Option>
</Select>
</Form.Item>
<Form.Item
label="题目分类"
name="type_id"
rules={[{ required: true, message: "请选择题目分类" }]}
>
{!showAddClass ? (
<>
<Select
onChange={(ev) => {
form.setFieldValue("type_id", ev);
setSelectedTypeId(ev);
}}
style={{ width: 220 }}
placeholder="请选择题目分类"
value={selectedTypeId}
>
{typeDate &&
typeDate.length &&
typeDate.map((item) => (
<Select.Option key={item.id} value={item.id}>
{item.typename}
</Select.Option>
))}
</Select>
<Button
onClick={() => {
setShowAddClass(true);
form.setFieldsValue({ type_id: "" });
}}
type="link"
style={{ textDecoration: "underline", color: "#1672EC" }}
>
添加分类
</Button>
</>
) : (
<>
<Input
style={{ width: 200 }}
value={addClassVal}
onChange={(e) => setAddClassVal(e.target.value)}
autoComplete="off"
placeholder="请输入题目分类"
></Input>
<Space>
<Button
onClick={sureFn}
type="link"
style={{ textDecoration: "underline", color: "#1672EC" }}
>
添加
</Button>
<Button
type="link"
onClick={() => setShowAddClass(false)}
style={{ textDecoration: "underline", color: "#AA1941", marginLeft: -30 }}
>
取消
</Button>
</Space>
</>
)}
</Form.Item>
<Form.Item
name="titles"
label="题干名称"
rules={[
{ required: true, message: "请输入题干名称" },
{ validator: vaildaterHtml, validateTrigger: "onChange" },
]}
extra={questionType === 4 ? "请在需要填空的位置使用2个或以上的下划线进行区分!" : ""}
>
<div style={{ border: "1px solid #ccc", zIndex: 100 }}>
<Toolbar
editor={titlesEditor}
defaultConfig={toolbarConfig}
mode="default"
style={{ borderBottom: "1px solid #ccc" }}
/>
{ossClient && (
<Editor
defaultConfig={editorConfig}
value={initialValues.titles}
onCreated={setTitlesEditor}
onChange={(editor) => {
form.setFieldValue("titles", editor.getHtml());
}}
config={{
uploadImgServer: "your_upload_server_address",
}}
mode="default"
style={{ height: "200px", overflowY: "hidden" }}
/>
)}
</div>
</Form.Item>
<Form.Item name="analysis" label="题目解析">
<Input.TextArea
placeholder="请输入题目解析"
autoSize={{
minRows: 5,
maxRows: 8,
}}
name="analysis"
/>
</Form.Item>
{(questionStyle === 1 || questionStyle === 2 || questionStyle === 3) && (
<Form.List name="question_list">
{(fields, { add, remove }) => (
<Form.Item label="题目选项">
{fields.map(({ key, name, ...restField }, index) => (
<Row gutter={4} key={key}>
<Col span={14} className="form_inside-out">
<Form.Item className="inside-it">
<QuestionEditr
editorValue={quesList && quesList[index] && quesList[index].option}
quesList={quesList}
setQuesList={setQuesList}
index={index}
form={form}
/>
</Form.Item>
<Form.Item
{...restField}
name={[name, "option"]}
rules={[
{ required: true, message: "请输入内容" },
{
validator: (_, value) => {
if (value !== "") {
if (value === "<p><br></p>") {
return Promise.reject(new Error("请输入正确的内容"));
} else {
return Promise.resolve();
}
} else {
return Promise.reject(new Error(""));
}
},
},
]}
className="form_inside-out-none"
>
<Input.TextArea />
</Form.Item>
</Col>
{/* 加和减 */}
<Col span={7}>
<Form.Item
{...restField}
name={[name, "correct"]}
valuePropName="checked"
initialValue={restField.correct || false}
>
<Checkbox
onChange={(e) => {
if (quesList.length) {
let temp = JSON.parse(JSON.stringify(quesList));
if (questionStyle === 1 || questionStyle === 3) {
if (e.target.checked) {
temp.forEach((item) => {
item.correct = false;
});
}
}
temp[index].correct = e.target.checked;
setQuesList(temp);
form.setFieldValue("question_list", temp);
}
}}
>
正确答案
</Checkbox>
</Form.Item>
</Col>
<Col span={3}>
<Button
onClick={async () => {
let temp = JSON.parse(JSON.stringify(quesList));
temp.splice(index, 1);
setQuesList(temp);
await remove(name);
}}
icon={<MinusOutlined />}
></Button>
</Col>
</Row>
))}
{quesList.length < 2 && questionStyle === 3 && (
<Form.Item>
<Button
type="dashed"
onClick={async () => {
let tempQuestion = JSON.parse(JSON.stringify(quesList));
tempQuestion.push({ option: "", correct: false });
setQuesList(tempQuestion);
await add();
}}
icon={<PlusOutlined />}
></Button>
</Form.Item>
)}
{[1, 2].includes(questionStyle) && (
<Form.Item>
<Button
type="dashed"
onClick={async () => {
let tempQuestion = JSON.parse(JSON.stringify(quesList));
tempQuestion.push({ option: "", correct: false });
setQuesList(tempQuestion);
await add();
}}
icon={<PlusOutlined />}
></Button>
</Form.Item>
)}
</Form.Item>
)}
</Form.List>
)}
{(questionStyle === 5 || questionStyle === 4) && (
<Form.Item name="answers" label="正确答案">
<QuestionEditr
editorValue={edierVal}
setedierVal={setedierVal}
form={form}
name="answers"
/>
</Form.Item>
)}
<Form.Item wrapperCol={{ offset: 11, span: 16 }}>
<Space size={20}>
<Button onClick={() => setShowModal(false)}>取消</Button>
<Button type="primary" disabled={showAddClass} htmlType="submit">
提交
</Button>
</Space>
</Form.Item>
</Form>
</div>
);
};
export default forwardRef(CustomerTopic);
.practice_customer {
.form_inside-out {
.form_inside-out-none {
.ant-form-item-control-input {
display: none;
}
}
.inside-it {
margin-bottom: 0;
}
}
}
import React, { useEffect, useState } from "react";
import {
Modal,
Input,
Button,
Row,
Col,
Space,
DatePicker,
Drawer,
Form,
Select,
Image,
Popover,
Result,
Upload,
} from "antd";
const { RangePicker } = DatePicker;
const { TextArea } = Input;
import PaginationCom from "@/common/Pagination";
import reset from "@/assets/images/icon/reset.png";
import filter from "@/assets/images/icon/filter.png";
import add from "@/assets/images/icon/add.png";
import imports from "@/assets/images/icon/import.png";
import reload from "@/assets/images/icon/reload.png";
import TableCom from "@/common/TableCom/index";
import { useSelector } from "react-redux";
import FormTopocModal from "./components/form-drawer";
import {
getList,
questionAdd,
questionInfo,
questionEdit,
questionDel,
questionImport,
questionAddType,
typeList,
bookNameList,
} from "./request";
import { uploadFiles } from "@/utils/upload";
import "@wangeditor/editor/dist/css/style.css"; // 引入 css
import { Editor, Toolbar } from "@wangeditor/editor-for-react";
import { DomEditor } from "@wangeditor/editor";
import { MinusOutlined, PlusOutlined } from "@ant-design/icons";
import QuestionEditr from "./questionEditr/index";
import dayjs from "dayjs";
import success from "@/assets/images/success.png";
import err from "@/assets/images/err.png";
import { downloadFile } from "@/utils/common";
import "./index.less";
const Audit = () => {
const [data, setData] = useState([]);
const [page_size, setpage_size] = useState(10);
const [page, setPage] = useState(1);
const [total, setTotal] = useState(0);
const [loading, setLoading] = useState(true); // loading
const [showModal, setShowModal] = useState(false); //抽屉显示
const [isadd, setisadd] = useState(false); // 是否新增
const [delMoadal, setDelModal] = useState(false); // 删除确认
const [dateVal, setDateVal] = useState(""); // 过滤
// 获取操作权限
const { operationPermissionsList } = useSelector((state) => state.user);
const [form] = Form.useForm();
const [id, setid] = useState(0);
const [typeId, setTypeId] = useState(0);
const [filterObj, setfilterObj] = useState({
question_style: null,
type_id: null,
titles: "",
start_time: "",
end_time: "",
});
// 编辑
const [topicData, setTopicData] = useState({});
const handleEdit = async (obj) => {
const data = await questionInfo({ id: obj.id }); // 传递题目类型的 ID
if (data) {
const id = data.id; // 从返回的对象中提取 ID 值
setisadd(false);
setid(id); // 设置状态变量 id 的值
setTypeId(data.type_id); // 设置状态变量 id 的值
setShowModal(true);
setTopicData(data);
} else {
// 处理数据获取失败的情况
console.log("处理数据获取失败的情况");
}
};
const [typeDate, setTypeDate] = useState([]);
const [bookNameData, setbookNameData] = useState([]);
const [bookIdList, setbookIdList] = useState([]);
// 题目分类
const getTypeList = async () => {
const { list } = await typeList({ page: 1, page_size: 999 });
setTypeDate(list);
};
// 书籍名称;
const getbookNameList = async () => {
const res = await bookNameList({});
const nameList = res.map((item) => item.name);
setbookNameData(nameList);
setbookIdList(res);
};
useEffect(() => {
getTypeList();
getbookNameList();
}, []);
// 过滤
const handleFilter = (flag, val) => {
if (flag === "newCategory") {
setNewCategory(val);
} else {
if (flag === "startandend") {
let val2 = val || [];
filterObj["start_time"] = parseInt(
new Date(dayjs(val2[0]).format("YYYY-MM-DD") + " 00:00:00").getTime() / 1000,
);
const endDate = new Date(dayjs(val2[1])).getDate();
const endTime = parseInt(
new Date(dayjs(val2[1]).format("YYYY-MM-") + endDate + " 23:59:59").getTime() / 1000,
);
filterObj["end_time"] = endTime;
setDateVal(val);
if (!val2.length) {
delete filterObj["start_time"];
delete filterObj["end_time"];
}
} else {
filterObj[flag] = val;
}
for (const key in filterObj) {
if (!filterObj[key]) {
delete filterObj[key];
}
}
setfilterObj({ ...filterObj });
if (!val) init(filterObj);
}
};
const init = async (obj) => {
setLoading(true);
const { total, list } = await getList({ page, page_size, ...obj });
setTotal(total);
setData(list);
setLoading(false);
};
// 删除
const delSuccess = async () => {
const bool = await questionDel({ id });
bool && init();
setDelModal(false);
};
const handleReset = () => {
setfilterObj({ question_style: null, type_id: null, titles: "" });
setDateVal("");
init();
};
// 控制是否显示题目分类输入框和添加和删除按钮
const [newCategory, setNewCategory] = useState("");
// eslint-disable-next-line no-sparse-arrays
const columns = [
{
title: "ID",
key: "id",
dataIndex: "id",
align: "center",
},
{
title: "书籍名称",
key: "book_name",
dataIndex: "book_name",
align: "center",
width: 250,
render: (text) => (
<Popover
overlayStyle={{ maxWidth: "500px" }}
content={
<div
className="quesName"
style={{ textAlign: "center" }}
dangerouslySetInnerHTML={{ __html: text }}
></div>
}
>
<div
className="quesName"
style={{
display: "-webkit-box",
WebkitLineClamp: "3",
WebkitBoxOrient: "vertical",
overflow: "hidden",
textOverflow: "ellipsis",
}}
dangerouslySetInnerHTML={{ __html: text }}
></div>
</Popover>
),
},
,
{
title: "题目名称",
key: "titles",
dataIndex: "titles",
align: "center",
width: 250,
render: (text) => (
<Popover
overlayStyle={{ maxWidth: "500px" }}
content={
<div
className="quesName"
style={{ textAlign: "center" }}
dangerouslySetInnerHTML={{ __html: text }}
></div>
}
>
<div
className="quesName"
style={{
display: "-webkit-box",
WebkitLineClamp: "3",
WebkitBoxOrient: "vertical",
overflow: "hidden",
textOverflow: "ellipsis",
}}
dangerouslySetInnerHTML={{ __html: text }}
></div>
</Popover>
),
},
{
title: "题目分类",
key: "type_id",
dataIndex: "type_id",
align: "center",
render(_, { type_id }) {
let title =
typeDate && typeDate.length && typeDate.find((item) => item.id === type_id).typename;
return title;
},
},
{
title: "题目类型",
key: "question_style",
dataIndex: "question_style",
align: "center",
render(_, record) {
switch (record.question_style) {
case 1:
return "单选题";
case 2:
return "多选题";
case 3:
return "判断题";
case 4:
return "填空题";
case 5:
return "简答题";
default:
return "-";
}
},
},
{
title: "创建时间",
key: "create_time",
dataIndex: "create_time",
align: "center",
},
{
title: "更新时间",
key: "update_time",
dataIndex: "update_time",
align: "center",
},
{
title: "操作",
key: "handle",
dataIndex: "handle",
render: (_, record) => {
return (
<Space>
{operationPermissionsList.includes("/books/question-bank/getInfoById") && (
<Button onClick={() => handleEdit(record)}>编辑</Button>
)}
{operationPermissionsList.includes("/books/question-bank/del") && (
<Button
onClick={() => {
setid(record.id);
setDelModal(true);
}}
>
删除
</Button>
)}
</Space>
);
},
},
];
// 上传弹框
const [uploadModal, setUploadModal] = useState(false);
const [form2] = Form.useForm(null);
useEffect(() => {
if (!uploadModal) {
form2.resetFields();
form2.setFieldsValue({ file: "" });
setimportFlag(false);
setFile(null);
}
}, [uploadModal]);
const UploadProps = {
name: "file",
accept: ".xlsx",
showUploadList: false,
beforeUpload(file) {
const suffixArr = ["xlsx"];
const suffix = file.name.substring(file.name.lastIndexOf(".") + 1);
if (!suffixArr.includes(suffix)) {
message.error(`请上传以${suffixArr.join("、")}的敏感词文件`);
return false;
}
setFile(file);
return false;
},
};
const [file, setFile] = useState("");
const [importRes, setimportRes] = useState(false);
const [importMsg, setimportMsg] = useState(false);
const [importFlag, setimportFlag] = useState(false);
const [importErrFile, setImportErrFile] = useState("");
const handleUpload = async () => {
const formData = new FormData();
formData.append("file", file);
const {
data: { err, name },
msg,
} = await questionImport(formData);
if (err === 0) {
setimportRes(true);
} else {
setimportRes(false);
setImportErrFile(name);
}
setimportMsg(msg);
setimportFlag(true);
};
// 下载模板
const handleDownload = async () => {
downloadFile("题库导入示例.xlsx");
};
return (
<div className="question-bank">
<Row justify={"space-between"}>
<Col span={21} className="form-devices-inline">
<Form layout="inline">
<Form.Item label="书籍名称">
<Select
value={filterObj.book_id}
onChange={(ev) => handleFilter("book_id", ev)}
placeholder="请输入书籍名称"
allowClear
id="classify"
style={{ width: "200px" }}
showSearch //下拉框出现搜素,并可以输入
optionFilterProp="children" // 使搜索匹配子节点的文本内容
>
{" "}
{bookIdList &&
bookIdList.length &&
bookIdList.map((item, index) => (
<Select.Option key={index} value={item.book_id}>
{item.name}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item label="题干名称">
<Input
autoComplete="off"
allowClear
value={filterObj.titles}
onChange={(ev) => handleFilter("titles", ev.target.value)}
placeholder="请输入题干名称"
id="name"
></Input>
</Form.Item>
<Form.Item label="题目分类">
<Select
value={filterObj.type_id}
onChange={(ev) => handleFilter("type_id", ev)}
placeholder="请选择题目分类"
allowClear
id="classify"
style={{ width: "200px" }}
>
{typeDate &&
typeDate.length &&
typeDate.map((item) => (
<Select.Option key={item.id} value={item.id}>
{item.typename}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item label="题目类型">
<Select
value={filterObj.question_style}
onChange={(ev) => handleFilter("question_style", ev)}
placeholder="请选择题目分类"
allowClear
style={{ width: "200px" }}
id="type"
>
<Select.Option value={1}>单选</Select.Option>
<Select.Option value={2}>多选</Select.Option>
<Select.Option value={3}>判断</Select.Option>
<Select.Option value={4}>填空</Select.Option>
<Select.Option value={5}>简答</Select.Option>
</Select>
</Form.Item>
<Form.Item label="创建时间">
<RangePicker
value={dateVal}
onChange={(ev) => handleFilter("startandend", ev)}
id="createTime"
></RangePicker>
</Form.Item>
<Space>
<Button
type="primary"
ghost
icon={
<span
style={{
display: "inline-block",
width: "11px",
height: "10px",
pointerEvents: "none",
}}
>
<Image src={reset} />
</span>
}
onClick={handleReset}
>
重置
</Button>
<Button
type="primary"
icon={
<span
style={{
display: "inline-block",
width: "13px",
height: "12px",
pointerEvents: "none",
}}
>
<Image src={filter} />
</span>
}
ghost
onClick={() => {
setPage(1);
Object.values(filterObj).some((item) => item) ? init(filterObj) : init();
}}
>
筛选
</Button>
<Button
icon={
<span
style={{
display: "inline-block",
width: "10px",
height: "12px",
pointerEvents: "none",
}}
>
<Image src={reload} />
</span>
}
type="primary"
ghost
onClick={() =>
Object.values(filterObj).some((item) => item) ? init(filterObj) : init()
}
>
刷新
</Button>
</Space>
</Form>
</Col>
<Col>
<Space>
{operationPermissionsList.includes("/books/question-bank/add") && (
<Button
type="primary"
icon={
<span
style={{
display: "inline-block",
width: "13px",
height: "12px",
pointerEvents: "none",
}}
>
<Image src={add} />
</span>
}
ghost
onClick={() => {
setisadd(true);
setShowModal(true);
}}
>
添加
</Button>
)}
{operationPermissionsList.includes("/books/question-bank/import") && (
<Button
type="primary"
icon={
<span
style={{
display: "inline-block",
width: "11px",
height: "12px",
pointerEvents: "none",
}}
>
<Image src={imports} />
</span>
}
onClick={() => setUploadModal(true)}
ghost
>
导入
</Button>
)}
</Space>
</Col>
</Row>
<br />
<Drawer
mask={false}
centered
footer={null}
open={uploadModal}
onCancel={() => setUploadModal(false)}
onClose={() => setUploadModal(false)}
>
{!importFlag ? (
<>
<Form onFinish={handleUpload} form={form2}>
<div style={{ paddingLeft: 75 }}>
<Form.Item label="下载模板" style={{ marginBottom: 10 }}>
<Button ghost type="primary" onClick={handleDownload}>
下载模板
</Button>
</Form.Item>
<p
className="linetext"
style={{ fontWeight: 400, color: "#999999", fontSize: 12, marginBottom: 30 }}
>
1.请勿修改模板表头,防止导入失败;2.按照示例填写
</p>
<Form.Item
style={{ marginLeft: -10 }}
label="导入模板"
name="file"
rules={[{ required: true, message: "请选择文件" }]}
>
<Upload {...UploadProps}>
<Button ghost type="primary">
选择文件
</Button>
<p style={{ fontSize: 12, marginTop: 8 }}>{file?.name}</p>
</Upload>
</Form.Item>
</div>
<Form.Item>
<Row gutter={20} justify={"center"}>
<Col>
<Button onClick={() => setUploadModal(false)}>取消</Button>
</Col>
<Col>
<Button type="primary" htmlType="submit">
确认
</Button>
</Col>
</Row>
</Form.Item>
</Form>
</>
) : (
<Result
status={importRes ? "success" : "error"}
icon={<img width={60} src={importRes ? success : err} />}
subTitle={<p style={{ color: importRes ? "#029b00" : "#aa1941" }}>{importMsg}</p>}
extra={[
!importRes && (
<Button
type="primary"
key="err"
ghost
onClick={() => {
downloadFile(`temp/${importErrFile}`);
}}
>
下载导入失败的数据
</Button>
),
<Button
type="primary"
key="finsh"
onClick={() => {
setUploadModal(false);
}}
>
完成
</Button>,
]}
/>
)}
</Drawer>
<Modal
mask={false}
centered
open={delMoadal}
onOk={delSuccess}
onCancel={() => setDelModal(false)}
footer={(_, { OkBtn, CancelBtn }) => {
return (
<Row justify={"center"} className="delModal" style={{ marginBottom: "30px" }}>
<Space size={20}>
<CancelBtn />
<OkBtn />
</Space>
</Row>
);
}}
okText="确认"
>
<p style={{ textAlign: "center", padding: "30px 0 6px", fontSize: 16 }}>
题目将永久删除,是否继续?
</p>
</Modal>
{TableCom({ columns, loading, data })}
<br />
<PaginationCom
total={total}
page_size={page_size}
setpage_size={setpage_size}
page={page}
setPage={setPage}
init={init}
filterObj={filterObj}
/>
<Drawer
placement="right"
destroyOnHidden
onClose={() => setShowModal(false)}
open={showModal}
mask={false}
width="600px"
>
<FormTopocModal
isadd={isadd}
typeDate={typeDate}
bookIdList={bookIdList}
bookNameData={bookNameData}
getTypeList={getTypeList}
topicData={topicData}
init={init}
typeId={typeId}
setShowModal={setShowModal}
/>
</Drawer>
</div>
);
};
export default Audit;
.question-bank {
.quesName {
p {
font-family:
-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans",
sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji" !important;
color: rgba(0, 0, 0, 0.88) !important;
span {
font-family:
-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial,
"Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol",
"Noto Color Emoji" !important;
color: rgba(0, 0, 0, 0.88) !important;
}
}
}
.quesName img {
width: 60px !important;
}
}
.ant-popover-content img {
max-width: 480px !important;
max-height: 100px !important;
}
.ant-popover-content span,
.ant-popover-content img {
vertical-align: middle;
}
import { useState, useEffect, useRef } from "react";
import { Spin } from "antd";
import "@wangeditor/editor/dist/css/style.css"; // 引入 css
import { Editor, Toolbar } from "@wangeditor/editor-for-react";
import { DomEditor } from "@wangeditor/editor";
import { uploadFiles } from "@/utils/upload";
import AliOSS from "ali-oss";
import dayjs from "dayjs";
import { getAliOSSSTSToken } from "../request";
const QuestionEditr = (props) => {
const [ossClient, setOssClient] = useState(null); // oss 操作
const [STSToken, setSTSToken] = useState(null); // oss 过期设置
let ossClientTemp;
const getStsAuthToken = () => {
return new Promise(async (resolve) => {
const data = await getAliOSSSTSToken();
if (data) {
window.sessionStorage.setItem("sts", JSON.stringify(data));
setSTSToken(data);
ossClientTemp = await new AliOSS({
accessKeyId: data.AccessKeyId,
accessKeySecret: data.AccessKeySecret,
stsToken: data.SecurityToken,
endpoint: data.endpoint,
bucket: "zxts-book-file",
timeout: 180000,
refreshSTSToken: async () => {
const info = await getAliOSSSTSToken();
return {
AccessKeyId: info.AccessKeyId,
AccessKeySecret: info.AccessKeySecret,
SecurityToken: info.SecurityToken,
};
},
refreshSTSTokenInterval: 14 * 60 * 1000,
});
setOssClient(ossClientTemp);
// console.log('AliOSS', ossClientTemp);
}
resolve();
});
};
useEffect(() => {
getStsAuthToken();
}, []);
const {
editorValue = "",
quesList = [],
setQuesList,
index,
setedierVal,
form = null,
name = "",
} = props;
const timeout = useRef();
// 题干名称的编辑器实例
const [titlesEditor, setTitlesEditor] = useState(null);
// 工具栏配置
const toolbarConfig = {};
toolbarConfig.toolbarKeys = ["uploadImage"];
const [loading, setLoading] = useState(true);
const [tempQuestion, setTempQuestion] = useState(quesList);
// 编辑器配置
const editorConfig = {
// JS 语法
placeholder: "请输入内容...",
hoverbarKeys: {
text: {
menuKeys: [],
},
},
MENU_CONF: {
// 配置默认字号
// 配置上传图片
uploadImage: {
timeout: 5 * 1000, // 5s
fieldName: "image",
headers: {
"Content-Type": "multipart/form-data",
Authorization: "Bearer " + localStorage.getItem("token") || "",
},
maxFileSize: 10 * 1024 * 1024, // 10M
base64LimitSize: 5 * 1024, // 5kb 以下插入 base64
// 用户自定义上传图片
customUpload: async (file, insertFn) => {
setLoading(true);
if (!ossClient) {
console.error("ossClient还未初始化", ossClient);
return false;
}
const fileExt = file.name.substring(file.name.lastIndexOf("."));
const fileName = `practice-${dayjs().valueOf()}-${Math.random().toString(36).substring(2)}${fileExt}`;
const filePath = `${dayjs().format("YYYY/MM/DD")}/${fileName}`;
const result = await ossClient.put(filePath, file);
console.log("result", result);
insertFn(result.url, "题库图片");
// const { url } = await uploadFiles({ file, file_type: 'question' });
// insertFn(url, '题库图片');
setLoading(false);
},
},
customPaste: (editor, event) => {
// const html = event.clipboardData.getData('text/html') // 获取粘贴的 html
const text = event.clipboardData.getData("text/plain"); // 获取粘贴的纯文本
// const rtf = event.clipboardData.getData('text/rtf') // 获取 rtf 数据(如从 word wsp 复制粘贴)
// 异步
setTimeout(() => {
editor.insertText(text);
}, 500);
// 阻止默认的粘贴行为
event.preventDefault();
return false;
},
},
customPaste: (editor, event) => {
// const html = event.clipboardData.getData('text/html') // 获取粘贴的 html
const text = event.clipboardData.getData("text/plain"); // 获取粘贴的纯文本
// const rtf = event.clipboardData.getData('text/rtf') // 获取 rtf 数据(如从 word wsp 复制粘贴)
// 异步
setTimeout(() => {
editor.insertText(text);
}, 500);
// 阻止默认的粘贴行为
event.preventDefault();
return false;
},
};
// 及时销毁 editor ,重要!
useEffect(() => {
if (titlesEditor) {
clearTimeout(timeout.current);
setLoading(false);
}
return () => {
if (titlesEditor === null) return;
titlesEditor.destroy();
setTitlesEditor(null);
};
}, [titlesEditor]);
return (
<div style={{ border: "1px solid #ccc", zIndex: 100 }}>
<Toolbar
editor={titlesEditor}
defaultConfig={toolbarConfig}
mode="default"
style={{ borderBottom: "1px solid #ccc" }}
/>
<Spin spinning={loading}>
<Editor
defaultConfig={editorConfig}
value={editorValue}
onCreated={setTitlesEditor}
onChange={(editor) => {
if (name) {
form.setFieldsValue({ [name]: editor.getHtml() });
setedierVal(editor.getHtml());
} else {
clearTimeout(timeout.current);
timeout.current = setTimeout(() => {
let obj = { option: "", correct: false };
if (editor.getHtml() === "<p><br></p>") {
tempQuestion[index] = obj;
} else {
obj.option = editor.getHtml();
tempQuestion[index] = obj;
}
setQuesList(tempQuestion);
setTempQuestion(tempQuestion);
form.setFieldsValue({ question_list: tempQuestion });
}, 100);
}
}}
config={{
uploadImgServer: "your_upload_server_address",
}}
mode="default"
style={{ height: "100px", overflowY: "hidden" }}
/>
</Spin>
</div>
);
};
export default QuestionEditr;
import axios from "@/utils/request.js";
import qs from "qs";
import { get } from "lodash-es";
import { message } from "antd";
// 题目列表
const getList = async (obj) => {
const res = await axios.post("/book/question/getList", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
};
// add
const questionAdd = async (obj) => {
const res = await axios.post("/book/question/add", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
};
// info
const questionInfo = async (obj) => {
const res = await axios.post("/book/question/getInfoById", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", {});
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
};
// edit
const questionEdit = async (obj) => {
const res = await axios.post("/book/question/edit", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
};
// const questionEdit = async (obj) => {
// const res = await axios.post('/book/question/edit', qs.stringify(obj));
// const code = get(res, 'data.code', 0);
// const data = get(res, 'data.data', []);
// const msg = get(res, 'data.message', '数据获取失败!');
// if (code === 200) {
// // const id = get(data, 'id', null);
// message.success(msg);
// return true;
// // return id; // 返回题目的 ID
// } else {
// message.error(msg);
// return null; // 或者返回适当的错误值
// }
// };
// del
const questionDel = async (obj) => {
const res = await axios.post("/book/question/del", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
};
// 导入题库
const questionImport = async (obj) => {
const res = await axios.post("/book/question/import", obj, {
headers: {
"Content-Type": " multipart/form-data",
},
});
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return { msg, data };
} else {
message.error(msg);
return false;
}
};
// 添加题目分类
const questionAddType = async (obj) => {
const res = await axios.post("/book/question/type/add", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return data;
} else {
message.error(msg);
return false;
}
};
// 题目分类列表
const typeList = async (obj) => {
const res = await axios.post("/book/question/type/getList", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
};
// 书籍名称下拉列表
const bookNameList = async (obj) => {
const res = await axios.post("book/teacher/getListAll", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
};
const getAliOSSSTSToken = async (obj) => {
try {
const res = await axios.post("/common/OssUpload/getUploadToken", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "获取OSS令牌失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
export {
getList,
questionAdd,
questionInfo,
questionEdit,
questionDel,
questionImport,
questionAddType,
typeList,
getAliOSSSTSToken,
bookNameList,
};
export { default } from "@/modules/books/sale/components/SaleDiscussionDetail";
@import "@/modules/books/sale/components/SaleDiscussionDetail.less";
import React, { useEffect, useState } from "react";
import { Table, Input, Button, Row, Col, Space, DatePicker, Image } from "antd";
const { RangePicker } = DatePicker;
import { useNavigate } from "react-router";
import PaginationCom from "@/common/Pagination";
import { getCommentList } from "../request";
import reset from "@/assets/images/icon/reset.png";
import filter from "@/assets/images/icon/filter.png";
import reload from "@/assets/images/icon/reload.png";
import { useLocation } from "react-router";
import dayjs from "dayjs";
import { get } from "lodash-es";
import TableCom from "@/common/TableCom/index";
const DiscussList = () => {
const [data, setData] = useState([]);
const { state = {} } = useLocation();
const book_id = get(state, "id", "");
const name = get(state, "name", "");
const navigate = useNavigate();
const [page_size, setpage_size] = useState(10);
const [page, setPage] = useState(1);
const [total, setTotal] = useState(0);
const [loading, setLoading] = useState(true);
const [id, setId] = useState(0);
const [filterObj, setfilterObj] = useState({
book_id: null,
title: "",
user_name: "",
start_time: "",
end_time: "",
});
const [dateVal, setDateVal] = useState("");
// 重置
const handleReset = () => {
setfilterObj({ name: "", authors: "", audit_status: "1" });
setDateVal("");
init();
};
const init = async (obj = {}) => {
setLoading(true);
const { total, list } = await getCommentList({ page, page_size, ...obj, book_id });
setTotal(total);
setData(list);
setLoading(false);
};
// 过滤
const handleFilter = (flag, val) => {
if (flag == "startandend") {
let val2 = val || [];
filterObj["start_time"] = parseInt(
new Date(dayjs(val2[0]).format("YYYY-MM-DD") + " 00:00:00").getTime() / 1000,
);
const endDate = new Date(dayjs(val2[1])).getDate();
const endTime = parseInt(
new Date(dayjs(val2[1]).format("YYYY-MM-") + endDate + " 23:59:59").getTime() / 1000,
);
filterObj["end_time"] = endTime;
setDateVal(val);
if (!val2.length) {
delete filterObj["start_time"];
delete filterObj["end_time"];
}
} else {
filterObj[flag] = val;
}
for (const key in filterObj) {
if (!filterObj[key]) {
delete filterObj[key];
}
}
setfilterObj({ ...filterObj });
if (!val) init(filterObj);
};
const columns = [
{
title: "话题",
key: "title",
dataIndex: "title",
width: 200,
},
{
title: "发起人",
key: "name",
dataIndex: "name",
},
{
title: "发起时间",
key: "create_time",
dataIndex: "create_time",
},
{
title: "操作",
key: "handle",
dataIndex: "handle",
render: (_, { id }) => {
return (
<Space>
<Button
onClick={() =>
navigate("/books/sale/discuss-detail", {
state: { id, name, book_id },
})
}
>
查看详情
</Button>
</Space>
);
},
},
];
return (
<div>
<Row justify={"space-between"}>
<Col>
<Space>
<label htmlFor="title">话题</label>
<Input
autoComplete="off"
placeholder="请输入话题"
id="title"
value={filterObj.title}
onChange={(ev) => handleFilter("title", ev.target.value)}
></Input>
<label htmlFor="user_name">发起人</label>
<Input
autoComplete="off"
placeholder="请输入发起人"
id="user_name"
value={filterObj.user_name}
onChange={(ev) => handleFilter("user_name", ev.target.value)}
></Input>
<label htmlFor="createTime">发起时间</label>
<RangePicker
id="createTime"
value={filterObj.startandend}
onChange={(ev) => handleFilter("startandend", ev)}
></RangePicker>
<Button
type="primary"
ghost
icon={
<span
style={{
display: "inline-block",
width: "11px",
height: "10px",
pointerEvents: "none",
}}
>
<Image src={reset} />
</span>
}
onClick={handleReset}
>
重置
</Button>
<Button
type="primary"
ghost
onClick={() => {
setPage(1);
Object.values(filterObj).some((item) => item) ? init(filterObj) : init();
}}
icon={
<span
style={{
display: "inline-block",
width: "13px",
height: "12px",
pointerEvents: "none",
}}
>
<Image src={filter} />
</span>
}
>
筛选
</Button>
<Button
type="primary"
ghost
icon={
<span
style={{
display: "inline-block",
width: "10px",
height: "12px",
pointerEvents: "none",
}}
>
<Image src={reload} />
</span>
}
onClick={() =>
Object.values(filterObj).some((item) => item) ? init(filterObj) : init()
}
>
刷新
</Button>
</Space>
</Col>
<Col></Col>
</Row>
<br />
{/* <Table
scroll={{
y: 600,
x: true,
}}
columns={columns}
rowKey={(record) => record.id}
dataSource={data}
loading={loading}
pagination={false}
></Table> */}
{TableCom({ columns, loading, data })}
<br />
<PaginationCom
total={total}
page_size={page_size}
setpage_size={setpage_size}
page={page}
setPage={setPage}
init={init}
filterObj={filterObj}
/>
</div>
);
};
export default DiscussList;
export { default } from "@/modules/books/sale/components/SaleEdit";
import { Outlet } from "react-router";
const BookSale = () => {
return (
<div>
<Outlet />
</div>
);
};
export default BookSale;
import axios from "@/utils/request.js";
import qs from "qs";
import { get } from "lodash-es";
import { message } from "antd";
// 在售列表
const getList = async (obj) => {
const res = await axios.post("/book/book/getList", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
};
// 上下架
const changeStatus = async (obj) => {
const res = await axios.post("/book/book/changeStatus", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
};
// 章节列表
const getListAll = async (obj) => {
const res = await axios.post("/book/chapter/getListAll", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
};
// 详情
const getInfoById = async (obj) => {
const res = await axios.post("/book/book/getInfoById", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
};
// 出品方列表
const publisherList = async (obj) => {
const res = await axios.post("/book/publisher/getListAll", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
};
// 出品方添加
const publisherAdd = async (obj) => {
const res = await axios.post("/book/publisher/add", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
};
// 编辑
const editBook = async (obj) => {
try {
const res = await axios.post("/book/book/edit", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 话题列表
const getCommentList = async (obj) => {
try {
const res = await axios.post("/comment/comment/getCommentList", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 话题详情列表
const getCommentInfo = async (obj) => {
try {
const res = await axios.post("/comment/comment/getCommentInfo", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 删除讨论
const commentDel = async (obj) => {
try {
const res = await axios.post("/comment/comment/del", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 回复讨论
const commentReply = async (obj) => {
try {
const res = await axios.post("/comment/comment/reply", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
export {
getList,
changeStatus,
getListAll,
getInfoById,
publisherList,
publisherAdd,
editBook,
getCommentList,
getCommentInfo,
commentDel,
commentReply,
};
import React, { useEffect, useState } from "react";
import {
Table,
Input,
Button,
Row,
Col,
Space,
DatePicker,
Image,
Drawer,
Form,
Select,
Modal,
} from "antd";
const { RangePicker } = DatePicker;
import { useNavigate } from "react-router";
import PaginationCom from "@/common/Pagination";
import { getList, changeStatus } from "./request";
import reset from "@/assets/images/icon/reset.png";
import filter from "@/assets/images/icon/filter.png";
import reload from "@/assets/images/icon/reload.png";
import add from "@/assets/images/icon/add.png";
import imports from "@/assets/images/icon/import.png";
import TableCom from "@/common/TableCom/index";
import dayjs from "dayjs";
import { useSelector } from "react-redux";
const Audit = () => {
const [data, setData] = useState([]);
const [page_size, setpage_size] = useState(10);
const [page, setPage] = useState(1);
const [total, setTotal] = useState(0);
const [loading, setLoading] = useState(true);
const [isEdit, setIsEdit] = useState(false);
const navigate = useNavigate();
// 是否上架
const [isShelf, setIsShelf] = useState(true);
const [showShelf, setShowShelf] = useState(false);
// 获取操作权限
const { operationPermissionsList } = useSelector((state) => state.user);
const [form] = Form.useForm();
const [id, setId] = useState(0);
const [filterObj, setfilterObj] = useState({
name: "",
authors: "",
status: null,
start_time: "",
end_time: "",
});
const [dateVal, setDateVal] = useState("");
// 重置
const handleReset = () => {
setfilterObj({ name: "", authors: "", audit_status: "1" });
setDateVal("");
init();
};
const init = async (obj = {}) => {
setLoading(true);
const { total, list } = await getList({ page, page_size, ...obj });
setTotal(total);
setData(list);
setLoading(false);
};
// 过滤
const handleFilter = (flag, val) => {
if (flag == "startandend") {
let val2 = val || [];
filterObj["start_time"] = parseInt(
new Date(dayjs(val2[0]).format("YYYY-MM-DD") + " 00:00:00").getTime() / 1000,
);
const endDate = new Date(dayjs(val2[1])).getDate();
const endTime = parseInt(
new Date(dayjs(val2[1]).format("YYYY-MM-") + endDate + " 23:59:59").getTime() / 1000,
);
filterObj["end_time"] = endTime;
setDateVal(val);
if (!val2.length) {
delete filterObj["start_time"];
delete filterObj["end_time"];
}
} else {
filterObj[flag] = val;
}
for (const key in filterObj) {
if (!filterObj[key]) {
delete filterObj[key];
}
}
setfilterObj({ ...filterObj });
if (!val) init(filterObj);
};
const onClose = () => {
setIsEdit(false);
};
const submitForm = (obj) => {
console.log(obj);
};
const shelfSuccess = async () => {
const bool = await changeStatus({ id, status: isShelf ? "0" : "1" });
setShowShelf(false);
if (!bool) return;
init();
};
// 将 0 转换成 "下架",将 1 转换成 "上架"
const renderStatus = (status) => {
return status === 0 ? "下架" : status === 1 ? "上架" : "";
};
const columns = [
{
title: "ID",
key: "id",
align: "center",
dataIndex: "id",
},
{
title: "书籍图片",
key: "img",
align: "center",
dataIndex: "img",
render: (_, { img }) => (
<Image
width={80}
height={80}
src={img}
fallback="data:image/png;base64,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"
/>
),
},
{
title: "书籍名称",
key: "name",
dataIndex: "name",
align: "center",
},
{
title: "作者",
key: "authors",
dataIndex: "authors",
align: "center",
},
{
title: "类型",
key: "status",
dataIndex: "status",
align: "center",
render: (text, record) => renderStatus(text),
},
{
title: "零售价",
key: "price",
dataIndex: "price",
align: "center",
},
{
title: "会员价",
key: "vip_price",
align: "center",
dataIndex: "vip_price",
},
{
title: "创建时间",
key: "create_time",
dataIndex: "create_time",
align: "center",
},
{
title: "上架时间",
key: "onsale_time",
dataIndex: "onsale_time",
align: "center",
render: (_, record) => {
switch (record.onsale_time) {
case 0:
return "--";
default:
return record.onsale_time;
}
},
},
{
title: "下架时间",
key: "offshelves_time",
dataIndex: "offshelves_time",
align: "center",
render: (_, record) => {
switch (record.offshelves_time) {
case 0:
return "--";
default:
return record.offshelves_time;
}
},
},
// {
// title: '操作',
// key: 'handle',
// dataIndex: 'handle',
// render: (_, { status, id, name }) => {
// let txt = status ? '下架' : '上架';
// return (
// <Space size={8}>
// {status ? (
// <Button
// onClick={() => {
// navigate('/books/sale/discussP', {
// state: { id, name },
// });
// }}
// >
// 讨论
// </Button>
// ) : (
// ''
// )}
// {!status ? (
// <Button
// onClick={() => {
// navigate('/books/sale/detail', {
// state: { id },
// });
// }}
// >
// 编辑
// </Button>
// ) : (
// ''
// )}
// <Button
// onClick={() => {
// setId(id);
// setIsShelf(status);
// setShowShelf(true);
// }}
// >
// {txt}
// </Button>
// </Space>
// );
// },
// },
{
title: "操作",
key: "handle",
dataIndex: "handle",
render: (_, { status, id, name }) => {
let txt = status ? "下架" : "上架";
const buttons = []; // 定义一个空数组,用于存储需要渲染的按钮
if (status && operationPermissionsList.includes("/books/sale/getCommentList")) {
// 当商品状态为下架时
buttons.push(
// 将 "讨论" 按钮添加到数组中
<Button
key="discuss"
onClick={() => {
navigate("/books/sale/discussP", {
state: { id, name },
});
}}
>
讨论
</Button>,
);
} else if (operationPermissionsList.includes("/books/sale/getInfoById")) {
// 当商品状态为上架时
buttons.push(
// 将 "编辑" 按钮添加到数组中
<Button
key="edit"
onClick={() => {
navigate("/books/sale/detail", {
state: { id },
});
}}
>
编辑
</Button>,
);
}
if (operationPermissionsList.includes("/books/sale/changeStatus")) {
// 将 "下架" 按钮添加到数组中
buttons.push(
<Button
key="shelf"
onClick={() => {
setId(id);
setIsShelf(status);
setShowShelf(true);
}}
>
{txt}
</Button>,
);
}
return <Space size={8}>{buttons}</Space>; // 将所有按钮包装在一个 Space 组件中,并设置相同的间距
},
},
];
return (
<div>
<Row justify={"space-between"}>
<Col span={21} className="form-devices-inline">
<Form layout="inline">
<Form.Item label="书籍名称">
<Input
autoComplete="off"
allowClear
value={filterObj.name}
onChange={(ev) => handleFilter("name", ev.target.value)}
placeholder="请输入书籍名称"
id="name"
></Input>
</Form.Item>
<Form.Item label="作者">
<Input
autoComplete="off"
allowClear
value={filterObj.authors}
onChange={(ev) => handleFilter("authors", ev.target.value)}
placeholder="请输入作者"
id="author"
></Input>
</Form.Item>
<Form.Item label="上架状态">
<Select
value={filterObj.status}
onChange={(ev) => handleFilter("status", ev)}
placeholder="请选择上架状态"
id="status"
allowClear
style={{ width: 200 }}
>
<Select.Option value={1}>上架</Select.Option>
<Select.Option value={"0"}>下架</Select.Option>
</Select>
</Form.Item>
<Form.Item label="创建时间">
<RangePicker
value={dateVal}
onChange={(ev) => handleFilter("startandend", ev)}
id="createTime"
></RangePicker>
</Form.Item>
<Space>
<Button
type="primary"
ghost
icon={
<span
style={{
display: "inline-block",
width: "11px",
height: "10px",
pointerEvents: "none",
}}
>
<Image src={reset} />
</span>
}
onClick={handleReset}
>
重置
</Button>
<Button
type="primary"
ghost
onClick={() => {
setPage(1);
Object.values(filterObj).some((item) => item) ? init(filterObj) : init();
}}
icon={
<span
style={{
display: "inline-block",
width: "13px",
height: "12px",
pointerEvents: "none",
}}
>
<Image src={filter} />
</span>
}
>
筛选
</Button>
<Button
type="primary"
ghost
icon={
<span
style={{
display: "inline-block",
width: "10px",
height: "12px",
pointerEvents: "none",
}}
>
<Image src={reload} />
</span>
}
onClick={() =>
Object.values(filterObj).some((item) => item) ? init(filterObj) : init()
}
>
刷新
</Button>
</Space>
</Form>
</Col>
<Col span={1}> </Col>
</Row>
<br />
<Modal
mask={false}
centered
open={showShelf}
onOk={shelfSuccess}
onCancel={() => setShowShelf(false)}
footer={(_, { OkBtn, CancelBtn }) => {
return (
<Row justify={"center"} className="delModal" style={{ marginBottom: "30px" }}>
<Space size={20}>
<CancelBtn />
<OkBtn />
</Space>
</Row>
);
}}
okText="确认"
>
<p style={{ textAlign: "center", padding: "30px 0 18px", fontSize: 16 }}>
书籍将{isShelf ? "下架" : "上架"},是否继续?
</p>
</Modal>
{TableCom({ columns, loading, data })}
<br />
<PaginationCom
total={total}
page_size={page_size}
setpage_size={setpage_size}
page={page}
setPage={setPage}
init={init}
filterObj={filterObj}
/>
<Drawer placement="right" onClose={onClose} open={isEdit} mask={false}>
<Form form={form} onFinish={submitForm} labelCol={{ span: 5 }}>
<Form.Item label="分类名称" rules={[{ required: true, message: "请输入分类名称" }]}>
<Input autoComplete="off" placeholder="请输入分类名称"></Input>
</Form.Item>
<Form.Item label="排序" rules={[{ required: true, message: "请输入排序" }]}>
<Input autoComplete="off" placeholder="请输入排序"></Input>
</Form.Item>
<Form.Item wrapperCol={{ offset: 5, span: 16 }} style={{ textAlign: "center" }}>
<Space>
<Button>取消</Button>
<Button type="primary" htmlType="submit">
提交
</Button>
</Space>
</Form.Item>
</Form>
</Drawer>
</div>
);
};
export default Audit;
export * from "@/modules/books/management/chapters/api";
export { default } from "@/modules/books/management/chapters/components/BindKnowledgeGraph";
export { default } from "@/modules/books/management/chapters/components/EditChapterEditors";
export { default } from "@/modules/books/management/chapters/components/form-chapter-title";
export { default } from "@/modules/books/management/components/ManagementChapter";
@import "@/modules/books/management/chapters/index.less";
export * from "@/modules/books/management/chapters/request";
import Editor from "@/components/editor/index";
export default Editor;
export { default } from '@/modules/access/Layout'
import axios from "@/utils/request.js";
import qs from "qs";
import { get } from "lodash-es";
import { message } from "antd";
// 角色列表
const getAllList = async (obj) => {
try {
const res = await axios.post("/user/role/getAllList", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 角色列表
const getList = async (obj) => {
try {
const res = await axios.post("/user/role/getList", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 添加
const addRole = async (obj) => {
try {
const res = await axios.post("/user/role/add", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 详情
const infoRole = async (obj) => {
try {
const res = await axios.post("/user/role/getInfoById", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 修改
const editRole = async (obj) => {
try {
const res = await axios.post("/user/role/edit", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
const delRole = async (obj) => {
try {
const res = await axios.post("/user/role/del", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
const changeStatus = async (obj) => {
try {
const res = await axios.post("/user/role/changeStatus", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 获取角色已选权限
const getPowerByRoleId = async (obj) => {
try {
const res = await axios.post("/user/role/getPowerByRoleId", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 设置角色权限
const setRolePower = async (obj) => {
try {
const res = await axios.post("/user/role/setRolePower", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 获取所有权限列表
const getAllrole = async (obj) => {
try {
const res = await axios.post("/system/power/getListAll", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
export {
getAllList,
getList,
addRole,
infoRole,
editRole,
changeStatus,
delRole,
getPowerByRoleId,
setRolePower,
getAllrole,
};
import React, { useEffect, useState } from "react";
import {
Table,
Input,
Button,
Row,
Col,
Space,
DatePicker,
Pagination,
Drawer,
Form,
Select,
} from "antd";
const { RangePicker } = DatePicker;
import { useLocation } from "react-router";
import PaginationCom from "@/common/Pagination/index.jsx";
import { getCouponList } from "../list/request";
const CouponRecord = (props) => {
const { id } = props;
const [data, setData] = useState([]);
const [page_size, setpage_size] = useState(10);
const [page, setPage] = useState(1);
const [total, setTotal] = useState(0);
const [loading, setLoading] = useState(true);
const [isEdit, setIsEdit] = useState(false);
const [form] = Form.useForm();
const init = async (obj = {}) => {
setLoading(true);
const { total, list } = await getCouponList({ id });
setTotal(total);
setData(list);
setLoading(false);
};
const columns = [
{
title: "优惠券名称",
key: "coupon_name",
dataIndex: "coupon_name",
align: "center",
},
{
title: "领取时间",
key: "create_time",
dataIndex: "create_time",
align: "center",
},
{
title: "有效期",
key: "end_time",
dataIndex: "end_time",
align: "center",
},
{
title: "使用状态",
key: "use_status_name",
dataIndex: "use_status_name",
align: "center",
},
];
return (
<div>
<Table
scroll={{
y: 600,
x: true,
}}
columns={columns}
rowKey={(record) => record.ID}
dataSource={data}
loading={loading}
pagination={false}
></Table>
<br />
<PaginationCom
total={total}
page_size={page_size}
setpage_size={setpage_size}
page={page}
setPage={setPage}
init={init}
/>
</div>
);
};
export default CouponRecord;
import React, { useEffect, useState } from "react";
import {
Table,
Input,
Button,
Row,
Col,
Space,
DatePicker,
Pagination,
Drawer,
Form,
Select,
} from "antd";
const { RangePicker } = DatePicker;
import { useLocation } from "react-router";
import PaginationCom from "@/common/Pagination/index.jsx";
import { getIntegralRecordList } from "../list/request";
import TableCom from "@/common/TableCom/index";
const IntegralRecord = (props) => {
const { id } = props;
const [data, setData] = useState([]);
const [page_size, setpage_size] = useState(2);
const [page, setPage] = useState(1);
const [total, setTotal] = useState(4);
const [loading, setLoading] = useState(true);
const [isEdit, setIsEdit] = useState(false);
const [form] = Form.useForm();
const init = async (obj = {}) => {
setLoading(true);
const { total, list } = await getIntegralRecordList({ id });
setTotal(total);
setData(list);
setLoading(false);
};
const columns = [
{
title: "积分变更",
key: "score_change",
dataIndex: "options",
align: "center",
render: (options, record) => {
let operationText;
switch (options) {
case 1:
operationText = `加`;
break;
case 2:
operationText = `减`;
break;
default:
operationText = "";
}
return (
<div>
{operationText}
{record.numbers}
</div>
);
},
},
{
title: "最终积分",
key: "result",
dataIndex: "result",
align: "center",
},
{
title: "备注",
key: "remarks",
dataIndex: "remarks",
align: "center",
},
{
title: "创建时间",
key: "create_time",
dataIndex: "create_time",
align: "center",
},
];
return (
<div>
{TableCom({ columns, loading, data })}
<br />
<PaginationCom
total={total}
page_size={page_size}
setpage_size={setpage_size}
page={page}
setPage={setPage}
init={init}
/>
</div>
);
};
export default IntegralRecord;
import React, { useEffect, useState } from "react";
import { Segmented, Button } from "antd";
import { Outlet, useNavigate, useLocation } from "react-router";
import CouponRecord from "./CouponRecord";
import OrderRecord from "./orderRecord";
import IntegralRecord from "./IntegralRecord";
const UserDetail = () => {
const navigate = useNavigate();
const location = useLocation();
const options = [
{
label: "订单记录",
key: "order-record",
value: 1,
},
{
label: "积分记录",
key: "integral-record",
value: 2,
},
{
label: "优惠券记录",
key: "coupon-record",
value: 3,
},
];
const [currentVal, setCurrentVal] = useState(1);
useEffect(() => {
let val = options.find((item) => {
if (location.pathname.includes(item.key)) {
return item;
}
});
if (val) {
setCurrentVal(val.value);
}
}, []);
const onChange = (value) => {
setCurrentVal(value);
};
return (
<div>
<Segmented
options={options}
onChange={onChange}
defaultValue={currentVal}
style={{ marginBottom: 20 }}
value={currentVal}
></Segmented>
{/* <Outlet/> */}
{currentVal === 3 && <CouponRecord id={location.state.id} />}
{currentVal === 2 && <IntegralRecord id={location.state.id} />}
{currentVal === 1 && <OrderRecord id={location.state.id} />}
</div>
);
};
export default UserDetail;
import React, { useEffect, useState } from "react";
import {
Table,
Input,
Button,
Row,
Col,
Space,
DatePicker,
Pagination,
Drawer,
Form,
Select,
} from "antd";
const { RangePicker } = DatePicker;
import { useLocation } from "react-router";
import PaginationCom from "@/common/Pagination/index.jsx";
import { getOrdersList } from "../list/request";
import TableCom from "@/common/TableCom/index";
const OrderRecord = (props) => {
const { id } = props;
const [data, setData] = useState([]);
const [page_size, setpage_size] = useState(10);
const [page, setPage] = useState(1);
const [total, setTotal] = useState(0);
const [loading, setLoading] = useState(true);
const [isEdit, setIsEdit] = useState(false);
const [form] = Form.useForm();
const init = async (obj = {}) => {
setLoading(true);
const { total, list } = await getOrdersList({ id });
setTotal(total);
setData(list);
setLoading(false);
};
const columns = [
{
title: "订单编号",
key: "id",
dataIndex: "id",
align: "center",
},
{
title: "书籍名称",
key: "book_name",
dataIndex: "book_name",
align: "center",
},
{
title: "总金额",
key: "total_price",
dataIndex: "total_price",
align: "center",
},
{
title: "实际付款金额",
key: "final_total_price",
dataIndex: "final_total_price",
align: "center",
},
{
title: "下单时间",
key: "create_time",
dataIndex: "create_time",
align: "center",
},
{
title: "支付时间",
key: "pay_time",
dataIndex: "pay_time",
align: "center",
},
{
title: "支付方式",
key: "pay_type_text", // 修改key,避免与原始数据冲突
dataIndex: "pay_type",
align: "center",
render: (_, record) => {
switch (record.pay_type) {
case 1:
return "微信";
case 2:
return "支付宝";
case 3:
return "紫荆币";
default:
return "-";
}
},
},
{
title: "结算状态",
key: "status",
dataIndex: "status",
align: "center",
render: (_, record) => {
switch (record.pay_type) {
case 1:
return "待支付";
case 2:
return "取消支付";
case 3:
return "已支付";
default:
return "-";
}
},
},
];
return (
<div>
{TableCom({ columns, loading, data })}
<br />
<PaginationCom
total={total}
page_size={page_size}
setpage_size={setpage_size}
page={page}
setPage={setPage}
init={init}
/>
</div>
);
};
export default OrderRecord;
export { default } from '@/modules/members/Layout'
import React from "react";
import { Outlet } from "react-router";
const List = () => {
return (
<div>
<Outlet></Outlet>
</div>
);
};
export default List;
import React, { useEffect, useState } from "react";
import {
Table,
Input,
Button,
Row,
Col,
Space,
DatePicker,
Pagination,
Drawer,
Form,
Modal,
Radio,
Image,
} from "antd";
import { useNavigate } from "react-router";
const { RangePicker } = DatePicker;
import PaginationCom from "@/common/Pagination";
import { getList, getInfo, addUser, editUser, delUser } from "./request";
import reload from "@/assets/images/icon/reload.png";
import reset from "@/assets/images/icon/reset.png";
import filter from "@/assets/images/icon/filter.png";
import add from "@/assets/images/icon/add.png";
import dayjs from "dayjs";
import TableCom from "@/common/TableCom/index";
import { useSelector } from "react-redux";
const Classify = () => {
const [data, setData] = useState([]);
const navigate = useNavigate();
const [page_size, setpage_size] = useState(10);
const [page, setPage] = useState(1);
const [total, setTotal] = useState(0);
const [loading, setLoading] = useState(true);
const [isEdit, setIsEdit] = useState(false);
const [delMoadal, setDelModal] = useState(false);
const [form] = Form.useForm();
const [isAdd, setIsAdd] = useState(false);
const [id, setId] = useState(0);
// 获取操作权限
const { operationPermissionsList } = useSelector((state) => state.user);
const [initialValues, setinitialValues] = useState({
name: "",
phone: "",
sex: "1",
birthday_time: "",
});
const [filterObj, setfilterObj] = useState({
name: "",
phone: "",
start_time: "",
end_time: "",
});
const [dateVal, setDateVal] = useState("");
// 过滤
const handleFilter = (flag, val) => {
if (flag == "startandend") {
let val2 = val || [];
filterObj["start_time"] = parseInt(
new Date(dayjs(val2[0]).format("YYYY-MM-DD") + " 00:00:00").getTime() / 1000,
);
const endDate = new Date(dayjs(val2[1])).getDate();
const endTime = parseInt(
new Date(dayjs(val2[1]).format("YYYY-MM-") + endDate + " 23:59:59").getTime() / 1000,
);
filterObj["end_time"] = endTime;
setDateVal(val);
if (!val2.length) {
delete filterObj["start_time"];
delete filterObj["end_time"];
}
} else {
filterObj[flag] = val;
}
for (const key in filterObj) {
if (!filterObj[key]) {
delete filterObj[key];
}
}
setfilterObj({ ...filterObj });
if (!val) init(filterObj);
};
// 重置
const handleReset = () => {
setfilterObj({ real_name: "", tel: "" });
setDateVal("");
init();
};
const init = async (obj = {}) => {
setLoading(true);
const { total, list } = await getList({ page, page_size, ...obj });
setTotal(total);
setData(list);
setLoading(false);
};
const handleEdit = async (obj) => {
const data = await getInfo({ id: obj.id });
let { birthday_time, id, sex } = data;
setIsAdd(false);
setIsEdit(true);
setId(id);
form.setFieldsValue({ ...data, birthday_time: dayjs(birthday_time), sex: sex + "" });
};
const onClose = () => {
setIsEdit(false);
};
const submitForm = async (obj) => {
let bool;
let { birthday_time } = obj;
if (isAdd) {
bool = await addUser({ ...obj, birthday_time: dayjs(birthday_time).unix() });
} else {
bool = await editUser({ ...obj, id, birthday_time: dayjs(birthday_time).unix() });
}
if (!bool) return;
init();
setIsEdit(false);
};
const delSuccess = async () => {
const bool = await delUser({ id });
bool && init();
setDelModal(false);
};
useEffect(() => {
if (!isEdit) {
form.setFieldsValue({ ...initialValues });
}
}, [isEdit]);
const columns = [
{
title: "ID",
key: "id",
align: "center",
dataIndex: "id",
},
{
title: "用户名",
key: "name",
align: "center",
dataIndex: "name",
width: 250,
},
{
title: "用户手机号",
key: "phone",
align: "center",
dataIndex: "phone",
},
{
title: "创建时间",
key: "create_time",
align: "center",
dataIndex: "create_time",
},
{
title: "最后登录时间",
key: "last_login_time",
align: "center",
dataIndex: "last_login_time",
},
{
title: "最后登录IP",
key: "last_ip",
align: "center",
dataIndex: "last_ip",
},
{
title: "操作",
key: "handle",
dataIndex: "handle",
render: (_, record) => {
return (
<Space>
{operationPermissionsList.includes("/member/list/getInfoById") && (
<Button onClick={() => handleEdit(record)}>编辑</Button>
)}
{operationPermissionsList.includes("/member/list/del") && (
<Button
onClick={() => {
setId(record.id);
setDelModal(true);
}}
>
删除
</Button>
)}
{operationPermissionsList.includes("/member/list/detail") && (
<Button
onClick={() => {
navigate("/member/list/detail", {
state: {
id: record.id,
},
});
}}
>
详情
</Button>
)}
</Space>
);
},
},
];
return (
<div className="classify">
<Row justify={"space-between"}>
<Col className="form-devices-inline">
<Form layout="inline">
<Form.Item label="用户名称">
<Input
autoComplete="off"
allowClear
value={filterObj.name}
onChange={(ev) => handleFilter("name", ev.target.value)}
placeholder="请输入用户名称"
id="name"
></Input>
</Form.Item>
<Form.Item label="手机号">
<Input
autoComplete="off"
allowClear
value={filterObj.phone}
onChange={(ev) => handleFilter("phone", ev.target.value)}
placeholder="请输入手机号"
id="phone"
></Input>
</Form.Item>
<Form.Item label="创建时间">
<RangePicker
value={dateVal}
onChange={(ev) => handleFilter("startandend", ev)}
id="createTime"
></RangePicker>
</Form.Item>
<Space>
<Button
type="primary"
ghost
icon={
<span
style={{
display: "inline-block",
width: "11px",
height: "10px",
pointerEvents: "none",
}}
>
<Image src={reset} />
</span>
}
onClick={handleReset}
>
重置
</Button>
<Button
type="primary"
ghost
icon={
<span
style={{
display: "inline-block",
width: "13px",
height: "12px",
pointerEvents: "none",
}}
>
<Image src={filter} />
</span>
}
onClick={() => {
setPage(1);
Object.values(filterObj).some((item) => item) ? init(filterObj) : init();
}}
>
筛选
</Button>
<Button
type="primary"
ghost
icon={
<span
style={{
display: "inline-block",
width: "10px",
height: "12px",
pointerEvents: "none",
}}
>
<Image src={reload} />
</span>
}
onClick={() =>
Object.values(filterObj).some((item) => item) ? init(filterObj) : init()
}
>
刷新
</Button>
</Space>
</Form>
</Col>
<Col>
<Space>
{operationPermissionsList.includes("/member/list/add") && (
<Button
type="primary"
ghost
icon={
<span
style={{
display: "inline-block",
width: "13px",
height: "12px",
pointerEvents: "none",
}}
>
<Image src={add} />
</span>
}
onClick={() => {
setIsAdd(true);
setIsEdit(true);
}}
>
添加
</Button>
)}
</Space>
</Col>
</Row>
<br />
{TableCom({ columns, loading, data })}
<Modal
mask={false}
centered
open={delMoadal}
onOk={delSuccess}
onCancel={() => setDelModal(false)}
footer={(_, { OkBtn, CancelBtn }) => {
return (
<Row justify={"center"} className="delModal" style={{ marginBottom: "30px" }}>
<Space size={20}>
<CancelBtn />
<OkBtn />
</Space>
</Row>
);
}}
okText="确认"
>
<p style={{ textAlign: "center", padding: "30px 0 18px", fontSize: 16 }}>
用户将永久删除,是否继续?
</p>
</Modal>
<br />
<PaginationCom
total={total}
page_size={page_size}
setpage_size={setpage_size}
page={page}
setPage={setPage}
init={init}
filterObj={filterObj}
/>
<Drawer
onCancel={() => setIsEdit(false)}
open={isEdit}
mask={false}
centered
footer={null}
onClose={() => setIsEdit(false)}
>
<Form form={form} onFinish={submitForm} labelCol={{ span: 7 }}>
<Form.Item
name="name"
label="用户名称"
rules={[
{ required: true, message: "请输入用户名称" },
{ max: 30, message: "最大30字符" },
]}
>
<Input autoComplete="off" placeholder="请输入用户名称"></Input>
</Form.Item>
<Form.Item
name="phone"
label="手机号名称"
rules={[
{ required: true, message: "请输入手机号名称" },
{
pattern: /^(?:(?:\+|00)86)?1[3-9]\d{9}$/,
message: "请输入正确格式的手机号",
trigger: "blur",
},
]}
>
<Input autoComplete="off" placeholder="请输入手机号名称"></Input>
</Form.Item>
<Form.Item name="sex" label="性别" rules={[{ required: true, message: "请选择性别" }]}>
<Radio.Group defaultValue={""}>
<Radio value={"1"}>男</Radio>
<Radio value={"2"}>女</Radio>
<Radio value={"3"}>保密</Radio>
</Radio.Group>
</Form.Item>
<Form.Item
name="birthday_time"
label="出生日期"
rules={[{ required: true, message: "请选择出生日期" }]}
>
<DatePicker />
</Form.Item>
<Form.Item wrapperCol={{ offset: 9, span: 16 }}>
<Space size={20}>
<Button onClick={() => setIsEdit(false)}>取消</Button>
<Button type="primary" htmlType="submit">
提交
</Button>
</Space>
</Form.Item>
</Form>
</Drawer>
</div>
);
};
export default Classify;
import axios from "@/utils/request.js";
import qs from "qs";
import { get } from "lodash-es";
import { message } from "antd";
// 用户列表
const getList = async (obj) => {
try {
const res = await axios.post("/members/members/getList", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 详情
const getInfo = async (obj) => {
try {
const res = await axios.post("/members/members/getInfoById", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 订单记录
const getOrdersList = async (obj) => {
try {
const res = await axios.post("/members/members/getOrdersList", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 积分记录
const getIntegralRecordList = async (obj) => {
try {
const res = await axios.post("/members/members/getIntegralRecordList", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 优惠券记录
const getCouponList = async (obj) => {
try {
const res = await axios.post("/members/members/getCouponList", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 添加
const addUser = async (obj) => {
try {
const res = await axios.post("/members/members/add", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 修改
const editUser = async (obj) => {
try {
const res = await axios.post("/members/members/edit", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 删除
const delUser = async (obj) => {
try {
const res = await axios.post("/members/members/del", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
export {
getList,
getInfo,
getOrdersList,
getIntegralRecordList,
getCouponList,
addUser,
editUser,
delUser,
};
import React, { useEffect, useState } from "react";
import {
Table,
Input,
Button,
Row,
Col,
Space,
DatePicker,
Pagination,
Drawer,
Form,
Modal,
Radio,
Image,
} from "antd";
import { useNavigate } from "react-router";
const { RangePicker } = DatePicker;
import PaginationCom from "@/common/Pagination";
import {
getList,
infoGrade,
addGrade,
editGrade,
delGrade,
getSystem,
systemEdit,
} from "./request";
import reload from "@/assets/images/icon/reload.png";
import reset from "@/assets/images/icon/reset.png";
import filter from "@/assets/images/icon/filter.png";
import add from "@/assets/images/icon/add.png";
import TableCom from "@/common/TableCom/index";
import dayjs from "dayjs";
import { useSelector } from "react-redux";
const Userlevel = () => {
const [data, setData] = useState([]);
const navigate = useNavigate();
const [page_size, setpage_size] = useState(10);
const [page, setPage] = useState(1);
const [total, setTotal] = useState(0);
const [loading, setLoading] = useState(true);
const [isEdit, setIsEdit] = useState(false);
const [delMoadal, setDelModal] = useState(false);
const [form] = Form.useForm();
const [form1] = Form.useForm();
const [isAdd, setIsAdd] = useState(false);
const [id, setId] = useState(0);
const [isEditValidity, setIsEditValidity] = useState(false);
const [initialValues, setinitialValues] = useState({
name: "",
consumption: "",
});
const [filterObj, setfilterObj] = useState({
name: "",
start_time: "",
end_time: "",
});
const [dateVal, setDateVal] = useState("");
// 获取操作权限
const { operationPermissionsList } = useSelector((state) => state.user);
const handleFilter = (flag, val) => {
if (flag == "startandend") {
let val2 = val || [];
filterObj["start_time"] = parseInt(
new Date(dayjs(val2[0]).format("YYYY-MM-DD") + " 00:00:00").getTime() / 1000,
);
const endDate = new Date(dayjs(val2[1])).getDate();
const endTime = parseInt(
new Date(dayjs(val2[1]).format("YYYY-MM-") + endDate + " 23:59:59").getTime() / 1000,
);
filterObj["end_time"] = endTime;
setDateVal(val);
if (!val2.length) {
delete filterObj["start_time"];
delete filterObj["end_time"];
}
} else {
filterObj[flag] = val;
}
for (const key in filterObj) {
if (!filterObj[key]) {
delete filterObj[key];
}
}
setfilterObj({ ...filterObj });
if (!val) init(filterObj);
};
// 重置
const handleReset = () => {
setfilterObj({ real_name: "", tel: "" });
setDateVal("");
init();
};
const init = async (obj = {}) => {
setLoading(true);
const { total, list } = await getList({ page, page_size, ...obj });
setTotal(total);
setData(list);
setLoading(false);
};
const handleEdit = async (obj) => {
const data = await infoGrade({ id: obj.id });
setIsAdd(false);
setIsEdit(true);
setId(data.id);
form.setFieldsValue(data);
};
const onClose = () => {
setIsEdit(false);
};
const submitForm = async (obj) => {
let bool;
if (isAdd) {
bool = await addGrade({ ...obj });
} else {
bool = await editGrade({ ...obj, id });
}
if (!bool) return;
init();
setIsEdit(false);
};
const delSuccess = async () => {
const bool = await delGrade({ id });
bool && init();
setDelModal(false);
};
useEffect(() => {
if (!isEdit) {
form.setFieldsValue({ ...initialValues });
form.resetFields();
}
}, [isEdit]);
// 获取信息
const getInfo = async () => {
const data = await getSystem({ type: 2 });
form1.setFieldsValue({
set_value: data.set_value,
});
};
// 设置个性化信息
const handleSystemEdit = async (values) => {
const { set_value } = values; // 获取输入框的内容
const data = { type: 2, set_value }; // 构造传递给systemEdit的参数对象
const bool = await systemEdit(data);
console.log("bool", bool);
bool && getInfo();
};
useEffect(() => {
// 是否有权限设置有效期
if (operationPermissionsList.includes("/member/userlevel/getSystem")) {
getInfo();
}
}, [operationPermissionsList]);
const columns = [
{
title: "ID",
key: "id",
align: "center",
dataIndex: "id",
},
{
title: "等级名称",
key: "name",
align: "center",
dataIndex: "name",
},
{
title: "达标金额",
key: "consumption",
align: "center",
dataIndex: "consumption",
},
{
title: "创建时间",
key: "create_time",
align: "center",
dataIndex: "create_time",
},
{
title: "更新时间",
key: "update_time",
align: "center",
dataIndex: "update_time",
},
{
title: "操作",
key: "handle",
dataIndex: "handle",
render: (_, record) => {
return (
<Space>
{operationPermissionsList.includes("/member/userlevel/getInfoById") && (
<Button
style={{
letterSpacing: "-2px",
}}
onClick={() => handleEdit(record)}
>
编辑
</Button>
)}
{operationPermissionsList.includes("/member/userlevel/del") && (
<Button
onClick={() => {
setId(record.id);
setDelModal(true);
}}
style={{
letterSpacing: "-2px",
}}
>
删除
</Button>
)}
</Space>
);
},
},
];
return (
<div>
{/* <Space style={{ paddingBottom: '20px' }}>
<Form form={form1} onFinish={handleSystemEdit}>
<Form.Item label='用户等级有效期(月)' name='user_grade_validity'>
<Input
autoComplete='off'
style={{ width: 260 }}
placeholder='请输入用户等级有效期'
></Input>
<span style={{ marginLeft: '10px', color: '#999999' }}>
用于计算用户等级,根据当前时间之前有效期内的消费金额总和来计算
</span>
</Form.Item>
</Form>
</Space> */}
<Row justify={"space-between"}>
<Col className="form-devices-inline">
<Form layout="inline">
<Form.Item label="等级名称">
<Input
autoComplete="off"
allowClear
value={filterObj.name}
onChange={(ev) => handleFilter("name", ev.target.value)}
placeholder="请输入等级名称"
id="name"
></Input>
</Form.Item>
<Form.Item label="创建时间">
<RangePicker
value={dateVal}
onChange={(ev) => handleFilter("startandend", ev)}
id="createTime"
></RangePicker>
</Form.Item>
<Space>
<Button
type="primary"
ghost
icon={
<span
style={{
display: "inline-block",
width: "11px",
height: "10px",
pointerEvents: "none",
}}
>
<Image src={reset} />
</span>
}
onClick={handleReset}
>
重置
</Button>
<Button
type="primary"
icon={
<span
style={{
display: "inline-block",
width: "13px",
height: "12px",
pointerEvents: "none",
}}
>
<Image src={filter} />
</span>
}
ghost
onClick={() => {
setPage(1);
Object.values(filterObj).some((item) => item) ? init(filterObj) : init();
}}
>
筛选
</Button>
<Button
type="primary"
ghost
icon={
<span
style={{
display: "inline-block",
width: "10px",
height: "12px",
pointerEvents: "none",
}}
>
<Image src={reload} />
</span>
}
onClick={() =>
Object.values(filterObj).some((item) => item) ? init(filterObj) : init()
}
>
刷新
</Button>
</Space>
</Form>
</Col>
<Col>
<Space>
{operationPermissionsList.includes("/member/userlevel/add") && (
<Button
type="primary"
ghost
icon={
<span
style={{
display: "inline-block",
width: "13px",
height: "12px",
pointerEvents: "none",
}}
>
<Image src={add} />
</span>
}
onClick={() => {
setIsAdd(true);
setIsEdit(true);
}}
>
添加
</Button>
)}
{operationPermissionsList.includes("/member/userlevel/getSystem") && (
<Button
type="primary"
ghost
onClick={() => {
setIsAdd(true);
setIsEditValidity(true);
}}
>
有效期设置
</Button>
)}
</Space>
</Col>
</Row>
<br />
{TableCom({ columns, loading, data })}
<Modal
mask={false}
centered
open={delMoadal}
onOk={delSuccess}
onCancel={() => setDelModal(false)}
footer={(_, { OkBtn, CancelBtn }) => {
return (
<Row justify={"center"} className="delModal" style={{ marginBottom: "30px" }}>
<Space size={20}>
<CancelBtn />
<OkBtn />
</Space>
</Row>
);
}}
okText="确认"
>
<p style={{ textAlign: "center", padding: "30px 0 18px", fontSize: 16 }}>
等级将永久删除,是否继续?
</p>
</Modal>
<br />
<PaginationCom
total={total}
page_size={page_size}
setpage_size={setpage_size}
page={page}
setPage={setPage}
init={init}
filterObj={filterObj}
/>
<Drawer
onClose={onClose}
open={isEdit}
footer={null}
mask={false}
keyboard={false}
onCancel={() => setIsEdit(false)}
>
<Form
form={form}
onFinish={submitForm}
labelCol={{ span: 8 }}
wrapperCol={{ span: 12 }}
style={{
marginBottom: "-30px",
}}
>
<Form.Item
label="等级名称"
name="name"
rules={[
{ required: true, message: "请输入等级名称" },
{ max: 30, message: "最大30字符" },
]}
>
<Input autoComplete="off" placeholder="请输入等级名称"></Input>
</Form.Item>
<Form.Item
label="达标金额"
name="consumption"
rules={[
{ required: true, message: "请输入达标金额" },
{ pattern: /^[0-9]\d*$/, message: "必须是正整数" },
]}
>
<Input autoComplete="off" placeholder="请输入达标金额"></Input>
</Form.Item>
<Form.Item wrapperCol={{ offset: 9, span: 16 }}>
<Space size={20}>
<Button onClick={() => setIsEdit(false)}>取消</Button>
<Button type="primary" htmlType="submit">
提交
</Button>
</Space>
</Form.Item>
</Form>
</Drawer>
<Drawer
onClose={() => setIsEditValidity(false)}
open={isEditValidity}
footer={null}
mask={false}
keyboard={false}
onCancel={() => setIsEditValidity(false)}
>
<Row align="middle" justify="center">
<Col span={20}>
<Form
form={form1}
onFinish={handleSystemEdit}
labelCol={{ span: 9 }}
wrapperCol={{ span: 26 }}
>
<Form.Item label="用户等级有效期(月)" name="set_value" style={{ marginBottom: 10 }}>
<Input
autoComplete="off"
style={{ width: "100%", marginRight: 10 }} // 使用百分比宽度
placeholder="请输入用户等级有效期"
/>
</Form.Item>
<Form.Item wrapperCol={{ offset: 0, span: 26 }}>
{/* 调整偏移和跨度,使按钮居中 */}
<div style={{ color: "#999999", fontSize: 12 }}>
用于计算用户等级,根据当前时间之前有效期内的消费金额总和来计算 (1-24正整数)
</div>
</Form.Item>
<Form.Item wrapperCol={{ offset: 9, span: 26 }}>
<Space size={20}>
{operationPermissionsList.includes("/member/userlevel/systemEdit") && (
<Button onClick={() => setIsEditValidity(false)}>取消</Button>
)}
{operationPermissionsList.includes("/member/userlevel/systemEdit") && (
<Button
type="primary"
htmlType="submit"
onClick={() => setIsEditValidity(false)}
>
提交
</Button>
)}
</Space>
</Form.Item>
</Form>
</Col>
</Row>
</Drawer>
</div>
);
};
export default Userlevel;
import axios from "@/utils/request.js";
import qs from "qs";
import { get } from "lodash-es";
import { message } from "antd";
// 等级列表
const getList = async (obj) => {
try {
const res = await axios.post("/members/grade/getList", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 详情
const infoGrade = async (obj) => {
try {
const res = await axios.post("/members/grade/getInfoById", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 添加
const addGrade = async (obj) => {
try {
const res = await axios.post("/members/grade/add", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 修改
const editGrade = async (obj) => {
try {
const res = await axios.post("/members/grade/edit", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 删除
const delGrade = async (obj) => {
try {
const res = await axios.post("/members/grade/del", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 获取设置信息
const getSystem = async (obj) => {
try {
const res = await axios.post("/system/config/getSystem", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
} catch (error) {
return error;
}
};
// 修改设置
const systemEdit = async (obj) => {
try {
const res = await axios.post("/system/config/systemEdit", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
} catch (error) {
return error;
}
};
export { getList, infoGrade, addGrade, editGrade, delGrade, getSystem, systemEdit };
import axios from "@/utils/axios";
// 优惠券列表
export function getList(data) {
return axios.post("/api/system/coupon/getList", data);
}
// 添加优惠券
export function addCoupon(data) {
return axios.post("/api/system/coupon/add", data);
}
// 修改优惠券
export function editCoupon(data) {
return axios.post("/api/system/coupon/edit", data);
}
// 删除优惠券
export function delCoupon(data) {
return axios.post("/api/system/coupon/del", data);
}
// 优惠券详情
export function getCoupon(data) {
return axios.post("/api/system/coupon/getInfoById", data);
}
// 优惠券发放
export function addCouponRecord(data) {
return axios.post("/api/system/coupon/record/add", data);
}
// 获取优惠券开关
export function getCouponConfig(data) {
return axios.post("/api/system/config/getCoupon", data);
}
// 设置优惠券开关
export function setCouponConfig(data) {
return axios.post("/api/system/config/couponEdit", data);
}
import { Drawer, Form, Input, DatePicker, Button, Space, message } from "antd";
import { addCoupon, editCoupon, getCoupon } from "../api";
import { useEffect } from "react";
import dayjs from "dayjs";
const { RangePicker } = DatePicker;
export default function FormDrawer({ id, onComplete, ...rest }) {
const [form] = Form.useForm();
async function fetchInfo() {
if (!id) return;
const { data } = await getCoupon({ id });
const time = [dayjs(data.start_time), dayjs(data.end_time)];
form.setFieldsValue({ ...data, time });
}
useEffect(() => {
fetchInfo();
}, [id]);
const title = id ? "编辑优惠券" : "添加优惠券";
const onFinish = async (values) => {
const { time, ...params } = values;
if (time) {
params.start_time = new Date(time[0]).getTime() / 1000;
params.end_time = new Date(time[1]).getTime() / 1000;
}
const res = id ? await editCoupon({ ...params, id }) : await addCoupon(params);
message.success(res.message);
rest.onClose && rest.onClose();
onComplete && onComplete();
};
const onClose = () => {
form.resetFields();
rest.onClose && rest.onClose();
};
return (
<Drawer placement="right" title={title} {...rest} onClose={onClose}>
<Form form={form} onFinish={onFinish} labelCol={{ span: 6 }} autoComplete="off">
<Form.Item
name="name"
label="优惠券名称"
rules={[
{ required: true, message: "请输入优惠券名称" },
{ max: 30, message: "最大30字符" },
]}
>
<Input placeholder="请输入优惠券名称" />
</Form.Item>
<Form.Item
name="norm_price"
label="达标金额"
rules={[
{ required: true, message: "请输入达标金额" },
{
validator: (_, value) => {
return new Promise((resolve, reject) => {
if (!value || /^[1-9]\d*$/.test(value)) {
resolve();
} else {
reject(new Error("必须是正整数"));
}
});
},
},
]}
>
<Input placeholder="请输入达标金额" />
</Form.Item>
<Form.Item
name="reduced_price"
label="满减金额"
rules={[
{ required: true, message: "请输入满减金额" },
{
validator: (_, value) => {
return new Promise((resolve, reject) => {
if (!value || /^[1-9]\d*$/.test(value)) {
resolve();
} else if (!(Number(value) <= Number(form.getFieldValue("norm_price")))) {
reject("小于等于达标金额");
}
});
},
},
]}
>
<Input placeholder="请输入满减金额" />
</Form.Item>
<Form.Item name="time" label="有效期" rules={[{ required: true, message: "请选择有效期" }]}>
<RangePicker></RangePicker>
</Form.Item>
<Form.Item style={{ textAlign: "center" }}>
<Space size={20}>
<Button type="default" onClick={onClose}>
取消
</Button>
<Button type="primary" htmlType="submit">
提交
</Button>
</Space>
</Form.Item>
</Form>
</Drawer>
);
}
import { Drawer, Form, Input, Select, Button, Space, App } from "antd";
const { TextArea } = Input;
import { addCouponRecord } from "../api";
export default function SendCouponDrawer({ couponList = [], ...rest }) {
const { message } = App.useApp();
const [form] = Form.useForm();
const onFinish = async (values) => {
const res = await addCouponRecord(values);
if (res.data.err.msg) {
message.error(res.data.err.title);
} else {
message.success(res.message);
rest.onClose && rest.onClose();
}
};
const onClose = () => {
form.resetFields();
rest.onClose && rest.onClose();
};
return (
<Drawer title="优惠券发放" placement="right" {...rest} onClose={onClose}>
<Form form={form} onFinish={onFinish} labelCol={{ span: 6 }} autoComplete="off">
<Form.Item
name="phone"
label="用户手机号"
rules={[{ required: true, message: "请输入用户手机号" }]}
>
<TextArea
placeholder="请输入用户手机号,用英文逗号隔开,每次最多100个"
autoSize={{ minRows: 5, maxRows: 8 }}
></TextArea>
</Form.Item>
<Form.Item
name="id"
label="发放优惠券"
rules={[{ required: true, message: "请选择优惠券" }]}
>
<Select placeholder="请选择优惠券">
{couponList.map((item) => (
<Select.Option value={item.id} key={item.id}>
{item.name}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item wrapperCol={{ offset: 5, span: 16 }} style={{ textAlign: "center" }}>
<Space size={20}>
<Button onClick={onClose}>取消</Button>
<Button type="primary" htmlType="submit">
提交
</Button>
</Space>
</Form.Item>
</Form>
</Drawer>
);
}
export { default } from "@/modules/settings/coupon/views/CouponListView";
import axios from "@/utils/request.js";
import qs from "qs";
import { get } from "lodash-es";
import { message } from "antd";
// 优惠券列表
const getList = async (obj) => {
try {
const res = await axios.post("/system/coupon/getList", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
// 添加优惠券
const addCoupon = async (obj) => {
try {
const res = await axios.post("/system/coupon/add", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
} catch (error) {
return error;
}
};
// 修改优惠券
const editCoupon = async (obj) => {
try {
const res = await axios.post("/system/coupon/edit", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
} catch (error) {
return error;
}
};
// 删除优惠券
const delCoupon = async (obj) => {
try {
const res = await axios.post("/system/coupon/del", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
} catch (error) {
return error;
}
};
// 优惠券发放
const recordCoupon = async (obj) => {
try {
const res = await axios.post("/system/coupon/record/add", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 3000) {
message.warning("优惠券已过期");
return true;
}
// 先检查空手机号的情况,如果满足就跳出后续错误提示
if (
data.err.msg === " 手机号:" &&
data.suc.title &&
!data.suc.title.includes("下发成功(0)")
) {
if (data.suc.msg !== "") {
message.success(`${data.suc.msg}下发成功`, 10);
}
message.error(`空手机号下发失败`, 10);
return true;
}
// 接下来处理其他非空错误消息和成功消息
if (data.suc.title && !data.suc.title.includes("下发成功(0)")) {
if (data.suc.msg !== "") {
message.success(`${data.suc.msg}下发成功`, 10);
}
}
if (data.err.title && !data.err.title.includes("下发失败(0)")) {
if (data.err.msg !== "") {
message.error(`${data.err.msg}下发失败`, 10);
}
}
return true;
} catch (error) {
return error;
}
};
// 获取优惠券开关
const getCoupon = async (obj) => {
try {
const res = await axios.post("/system/config/getCoupon", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
} catch (error) {
return error;
}
};
// 设置优惠券开关
const setCoupon = async (obj) => {
try {
const res = await axios.post("/system/config/couponEdit", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return true;
} else {
message.error(msg);
return false;
}
} catch (error) {
return error;
}
};
// 详情
const getInfoById = async (obj) => {
try {
const res = await axios.post("/system/coupon/getInfoById", qs.stringify(obj));
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
return data;
} else {
message.error(msg);
return false;
}
} catch (error) {
return error;
}
};
export {
getList,
addCoupon,
editCoupon,
delCoupon,
recordCoupon,
getCoupon,
setCoupon,
getInfoById,
};
export * from "@/modules/settings/help/request";
export { default } from "@/modules/settings/SettingsLayout";
export { default } from "@/modules/settings/personalized/views/PersonalizedSettingsView";
export { default } from "@/modules/settings/point-management/views/PointManagementView";
export {
getPointSettings as getIntegral,
setPointSwitch as integralSet,
savePointSettings as integralEdit,
rechargePoints as integralAdd,
} from "@/modules/settings/point-management/api";
export {
getPersonalizedSettings as getSystem,
savePersonalizedSettings as systemEdit,
} from "@/modules/settings/personalized/api";
export { getFeedbackList as getList } from "@/modules/settings/feedback/api";
export { default } from "@/modules/student-book/views/StudentBookView";
export * from "@/modules/student-book/request";
.custom-error-message {
font-size: 10px;
/* 调整为合适的文字大小 */
}
export { default } from '@/modules/teachers/Layout'
export { default } from "@/modules/auth/views/AuthLayout";
export { default } from "@/modules/auth/views/LoginView";
export { default } from "@/modules/auth/request/login-api";
export { default } from "@/modules/auth/request/sso-api";
export { default } from "@/modules/account/views/AccountView";
import axios from "./request";
import { get } from "lodash-es";
import { message } from "antd";
// 统一上传
export const uploadFiles = async (obj) => {
try {
const res = await axios.post("/common/OssUpload/uploadFiles", obj, {
headers: {
"Content-Type": "multipart/form-data",
},
});
const code = get(res, "data.code", 0);
const data = get(res, "data.data", []);
const msg = get(res, "data.message", "数据获取失败!");
if (code === 200) {
message.success(msg);
return data;
} else {
message.error(msg);
return false;
}
} catch (error) {
return false;
}
};
Markdown 格式
0% 或
您添加了 0 人 到此讨论。请谨慎行事。
请先完成此评论的编辑!
请 注册 或者 后发表评论