添加合伙人和代运营设置
This commit is contained in:
parent
b175252714
commit
c6ac80cd12
|
@ -190,6 +190,28 @@ export default function Wallet() {
|
|||
style={{ maxWidth: "12px" }}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
onClick={() => router.push("/bill/income/income_querry")}
|
||||
className="flex justify-between items-center py-4"
|
||||
>
|
||||
<div className="flex flex-row items-center">
|
||||
<FontAwesomeIcon
|
||||
icon={faPrint}
|
||||
size="xl"
|
||||
color="#60a5fa"
|
||||
className="w-[28px]"
|
||||
style={{ maxWidth: "20px" }}
|
||||
/>
|
||||
<span className="text-base text-white font-medium ml-2">
|
||||
近一周收支
|
||||
</span>
|
||||
</div>
|
||||
<FontAwesomeIcon
|
||||
icon={faAngleRight}
|
||||
size="xl"
|
||||
style={{ maxWidth: "12px" }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -155,7 +155,6 @@ export default function CreateImagePost() {
|
|||
...imageAssets.filter((it) => it.id != undefined).map((it) => it.id),
|
||||
...media.image_ids,
|
||||
];
|
||||
debugger;
|
||||
try {
|
||||
const body = {
|
||||
c_type: parseInt(price) ? 1 : 0,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
"use client";
|
||||
|
||||
import React, { useEffect, useState, useRef } from "react";
|
||||
import { Image, Toast } from "antd-mobile";
|
||||
import { Image, Toast, Dialog } from "antd-mobile";
|
||||
import { useRouter, useParams } from "next/navigation";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import {
|
||||
|
@ -20,6 +20,7 @@ export default function PersonSpaceIntroduce() {
|
|||
const base = baseRequest();
|
||||
const router = useRouter();
|
||||
const contentBox = useRef();
|
||||
const handler = useRef(null);
|
||||
// 获取屏幕高度
|
||||
// const scrollHeight = 600;
|
||||
const [visible, setVisible] = useState(false);
|
||||
|
@ -249,17 +250,34 @@ export default function PersonSpaceIntroduce() {
|
|||
if (data?.admission_price === 0) {
|
||||
handleJoinFreeSpace();
|
||||
} else {
|
||||
// router.push("/pay");
|
||||
router.push(
|
||||
"/webView/" +
|
||||
encodeURIComponent(
|
||||
"/zone/pay/" +
|
||||
data?.id +
|
||||
"/h5_zone_admission/0" +
|
||||
"?base=" +
|
||||
encodeURIComponent(JSON.stringify(base))
|
||||
)
|
||||
);
|
||||
handler.current = Dialog.confirm({
|
||||
header: "提醒",
|
||||
content:
|
||||
"本空间内容为达人创建并维护,属于虚拟服务,不可退款,且空间内存在部分内容需要另外付费,请再次确认是否付费加入",
|
||||
bodyStyle: {
|
||||
maxHeight: "none",
|
||||
width: "80vw",
|
||||
position: "fixed",
|
||||
top: "200px",
|
||||
left: "10vw",
|
||||
"--text-color": "#fff",
|
||||
},
|
||||
onAction: (res) => {
|
||||
if (res.key === "confirm") {
|
||||
// router.push("/pay");
|
||||
router.push(
|
||||
"/webView/" +
|
||||
encodeURIComponent(
|
||||
"/zone/pay/" +
|
||||
data?.id +
|
||||
"/h5_zone_admission/0" +
|
||||
"?base=" +
|
||||
encodeURIComponent(JSON.stringify(base))
|
||||
)
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
|
|
@ -19,6 +19,8 @@ export default function Setting() {
|
|||
const router = useRouter();
|
||||
const params = useParams();
|
||||
const [streamerInfo, setStreamerInfo] = useState(null);
|
||||
//是否对主播展示代运营设置
|
||||
const [isAgencyHided, setIsAgencyHided] = useState(false);
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const { mid } = params;
|
||||
|
@ -31,6 +33,7 @@ export default function Setting() {
|
|||
...info,
|
||||
refund_enable: res?.refund_enable,
|
||||
});
|
||||
isShowThird(parseInt(mid));
|
||||
});
|
||||
}
|
||||
})();
|
||||
|
@ -112,6 +115,27 @@ export default function Setting() {
|
|||
// console.error(error);
|
||||
}
|
||||
};
|
||||
const isShowThird = async (id) => {
|
||||
console.log(id);
|
||||
try {
|
||||
const _data = await requireAPI("POST", "/api/zone_third_partner/list", {
|
||||
body: {
|
||||
zid: id,
|
||||
},
|
||||
});
|
||||
if (_data.ret === -1) {
|
||||
Toast.show({
|
||||
icon: "fail",
|
||||
content: _data.msg,
|
||||
position: "top",
|
||||
});
|
||||
return;
|
||||
}
|
||||
setIsAgencyHided(_data.data.zone_third_partner?.is_hided != 1);
|
||||
} catch (error) {
|
||||
// console.error(error);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className="">
|
||||
<div className="p-4 fixed top-0 z-10 w-full">
|
||||
|
@ -233,8 +257,47 @@ export default function Setting() {
|
|||
</div>
|
||||
<Divider />
|
||||
</li>
|
||||
{isAgencyHided && (
|
||||
<li>
|
||||
<div
|
||||
onClick={() =>
|
||||
router.push("agencySetting?zid=" + streamerInfo.id)
|
||||
}
|
||||
className="flex justify-between"
|
||||
>
|
||||
<span className="text-base text-white">代运营设置</span>
|
||||
<FontAwesomeIcon
|
||||
icon={faAngleRight}
|
||||
size="xl"
|
||||
style={{ maxWidth: "12px" }}
|
||||
/>
|
||||
</div>
|
||||
<Divider />
|
||||
</li>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{(streamerInfo?.visitor_role === 1 ||
|
||||
streamerInfo?.visitor_role === 2) && (
|
||||
<li>
|
||||
<div
|
||||
onClick={() =>
|
||||
router.push(
|
||||
`collaboratorSetting?zid=${streamerInfo.id}&visitor_role=${streamerInfo.visitor_role}`
|
||||
)
|
||||
}
|
||||
className="flex justify-between"
|
||||
>
|
||||
<span className="text-base text-white">合伙人设置</span>
|
||||
<FontAwesomeIcon
|
||||
icon={faAngleRight}
|
||||
size="xl"
|
||||
style={{ maxWidth: "12px" }}
|
||||
/>
|
||||
</div>
|
||||
<Divider />
|
||||
</li>
|
||||
)}
|
||||
{streamerInfo?.refund_enable === 1 &&
|
||||
streamerInfo?.admission_price > 0 &&
|
||||
streamerInfo?.visitor_role === 0 && (
|
||||
|
|
|
@ -0,0 +1,448 @@
|
|||
"use client";
|
||||
import React, {
|
||||
useState,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useCallback,
|
||||
useRef,
|
||||
} from "react";
|
||||
import {
|
||||
Image,
|
||||
Toast,
|
||||
Dialog,
|
||||
Picker,
|
||||
Avatar,
|
||||
Space,
|
||||
SpinLoading,
|
||||
} from "antd-mobile";
|
||||
import Empty from "@/components/Empty";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import {
|
||||
faAngleLeft,
|
||||
faAdd,
|
||||
faSortDown,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import requireAPI from "@/utils/requireAPI";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import OwnInput from "@/components/OwnInput";
|
||||
import { get } from "@/utils/storeInfo";
|
||||
import { JSEncrypt } from "jsencrypt";
|
||||
export default function AgencySetting() {
|
||||
const [data, setData] = useState(null);
|
||||
const [modalVisible, setModalVisible] = useState(false);
|
||||
const [isloading, setIsloading] = useState([]);
|
||||
const searchParams = useSearchParams();
|
||||
const router = useRouter();
|
||||
const handler = useRef(null);
|
||||
const getData = async () => {
|
||||
const zid = Number(searchParams.get("zid"));
|
||||
try {
|
||||
setIsloading(true);
|
||||
const body = {
|
||||
zid,
|
||||
};
|
||||
const _data = await requireAPI("POST", "/api/zone_third_partner/list", {
|
||||
body,
|
||||
});
|
||||
if (_data.ret === -1) {
|
||||
setIsloading(false);
|
||||
Toast.show({
|
||||
icon: "fail",
|
||||
content: _data.msg,
|
||||
position: "top",
|
||||
});
|
||||
return;
|
||||
}
|
||||
setData(_data.data.zone_third_partner);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
setIsloading(false);
|
||||
};
|
||||
useEffect(() => {
|
||||
getData();
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
if (!modalVisible && handler.current) {
|
||||
handler.current?.close();
|
||||
}
|
||||
}, [modalVisible]);
|
||||
|
||||
const handleAddAgency = () => {
|
||||
setModalVisible(!modalVisible);
|
||||
const zid = Number(searchParams.get("zid"));
|
||||
handler.current = Dialog.show({
|
||||
content: <ModalMask setModalVisible={setModalVisible} zid={zid} />,
|
||||
bodyStyle: {
|
||||
backgroundColor: "#17161A",
|
||||
maxHeight: "none",
|
||||
width: "90vw",
|
||||
position: "fixed",
|
||||
top: "200px",
|
||||
left: "5vw",
|
||||
"--text-color": "#fff",
|
||||
color: "#fff",
|
||||
},
|
||||
});
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
{isloading && (
|
||||
<div
|
||||
className="bg-[#00000099] fixed top-0 w-full text-center flex items-center justify-center h-screen"
|
||||
// style={{ height: scrollHeight - 60 + "px" }}
|
||||
>
|
||||
<SpinLoading />
|
||||
</div>
|
||||
)}
|
||||
{/* 头部标题 */}
|
||||
<div className="p-4 fixed top-0 z-10 w-full bg-black">
|
||||
<div className="w-9 h-9 flex items-center justify-center bg-[#FFFFFF1A] rounded-full absolute">
|
||||
<FontAwesomeIcon
|
||||
icon={faAngleLeft}
|
||||
style={{ maxWidth: "12px" }}
|
||||
size="xl"
|
||||
onClick={() => {
|
||||
router.back();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<p className="text-base text-center leading-9">代运营设置</p>
|
||||
</div>
|
||||
{/* 内容 */}
|
||||
<div className="p-4 pt-20">
|
||||
<div className="flex justify-center items-center">
|
||||
{data ? (
|
||||
<Image
|
||||
src={data?.third_partner_account.avatar.images[0].urls[0]}
|
||||
width={74}
|
||||
height={74}
|
||||
className="h-full mr-1"
|
||||
placeholder=""
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
onClick={handleAddAgency}
|
||||
className="border border-white rounded-full w-[4.6rem] h-[4.6rem] flex items-center justify-center"
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon={faAdd}
|
||||
style={{ maxWidth: "20px" }}
|
||||
size="2xl"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="mt-8 text-[#FFFFFF80] text-sm">
|
||||
<p className="text-base font-medium">注意事项:</p>
|
||||
<p>
|
||||
1、一个空间仅可设置一个代运营,若您的代运营团队为多人,请设置代运营主账号后,让代运营主账号进入当前空间设置合伙人;
|
||||
</p>
|
||||
<p>
|
||||
2、设置完成后无法再次修改人员和分成比例,请确认后再提交,后续如需修改请联系人工客服;
|
||||
</p>
|
||||
<p>
|
||||
3、您获得的收益会按照您设置的分成比例直接转移至代运营及协作者账户,您将不会得到这部分的收益,如有疑问请咨询人工客服。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const ModalMask = ({ setModalVisible, zid }) => {
|
||||
const [isloading, setIsloading] = useState([]);
|
||||
const [isSelected, setIsSelected] = useState(false);
|
||||
const [seconds, setSeconds] = useState(60);
|
||||
const [agencyData, setAgencyData] = useState(null);
|
||||
const [mobilePhone, setMobilePhone] = useState("");
|
||||
const [regionCode, setRegionCode] = useState("");
|
||||
const [veriCode, setVeriCode] = useState("");
|
||||
const [userId, setUserId] = useState("");
|
||||
const [isCounting, setIsCounting] = useState(false);
|
||||
const [rate, setRate] = useState([null]);
|
||||
//生成比例选项
|
||||
const generateItems = useCallback((min, max) => {
|
||||
const items = [];
|
||||
for (let i = min; i <= max; i++) {
|
||||
items.push({ label: `${i.toString()}%`, value: i.toString() });
|
||||
}
|
||||
return items;
|
||||
}, []);
|
||||
const rates = useMemo(() => generateItems(1, 50), []);
|
||||
useEffect(() => {
|
||||
async function getMobilePhone() {
|
||||
setMobilePhone(await get("mobile_phone"));
|
||||
setRegionCode(await get("region_code"));
|
||||
}
|
||||
getMobilePhone();
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
let interval;
|
||||
if (isCounting && seconds > 0) {
|
||||
interval = setInterval(() => {
|
||||
setSeconds(seconds - 1);
|
||||
}, 1000);
|
||||
} else {
|
||||
setIsCounting(false);
|
||||
setSeconds(60);
|
||||
clearInterval(interval);
|
||||
}
|
||||
return () => {
|
||||
clearInterval(interval);
|
||||
};
|
||||
}, [isCounting, seconds]);
|
||||
|
||||
const handleSearch = async (id) => {
|
||||
try {
|
||||
setIsloading(true);
|
||||
const body = {
|
||||
user_id: parseInt(id, 10),
|
||||
};
|
||||
const _data = await requireAPI(
|
||||
"POST",
|
||||
"/api/account/list_others_by_user_id",
|
||||
{
|
||||
body,
|
||||
}
|
||||
);
|
||||
if (_data.ret === -1) {
|
||||
setIsloading(false);
|
||||
Toast.show({
|
||||
icon: "fail",
|
||||
content: _data.msg,
|
||||
position: "top",
|
||||
});
|
||||
return;
|
||||
}
|
||||
setAgencyData(_data.data.account);
|
||||
setIsSelected(false);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
//点击获取验证码
|
||||
const handleVerification = async () => {
|
||||
if (!isSelected) {
|
||||
Toast.show({
|
||||
icon: "fail",
|
||||
content: "请先选中用户",
|
||||
position: "top",
|
||||
});
|
||||
return;
|
||||
}
|
||||
//开始倒计时
|
||||
setIsCounting(true);
|
||||
//对手机号进行RSA加密
|
||||
const encrypt = new JSEncrypt();
|
||||
encrypt.setPublicKey(process.env.NEXT_PUBLIC_RSA_KEY);
|
||||
const mobile_phone = encrypt.encrypt(mobilePhone);
|
||||
//发送短信验证码
|
||||
try {
|
||||
const data = await fetch(`/api/veri_code/send`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
mobile_phone,
|
||||
region_code: regionCode,
|
||||
}),
|
||||
});
|
||||
if (data.ret === -1) {
|
||||
Toast.show({
|
||||
icon: "fail",
|
||||
content: data.msg,
|
||||
position: "top",
|
||||
});
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
// console.error(error);
|
||||
}
|
||||
};
|
||||
const handleSubmit = async () => {
|
||||
if (!isSelected) {
|
||||
Toast.show({
|
||||
icon: "fail",
|
||||
content: "请先选中用户",
|
||||
position: "top",
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (!veriCode) {
|
||||
Toast.show({
|
||||
icon: "fail",
|
||||
content: "请输入验证码",
|
||||
position: "top",
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (!rate[0]) {
|
||||
Toast.show({
|
||||
icon: "fail",
|
||||
content: "请选择分成比例",
|
||||
position: "top",
|
||||
});
|
||||
return;
|
||||
}
|
||||
try {
|
||||
//对手机号进行RSA加密
|
||||
const encrypt = new JSEncrypt();
|
||||
encrypt.setPublicKey(process.env.NEXT_PUBLIC_RSA_KEY);
|
||||
const mobile_phone = encrypt.encrypt(mobilePhone);
|
||||
const body = {
|
||||
zid,
|
||||
third_partner_mid: agencyData.mid,
|
||||
region_code: regionCode,
|
||||
mobile_phone: mobile_phone,
|
||||
veri_code: veriCode,
|
||||
sharing_ratio: parseInt(rate[0], 10) / 100,
|
||||
};
|
||||
const _data = await requireAPI("POST", "/api/zone_third_partner/create", {
|
||||
body,
|
||||
});
|
||||
if (_data.ret === -1) {
|
||||
Toast.show({
|
||||
icon: "fail",
|
||||
content: _data.msg,
|
||||
position: "top",
|
||||
});
|
||||
return;
|
||||
}
|
||||
setModalVisible(false);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className="flex flex-col w-full rounded-3xl ">
|
||||
<div className="flex flex-row items-center mb-4">
|
||||
<span className="text-base font-medium">搜索用户:</span>
|
||||
<OwnInput
|
||||
placeholder="请输入用户ID"
|
||||
type="number"
|
||||
onChange={(value) => {
|
||||
setUserId(value);
|
||||
}}
|
||||
value={userId}
|
||||
className="flex-1 bg-[#FFFFFF1A] text-white rounded-2xl px-4 h-8 mx-2"
|
||||
/>
|
||||
<span
|
||||
onClick={() => handleSearch(userId)}
|
||||
className="text-[#FF669E] text-base font-medium"
|
||||
>
|
||||
搜索
|
||||
</span>
|
||||
</div>
|
||||
{agencyData && (
|
||||
<div
|
||||
className="flex flex-row items-center rounded-2xl p-4 mb-4 border"
|
||||
style={{ borderColor: isSelected ? "#FF669E" : "#2c2b2f" }}
|
||||
>
|
||||
<Avatar
|
||||
src={agencyData?.avatar?.images[0]?.urls[0]}
|
||||
fit="cover"
|
||||
style={{ "--border-radius": "100px", "--size": "42px" }}
|
||||
/>
|
||||
<div className="flex flex-1 flex-col justify-around items-start ml-2">
|
||||
<span className="text-base font-medium whitespace-nowrap">
|
||||
{agencyData?.name}
|
||||
</span>
|
||||
<div className="flex flex-row items-center py-0.5 px-2 bg-[#FFFFFF1A] rounded-full">
|
||||
<Image
|
||||
src={
|
||||
process.env.NEXT_PUBLIC_WEB_ASSETS_URL + "/icons/info/ID.png"
|
||||
}
|
||||
width={14}
|
||||
height={14}
|
||||
className="w-4 h-full mr-1"
|
||||
/>
|
||||
<span className="text-xs font-medium ml-0.5 whitespace-nowrap">
|
||||
{agencyData?.user_id}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="px-4 py-2 text-sm font-medium rounded-full"
|
||||
style={{
|
||||
backgroundColor: isSelected ? "#FFFFFF1A" : "#FF669E",
|
||||
}}
|
||||
onClick={() => setIsSelected(!isSelected)}
|
||||
>
|
||||
{isSelected ? "取消" : "选择"}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex flex-row items-center mb-4">
|
||||
<span className="text-base font-medium">
|
||||
手机号:
|
||||
<span className="text-[#FFFFFF80] ml-2">
|
||||
{mobilePhone?.replace(/(\d{3})\d{4}(\d{4})/, "$1****$2")}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex flex-row items-center mb-4">
|
||||
<span className="text-base font-medium">验证码:</span>
|
||||
<OwnInput
|
||||
placeholder="请输入验证码"
|
||||
type="number"
|
||||
onChange={(value) => setVeriCode(value)}
|
||||
value={veriCode}
|
||||
className="flex-1 bg-[#FFFFFF1A] rounded-2xl px-4 h-8 mx-2"
|
||||
/>
|
||||
<div
|
||||
className="bg-[#FF669E] px-2 py-1 text-sm font-medium rounded-full"
|
||||
disabled={isCounting}
|
||||
onClick={handleVerification}
|
||||
>
|
||||
{isCounting ? `(${seconds})重新发送` : "获取验证码"}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row items-center mb-4">
|
||||
<span className="text-base font-medium">分成比例:</span>
|
||||
<div className="w-1/3">
|
||||
<Picker columns={[rates]} onConfirm={setRate} value={rate}>
|
||||
{(items, { open }) => {
|
||||
return (
|
||||
<Space
|
||||
align="center"
|
||||
direction="horizontal"
|
||||
justify="center"
|
||||
onClick={open}
|
||||
>
|
||||
{items.every((item) => item === null)
|
||||
? "未选择"
|
||||
: items.map((item) => item?.label ?? "未选择").join("")}
|
||||
<FontAwesomeIcon
|
||||
icon={faSortDown}
|
||||
style={{ maxWidth: "12px", marginBottom: 6 }}
|
||||
size="lg"
|
||||
/>
|
||||
</Space>
|
||||
);
|
||||
}}
|
||||
</Picker>
|
||||
</div>
|
||||
</div>
|
||||
<span className="text-[#F53030] text-xs font-medium">
|
||||
注意事项:分成比例不得超过50%且确认后无法修改
|
||||
</span>
|
||||
<div className="grid grid-cols-2 gap-2 mt-2">
|
||||
<div
|
||||
className="bg-[#FF669E] text-center w-full py-2 text-md font-medium rounded-full"
|
||||
onClick={handleSubmit}
|
||||
>
|
||||
确认
|
||||
</div>
|
||||
<div
|
||||
className="bg-[#FFFFFF1A] text-center w-full py-2 text-md font-medium rounded-full"
|
||||
onClick={() => setModalVisible(false)}
|
||||
>
|
||||
取消
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,515 @@
|
|||
"use client";
|
||||
import React, {
|
||||
useState,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useCallback,
|
||||
useRef,
|
||||
} from "react";
|
||||
import {
|
||||
Image,
|
||||
Toast,
|
||||
SpinLoading,
|
||||
Picker,
|
||||
Avatar,
|
||||
Space,
|
||||
List,
|
||||
Dialog,
|
||||
} from "antd-mobile";
|
||||
import Empty from "@/components/Empty";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import {
|
||||
faAngleLeft,
|
||||
faAdd,
|
||||
faSortDown,
|
||||
faMoneyBill,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import requireAPI from "@/utils/requireAPI";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import OwnInput from "@/components/OwnInput";
|
||||
import { get } from "@/utils/storeInfo";
|
||||
import { JSEncrypt } from "jsencrypt";
|
||||
export default function CollaboratorSetting() {
|
||||
const [data, setData] = useState(null);
|
||||
const [selfMid, setSelfMid] = useState();
|
||||
const [modalVisible, setModalVisible] = useState(false);
|
||||
const [isloading, setIsloading] = useState([]);
|
||||
const searchParams = useSearchParams();
|
||||
const router = useRouter();
|
||||
const handler = useRef(null);
|
||||
useEffect(() => {
|
||||
getData();
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
if (!modalVisible && handler.current) {
|
||||
handler.current?.close();
|
||||
}
|
||||
}, [modalVisible]);
|
||||
//计算代运营剩余比例
|
||||
const remainingRate = useMemo(() => {
|
||||
if (!data) return;
|
||||
const totalRate = data?.list?.reduce((acc, cur) => {
|
||||
return acc + cur.sharing_ratio || 0;
|
||||
}, 0);
|
||||
return !totalRate
|
||||
? 0
|
||||
: (data?.zone_third_partner?.sharing_ratio - totalRate).toFixed(2);
|
||||
}, [data]);
|
||||
const getData = async () => {
|
||||
const zid = Number(searchParams.get("zid"));
|
||||
const visitor_role = Number(searchParams.get("visitor_role"));
|
||||
try {
|
||||
setIsloading(true);
|
||||
setSelfMid(zid);
|
||||
const body = {
|
||||
zid,
|
||||
visitor_role,
|
||||
};
|
||||
const _data = await requireAPI("POST", "/api/zone_collaborator/list", {
|
||||
body,
|
||||
});
|
||||
if (_data.ret === -1) {
|
||||
setIsloading(false);
|
||||
Toast.show({
|
||||
icon: "fail",
|
||||
content: _data.msg,
|
||||
position: "top",
|
||||
});
|
||||
return;
|
||||
}
|
||||
setData(_data.data);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
setIsloading(false);
|
||||
};
|
||||
const handleAddCollaborator = () => {
|
||||
setModalVisible(!modalVisible);
|
||||
const zid = Number(searchParams.get("zid"));
|
||||
handler.current = Dialog.show({
|
||||
content: (
|
||||
<ModalMask
|
||||
setModalVisible={setModalVisible}
|
||||
zid={zid}
|
||||
handleRefresh={getData}
|
||||
/>
|
||||
),
|
||||
bodyStyle: {
|
||||
backgroundColor: "#17161A",
|
||||
maxHeight: "none",
|
||||
width: "80vw",
|
||||
position: "fixed",
|
||||
top: "200px",
|
||||
left: "10vw",
|
||||
"--text-color": "#fff",
|
||||
color: "#fff",
|
||||
},
|
||||
});
|
||||
};
|
||||
//删除协作者
|
||||
const handleDelete = async (id) => {
|
||||
const visitor_role = Number(searchParams.get("visitor_role"));
|
||||
if (visitor_role !== 1) return;
|
||||
try {
|
||||
const body = {
|
||||
id,
|
||||
};
|
||||
const _data = await requireAPI("POST", "/api/zone_collaborator/delete", {
|
||||
body,
|
||||
});
|
||||
if (_data.ret === -1) {
|
||||
Toast.show({
|
||||
icon: "fail",
|
||||
content: _data.msg,
|
||||
position: "top",
|
||||
});
|
||||
return;
|
||||
}
|
||||
Toast.show({
|
||||
icon: "success",
|
||||
content: "移除合伙人成功",
|
||||
position: "top",
|
||||
});
|
||||
getData();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
//单个协作者组件
|
||||
const CollaboratorItem = useCallback(({ item }) => {
|
||||
return (
|
||||
<div className="border-b border-[#ffffff80]">
|
||||
<List.Item className="p-0 bg-transparent">
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center">
|
||||
<Avatar
|
||||
src={item?.collaborator_account?.avatar.images[0].urls[0]}
|
||||
fit="cover"
|
||||
style={{ "--border-radius": "100px", "--size": "42px" }}
|
||||
/>
|
||||
<div className="ml-2 justify-around flex-1">
|
||||
<p
|
||||
className="text-base font-medium"
|
||||
numberOfLines={1}
|
||||
ellipsizeMode="tail"
|
||||
>
|
||||
{item?.collaborator_account?.name}
|
||||
</p>
|
||||
<div className="flex">
|
||||
<div className="h-4 mr-1 flex items-center text-xs bg-[#ffffff18] rounded-full px-2 py-2.5 mt-1 w-max">
|
||||
<Image
|
||||
src={
|
||||
process.env.NEXT_PUBLIC_WEB_ASSETS_URL +
|
||||
"/icons/info/ID.png"
|
||||
}
|
||||
width={14}
|
||||
height={14}
|
||||
className="w-4 h-full mr-1"
|
||||
placeholder=""
|
||||
/>
|
||||
<span>{item?.collaborator_account?.user_id}</span>
|
||||
</div>
|
||||
<div className="h-4 mr-1 flex items-center text-xs bg-[#ffffff18] rounded-full px-2 py-2.5 mt-1 w-max">
|
||||
<div className="mr-1">
|
||||
<FontAwesomeIcon icon={faMoneyBill} size="sm" />
|
||||
</div>
|
||||
|
||||
<span>
|
||||
{item?.sharing_ratio
|
||||
? (item?.sharing_ratio * 100).toFixed()
|
||||
: 0}
|
||||
%
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{!item?.isDeleteBtnInvisible && (
|
||||
<div
|
||||
className="bg-[#F53030] ml-2 h-8 flex justify-center items-center text-sm font-medium px-3 rounded-full"
|
||||
onClick={() => handleDelete(item.id)}
|
||||
>
|
||||
移除
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</List.Item>
|
||||
</div>
|
||||
);
|
||||
}, []);
|
||||
const selfData = data?.list?.filter(
|
||||
(item) => item.collaborator_mid === selfMid
|
||||
);
|
||||
return (
|
||||
<div>
|
||||
{isloading && (
|
||||
<div
|
||||
className="bg-[#00000099] fixed top-0 w-full text-center flex items-center justify-center h-screen"
|
||||
// style={{ height: scrollHeight - 60 + "px" }}
|
||||
>
|
||||
<SpinLoading />
|
||||
</div>
|
||||
)}
|
||||
{/* 头部标题 */}
|
||||
<div className="p-4 fixed top-0 z-10 w-full bg-black">
|
||||
<div className="w-9 h-9 flex items-center justify-center bg-[#FFFFFF1A] rounded-full absolute">
|
||||
<FontAwesomeIcon
|
||||
icon={faAngleLeft}
|
||||
style={{ maxWidth: "12px" }}
|
||||
size="xl"
|
||||
onClick={() => {
|
||||
router.back();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<p className="text-base text-center leading-9">合伙人设置</p>
|
||||
</div>
|
||||
{/* 内容 */}
|
||||
<div className="p-4 pt-20">
|
||||
{searchParams.get("visitor_role") === 2 ? (
|
||||
<div className="p-4 pt-20">
|
||||
<div className="flex flex-col border-2 border-[#2c2b2f] rounded-2xl p-4 w-full mt-6">
|
||||
<p>合伙人昵称:{selfData[0]?.collaborator_account?.name}</p>
|
||||
<p>ID:{selfData[0]?.collaborator_account?.user_id}</p>
|
||||
<p>
|
||||
分成比例:
|
||||
{selfData[0]?.sharing_ratio
|
||||
? (selfData[0]?.sharing_ratio * 100).toFixed()
|
||||
: 0}
|
||||
%
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-col items-center p-4">
|
||||
<div className="flex flex-col items-center">
|
||||
<p className="text-base font-medium">总分成比例</p>
|
||||
<p className="text-[#F53030] text-3xl font-medium my-2">
|
||||
{data?.zone_third_partner?.sharing_ratio
|
||||
? (data?.zone_third_partner?.sharing_ratio * 100).toFixed()
|
||||
: 0}
|
||||
%
|
||||
</p>
|
||||
<p className="text-[#FFFFFF80] text-sm">修改比例请联系平台客服</p>
|
||||
</div>
|
||||
<div className="h-[3px] rounded-full w-full bg-[#FFFFFF26] mt-4"></div>
|
||||
<div className="flex flex-col w-full mb-4">
|
||||
<List style={{ "--padding-left": "0" }}>
|
||||
<CollaboratorItem
|
||||
item={{
|
||||
collaborator_account:
|
||||
data?.zone_third_partner?.third_partner_account,
|
||||
sharing_ratio: remainingRate,
|
||||
isDeleteBtnInvisible: true,
|
||||
}}
|
||||
/>
|
||||
{data?.list?.map((item, index) => (
|
||||
<CollaboratorItem key={index} item={item} />
|
||||
))}
|
||||
</List>
|
||||
</div>
|
||||
<div
|
||||
onClick={handleAddCollaborator}
|
||||
className="border border-white rounded-full w-[4.6rem] h-[4.6rem] flex items-center justify-center"
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon={faAdd}
|
||||
style={{ maxWidth: "20px" }}
|
||||
size="2xl"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="mt-8 text-[#FFFFFF80] text-sm">
|
||||
<p className="text-base font-medium">注意事项:</p>
|
||||
<p>
|
||||
1、总分成比例由当前空间主人设置,您的个人分成比例由代运营进行设置;
|
||||
</p>
|
||||
<p>
|
||||
2、您的个人收益计算公式为:空间收益x个人分成比例=个人收益,收益将以钻石形式发放至您的钱包;
|
||||
</p>
|
||||
<p>3、若您对收益情况存在任何疑问,请联系人工客服。</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const ModalMask = ({ setModalVisible, zid, handleRefresh }) => {
|
||||
const [isloading, setIsloading] = useState([]);
|
||||
const [isSelected, setIsSelected] = useState(false);
|
||||
const [seconds, setSeconds] = useState(60);
|
||||
const [mobilePhone, setMobilePhone] = useState("");
|
||||
const [regionCode, setRegionCode] = useState("");
|
||||
const [userId, setUserId] = useState("");
|
||||
const [isCounting, setIsCounting] = useState(false);
|
||||
const [rate, setRate] = useState([null]);
|
||||
const [collaboratorData, setCollaboratorData] = useState();
|
||||
//生成比例选项
|
||||
const generateItems = useCallback((min, max) => {
|
||||
const items = [];
|
||||
for (let i = min; i <= max; i++) {
|
||||
items.push({ label: `${i.toString()}%`, value: i.toString() });
|
||||
}
|
||||
return items;
|
||||
}, []);
|
||||
const rates = useMemo(() => generateItems(1, 50), []);
|
||||
useEffect(() => {
|
||||
async function getMobilePhone() {
|
||||
setMobilePhone(await get("mobile_phone"));
|
||||
setRegionCode(await get("region_code"));
|
||||
}
|
||||
getMobilePhone();
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
let interval;
|
||||
if (isCounting && seconds > 0) {
|
||||
interval = setInterval(() => {
|
||||
setSeconds(seconds - 1);
|
||||
}, 1000);
|
||||
} else {
|
||||
setIsCounting(false);
|
||||
setSeconds(60);
|
||||
clearInterval(interval);
|
||||
}
|
||||
return () => {
|
||||
clearInterval(interval);
|
||||
};
|
||||
}, [isCounting, seconds]);
|
||||
|
||||
const handleSearch = async (id) => {
|
||||
try {
|
||||
setIsloading(true);
|
||||
const body = {
|
||||
user_id: parseInt(id, 10),
|
||||
};
|
||||
const _data = await requireAPI(
|
||||
"POST",
|
||||
"/api/account/list_others_by_user_id",
|
||||
{
|
||||
body,
|
||||
}
|
||||
);
|
||||
if (_data.ret === -1) {
|
||||
setIsloading(false);
|
||||
Toast.show({
|
||||
icon: "fail",
|
||||
content: _data.msg,
|
||||
position: "top",
|
||||
});
|
||||
return;
|
||||
}
|
||||
setCollaboratorData(_data.data.account);
|
||||
setIsSelected(false);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
const handleSubmit = async () => {
|
||||
if (!isSelected) {
|
||||
Toast.show({
|
||||
icon: "fail",
|
||||
content: "请先选中用户",
|
||||
position: "top",
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (!rate[0]) {
|
||||
Toast.show({
|
||||
icon: "fail",
|
||||
content: "请选择分成比例",
|
||||
position: "top",
|
||||
});
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const body = {
|
||||
zid,
|
||||
collaborator_mid: collaboratorData.mid,
|
||||
sharing_ratio: parseInt(rate[0], 10) / 100,
|
||||
};
|
||||
const data = await requireAPI("POST", "/api/zone_collaborator/create", {
|
||||
body,
|
||||
});
|
||||
if (data.ret === -1) {
|
||||
Toast.show({
|
||||
icon: "fail",
|
||||
content: data.msg,
|
||||
position: "top",
|
||||
});
|
||||
return;
|
||||
}
|
||||
Toast.show({
|
||||
icon: "success",
|
||||
content: "添加合伙人成功",
|
||||
position: "top",
|
||||
});
|
||||
handleRefresh();
|
||||
setModalVisible(false);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className="flex flex-col w-full bg-[#17161A] rounded-3xl ">
|
||||
<div className="flex flex-row items-center mb-4">
|
||||
<span className="text-base font-medium">搜索用户:</span>
|
||||
<OwnInput
|
||||
placeholder="请输入用户ID"
|
||||
type="number"
|
||||
onChange={(value) => setUserId(value)}
|
||||
value={userId}
|
||||
className="flex-1 bg-[#FFFFFF1A] text-white rounded-2xl px-4 h-8 mx-2"
|
||||
/>
|
||||
<span
|
||||
onClick={() => handleSearch(userId)}
|
||||
className="text-[#FF669E] text-base font-medium"
|
||||
>
|
||||
搜索
|
||||
</span>
|
||||
</div>
|
||||
{collaboratorData && (
|
||||
<div
|
||||
className="flex flex-row items-center rounded-2xl p-4 mb-4 border"
|
||||
style={{ borderColor: isSelected ? "#FF669E" : "#2c2b2f" }}
|
||||
>
|
||||
<Avatar
|
||||
src={collaboratorData?.avatar?.images[0]?.urls[0]}
|
||||
fit="cover"
|
||||
style={{ "--border-radius": "100px", "--size": "42px" }}
|
||||
/>
|
||||
<div className="flex flex-1 flex-col justify-around items-start ml-2">
|
||||
<span className="text-base font-medium whitespace-nowrap">
|
||||
{collaboratorData?.name}
|
||||
</span>
|
||||
<div className="flex flex-row items-center py-0.5 px-2 bg-[#FFFFFF1A] rounded-full">
|
||||
<Image
|
||||
src={
|
||||
process.env.NEXT_PUBLIC_WEB_ASSETS_URL + "/icons/info/ID.png"
|
||||
}
|
||||
width={14}
|
||||
height={14}
|
||||
className="w-4 h-full mr-1"
|
||||
/>
|
||||
<span className="text-xs font-medium ml-0.5 whitespace-nowrap">
|
||||
{collaboratorData?.user_id}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="px-4 py-2 text-sm font-medium rounded-full"
|
||||
style={{
|
||||
backgroundColor: isSelected ? "#FFFFFF1A" : "#FF669E",
|
||||
}}
|
||||
onClick={() => setIsSelected(!isSelected)}
|
||||
>
|
||||
{isSelected ? "取消" : "选择"}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex flex-row items-center mb-4">
|
||||
<span className="text-base font-medium">分成比例:</span>
|
||||
<div className="w-1/3">
|
||||
<Picker columns={[rates]} onConfirm={setRate} value={rate}>
|
||||
{(items, { open }) => {
|
||||
return (
|
||||
<Space
|
||||
align="center"
|
||||
direction="horizontal"
|
||||
justify="center"
|
||||
onClick={open}
|
||||
>
|
||||
{items.every((item) => item === null)
|
||||
? "未选择"
|
||||
: items.map((item) => item?.label ?? "未选择").join("")}
|
||||
<FontAwesomeIcon
|
||||
icon={faSortDown}
|
||||
style={{ maxWidth: "12px", marginBottom: 6 }}
|
||||
size="lg"
|
||||
/>
|
||||
</Space>
|
||||
);
|
||||
}}
|
||||
</Picker>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-[#F53030] text-xs font-medium">
|
||||
注意事项:分成比例不得超过50%且确认后无法修改
|
||||
</p>
|
||||
<div className="grid grid-cols-2 gap-2 mt-2">
|
||||
<div
|
||||
className="bg-[#FF669E] text-center w-full py-2 text-md font-medium rounded-full"
|
||||
onClick={handleSubmit}
|
||||
>
|
||||
确认
|
||||
</div>
|
||||
<div
|
||||
className="bg-[#FFFFFF1A] text-center w-full py-2 text-md font-medium rounded-full"
|
||||
onClick={() => setModalVisible(false)}
|
||||
>
|
||||
取消
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
Loading…
Reference in New Issue