2024-11-18 18:40:08 +08:00
|
|
|
|
"use client";
|
|
|
|
|
import React, {
|
|
|
|
|
useState,
|
|
|
|
|
useEffect,
|
|
|
|
|
useMemo,
|
|
|
|
|
useCallback,
|
|
|
|
|
useRef,
|
|
|
|
|
} from "react";
|
2024-12-20 20:47:20 +08:00
|
|
|
|
import { Toast, Dialog, Picker, Avatar, Space, SpinLoading } from "antd-mobile";
|
2024-11-18 18:40:08 +08:00
|
|
|
|
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";
|
2024-12-20 20:47:20 +08:00
|
|
|
|
import OwnIcon from "@/components/OwnIcon";
|
|
|
|
|
import OwnImage from "@/components/OwnImage";
|
2024-11-18 18:40:08 +08:00
|
|
|
|
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({
|
2024-11-19 16:06:55 +08:00
|
|
|
|
content: (
|
|
|
|
|
<ModalMask
|
|
|
|
|
setModalVisible={setModalVisible}
|
|
|
|
|
zid={zid}
|
|
|
|
|
router={router}
|
|
|
|
|
/>
|
|
|
|
|
),
|
2024-11-18 18:40:08 +08:00
|
|
|
|
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 ? (
|
2024-11-19 16:06:55 +08:00
|
|
|
|
<div className="flex-1 flex flex-col justify-center items-center">
|
2024-12-20 20:47:20 +08:00
|
|
|
|
<OwnImage
|
2024-11-19 16:06:55 +08:00
|
|
|
|
src={data?.third_partner_account.avatar.images[0].urls[0]}
|
2024-12-20 20:47:20 +08:00
|
|
|
|
className="h-[74px] w-[74px]"
|
|
|
|
|
outClassName="mr-1"
|
2024-11-19 16:06:55 +08:00
|
|
|
|
/>
|
|
|
|
|
<div className="flex flex-col border-2 border-[#2c2b2f] rounded-2xl p-4 w-full mt-6 text-base font-medium">
|
|
|
|
|
<p>代运营昵称:{data.third_partner_account.name}</p>
|
|
|
|
|
<p>ID:{data.third_partner_account.user_id}</p>
|
|
|
|
|
<p>分成比例:{(data.sharing_ratio * 100).toFixed()}%</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2024-11-18 18:40:08 +08:00
|
|
|
|
) : (
|
|
|
|
|
<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>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-19 16:06:55 +08:00
|
|
|
|
const ModalMask = ({ setModalVisible, zid, router }) => {
|
2024-11-18 18:40:08 +08:00
|
|
|
|
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]);
|
2024-11-19 16:06:55 +08:00
|
|
|
|
// const router = useRouter();
|
2024-11-18 18:40:08 +08:00
|
|
|
|
//生成比例选项
|
|
|
|
|
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);
|
2024-11-19 16:06:55 +08:00
|
|
|
|
router.back();
|
2024-11-18 18:40:08 +08:00
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error(error);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
return (
|
2024-11-20 18:17:11 +08:00
|
|
|
|
<div className="flex flex-col w-full rounded-3xl text-white">
|
2024-11-18 18:40:08 +08:00
|
|
|
|
<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">
|
2024-12-20 20:47:20 +08:00
|
|
|
|
<OwnIcon
|
|
|
|
|
src="/icons/info/ID.png"
|
|
|
|
|
className="w-[14px] h-[14px]"
|
|
|
|
|
outClassName="mr-1"
|
2024-11-18 18:40:08 +08:00
|
|
|
|
/>
|
|
|
|
|
<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>
|
|
|
|
|
);
|
|
|
|
|
};
|