“联系客服”增加session接口;图片审核加入“机审失败”tab
This commit is contained in:
parent
97a3e498b4
commit
e9a2245ba3
1
.env
1
.env
|
@ -1,2 +1 @@
|
||||||
REACT_APP_API_URL=https://api.wishpal.cn
|
|
||||||
REACT_APP_RSA_KEY=-----BEGIN PUBLIC KEY-----MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMXPIjKV6CMi5O9tIXJWNIfnqXjqOZ1KmRByRAP073DU+gzMLygzEsrztJzbz/K/Julkz6XhheZ8vdz+boAl1HsCAwEAAQ==-----END PUBLIC KEY-----
|
REACT_APP_RSA_KEY=-----BEGIN PUBLIC KEY-----MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMXPIjKV6CMi5O9tIXJWNIfnqXjqOZ1KmRByRAP073DU+gzMLygzEsrztJzbz/K/Julkz6XhheZ8vdz+boAl1HsCAwEAAQ==-----END PUBLIC KEY-----
|
|
@ -47,5 +47,5 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"tailwindcss": "^3.3.5"
|
"tailwindcss": "^3.3.5"
|
||||||
},
|
},
|
||||||
"proxy": "https://api.tiefen.fun"
|
"proxy": "https://api.wishpal.cn"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,27 @@
|
||||||
import React, { useState, useEffect, useRef } from "react";
|
import React, { useState, useEffect, useRef } from "react";
|
||||||
import { Divider, Modal, Input, Button } from "antd";
|
import { Divider, Modal, Input, Button, Menu } from "antd";
|
||||||
|
import {
|
||||||
|
Routes,
|
||||||
|
Route,
|
||||||
|
Navigate,
|
||||||
|
useNavigate,
|
||||||
|
useLocation,
|
||||||
|
} from "react-router-dom";
|
||||||
import baseRequest from "../../utils/baseRequest";
|
import baseRequest from "../../utils/baseRequest";
|
||||||
|
|
||||||
export default function Contact() {
|
const ContactContent = (props) => {
|
||||||
|
const current = props.current;
|
||||||
//请求未读消息队列
|
//请求未读消息队列
|
||||||
const [unreadMessageList, setUnreadMessageList] = useState([]);
|
const [unreadMessageList, setUnreadMessageList] = useState([]);
|
||||||
const getUnreadMessages = async () => {
|
const getUnreadMessages = async () => {
|
||||||
if (isModalOpen) return;
|
if (isModalOpen) return;
|
||||||
|
if (current !== "unread") return;
|
||||||
try {
|
try {
|
||||||
//获取全部未读信息
|
//获取全部未读信息
|
||||||
const base = baseRequest();
|
const base = baseRequest();
|
||||||
|
//查询所有未读信息的session
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
"/op/contact_customer_service/list_unread_group_by_mid",
|
"/op/contact_customer_service/list_unread_group_by_session_id",
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
|
@ -28,7 +38,7 @@ export default function Contact() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//获取用户头像、昵称
|
//获取用户头像、昵称
|
||||||
const userMids = data.data.list.map((item) => item.mid);
|
const userMids = data.data.list.map((item) => item.sub_mid);
|
||||||
if (userMids.length === 0) {
|
if (userMids.length === 0) {
|
||||||
setUnreadMessageList([]);
|
setUnreadMessageList([]);
|
||||||
return;
|
return;
|
||||||
|
@ -51,7 +61,7 @@ export default function Contact() {
|
||||||
//合并未读信息和用户资料数组
|
//合并未读信息和用户资料数组
|
||||||
const combinedArray = data.data.list.map((item1) => {
|
const combinedArray = data.data.list.map((item1) => {
|
||||||
const matchingItem = userData.data.list.find(
|
const matchingItem = userData.data.list.find(
|
||||||
(item2) => item2.mid === item1.mid
|
(item2) => item2.mid === item1.sub_mid
|
||||||
);
|
);
|
||||||
return { ...item1, ...matchingItem };
|
return { ...item1, ...matchingItem };
|
||||||
});
|
});
|
||||||
|
@ -66,6 +76,7 @@ export default function Contact() {
|
||||||
|
|
||||||
//轮询更新未读消息队列
|
//轮询更新未读消息队列
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (current !== "unread") return;
|
||||||
getUnreadMessages();
|
getUnreadMessages();
|
||||||
// 设置轮询请求,每隔一定时间执行一次
|
// 设置轮询请求,每隔一定时间执行一次
|
||||||
const intervalId = setInterval(() => {
|
const intervalId = setInterval(() => {
|
||||||
|
@ -80,6 +91,7 @@ export default function Contact() {
|
||||||
|
|
||||||
const { TextArea } = Input;
|
const { TextArea } = Input;
|
||||||
|
|
||||||
|
//对话列表的单个组件
|
||||||
const MassageItem = ({ item }) => {
|
const MassageItem = ({ item }) => {
|
||||||
//设置消息为已读
|
//设置消息为已读
|
||||||
const ids = item.contents?.map((temItem) => temItem.id);
|
const ids = item.contents?.map((temItem) => temItem.id);
|
||||||
|
@ -109,7 +121,6 @@ export default function Contact() {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="flex flex-col cursor-pointer"
|
className="flex flex-col cursor-pointer"
|
||||||
|
@ -128,9 +139,11 @@ export default function Contact() {
|
||||||
/>
|
/>
|
||||||
<div className="flex flex-col justify-around ml-2">
|
<div className="flex flex-col justify-around ml-2">
|
||||||
<p className="text-base font-bold">
|
<p className="text-base font-bold">
|
||||||
<span className="text-white font-bold bg-red-400 px-2 rounded-lg">
|
{current === "unread" && (
|
||||||
{item.contents.length}条未读
|
<span className="text-white font-bold bg-red-400 px-2 rounded-lg">
|
||||||
</span>
|
{item.contents.length}条未读
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
{item.name}
|
{item.name}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-gray-400 text-sm">
|
<p className="text-gray-400 text-sm">
|
||||||
|
@ -155,20 +168,20 @@ export default function Contact() {
|
||||||
//获取聊天记录,并设置为已读
|
//获取聊天记录,并设置为已读
|
||||||
const getHistory = async () => {
|
const getHistory = async () => {
|
||||||
try {
|
try {
|
||||||
//获取全部未读信息
|
//获取最新100条信息
|
||||||
const base = baseRequest();
|
const base = baseRequest();
|
||||||
if (!modalInfo.mid) return;
|
if (!modalInfo.mid) return;
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
"/op/contact_customer_service/list_by_mid",
|
"/op/contact_customer_service/list_by_session_id",
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
mid: modalInfo.mid,
|
session_id: modalInfo.session_id,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
limit: 30,
|
limit: 100,
|
||||||
...base,
|
...base,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
@ -252,8 +265,7 @@ export default function Contact() {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
sub_mid: modalInfo.mid,
|
session_id: modalInfo.session_id,
|
||||||
obj_mid: base.b_mid,
|
|
||||||
predicate: 1,
|
predicate: 1,
|
||||||
message: message,
|
message: message,
|
||||||
...base,
|
...base,
|
||||||
|
@ -352,15 +364,134 @@ export default function Contact() {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//获取所有对话
|
||||||
|
const [allMessageList, setAllMessageList] = useState([]);
|
||||||
|
useEffect(() => {
|
||||||
|
if (current !== "all") return;
|
||||||
|
const getAllMessages = async () => {
|
||||||
|
try {
|
||||||
|
const base = baseRequest();
|
||||||
|
const response = await fetch(
|
||||||
|
`/op/contact_customer_service_session/list`,
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
offset: 0,
|
||||||
|
limit: 1000,
|
||||||
|
...base,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const data = await response.json();
|
||||||
|
if (data.ret === -1) {
|
||||||
|
alert(data.msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//获取用户头像、昵称
|
||||||
|
const userMids = data.data.list.map((item) => item.sub_mid);
|
||||||
|
if (userMids.length === 0) {
|
||||||
|
setAllMessageList([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const userResponse = await fetch("/op/account/list_others_by_mids", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
mids: userMids,
|
||||||
|
...base,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
const userData = await userResponse.json();
|
||||||
|
if (userData.ret === -1) {
|
||||||
|
alert(userData.msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//合并信息数组和用户资料数组
|
||||||
|
const combinedArray = data.data.list.map((item1) => {
|
||||||
|
const matchingItem = userData.data.list.find(
|
||||||
|
(item2) => item2.mid === item1.sub_mid
|
||||||
|
);
|
||||||
|
return { ...item1, ...matchingItem };
|
||||||
|
});
|
||||||
|
//处理数据格式
|
||||||
|
const processedArray = combinedArray.map((item) => {
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
session_id: item.id,
|
||||||
|
contents: [{ message: item.recent_message, ct: item.ut }],
|
||||||
|
};
|
||||||
|
});
|
||||||
|
setAllMessageList(processedArray);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
getAllMessages();
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="p-4">
|
<section className="p-4">
|
||||||
<div>
|
{current === "unread" && (
|
||||||
{unreadMessageList.length === 0 && <h1>暂无未读消息</h1>}
|
<div>
|
||||||
{unreadMessageList?.map((item, index) => (
|
{unreadMessageList.length === 0 && <h1>暂无未读消息</h1>}
|
||||||
<MassageItem key={index} item={item} />
|
{unreadMessageList?.map((item, index) => (
|
||||||
))}
|
<MassageItem key={index} item={item} />
|
||||||
</div>
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{current === "all" && (
|
||||||
|
<div>
|
||||||
|
{allMessageList?.map((item, index) => (
|
||||||
|
<MassageItem key={index} item={item} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<MessageModal />
|
<MessageModal />
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function Contact() {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
//当前tab
|
||||||
|
const location = useLocation();
|
||||||
|
const pathname = location.pathname.split("/")[2] || "unread";
|
||||||
|
const [current, setCurrent] = useState(pathname);
|
||||||
|
//tab名称
|
||||||
|
const items = [
|
||||||
|
{
|
||||||
|
label: "未读消息",
|
||||||
|
key: "unread",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "全部消息",
|
||||||
|
key: "all",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const onClick = (e) => {
|
||||||
|
setCurrent(e.key);
|
||||||
|
navigate(e.key);
|
||||||
|
window.location.reload();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Menu
|
||||||
|
onClick={onClick}
|
||||||
|
selectedKeys={[current]}
|
||||||
|
mode="horizontal"
|
||||||
|
items={items}
|
||||||
|
/>
|
||||||
|
<Routes>
|
||||||
|
<Route path="/" element={<Navigate to="/contact/unread" />} />
|
||||||
|
<Route path="/unread" element={<ContactContent current="unread" />} />
|
||||||
|
<Route path="/all" element={<ContactContent current="all" />} />
|
||||||
|
</Routes>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,12 +45,20 @@ const ImageMachineReviewContent = (props) => {
|
||||||
render: (data) => (
|
render: (data) => (
|
||||||
<div>
|
<div>
|
||||||
{data?.map((item, index) => {
|
{data?.map((item, index) => {
|
||||||
if (item.status === 2 || item.status === 5)
|
if (item.status === 2 || item.status === 5) {
|
||||||
return (
|
return (
|
||||||
<p key={index} className="text-green-400">
|
<p key={index} className="text-green-400">
|
||||||
图{index + 1}无违规
|
图{index + 1}无违规
|
||||||
</p>
|
</p>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
if (item.status === 10) {
|
||||||
|
return (
|
||||||
|
<p key={index} className="text-green-400">
|
||||||
|
图{index + 1}审核失败
|
||||||
|
</p>
|
||||||
|
);
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div key={index}>
|
<div key={index}>
|
||||||
<p className="text-red-400">图{index + 1}违规情况:</p>
|
<p className="text-red-400">图{index + 1}违规情况:</p>
|
||||||
|
@ -125,9 +133,10 @@ const ImageMachineReviewContent = (props) => {
|
||||||
<div>
|
<div>
|
||||||
<Space>
|
<Space>
|
||||||
<Space.Compact direction="vertical">
|
<Space.Compact direction="vertical">
|
||||||
{current === "rollbackbymachine" && (
|
{(current === "rollbackbymachine" ||
|
||||||
|
current === "machinereviewfail") && (
|
||||||
<Button type="primary" onClick={() => onClickSubmit(record)}>
|
<Button type="primary" onClick={() => onClickSubmit(record)}>
|
||||||
{current === "rollbackbymachine" && "通过"}
|
通过
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</Space.Compact>
|
</Space.Compact>
|
||||||
|
@ -183,6 +192,9 @@ const ImageMachineReviewContent = (props) => {
|
||||||
case "rollbackbymachine":
|
case "rollbackbymachine":
|
||||||
querryStatus = 4;
|
querryStatus = 4;
|
||||||
break;
|
break;
|
||||||
|
case "machinereviewfail":
|
||||||
|
querryStatus = 10;
|
||||||
|
break;
|
||||||
case "passbymachine":
|
case "passbymachine":
|
||||||
querryStatus = 2;
|
querryStatus = 2;
|
||||||
break;
|
break;
|
||||||
|
@ -287,6 +299,10 @@ export default function ImageMachineReview() {
|
||||||
label: "机审回退",
|
label: "机审回退",
|
||||||
key: "rollbackbymachine",
|
key: "rollbackbymachine",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: "机审失败",
|
||||||
|
key: "machinereviewfail",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: "机审通过",
|
label: "机审通过",
|
||||||
key: "passbymachine",
|
key: "passbymachine",
|
||||||
|
@ -319,6 +335,10 @@ export default function ImageMachineReview() {
|
||||||
path="/rollbackbymachine"
|
path="/rollbackbymachine"
|
||||||
element={<ImageMachineReviewContent current="rollbackbymachine" />}
|
element={<ImageMachineReviewContent current="rollbackbymachine" />}
|
||||||
/>
|
/>
|
||||||
|
<Route
|
||||||
|
path="/machinereviewfail"
|
||||||
|
element={<ImageMachineReviewContent current="machinereviewfail" />}
|
||||||
|
/>
|
||||||
<Route
|
<Route
|
||||||
path="/passbymachine"
|
path="/passbymachine"
|
||||||
element={<ImageMachineReviewContent current="passbymachine" />}
|
element={<ImageMachineReviewContent current="passbymachine" />}
|
||||||
|
|
643
test.jsx
643
test.jsx
|
@ -1,5 +1,5 @@
|
||||||
import React, { useState, useRef, useEffect } from "react";
|
import React, { useState, useEffect, useRef } from "react";
|
||||||
import { Form, Input, Button, Space, Table, Menu, Image } from "antd";
|
import { Divider, Modal, Input, Button, Menu } from "antd";
|
||||||
import {
|
import {
|
||||||
Routes,
|
Routes,
|
||||||
Route,
|
Route,
|
||||||
|
@ -9,334 +9,365 @@ import {
|
||||||
} from "react-router-dom";
|
} from "react-router-dom";
|
||||||
import baseRequest from "../../utils/baseRequest";
|
import baseRequest from "../../utils/baseRequest";
|
||||||
|
|
||||||
const ImageReviewContent = (props) => {
|
export default function Contact() {
|
||||||
const { TextArea } = Input;
|
//请求未读消息队列
|
||||||
const current = props.current;
|
const [unreadMessageList, setUnreadMessageList] = useState([]);
|
||||||
//表头
|
const getUnreadMessages = async () => {
|
||||||
const columns = [
|
if (isModalOpen) return;
|
||||||
{
|
|
||||||
title: "新内容",
|
|
||||||
dataIndex: "newMedia",
|
|
||||||
key: "newMedia",
|
|
||||||
render: (data) => (
|
|
||||||
<div className="flex flex-wrap gap-1">
|
|
||||||
{data?.map((item, index) => (
|
|
||||||
<Image key={index} src={item.urls[0]} width={100} />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "旧内容",
|
|
||||||
dataIndex: "oldMedia",
|
|
||||||
key: "oldMedia",
|
|
||||||
render: (data) => (
|
|
||||||
<div className="flex flex-wrap gap-1">
|
|
||||||
{data?.map((item, index) => (
|
|
||||||
<Image key={index} src={item.urls[0]} width={100} />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "审核结果",
|
|
||||||
dataIndex: "info",
|
|
||||||
key: "info",
|
|
||||||
render: (data) => (
|
|
||||||
<div>
|
|
||||||
{data?.map((item, index) => {
|
|
||||||
if (item.status === 2 || item.status === 5)
|
|
||||||
return (
|
|
||||||
<p key={index} className="text-green-400">
|
|
||||||
图{index + 1}无违规
|
|
||||||
</p>
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<div key={index}>
|
|
||||||
<p className="text-red-400">图{index + 1}违规情况:</p>
|
|
||||||
<div className="flex flex-wrap gap-1">
|
|
||||||
<div className="border-2 border-black bg-gray-200">
|
|
||||||
<p>色情审核:</p>
|
|
||||||
<ul>
|
|
||||||
<li>建议:{item.porn_scene_suggestion}</li>
|
|
||||||
<li>结果:{item.porn_scene_label}</li>
|
|
||||||
<li>结果:{item.porn_scene_rate.toFixed(1)}</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div className="border-2 border-black bg-gray-200">
|
|
||||||
<p>暴恐审核:</p>
|
|
||||||
<ul>
|
|
||||||
<li>建议:{item.terrorism_scene_suggestion}</li>
|
|
||||||
<li>结果:{item.terrorism_scene_label}</li>
|
|
||||||
<li>结果:{item.terrorism_scene_rate.toFixed(1)}</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div className="border-2 border-black bg-gray-200">
|
|
||||||
<p>广告审核:</p>
|
|
||||||
<ul>
|
|
||||||
<li>建议:{item.ad_scene_suggestion}</li>
|
|
||||||
<li>结果:{item.ad_scene_label}</li>
|
|
||||||
<li>结果:{item.ad_scene_rate.toFixed(1)}</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div className="border-2 border-black bg-gray-200">
|
|
||||||
<p>不良审核:</p>
|
|
||||||
<ul>
|
|
||||||
<li>建议:{item.live_scene_suggestion}</li>
|
|
||||||
<li>结果:{item.live_scene_label}</li>
|
|
||||||
<li>结果:{item.live_scene_rate.toFixed(1)}</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div className="border-2 border-black bg-gray-200">
|
|
||||||
<p>logo审核:</p>
|
|
||||||
<ul>
|
|
||||||
<li>建议:{item.logo_scene_suggestion}</li>
|
|
||||||
<li>结果:{item.logo_scene_label}</li>
|
|
||||||
<li>结果:{item.logo_scene_rate.toFixed(1)}</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "提交时间",
|
|
||||||
dataIndex: "submitTime",
|
|
||||||
key: "submitTime",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "备注",
|
|
||||||
dataIndex: "remarks",
|
|
||||||
key: "remarks",
|
|
||||||
render: (_, record) => (
|
|
||||||
<Form.Item name={record.id} initialValue={record.remarks}>
|
|
||||||
<TextArea disabled rows={4} />
|
|
||||||
</Form.Item>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "操作",
|
|
||||||
dataIndex: "opeartion",
|
|
||||||
key: "opeartion",
|
|
||||||
render: (_, record) => (
|
|
||||||
<div>
|
|
||||||
<Space>
|
|
||||||
<Space.Compact direction="vertical">
|
|
||||||
{current === "rollbackbymachine" && (
|
|
||||||
<Button type="primary" onClick={() => onClickSubmit(record)}>
|
|
||||||
{current === "rollbackbymachine" && "通过"}
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</Space.Compact>
|
|
||||||
</Space>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
//给表单绑定ref
|
|
||||||
const formRef = useRef(null);
|
|
||||||
//点击修改状态按钮
|
|
||||||
const onClickSubmit = (record) => {
|
|
||||||
formRef.current.record = record;
|
|
||||||
formRef.current.btn = "submit";
|
|
||||||
formRef.current.submit();
|
|
||||||
};
|
|
||||||
//提交表单
|
|
||||||
const handleSubmit = async () => {
|
|
||||||
//提交数据
|
|
||||||
try {
|
try {
|
||||||
|
//获取全部未读信息
|
||||||
const base = baseRequest();
|
const base = baseRequest();
|
||||||
const response = await fetch("/op/image_audit_task/pass_batch", {
|
const response = await fetch(
|
||||||
|
"/op/contact_customer_service/list_unread_group_by_mid",
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
...base,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const data = await response.json();
|
||||||
|
if (data.ret === -1) {
|
||||||
|
alert(data.msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//获取用户头像、昵称
|
||||||
|
const userMids = data.data.list.map((item) => item.mid);
|
||||||
|
if (userMids.length === 0) {
|
||||||
|
setUnreadMessageList([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const userResponse = await fetch("/op/account/list_others_by_mids", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
tasks: [formRef.current.record.others],
|
mids: userMids,
|
||||||
...base,
|
...base,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
const approveData = await response.json();
|
const userData = await userResponse.json();
|
||||||
if (approveData.ret === -1) {
|
if (userData.ret === -1) {
|
||||||
alert(approveData.msg);
|
alert(userData.msg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
//合并未读信息和用户资料数组
|
||||||
|
const combinedArray = data.data.list.map((item1) => {
|
||||||
|
const matchingItem = userData.data.list.find(
|
||||||
|
(item2) => item2.mid === item1.mid
|
||||||
|
);
|
||||||
|
return { ...item1, ...matchingItem };
|
||||||
|
});
|
||||||
|
setUnreadMessageList(combinedArray);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
//取消数据展示
|
};
|
||||||
setShowData(
|
|
||||||
showData.filter((item) => item.id !== formRef.current.record.others.id)
|
//控制modal是否出现
|
||||||
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
|
|
||||||
|
//轮询更新未读消息队列
|
||||||
|
useEffect(() => {
|
||||||
|
getUnreadMessages();
|
||||||
|
// 设置轮询请求,每隔一定时间执行一次
|
||||||
|
const intervalId = setInterval(() => {
|
||||||
|
getUnreadMessages();
|
||||||
|
}, 3000); // 间隔时间为3秒
|
||||||
|
|
||||||
|
// 在组件卸载时清除定时器
|
||||||
|
return () => {
|
||||||
|
clearInterval(intervalId);
|
||||||
|
};
|
||||||
|
}, [isModalOpen]);
|
||||||
|
|
||||||
|
const { TextArea } = Input;
|
||||||
|
|
||||||
|
const MassageItem = ({ item }) => {
|
||||||
|
//设置消息为已读
|
||||||
|
const ids = item.contents?.map((temItem) => temItem.id);
|
||||||
|
const updateMessageStatus = async () => {
|
||||||
|
try {
|
||||||
|
const base = baseRequest();
|
||||||
|
const response = await fetch(
|
||||||
|
`/op/contact_customer_service/update_by_ids`,
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
is_read: 1,
|
||||||
|
ids: ids,
|
||||||
|
...base,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const data = await response.json();
|
||||||
|
if (data.ret === -1) {
|
||||||
|
alert(data.msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="flex flex-col cursor-pointer"
|
||||||
|
onClick={() => {
|
||||||
|
setModalInfo(item);
|
||||||
|
setIsModalOpen(!isModalOpen);
|
||||||
|
updateMessageStatus();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="flex flex-row py-2 items-center justify-between">
|
||||||
|
<div className="flex flex-row">
|
||||||
|
<img
|
||||||
|
className="w-12 h-12 rounded-full"
|
||||||
|
src={item.avatar.images[0].urls[0]}
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
<div className="flex flex-col justify-around ml-2">
|
||||||
|
<p className="text-base font-bold">
|
||||||
|
<span className="text-white font-bold bg-red-400 px-2 rounded-lg">
|
||||||
|
{item.contents.length}条未读
|
||||||
|
</span>
|
||||||
|
{item.name}
|
||||||
|
</p>
|
||||||
|
<p className="text-gray-400 text-sm">
|
||||||
|
{item.contents[0].message}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p className="text-gray-400 text-sm">
|
||||||
|
{new Date(item.contents[0].ct * 1000).toLocaleString()}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<Divider className="m-0 border-gray-400" />
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
//获取数据
|
|
||||||
const [data, setData] = useState([]);
|
//保存modal中用户的mid和昵称和历史记录
|
||||||
const [offset, setOffset] = useState(0);
|
const [modalInfo, setModalInfo] = useState({});
|
||||||
const [more, setMore] = useState(1);
|
const MessageModal = () => {
|
||||||
const getData = async () => {
|
//获取并保存用户聊天历史记录
|
||||||
if (!more) return;
|
const [history, setHistory] = useState([]);
|
||||||
let querryStatus;
|
//获取聊天记录,并设置为已读
|
||||||
switch (current) {
|
const getHistory = async () => {
|
||||||
case "rollbackbymachine":
|
try {
|
||||||
querryStatus = 4;
|
//获取全部未读信息
|
||||||
break;
|
const base = baseRequest();
|
||||||
case "passbymachine":
|
if (!modalInfo.mid) return;
|
||||||
querryStatus = 2;
|
const response = await fetch(
|
||||||
break;
|
"/op/contact_customer_service/list_by_mid",
|
||||||
case "passbyoperator":
|
{
|
||||||
querryStatus = 5;
|
method: "POST",
|
||||||
break;
|
headers: {
|
||||||
default:
|
"Content-Type": "application/json",
|
||||||
break;
|
},
|
||||||
}
|
body: JSON.stringify({
|
||||||
try {
|
mid: modalInfo.mid,
|
||||||
const base = baseRequest();
|
offset: 0,
|
||||||
const response = await fetch("/op/image_audit_task/list", {
|
limit: 100,
|
||||||
method: "POST",
|
...base,
|
||||||
headers: {
|
}),
|
||||||
"Content-Type": "application/json",
|
}
|
||||||
},
|
);
|
||||||
body: JSON.stringify({
|
const data = await response.json();
|
||||||
status: querryStatus,
|
if (data.ret === -1) {
|
||||||
offset: offset,
|
alert(data.msg);
|
||||||
limit: 20,
|
return;
|
||||||
...base,
|
}
|
||||||
}),
|
setHistory((prev) => {
|
||||||
});
|
const temData = [...data.data.list].reverse();
|
||||||
const temData = await response.json();
|
if (
|
||||||
console.log(
|
prev.length === 0 ||
|
||||||
JSON.stringify({
|
temData[temData.length - 1].id !== prev[prev.length - 1].id
|
||||||
status: querryStatus,
|
) {
|
||||||
offset: offset,
|
return temData;
|
||||||
limit: 20,
|
} else {
|
||||||
...base,
|
return prev;
|
||||||
})
|
}
|
||||||
);
|
});
|
||||||
console.log(temData);
|
} catch (error) {
|
||||||
if (temData.ret === -1) {
|
console.error(error);
|
||||||
alert(temData.msg);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
//匹配表格格式
|
};
|
||||||
const structedData = temData.data.list.map((item, index) => {
|
|
||||||
return {
|
//轮询更新聊天记录
|
||||||
key: index,
|
useEffect(() => {
|
||||||
id: item.id,
|
getHistory();
|
||||||
newMedia: item?.audited_media?.images,
|
// 设置轮询请求,每隔一定时间执行一次
|
||||||
oldMedia: item?.old_media?.images,
|
const intervalId = setInterval(() => {
|
||||||
info: item.image_audits,
|
getHistory();
|
||||||
submitTime: new Date(item.ct * 1000).toLocaleString(),
|
}, 3000); // 间隔时间为3秒
|
||||||
remarks: item.remarks,
|
|
||||||
others: {
|
// 在组件卸载时清除定时器
|
||||||
id: item.id,
|
return () => {
|
||||||
audited_media: item.audited_media,
|
clearInterval(intervalId);
|
||||||
associative_database: item.associative_database,
|
};
|
||||||
associative_table_name: item.associative_table_name,
|
}, [modalInfo]);
|
||||||
associative_table_id: item.associative_table_id,
|
|
||||||
associative_table_column: item.associative_table_column,
|
//将最新发送的信息设置为已读
|
||||||
|
const updateNewMessageToRead = async () => {
|
||||||
|
try {
|
||||||
|
//设置消息为已读
|
||||||
|
const base = baseRequest();
|
||||||
|
const ids = history.map((item) => {
|
||||||
|
if (item.is_read === 1) return;
|
||||||
|
return item.id;
|
||||||
|
});
|
||||||
|
if (ids.length === 0) return;
|
||||||
|
const updateResponse = await fetch(
|
||||||
|
`/op/contact_customer_service/update_by_ids`,
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
is_read: 1,
|
||||||
|
ids: ids,
|
||||||
|
...base,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const updateData = await updateResponse.json();
|
||||||
|
if (updateData.ret === -1) {
|
||||||
|
alert(updateData.msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const sendMessage = async (message) => {
|
||||||
|
//请求接口发送私信
|
||||||
|
try {
|
||||||
|
const base = baseRequest();
|
||||||
|
const response = await fetch(`/op/contact_customer_service/create`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
};
|
body: JSON.stringify({
|
||||||
});
|
sub_mid: modalInfo.mid,
|
||||||
setData([...data, ...structedData]);
|
obj_mid: base.b_mid,
|
||||||
setOffset(temData.data.offset);
|
predicate: 1,
|
||||||
setMore(temData.data.more);
|
message: message,
|
||||||
} catch (error) {
|
...base,
|
||||||
console.error(error);
|
}),
|
||||||
}
|
});
|
||||||
};
|
const data = await response.json();
|
||||||
useEffect(() => {
|
if (data.ret === -1) {
|
||||||
getData();
|
alert(data.msg);
|
||||||
}, [current]);
|
return;
|
||||||
|
}
|
||||||
|
setMessageContent("");
|
||||||
|
getHistory();
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
//展示的数据
|
//保存输入框内容
|
||||||
const [showData, setShowData] = useState([]);
|
const [messageContent, setMessageContent] = useState("");
|
||||||
useEffect(() => {
|
|
||||||
setShowData(data);
|
//自动滚动到聊天记录底部
|
||||||
}, [data]);
|
const modalContentRef = useRef();
|
||||||
//表单提交失败
|
useEffect(() => {
|
||||||
const onFinishFailed = (errorInfo) => {
|
if (modalContentRef.current) {
|
||||||
console.log("Failed:", errorInfo);
|
modalContentRef.current.scrollTop =
|
||||||
};
|
modalContentRef.current.scrollHeight;
|
||||||
return (
|
}
|
||||||
<div style={{ marginLeft: 20, marginRight: 20 }}>
|
}, [history]);
|
||||||
<p>
|
|
||||||
{current === "rollbackbymachine" && `机审核违规${showData.length}条`}
|
return (
|
||||||
{current === "passbymachine" && `机审核通过${showData.length}条`}
|
<Modal
|
||||||
{current === "passbyoperator" && `运营通过${showData.length}条`}
|
title={`与【${modalInfo.name}】的聊天`}
|
||||||
</p>
|
footer={null}
|
||||||
<Form
|
open={isModalOpen}
|
||||||
ref={formRef}
|
onCancel={async () => {
|
||||||
name="remarks"
|
setIsModalOpen(false);
|
||||||
onFinish={handleSubmit}
|
await updateNewMessageToRead();
|
||||||
onFinishFailed={onFinishFailed}
|
}}
|
||||||
>
|
>
|
||||||
<Table
|
<div
|
||||||
columns={columns}
|
ref={modalContentRef}
|
||||||
dataSource={showData}
|
className="flex flex-col h-96 overflow-y-scroll"
|
||||||
pagination={{ pageSize: 20 }}
|
>
|
||||||
scroll={{ y: window.innerHeight - 300 }}
|
{history?.map((item, index) => {
|
||||||
|
if (item.predicate === 1) {
|
||||||
|
return (
|
||||||
|
<div key={index} className="flex flex-row justify-end my-1">
|
||||||
|
<div className="flex flex-col mr-2 p-2 bg-gray-200 rounded-lg">
|
||||||
|
<p className="text-base">{item.message}</p>
|
||||||
|
<p className="text-gray-400 text-sm text-end mt-2">
|
||||||
|
{new Date(item.ct * 1000).toLocaleString()}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<img
|
||||||
|
className="w-12 h-12 rounded-full"
|
||||||
|
src="https://s2.loli.net/2023/08/03/mu51haYNUOPXkqR.png"
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<div key={index} className="flex flex-row justify-start my-1">
|
||||||
|
<img
|
||||||
|
className="w-12 h-12 rounded-full"
|
||||||
|
src={modalInfo.avatar.images[0].urls[0]}
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
<div className="flex flex-col ml-2 p-2 bg-gray-200 rounded-lg">
|
||||||
|
<p className="text-base">{item.message}</p>
|
||||||
|
<p className="text-gray-400 text-sm text-start mt-2">
|
||||||
|
{new Date(item.ct * 1000).toLocaleString()}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<TextArea
|
||||||
|
value={messageContent}
|
||||||
|
onChange={(e) => setMessageContent(e.target.value)}
|
||||||
|
className="mt-2"
|
||||||
|
rows={2}
|
||||||
/>
|
/>
|
||||||
</Form>
|
<div className="flex flex-col mt-2">
|
||||||
</div>
|
<Button
|
||||||
);
|
onClick={() => sendMessage(messageContent)}
|
||||||
};
|
className="w-24 self-end"
|
||||||
|
type="primary"
|
||||||
export default function ImageReview() {
|
>
|
||||||
const navigate = useNavigate();
|
发送
|
||||||
//当前tab
|
</Button>
|
||||||
const location = useLocation();
|
</div>
|
||||||
const pathname = location.pathname.split("/")[2] || "rollbackbymachine";
|
</Modal>
|
||||||
const [current, setCurrent] = useState(pathname);
|
);
|
||||||
//tab名称
|
|
||||||
const items = [
|
|
||||||
{
|
|
||||||
label: "机审回退",
|
|
||||||
key: "rollbackbymachine",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "机审通过",
|
|
||||||
key: "passbymachine",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "运营通过",
|
|
||||||
key: "passbyoperator",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
const onClick = (e) => {
|
|
||||||
setCurrent(e.key);
|
|
||||||
navigate(e.key);
|
|
||||||
window.location.reload();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<section className="p-4">
|
||||||
<Menu
|
<div>
|
||||||
onClick={onClick}
|
{unreadMessageList.length === 0 && <h1>暂无未读消息</h1>}
|
||||||
selectedKeys={[current]}
|
{unreadMessageList?.map((item, index) => (
|
||||||
mode="horizontal"
|
<MassageItem key={index} item={item} />
|
||||||
items={items}
|
))}
|
||||||
/>
|
</div>
|
||||||
<Routes>
|
<MessageModal />
|
||||||
<Route
|
</section>
|
||||||
path="/"
|
|
||||||
element={<Navigate to="/imageReview/rollbackbymachine" />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path="/rollbackbymachine"
|
|
||||||
element={<ImageReviewContent current="rollbackbymachine" />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path="/passbymachine"
|
|
||||||
element={<ImageReviewContent current="passbymachine" />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path="/passbyoperator"
|
|
||||||
element={<ImageReviewContent current="passbyoperator" />}
|
|
||||||
/>
|
|
||||||
</Routes>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue