Merge branch 'yongpxu-dev' into yongpxu-dev4
This commit is contained in:
@@ -21,12 +21,7 @@ export async function uploadFile(
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// 检查响应结果
|
return res.url;
|
||||||
if (res?.code === 200 && res?.data?.url) {
|
|
||||||
return res.data.url;
|
|
||||||
} else {
|
|
||||||
throw new Error(res?.msg || "文件上传失败");
|
|
||||||
}
|
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
throw new Error(e?.message || "文件上传失败");
|
throw new Error(e?.message || "文件上传失败");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ const StepIndicator: React.FC<StepIndicatorProps> = ({
|
|||||||
steps,
|
steps,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div style={{ marginBottom: 24, overflowX: "auto" }}>
|
<div style={{ overflowX: "auto", padding: "30px 0px", background: "#fff" }}>
|
||||||
<Steps current={currentStep - 1}>
|
<Steps current={currentStep - 1}>
|
||||||
{steps.map((step, idx) => (
|
{steps.map((step, idx) => (
|
||||||
<Steps.Step
|
<Steps.Step
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { useNavigate, useParams } from "react-router-dom";
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
import { message } from "antd";
|
import { message } from "antd";
|
||||||
import { NavBar } from "antd-mobile";
|
import NavCommon from "@/components/NavCommon";
|
||||||
import { ArrowLeftOutlined } from "@ant-design/icons";
|
|
||||||
import BasicSettings from "./steps/BasicSettings";
|
import BasicSettings from "./steps/BasicSettings";
|
||||||
import FriendRequestSettings from "./steps/FriendRequestSettings";
|
import FriendRequestSettings from "./steps/FriendRequestSettings";
|
||||||
import MessageSettings from "./steps/MessageSettings";
|
import MessageSettings from "./steps/MessageSettings";
|
||||||
@@ -205,25 +204,8 @@ export default function NewPlan() {
|
|||||||
<Layout
|
<Layout
|
||||||
header={
|
header={
|
||||||
<>
|
<>
|
||||||
<NavBar
|
<NavCommon title={isEdit ? "编辑场景计划" : "新建场景计划"} />
|
||||||
back={null}
|
<StepIndicator currentStep={currentStep} steps={steps} />
|
||||||
style={{ background: "#fff" }}
|
|
||||||
left={
|
|
||||||
<div className="nav-title">
|
|
||||||
<ArrowLeftOutlined
|
|
||||||
twoToneColor="#1677ff"
|
|
||||||
onClick={() => router(-1)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<span className="nav-title">
|
|
||||||
{isEdit ? "编辑朋友圈同步" : "新建朋友圈同步"}
|
|
||||||
</span>
|
|
||||||
</NavBar>
|
|
||||||
<div className="px-4 py-6">
|
|
||||||
<StepIndicator currentStep={currentStep} steps={steps} />
|
|
||||||
</div>
|
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,17 +1,6 @@
|
|||||||
import React, { useState, useEffect, useRef } from "react";
|
import React, { useState, useEffect, useRef } from "react";
|
||||||
import {
|
import { Form, Input, Button, Tag, Switch, Modal, Spin } from "antd";
|
||||||
Form,
|
import { Button as ButtonMobile } from "antd-mobile";
|
||||||
Input,
|
|
||||||
Button,
|
|
||||||
Tag,
|
|
||||||
Switch,
|
|
||||||
// Upload,
|
|
||||||
Modal,
|
|
||||||
Alert,
|
|
||||||
Row,
|
|
||||||
Col,
|
|
||||||
message,
|
|
||||||
} from "antd";
|
|
||||||
import {
|
import {
|
||||||
PlusOutlined,
|
PlusOutlined,
|
||||||
EyeOutlined,
|
EyeOutlined,
|
||||||
@@ -21,6 +10,7 @@ import {
|
|||||||
CheckOutlined,
|
CheckOutlined,
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import { uploadFile } from "@/api/common";
|
import { uploadFile } from "@/api/common";
|
||||||
|
import styles from "./base.module.scss";
|
||||||
|
|
||||||
interface BasicSettingsProps {
|
interface BasicSettingsProps {
|
||||||
isEdit: boolean;
|
isEdit: boolean;
|
||||||
@@ -424,31 +414,44 @@ const BasicSettings: React.FC<BasicSettingsProps> = ({
|
|||||||
formData.scenario !== 1 ? { display: "none" } : { display: "block" };
|
formData.scenario !== 1 ? { display: "none" } : { display: "block" };
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className={styles["basic-container"]}>
|
||||||
{/* 场景选择区块 */}
|
{/* 场景选择区块 */}
|
||||||
{sceneLoading ? (
|
<div className={styles["basic-scene-select"]}>
|
||||||
<div style={{ padding: 16, textAlign: "center" }}>加载中...</div>
|
{sceneLoading ? (
|
||||||
) : (
|
<div
|
||||||
<Row gutter={20} className="grid grid-cols-3 gap-4">
|
style={{
|
||||||
{sceneList.map((scene) => (
|
display: "flex",
|
||||||
<Col span={8} key={scene.id}>
|
justifyContent: "center",
|
||||||
<Button
|
alignItems: "center",
|
||||||
type="primary"
|
minHeight: 80,
|
||||||
onClick={() => handleScenarioSelect(scene.id)}
|
}}
|
||||||
className={`w-full ${formData.scenario === scene.id ? "bg-blue-500" : "bg-gray-200"}`}
|
>
|
||||||
>
|
<Spin size="large"></Spin>
|
||||||
{scene.name.replace("获客", "")}
|
</div>
|
||||||
</Button>
|
) : (
|
||||||
</Col>
|
<div className={styles["basic-scene-grid"]}>
|
||||||
))}
|
{sceneList.map((scene) => {
|
||||||
</Row>
|
const selected = formData.scenario === scene.id;
|
||||||
)}
|
return (
|
||||||
|
<button
|
||||||
|
key={scene.id}
|
||||||
|
onClick={() => handleScenarioSelect(scene.id)}
|
||||||
|
className={
|
||||||
|
styles["basic-scene-btn"] +
|
||||||
|
(selected ? " " + styles.selected : "")
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{scene.name.replace("获客", "")}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
{/* 计划名称输入区 */}
|
{/* 计划名称输入区 */}
|
||||||
<div className="mb-4">计划名称</div>
|
<div className={styles["basic-label"]}>计划名称</div>
|
||||||
<div className="border p-2 mb-4">
|
<div className={styles["basic-input-block"]}>
|
||||||
<Input
|
<Input
|
||||||
className="w-full"
|
|
||||||
value={formData.name}
|
value={formData.name}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
onChange({ ...formData, name: String(e.target.value) })
|
onChange({ ...formData, name: String(e.target.value) })
|
||||||
@@ -456,17 +459,16 @@ const BasicSettings: React.FC<BasicSettingsProps> = ({
|
|||||||
placeholder="请输入计划名称"
|
placeholder="请输入计划名称"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className={styles["basic-label"]}>获客标签(可多选)</div>
|
||||||
<div className="mb-4">获客标签(可多选)</div>
|
|
||||||
{/* 标签选择区块 */}
|
{/* 标签选择区块 */}
|
||||||
{formData.scenario && (
|
{formData.scenario && (
|
||||||
<div className="flex pb-4" style={{ flexWrap: "wrap", gap: 8 }}>
|
<div className={styles["basic-tag-list"]}>
|
||||||
{(currentScene?.scenarioTags || []).map((tag: string) => (
|
{(currentScene?.scenarioTags || []).map((tag: string) => (
|
||||||
<Tag
|
<Tag
|
||||||
key={tag}
|
key={tag}
|
||||||
color={selectedScenarioTags.includes(tag) ? "blue" : "default"}
|
color={selectedScenarioTags.includes(tag) ? "blue" : "default"}
|
||||||
onClick={() => handleScenarioTagToggle(tag)}
|
onClick={() => handleScenarioTagToggle(tag)}
|
||||||
style={{ marginBottom: 4 }}
|
className={styles["basic-tag-item"]}
|
||||||
>
|
>
|
||||||
{tag}
|
{tag}
|
||||||
</Tag>
|
</Tag>
|
||||||
@@ -477,9 +479,9 @@ const BasicSettings: React.FC<BasicSettingsProps> = ({
|
|||||||
key={tag.id}
|
key={tag.id}
|
||||||
color={selectedScenarioTags.includes(tag.id) ? "blue" : "default"}
|
color={selectedScenarioTags.includes(tag.id) ? "blue" : "default"}
|
||||||
onClick={() => handleScenarioTagToggle(tag.id)}
|
onClick={() => handleScenarioTagToggle(tag.id)}
|
||||||
style={{ marginBottom: 4 }}
|
|
||||||
closable
|
closable
|
||||||
onClose={() => handleRemoveCustomTag(tag.id)}
|
onClose={() => handleRemoveCustomTag(tag.id)}
|
||||||
|
className={styles["basic-tag-item"]}
|
||||||
>
|
>
|
||||||
{tag.name}
|
{tag.name}
|
||||||
</Tag>
|
</Tag>
|
||||||
@@ -487,49 +489,33 @@ const BasicSettings: React.FC<BasicSettingsProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{/* 自定义标签输入区 */}
|
{/* 自定义标签输入区 */}
|
||||||
<div className="flex p" style={{ gap: 8 }}>
|
<div className={styles["basic-custom-tag-input"]}>
|
||||||
<div className="border p-2 flex-1">
|
<Input
|
||||||
<Input
|
type="text"
|
||||||
type="text"
|
value={customTagInput}
|
||||||
value={customTagInput}
|
onChange={(e) => setCustomTagInput(e.target.value)}
|
||||||
onChange={(e) => setCustomTagInput(e.target.value)}
|
placeholder="添加自定义标签"
|
||||||
placeholder="添加自定义标签"
|
/>
|
||||||
className="w-full"
|
<Button type="primary" onClick={handleAddCustomTag}>
|
||||||
/>
|
添加
|
||||||
</div>
|
</Button>
|
||||||
<div className="pt-1">
|
|
||||||
<Button type="primary" onClick={handleAddCustomTag}>
|
|
||||||
添加
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 输入获客成功提示 */}
|
{/* 输入获客成功提示 */}
|
||||||
<div className="flex pt-4">
|
<div className={styles["basic-success-tip"]}>
|
||||||
<div className="border p-2 flex-1">
|
<Input
|
||||||
<Input
|
type="text"
|
||||||
type="text"
|
value={tips}
|
||||||
value={tips}
|
onChange={(e) => {
|
||||||
onChange={(e) => {
|
setTips(e.target.value);
|
||||||
setTips(e.target.value);
|
onChange({ ...formData, tips: e.target.value });
|
||||||
onChange({ ...formData, tips: e.target.value });
|
|
||||||
}}
|
|
||||||
placeholder="请输入获客成功提示"
|
|
||||||
className="w-full"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 选素材 */}
|
|
||||||
<div className="my-4" style={openPoster}>
|
|
||||||
<div className="mb-4">选择海报</div>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: "grid",
|
|
||||||
gridTemplateColumns: "repeat(3, 1fr)",
|
|
||||||
gap: 12,
|
|
||||||
}}
|
}}
|
||||||
>
|
placeholder="请输入获客成功提示"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/* 选素材 */}
|
||||||
|
<div className={styles["basic-materials"]} style={openPoster}>
|
||||||
|
<div className={styles["basic-label"]}>选择海报</div>
|
||||||
|
<div className={styles["basic-materials-grid"]}>
|
||||||
{[...materials, ...customPosters].map((material) => {
|
{[...materials, ...customPosters].map((material) => {
|
||||||
const isSelected = selectedMaterials.some(
|
const isSelected = selectedMaterials.some(
|
||||||
(m) => m.id === material.id
|
(m) => m.id === material.id
|
||||||
@@ -538,35 +524,15 @@ const BasicSettings: React.FC<BasicSettingsProps> = ({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={material.id}
|
key={material.id}
|
||||||
style={{
|
className={
|
||||||
border: isSelected ? "2px solid #1890ff" : "2px solid #eee",
|
styles["basic-material-card"] +
|
||||||
borderRadius: 8,
|
(isSelected ? " " + styles.selected : "")
|
||||||
padding: 6,
|
}
|
||||||
cursor: "pointer",
|
|
||||||
background: isSelected ? "#e6f7ff" : "#fff",
|
|
||||||
transition: "border 0.2s",
|
|
||||||
textAlign: "center",
|
|
||||||
position: "relative",
|
|
||||||
height: 180 + 12, // 图片高度180+上下padding
|
|
||||||
overflow: "hidden",
|
|
||||||
minHeight: 192,
|
|
||||||
}}
|
|
||||||
onClick={() => handleMaterialSelect(material)}
|
onClick={() => handleMaterialSelect(material)}
|
||||||
>
|
>
|
||||||
{/* 预览按钮:自定义海报在左上,内置海报在右上 */}
|
{/* 预览按钮:自定义海报在左上,内置海报在右上 */}
|
||||||
<Button
|
<span
|
||||||
style={{
|
className={styles["basic-material-preview"]}
|
||||||
position: "absolute",
|
|
||||||
top: 8,
|
|
||||||
left: isCustom ? 8 : "auto",
|
|
||||||
right: isCustom ? "auto" : 8,
|
|
||||||
background: "rgba(0,0,0,0.5)",
|
|
||||||
border: "none",
|
|
||||||
borderRadius: "50%",
|
|
||||||
padding: 2,
|
|
||||||
zIndex: 2,
|
|
||||||
cursor: "pointer",
|
|
||||||
}}
|
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
handlePreviewImage(material.preview);
|
handlePreviewImage(material.preview);
|
||||||
@@ -575,7 +541,7 @@ const BasicSettings: React.FC<BasicSettingsProps> = ({
|
|||||||
<EyeOutlined
|
<EyeOutlined
|
||||||
style={{ color: "#fff", width: 18, height: 18 }}
|
style={{ color: "#fff", width: 18, height: 18 }}
|
||||||
/>
|
/>
|
||||||
</Button>
|
</span>
|
||||||
{/* 删除自定义海报按钮 */}
|
{/* 删除自定义海报按钮 */}
|
||||||
{isCustom && (
|
{isCustom && (
|
||||||
<Button
|
<Button
|
||||||
@@ -607,31 +573,9 @@ const BasicSettings: React.FC<BasicSettingsProps> = ({
|
|||||||
<img
|
<img
|
||||||
src={material.preview}
|
src={material.preview}
|
||||||
alt={material.name}
|
alt={material.name}
|
||||||
style={{
|
className={styles["basic-material-img"]}
|
||||||
width: 100,
|
|
||||||
height: 180,
|
|
||||||
objectFit: "cover",
|
|
||||||
borderRadius: 4,
|
|
||||||
marginBottom: 0,
|
|
||||||
display: "block",
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<div
|
<div className={styles["basic-material-name"]}>
|
||||||
style={{
|
|
||||||
position: "absolute",
|
|
||||||
left: 0,
|
|
||||||
bottom: 0,
|
|
||||||
width: "100%",
|
|
||||||
background: "rgba(0,0,0,0.5)",
|
|
||||||
color: "#fff",
|
|
||||||
fontSize: 14,
|
|
||||||
padding: "4px 0",
|
|
||||||
borderBottomLeftRadius: 4,
|
|
||||||
borderBottomRightRadius: 4,
|
|
||||||
textAlign: "center",
|
|
||||||
zIndex: 3,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{material.name}
|
{material.name}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -639,19 +583,7 @@ const BasicSettings: React.FC<BasicSettingsProps> = ({
|
|||||||
})}
|
})}
|
||||||
{/* 添加海报卡片 */}
|
{/* 添加海报卡片 */}
|
||||||
<div
|
<div
|
||||||
style={{
|
className={styles["basic-add-material"]}
|
||||||
border: "2px dashed #bbb",
|
|
||||||
borderRadius: 8,
|
|
||||||
padding: 6,
|
|
||||||
cursor: "pointer",
|
|
||||||
background: "#fafbfc",
|
|
||||||
textAlign: "center",
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center",
|
|
||||||
height: 190,
|
|
||||||
}}
|
|
||||||
onClick={() => uploadInputRef.current?.click()}
|
onClick={() => uploadInputRef.current?.click()}
|
||||||
>
|
>
|
||||||
<span style={{ fontSize: 36, color: "#bbb", marginBottom: 8 }}>
|
<span style={{ fontSize: 36, color: "#bbb", marginBottom: 8 }}>
|
||||||
@@ -666,7 +598,6 @@ const BasicSettings: React.FC<BasicSettingsProps> = ({
|
|||||||
onChange={async (e) => {
|
onChange={async (e) => {
|
||||||
const file = e.target.files?.[0];
|
const file = e.target.files?.[0];
|
||||||
if (file) {
|
if (file) {
|
||||||
// 直接上传
|
|
||||||
try {
|
try {
|
||||||
const url = await uploadFile(file);
|
const url = await uploadFile(file);
|
||||||
const newPoster = {
|
const newPoster = {
|
||||||
@@ -675,9 +606,14 @@ const BasicSettings: React.FC<BasicSettingsProps> = ({
|
|||||||
type: "poster",
|
type: "poster",
|
||||||
preview: url,
|
preview: url,
|
||||||
};
|
};
|
||||||
|
console.log(newPoster);
|
||||||
|
|
||||||
setCustomPosters((prev) => [...prev, newPoster]);
|
setCustomPosters((prev) => [...prev, newPoster]);
|
||||||
|
setSelectedMaterials([newPoster]);
|
||||||
|
onChange({ ...formData, materials: [newPoster] });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// 可加toast提示
|
// 可加toast提示
|
||||||
|
log;
|
||||||
}
|
}
|
||||||
e.target.value = "";
|
e.target.value = "";
|
||||||
}
|
}
|
||||||
@@ -705,11 +641,10 @@ const BasicSettings: React.FC<BasicSettingsProps> = ({
|
|||||||
</Modal>
|
</Modal>
|
||||||
</div>
|
</div>
|
||||||
{/* 订单导入区块优化 */}
|
{/* 订单导入区块优化 */}
|
||||||
<div style={openOrder} className="my-4">
|
<div className={styles["basic-order-upload"]} style={openOrder}>
|
||||||
<div style={{ fontWeight: 500, marginBottom: 8 }}>订单表格上传</div>
|
<div className={styles["basic-order-upload-label"]}>订单表格上传</div>
|
||||||
<div style={{ display: "flex", gap: 12, marginBottom: 4 }}>
|
<div className={styles["basic-order-upload-actions"]}>
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
|
||||||
style={{ display: "flex", alignItems: "center", gap: 4 }}
|
style={{ display: "flex", alignItems: "center", gap: 4 }}
|
||||||
onClick={handleDownloadTemplate}
|
onClick={handleDownloadTemplate}
|
||||||
>
|
>
|
||||||
@@ -741,78 +676,68 @@ const BasicSettings: React.FC<BasicSettingsProps> = ({
|
|||||||
/>
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ color: "#888", fontSize: 13, marginBottom: 8 }}>
|
<div className={styles["basic-order-upload-tip"]}>
|
||||||
支持 CSV、Excel 格式,上传后将文件保存到服务器
|
支持 CSV、Excel 格式,上传后将文件保存到服务器
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* 电话获客设置区块,仅在选择电话获客场景时显示 */}
|
{/* 电话获客设置区块,仅在选择电话获客场景时显示 */}
|
||||||
{formData.scenario === 5 && (
|
{formData.scenario === 5 && (
|
||||||
<div style={{ margin: "16px 0" }}>
|
<div className={styles["basic-phone-settings"]}>
|
||||||
<div
|
<div style={{ fontWeight: 600, fontSize: 16, marginBottom: 16 }}>
|
||||||
style={{
|
电话获客设置
|
||||||
background: "#f7f8fa",
|
</div>
|
||||||
borderRadius: 10,
|
<div style={{ display: "flex", flexDirection: "column", gap: 18 }}>
|
||||||
padding: 20,
|
<div
|
||||||
boxShadow: "0 2px 8px rgba(0,0,0,0.03)",
|
style={{
|
||||||
marginBottom: 12,
|
display: "flex",
|
||||||
}}
|
alignItems: "center",
|
||||||
>
|
justifyContent: "space-between",
|
||||||
<div style={{ fontWeight: 600, fontSize: 16, marginBottom: 16 }}>
|
}}
|
||||||
电话获客设置
|
>
|
||||||
|
<span>自动加好友</span>
|
||||||
|
<Switch
|
||||||
|
checked={phoneSettings.autoAdd}
|
||||||
|
onChange={(v) =>
|
||||||
|
setPhoneSettings((s) => ({ ...s, autoAdd: v }))
|
||||||
|
}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ display: "flex", flexDirection: "column", gap: 18 }}>
|
<div
|
||||||
<div
|
style={{
|
||||||
style={{
|
display: "flex",
|
||||||
display: "flex",
|
alignItems: "center",
|
||||||
alignItems: "center",
|
justifyContent: "space-between",
|
||||||
justifyContent: "space-between",
|
}}
|
||||||
}}
|
>
|
||||||
>
|
<span>语音转文字</span>
|
||||||
<span>自动加好友</span>
|
<Switch
|
||||||
<Switch
|
checked={phoneSettings.speechToText}
|
||||||
checked={phoneSettings.autoAdd}
|
onChange={(v) =>
|
||||||
onChange={(v) =>
|
setPhoneSettings((s) => ({ ...s, speechToText: v }))
|
||||||
setPhoneSettings((s) => ({ ...s, autoAdd: v }))
|
}
|
||||||
}
|
/>
|
||||||
/>
|
</div>
|
||||||
</div>
|
<div
|
||||||
<div
|
style={{
|
||||||
style={{
|
display: "flex",
|
||||||
display: "flex",
|
alignItems: "center",
|
||||||
alignItems: "center",
|
justifyContent: "space-between",
|
||||||
justifyContent: "space-between",
|
}}
|
||||||
}}
|
>
|
||||||
>
|
<span>问题提取</span>
|
||||||
<span>语音转文字</span>
|
<Switch
|
||||||
<Switch
|
checked={phoneSettings.questionExtraction}
|
||||||
checked={phoneSettings.speechToText}
|
onChange={(v) =>
|
||||||
onChange={(v) =>
|
setPhoneSettings((s) => ({ ...s, questionExtraction: v }))
|
||||||
setPhoneSettings((s) => ({ ...s, speechToText: v }))
|
}
|
||||||
}
|
/>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span>问题提取</span>
|
|
||||||
<Switch
|
|
||||||
checked={phoneSettings.questionExtraction}
|
|
||||||
onChange={(v) =>
|
|
||||||
setPhoneSettings((s) => ({ ...s, questionExtraction: v }))
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{/* 微信群设置区块,仅在选择微信群场景时显示 */}
|
{/* 微信群设置区块,仅在选择微信群场景时显示 */}
|
||||||
{formData.scenario === 7 && (
|
{formData.scenario === 7 && (
|
||||||
<div style={{ margin: "16px 0" }}>
|
<div className={styles["basic-wechat-group"]}>
|
||||||
<div style={{ marginBottom: 8 }}>
|
<div style={{ marginBottom: 8 }}>
|
||||||
<Input
|
<Input
|
||||||
value={weixinqunName}
|
value={weixinqunName}
|
||||||
@@ -833,24 +758,19 @@ const BasicSettings: React.FC<BasicSettingsProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
<div className={styles["basic-footer-switch"]}>
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
margin: "16px 0",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span>是否启用</span>
|
<span>是否启用</span>
|
||||||
<Switch
|
<Switch
|
||||||
checked={formData.enabled}
|
checked={formData.enabled}
|
||||||
onChange={(value) => onChange({ ...formData, enabled: value })}
|
onChange={(value) => onChange({ ...formData, enabled: value })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Button className="mt-4" type="primary" onClick={onNext}>
|
|
||||||
下一步
|
<div className={styles["basic-footer-switch"]}>
|
||||||
</Button>
|
<ButtonMobile block color="primary" onClick={onNext}>
|
||||||
|
下一步
|
||||||
|
</ButtonMobile>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
} from "antd";
|
} from "antd";
|
||||||
import { QuestionCircleOutlined, MessageOutlined } from "@ant-design/icons";
|
import { QuestionCircleOutlined, MessageOutlined } from "@ant-design/icons";
|
||||||
import DeviceSelection from "@/components/DeviceSelection";
|
import DeviceSelection from "@/components/DeviceSelection";
|
||||||
|
import Layout from "@/components/Layout/Layout";
|
||||||
|
|
||||||
interface FriendRequestSettingsProps {
|
interface FriendRequestSettingsProps {
|
||||||
formData: any;
|
formData: any;
|
||||||
@@ -97,143 +98,151 @@ const FriendRequestSettings: React.FC<FriendRequestSettingsProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="space-y-6">
|
<Layout
|
||||||
<div>
|
footer={
|
||||||
<span className="font-medium text-base">选择设备</span>
|
<div className="p-4 border-t bg-white">
|
||||||
<div className="mt-2">
|
<div className="flex justify-between">
|
||||||
<DeviceSelection
|
<Button onClick={onPrev}>上一步</Button>
|
||||||
selectedDevices={selectedDevices.map((d) => d.id)}
|
<Button type="primary" onClick={handleNext}>
|
||||||
onSelect={(deviceIds) => {
|
下一步
|
||||||
const newSelectedDevices = deviceIds.map((id) => ({
|
</Button>
|
||||||
id,
|
</div>
|
||||||
name: `设备 ${id}`,
|
</div>
|
||||||
status: "online",
|
}
|
||||||
}));
|
>
|
||||||
setSelectedDevices(newSelectedDevices);
|
<div className="p-4 space-y-6">
|
||||||
onChange({ ...formData, device: deviceIds });
|
<div>
|
||||||
}}
|
<span className="font-medium text-base">选择设备</span>
|
||||||
placeholder="选择设备"
|
<div className="mt-2">
|
||||||
/>
|
<DeviceSelection
|
||||||
|
selectedDevices={selectedDevices.map((d) => d.id)}
|
||||||
|
onSelect={(deviceIds) => {
|
||||||
|
const newSelectedDevices = deviceIds.map((id) => ({
|
||||||
|
id,
|
||||||
|
name: `设备 ${id}`,
|
||||||
|
status: "online",
|
||||||
|
}));
|
||||||
|
setSelectedDevices(newSelectedDevices);
|
||||||
|
onChange({ ...formData, device: deviceIds });
|
||||||
|
}}
|
||||||
|
placeholder="选择设备"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<div className="flex items-center space-x-2 mb-1 relative">
|
<div className="flex items-center space-x-2 mb-1 relative">
|
||||||
<span className="font-medium text-base">好友备注</span>
|
<span className="font-medium text-base">好友备注</span>
|
||||||
<span
|
<span
|
||||||
className="inline-flex items-center justify-center w-5 h-5 rounded-full bg-gray-200 text-gray-500 text-xs cursor-pointer hover:bg-gray-300 transition-colors"
|
className="inline-flex items-center justify-center w-5 h-5 rounded-full bg-gray-200 text-gray-500 text-xs cursor-pointer hover:bg-gray-300 transition-colors"
|
||||||
onMouseEnter={() => setShowRemarkTip(true)}
|
onMouseEnter={() => setShowRemarkTip(true)}
|
||||||
onMouseLeave={() => setShowRemarkTip(false)}
|
onMouseLeave={() => setShowRemarkTip(false)}
|
||||||
onClick={() => setShowRemarkTip((v) => !v)}
|
onClick={() => setShowRemarkTip((v) => !v)}
|
||||||
>
|
>
|
||||||
?
|
?
|
||||||
</span>
|
</span>
|
||||||
{showRemarkTip && (
|
{showRemarkTip && (
|
||||||
<div className="absolute left-24 top-0 z-20 w-64 p-3 bg-white border border-gray-200 rounded shadow-lg text-sm text-gray-700">
|
<div className="absolute left-24 top-0 z-20 w-64 p-3 bg-white border border-gray-200 rounded shadow-lg text-sm text-gray-700">
|
||||||
<div>设置添加好友时的备注格式</div>
|
<div>设置添加好友时的备注格式</div>
|
||||||
<div className="mt-2 text-xs text-gray-500">备注格式预览:</div>
|
<div className="mt-2 text-xs text-gray-500">
|
||||||
<div className="mt-1 text-blue-600">
|
备注格式预览:
|
||||||
{formData.remarkType === "phone" &&
|
</div>
|
||||||
`138****1234+${getScenarioTitle()}`}
|
<div className="mt-1 text-blue-600">
|
||||||
{formData.remarkType === "nickname" &&
|
{formData.remarkType === "phone" &&
|
||||||
`小红书用户2851+${getScenarioTitle()}`}
|
`138****1234+${getScenarioTitle()}`}
|
||||||
{formData.remarkType === "source" &&
|
{formData.remarkType === "nickname" &&
|
||||||
`抖音直播+${getScenarioTitle()}`}
|
`小红书用户2851+${getScenarioTitle()}`}
|
||||||
|
{formData.remarkType === "source" &&
|
||||||
|
`抖音直播+${getScenarioTitle()}`}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
)}
|
</div>
|
||||||
</div>
|
<Select
|
||||||
<Select
|
value={formData.remarkType || "phone"}
|
||||||
value={formData.remarkType || "phone"}
|
onChange={(value) => onChange({ ...formData, remarkType: value })}
|
||||||
onChange={(value) => onChange({ ...formData, remarkType: value })}
|
className="w-full mt-2"
|
||||||
className="w-full mt-2"
|
|
||||||
>
|
|
||||||
{remarkTypes.map((type) => (
|
|
||||||
<Select.Option key={type.value} value={type.value}>
|
|
||||||
{type.label}
|
|
||||||
</Select.Option>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<span className="font-medium text-base">招呼语</span>
|
|
||||||
<Button
|
|
||||||
onClick={() => setIsTemplateDialogOpen(true)}
|
|
||||||
className="text-blue-500"
|
|
||||||
>
|
>
|
||||||
<MessageOutlined className="h-4 w-4 mr-2" />
|
{remarkTypes.map((type) => (
|
||||||
参考模板
|
<Select.Option key={type.value} value={type.value}>
|
||||||
</Button>
|
{type.label}
|
||||||
|
</Select.Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<Input
|
|
||||||
value={formData.greeting}
|
|
||||||
onChange={(e) =>
|
|
||||||
onChange({ ...formData, greeting: e.target.value })
|
|
||||||
}
|
|
||||||
placeholder="请输入招呼语"
|
|
||||||
className="mt-2"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<span className="font-medium text-base">添加间隔</span>
|
<div className="flex items-center justify-between">
|
||||||
<div className="flex items-center space-x-2 mt-2">
|
<span className="font-medium text-base">招呼语</span>
|
||||||
|
<Button
|
||||||
|
onClick={() => setIsTemplateDialogOpen(true)}
|
||||||
|
className="text-blue-500"
|
||||||
|
>
|
||||||
|
<MessageOutlined className="h-4 w-4 mr-2" />
|
||||||
|
参考模板
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
<Input
|
<Input
|
||||||
type="number"
|
value={formData.greeting}
|
||||||
value={formData.addFriendInterval || 1}
|
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
onChange({
|
onChange({ ...formData, greeting: e.target.value })
|
||||||
...formData,
|
|
||||||
addFriendInterval: Number(e.target.value),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
/>
|
placeholder="请输入招呼语"
|
||||||
<div className="w-10">分钟</div>
|
className="mt-2"
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<span className="font-medium text-base">允许加人的时间段</span>
|
|
||||||
<div className="flex items-center space-x-2 mt-2">
|
|
||||||
<Input
|
|
||||||
type="time"
|
|
||||||
value={formData.addFriendTimeStart || "09:00"}
|
|
||||||
onChange={(e) =>
|
|
||||||
onChange({ ...formData, addFriendTimeStart: e.target.value })
|
|
||||||
}
|
|
||||||
className="w-32"
|
|
||||||
/>
|
|
||||||
<span>至</span>
|
|
||||||
<Input
|
|
||||||
type="time"
|
|
||||||
value={formData.addFriendTimeEnd || "18:00"}
|
|
||||||
onChange={(e) =>
|
|
||||||
onChange({ ...formData, addFriendTimeEnd: e.target.value })
|
|
||||||
}
|
|
||||||
className="w-32"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
{hasWarnings && (
|
<div>
|
||||||
<Alert
|
<span className="font-medium text-base">添加间隔</span>
|
||||||
message="警告"
|
<div className="flex items-center space-x-2 mt-2">
|
||||||
description="您有未完成的设置项,建议完善后再进入下一步。"
|
<Input
|
||||||
type="warning"
|
type="number"
|
||||||
showIcon
|
value={formData.addFriendInterval || 1}
|
||||||
className="bg-amber-50 border-amber-200"
|
onChange={(e) =>
|
||||||
/>
|
onChange({
|
||||||
)}
|
...formData,
|
||||||
|
addFriendInterval: Number(e.target.value),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<div className="w-10">分钟</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-between pt-4">
|
<div>
|
||||||
<Button onClick={onPrev}>上一步</Button>
|
<span className="font-medium text-base">允许加人的时间段</span>
|
||||||
<Button type="primary" onClick={handleNext}>
|
<div className="flex items-center space-x-2 mt-2">
|
||||||
下一步
|
<Input
|
||||||
</Button>
|
type="time"
|
||||||
|
value={formData.addFriendTimeStart || "09:00"}
|
||||||
|
onChange={(e) =>
|
||||||
|
onChange({ ...formData, addFriendTimeStart: e.target.value })
|
||||||
|
}
|
||||||
|
className="w-32"
|
||||||
|
/>
|
||||||
|
<span>至</span>
|
||||||
|
<Input
|
||||||
|
type="time"
|
||||||
|
value={formData.addFriendTimeEnd || "18:00"}
|
||||||
|
onChange={(e) =>
|
||||||
|
onChange({ ...formData, addFriendTimeEnd: e.target.value })
|
||||||
|
}
|
||||||
|
className="w-32"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{hasWarnings && (
|
||||||
|
<Alert
|
||||||
|
message="警告"
|
||||||
|
description="您有未完成的设置项,建议完善后再进入下一步。"
|
||||||
|
type="warning"
|
||||||
|
showIcon
|
||||||
|
className="bg-amber-50 border-amber-200"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Layout>
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
open={isTemplateDialogOpen}
|
open={isTemplateDialogOpen}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
LinkOutlined,
|
LinkOutlined,
|
||||||
TeamOutlined,
|
TeamOutlined,
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
|
import Layout from "@/components/Layout/Layout";
|
||||||
|
|
||||||
interface MessageContent {
|
interface MessageContent {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -533,23 +534,29 @@ const MessageSettings: React.FC<MessageSettingsProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="space-y-6">
|
<Layout
|
||||||
<div className="flex items-center justify-between">
|
footer={
|
||||||
<h2 className="text-lg font-semibold">消息设置</h2>
|
<div className="p-4 border-t bg-white">
|
||||||
<Button onClick={() => setIsAddDayPlanOpen(true)}>
|
<div className="flex justify-between">
|
||||||
<PlusOutlined className="h-4 w-4" />
|
<Button onClick={onPrev}>上一步</Button>
|
||||||
</Button>
|
<Button type="primary" onClick={onNext}>
|
||||||
</div>
|
下一步
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className="p-4 space-y-6">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<h2 className="text-lg font-semibold">消息设置</h2>
|
||||||
|
<Button onClick={() => setIsAddDayPlanOpen(true)}>
|
||||||
|
<PlusOutlined className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Tabs defaultActiveKey="0" items={items} />
|
<Tabs defaultActiveKey="0" items={items} />
|
||||||
|
|
||||||
<div className="flex justify-between pt-4">
|
|
||||||
<Button onClick={onPrev}>上一步</Button>
|
|
||||||
<Button type="primary" onClick={onNext}>
|
|
||||||
下一步
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Layout>
|
||||||
|
|
||||||
{/* 添加天数计划弹窗 */}
|
{/* 添加天数计划弹窗 */}
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
164
nkebao/src/pages/scenarios/plan/new/steps/base.module.scss
Normal file
164
nkebao/src/pages/scenarios/plan/new/steps/base.module.scss
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
.basic-container {
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
.basic-scene-select {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 24px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
.basic-scene-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
.basic-scene-btn {
|
||||||
|
height: 40px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 10px;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 16px;
|
||||||
|
outline: none;
|
||||||
|
cursor: pointer;
|
||||||
|
background: rgba(#1677ff,0.1);
|
||||||
|
color: #1677ff;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
.basic-scene-btn.selected {
|
||||||
|
background: #1677ff;
|
||||||
|
color: #fff;
|
||||||
|
box-shadow: 0 2px 8px rgba(22,119,255,0.08);
|
||||||
|
}
|
||||||
|
.basic-label {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.basic-input-block {
|
||||||
|
border: 1px solid #eee;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
.basic-tag-list {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 5px;
|
||||||
|
padding-bottom: 16px;
|
||||||
|
}
|
||||||
|
.basic-tag-item{
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
.basic-custom-tag-input {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.basic-success-tip {
|
||||||
|
display: flex;
|
||||||
|
padding-top: 16px;
|
||||||
|
}
|
||||||
|
.basic-materials {
|
||||||
|
margin: 16px 0;
|
||||||
|
}
|
||||||
|
.basic-materials-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.basic-material-preview{
|
||||||
|
position: absolute;
|
||||||
|
top: 8px;
|
||||||
|
padding-left: 2px;
|
||||||
|
right: 8px;
|
||||||
|
background:rgba(0,0,0,0.5);
|
||||||
|
border-radius: 50%;
|
||||||
|
height: 20px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 2;
|
||||||
|
cursor:pointer;
|
||||||
|
}
|
||||||
|
.basic-material-card {
|
||||||
|
border: 2px solid #eee;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
background: #fff;
|
||||||
|
text-align: center;
|
||||||
|
position: relative;
|
||||||
|
min-height: 192px;
|
||||||
|
transition: border 0.2s;
|
||||||
|
}
|
||||||
|
.basic-material-card.selected {
|
||||||
|
border: 2px solid #1890ff;
|
||||||
|
background: #e6f7ff;
|
||||||
|
}
|
||||||
|
.basic-material-img {
|
||||||
|
width: 100px;
|
||||||
|
height: 180px;
|
||||||
|
object-fit: cover;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-bottom: 0;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.basic-material-name {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
background: rgba(0,0,0,0.5);
|
||||||
|
color: #fff;
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 4px 0;
|
||||||
|
border-bottom-left-radius: 4px;
|
||||||
|
border-bottom-right-radius: 4px;
|
||||||
|
text-align: center;
|
||||||
|
z-index: 3;
|
||||||
|
}
|
||||||
|
.basic-add-material {
|
||||||
|
border: 2px dashed #bbb;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
background: #fafbfc;
|
||||||
|
text-align: center;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 190px;
|
||||||
|
}
|
||||||
|
.basic-order-upload {
|
||||||
|
margin: 16px 0;
|
||||||
|
}
|
||||||
|
.basic-order-upload-label {
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
.basic-order-upload-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
.basic-order-upload-tip {
|
||||||
|
color: #888;
|
||||||
|
font-size: 13px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
.basic-phone-settings {
|
||||||
|
margin: 16px 0;
|
||||||
|
background: #f7f8fa;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 20px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0,0,0,0.03);
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
.basic-wechat-group {
|
||||||
|
margin: 16px 0;
|
||||||
|
}
|
||||||
|
.basic-footer-switch {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin: 16px 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user