This commit is contained in:
wong
2025-09-13 17:20:57 +08:00
33 changed files with 1763 additions and 312 deletions

View File

@@ -1,14 +1,18 @@
{
"_charts-DN1cefc8.js": {
"file": "assets/charts-DN1cefc8.js",
"_charts-aNYyX7D2.js": {
"file": "assets/charts-aNYyX7D2.js",
"name": "charts",
"imports": [
"_ui-5V-xZWkf.js",
"_ui-DZwp85UP.js",
"_vendor-Bq99rrm8.js"
]
},
"_ui-5V-xZWkf.js": {
"file": "assets/ui-5V-xZWkf.js",
"_ui-D0C0OGrH.css": {
"file": "assets/ui-D0C0OGrH.css",
"src": "_ui-D0C0OGrH.css"
},
"_ui-DZwp85UP.js": {
"file": "assets/ui-DZwp85UP.js",
"name": "ui",
"imports": [
"_vendor-Bq99rrm8.js"
@@ -17,10 +21,6 @@
"assets/ui-D0C0OGrH.css"
]
},
"_ui-D0C0OGrH.css": {
"file": "assets/ui-D0C0OGrH.css",
"src": "_ui-D0C0OGrH.css"
},
"_utils-Ft3ushmX.js": {
"file": "assets/utils-Ft3ushmX.js",
"name": "utils",
@@ -33,18 +33,18 @@
"name": "vendor"
},
"index.html": {
"file": "assets/index-9Clf2a7g.js",
"file": "assets/index-CCIZs36L.js",
"name": "index",
"src": "index.html",
"isEntry": true,
"imports": [
"_vendor-Bq99rrm8.js",
"_ui-5V-xZWkf.js",
"_ui-DZwp85UP.js",
"_utils-Ft3ushmX.js",
"_charts-DN1cefc8.js"
"_charts-aNYyX7D2.js"
],
"css": [
"assets/index-CK1wd128.css"
"assets/index-DRrzDMi4.css"
]
}
}

View File

@@ -11,13 +11,13 @@
</style>
<!-- 引入 uni-app web-view SDK必须 -->
<script type="text/javascript" src="/websdk.js"></script>
<script type="module" crossorigin src="/assets/index-9Clf2a7g.js"></script>
<script type="module" crossorigin src="/assets/index-CCIZs36L.js"></script>
<link rel="modulepreload" crossorigin href="/assets/vendor-Bq99rrm8.js">
<link rel="modulepreload" crossorigin href="/assets/ui-5V-xZWkf.js">
<link rel="modulepreload" crossorigin href="/assets/ui-DZwp85UP.js">
<link rel="modulepreload" crossorigin href="/assets/utils-Ft3ushmX.js">
<link rel="modulepreload" crossorigin href="/assets/charts-DN1cefc8.js">
<link rel="modulepreload" crossorigin href="/assets/charts-aNYyX7D2.js">
<link rel="stylesheet" crossorigin href="/assets/ui-D0C0OGrH.css">
<link rel="stylesheet" crossorigin href="/assets/index-CK1wd128.css">
<link rel="stylesheet" crossorigin href="/assets/index-DRrzDMi4.css">
</head>
<body>
<div id="root"></div>

View File

@@ -43,33 +43,91 @@
.headerRight {
display: flex;
align-items: center;
gap: 16px;
}
.userInfo {
cursor: pointer;
padding: 8px 12px;
border-radius: 6px;
transition: all 0.3s;
display: flex;
align-items: center;
gap: 16px;
padding: 8px 0;
&:hover {
background-color: #f5f5f5;
}
.suanli {
font-size: 16px;
display: flex;
align-items: center;
gap: 4px;
font-size: 14px;
color: #666;
font-weight: 500;
padding: 6px 12px;
background: #f8f9fa;
border-radius: 20px;
border: 1px solid #e9ecef;
.suanliIcon {
font-size: 24px;
font-size: 16px;
color: #ffc107;
}
}
}
.avatar {
border: 2px solid #f0f0f0;
.messageButton {
display: flex;
align-items: center;
justify-content: center;
width: 44px;
height: 44px;
border-radius: 50%;
transition: all 0.3s;
border: 1px solid #e9ecef;
background: #fff;
&:hover {
background-color: #f8f9fa;
border-color: #1890ff;
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(24, 144, 255, 0.15);
}
.anticon {
font-size: 18px;
color: #666;
}
}
.userSection {
display: flex;
align-items: center;
gap: 12px;
cursor: pointer;
padding: 8px 16px;
border-radius: 24px;
transition: all 0.3s;
border: 1px solid #e9ecef;
background: #fff;
&:hover {
background-color: #f8f9fa;
border-color: #1890ff;
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(24, 144, 255, 0.15);
}
}
.userNickname {
font-size: 14px;
color: #333;
font-weight: 600;
max-width: 100px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.avatar {
border: 2px solid #e9ecef;
transition: all 0.3s;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.username {

View File

@@ -28,10 +28,10 @@ const Login: React.FC = () => {
const { login, login2 } = useUserStore();
const [verify, setVerify] = useState<{
verifyCodeImage: string;
verifyCode: string;
verifySessionId: string;
}>({
verifyCodeImage: "",
verifyCode: "",
verifySessionId: "",
});
// 倒计时效果
useEffect(() => {
@@ -45,7 +45,7 @@ const Login: React.FC = () => {
const res = await getVerifyCode();
setVerify({
verifyCodeImage: res.verifyCodeImage,
verifyCode: res.verifyCode,
verifySessionId: res.verifySessionId,
});
};
@@ -88,7 +88,7 @@ const Login: React.FC = () => {
// 添加typeId参数
const loginParams = {
...values,
typeId: activeTab as number,
verifySessionId: verify.verifySessionId,
};
const response =
@@ -98,10 +98,10 @@ const Login: React.FC = () => {
response
.then(res => {
const { member, kefuData, deviceTotal } = res;
const { member, kefuData } = res;
// 清空WebSocket连接状态
useWebSocketStore.getState().clearConnectionState();
login(res.token, member, deviceTotal);
login(res.token, member);
const { self, token } = kefuData;
login2(token.access_token);
setUserInfo(self);
@@ -218,7 +218,7 @@ const Login: React.FC = () => {
)}
{activeTab == 1 && verify.verifyCodeImage && (
<Form.Item
name="verificationCode"
name="verifyCode"
label="验证码"
rules={[{ required: true, message: "请输入验证码" }]}
>

View File

@@ -3,7 +3,7 @@ import request2 from "@/api/request2";
// 密码登录
export function loginWithPassword(params: any) {
return request("/v1/auth/login", params, "POST");
return request("/v1/kefu/login", params, "POST");
}
// 验证码登录

View File

@@ -1,24 +1,25 @@
import { BarChartOutlined, RobotOutlined } from "@ant-design/icons";
// 菜单项接口
export interface MenuItem {
id: string;
title: string;
icon: string;
icon: React.ReactNode;
path?: string;
}
// 菜单列表数据
export const menuList: MenuItem[] = [
{
id: "dashboard",
title: "数据面板",
icon: "📊",
path: "/pc/dashboard",
id: "wechat",
title: "AI智能客服",
icon: <RobotOutlined className="menuIcon" style={{ fontSize: 20 }} />,
path: "/pc/weChat",
},
{
id: "wechat",
title: "微信管理",
icon: "💬",
path: "/pc/weChat",
id: "powerCenter",
title: "功能中心",
icon: <BarChartOutlined className="menuIcon" style={{ fontSize: 20 }} />,
path: "/pc/powerCenter",
},
];
@@ -29,10 +30,6 @@ export const drawerMenuData = {
appName: "触客宝",
appDesc: "AI智能营销系统",
},
primaryButton: {
title: "AI智能客服",
icon: "🔒",
},
footer: {
balanceIcon: "⚡",
balanceLabel: "算力余额",

View File

@@ -1,3 +1,32 @@
/**
* NavCommon 组件样式文件
*
* 文件结构说明:
* 1. Header 区域样式 - 顶部导航栏相关样式
* - headerLeft: 左侧菜单按钮和标题
* - headerRight: 右侧用户信息、算力显示、消息按钮等
*
* 2. Drawer 抽屉区域样式 - 左侧菜单抽屉相关样式
* - drawerContent: 抽屉内容容器
* - drawerHeader: 抽屉头部Logo区域
* - drawerBody: 抽屉主体(主要按钮和菜单项)
* - drawerFooter: 抽屉底部(余额显示)
*
* 3. 消息抽屉区域样式 - 右侧消息中心抽屉相关样式
* - messageContent: 消息列表容器
* - messageItem: 单个消息项样式
*
* 4. 兼容样式 - 保持向后兼容的独立样式类
*
* 5. 响应式设计 - 移动端适配样式
*
* 样式组织原则:
* - 按照 tsx 文件中的嵌套结构组织样式
* - 使用 SCSS 嵌套语法体现 DOM 层级关系
* - 相关样式就近放置,便于维护
*/
// ===== Header 区域样式 =====
.header {
background: #fff;
padding: 0 16px;
@@ -7,69 +36,124 @@
justify-content: space-between;
height: 64px;
border-bottom: 1px solid #f0f0f0;
}
.headerLeft {
display: flex;
align-items: center;
gap: 12px;
}
// Header 左侧区域
.headerLeft {
display: flex;
align-items: center;
gap: 12px;
.menuButton {
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
border-radius: 6px;
transition: all 0.3s;
.menuButton {
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
border-radius: 6px;
transition: all 0.3s;
&:hover {
background-color: #f5f5f5;
&:hover {
background-color: #f5f5f5;
}
.anticon {
font-size: 18px;
color: #666;
}
}
.title {
font-size: 18px;
color: #333;
margin: 0;
}
}
.anticon {
font-size: 18px;
color: #666;
}
}
// Header 右侧区域
.headerRight {
display: flex;
align-items: center;
padding-right: 10px;
.title {
font-size: 18px;
color: #333;
margin: 0;
}
.userInfo {
display: flex;
align-items: center;
padding: 8px 0;
.suanli {
display: flex;
align-items: center;
gap: 4px;
font-size: 16px;
color: #666;
font-weight: 500;
cursor: pointer;
.headerRight {
display: flex;
align-items: center;
}
.suanliIcon {
font-size: 20px;
color: #ffc107;
}
.userInfo {
cursor: pointer;
padding: 8px 12px;
border-radius: 6px;
transition: all 0.3s;
&:hover {
color: #52c41a;
transition: all 0.3s;
}
}
&:hover {
background-color: #f5f5f5;
}
.suanli {
font-size: 16px;
color: #666;
.suanliIcon {
font-size: 24px;
.avatar {
border: 2px solid #e9ecef;
transition: all 0.3s;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
}
.messageButton {
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
border-radius: 80px;
transition: all 0.3s;
background: #fff;
cursor: pointer;
border: 1px solid #e9ecef;
&:hover {
background-color: #f8f9fa;
border-color: #1890ff;
box-shadow: 0 0px 12px rgba(24, 144, 255, 0.15);
border: 1px solid #e9ecef;
}
.anticon {
font-size: 18px;
color: #666;
}
}
.userSection {
display: flex;
align-items: center;
gap: 12px;
cursor: pointer;
padding: 8px 16px;
}
.userNickname {
font-size: 14px;
color: #333;
font-weight: 600;
max-width: 100px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
.avatar {
border: 2px solid #f0f0f0;
transition: all 0.3s;
&:hover {
border-color: #1890ff;
}
// 兼容旧样式(避免破坏性更改)
.bell {
cursor: pointer;
background: #f5f5f5;
}
.username {
@@ -79,7 +163,8 @@
margin-left: 8px;
}
// 抽屉样式
// ===== Drawer 抽屉区域样式 =====
// 左侧菜单抽屉
.drawer {
:global(.ant-drawer-header) {
background: #fafafa;
@@ -89,142 +174,264 @@
:global(.ant-drawer-body) {
padding: 0;
}
}
.drawerContent {
height: 100%;
display: flex;
flex-direction: column;
background: #f8f9fa;
}
.drawerHeader {
padding: 20px;
background: #fff;
border-bottom: none;
}
.logoSection {
display: flex;
align-items: center;
gap: 12px;
}
.logoIcon {
width: 48px;
height: 48px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
color: white;
}
.logoText {
display: flex;
flex-direction: column;
}
.appName {
font-size: 18px;
font-weight: 600;
color: #333;
margin: 0;
}
.appDesc {
font-size: 12px;
color: #999;
margin: 2px 0 0 0;
}
.drawerBody {
flex: 1;
padding: 20px;
display: flex;
flex-direction: column;
gap: 16px;
}
.primaryButton {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 12px;
padding: 16px 20px;
display: flex;
align-items: center;
gap: 12px;
cursor: pointer;
transition: all 0.3s;
&:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.3);
}
.buttonIcon {
font-size: 20px;
}
span {
font-size: 16px;
font-weight: 600;
color: white;
}
}
.menuSection {
margin-top: 8px;
}
.menuItem {
display: flex;
align-items: center;
padding: 16px 20px;
cursor: pointer;
transition: all 0.3s;
border-radius: 8px;
&:hover {
background-color: #f0f0f0;
}
span {
font-size: 16px;
color: #333;
font-weight: 500;
}
}
.menuIcon {
font-size: 20px;
margin-right: 12px;
width: 20px;
text-align: center;
}
.drawerFooter {
padding: 20px;
background: #fff;
border-top: 1px solid #f0f0f0;
.balanceSection {
// 抽屉头部
.drawerHeader {
padding: 20px;
background: #fff;
border-bottom: none;
display: flex;
justify-content: space-between;
align-items: center;
.balanceIcon {
color: #666;
.suanliIcon {
font-size: 20px;
.logoSection {
display: flex;
align-items: center;
gap: 12px;
.logoIcon {
width: 40px;
height: 40px;
background: linear-gradient(135deg, #3b81f6 0%, #9035ea 100%);
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
color: white;
}
.logoText {
display: flex;
flex-direction: column;
.appName {
font-size: 16px;
font-weight: 600;
color: #333;
margin: 0;
}
.appDesc {
font-size: 12px;
color: #999;
margin: 2px 0 0 0;
}
}
}
.anticon {
cursor: pointer;
}
}
// 抽屉内容容器
.drawerContent {
height: 100%;
display: flex;
flex-direction: column;
background: #f8f9fa;
// 抽屉主体内容
.drawerBody {
flex: 1;
display: flex;
flex-direction: column;
gap: 16px;
.primaryButton {
background: linear-gradient(135deg, #3d80f6 0%, #764ba2 100%);
border-radius: 12px;
padding: 16px 20px;
display: flex;
align-items: center;
gap: 12px;
cursor: pointer;
transition: all 0.3s;
&:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.3);
}
.buttonIcon {
font-size: 20px;
}
span {
font-size: 16px;
font-weight: 600;
color: white;
}
}
// 菜单区域
.menuSection {
padding: 0px 20px;
.menuItem {
display: flex;
align-items: center;
padding: 8px 20px;
cursor: pointer;
transition: all 0.3s;
border-radius: 8px;
margin: 4px 0;
&:hover {
background-color: #f0f0f0;
}
.menuIcon {
font-size: 20px;
margin-right: 12px;
width: 20px;
text-align: center;
}
span {
font-size: 16px;
color: #333;
font-weight: 500;
}
&.menuItemActive {
background: linear-gradient(to right, #3b82f6, #9333ea);
color: white;
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
span {
color: white;
font-weight: 600;
}
.menuIcon {
color: white;
}
&:hover {
background: linear-gradient(to right, #3b82f6, #9333ea);
box-shadow: 0 6px 16px rgba(102, 126, 234, 0.4);
}
}
}
}
}
.balanceText {
color: #3d9c0d;
// 抽屉底部
.drawerFooter {
padding: 20px;
background: #fff;
border-top: 1px solid #f0f0f0;
.balanceSection {
display: flex;
justify-content: space-between;
align-items: center;
.balanceIcon {
color: #666;
.suanliIcon {
font-size: 20px;
}
}
.balanceText {
color: #3d9c0d;
}
}
}
}
}
// ===== 消息抽屉区域样式 =====
.messageDrawer {
:global(.ant-drawer-header) {
border-bottom: 1px solid #f0f0f0;
}
:global(.ant-drawer-body) {
padding: 0;
}
.messageContent {
height: 100%;
overflow-y: auto;
.messageItem {
display: flex;
padding: 16px;
border-bottom: 1px solid #f5f5f5;
transition: background-color 0.2s;
&:hover {
background-color: #fafafa;
}
&:last-child {
border-bottom: none;
}
.messageAvatar {
margin-right: 12px;
flex-shrink: 0;
}
.messageInfo {
flex: 1;
min-width: 0;
.messageTitle {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 8px;
.messageType {
font-size: 14px;
font-weight: 500;
color: #262626;
}
.messageStatus {
width: 8px;
height: 8px;
border-radius: 50%;
background-color: #1890ff;
}
}
.messageText {
font-size: 13px;
color: #595959;
line-height: 1.4;
margin-bottom: 8px;
word-break: break-word;
}
.messageTime {
display: flex;
align-items: center;
justify-content: space-between;
font-size: 12px;
color: #8c8c8c;
}
.messageActions {
display: flex;
gap: 8px;
margin-top: 8px;
:global(.ant-btn) {
height: 24px;
padding: 0 8px;
font-size: 12px;
}
}
}
}
}
}
// ===== 兼容样式 =====
.balanceLabel {
font-size: 12px;
color: #999;
@@ -237,22 +444,3 @@
color: #52c41a;
margin: 2px 0 0 0;
}
// 响应式设计
@media (max-width: 768px) {
.header {
padding: 0 12px;
}
.title {
font-size: 16px;
}
.username {
display: none;
}
.drawer {
width: 280px !important;
}
}

View File

@@ -1,9 +1,17 @@
import React, { useState } from "react";
import { Layout, Drawer, Avatar, Space, Button } from "antd";
import { MenuOutlined, UserOutlined } from "@ant-design/icons";
import { Layout, Drawer, Avatar, Space, Button, Badge, Dropdown } from "antd";
import {
MenuOutlined,
UserOutlined,
BellOutlined,
CloseOutlined,
LogoutOutlined,
UserSwitchOutlined,
} from "@ant-design/icons";
import { useUserStore } from "@/store/module/user";
import { useCkChatStore } from "@/store/module/ckchat/ckchat";
import { useNavigate } from "react-router-dom";
// import { useCkChatStore } from "@/store/module/ckchat/ckchat";
import { useNavigate, useLocation } from "react-router-dom";
import { drawerMenuData, menuList } from "./index.data";
import styles from "./index.module.scss";
@@ -19,10 +27,12 @@ const NavCommon: React.FC<NavCommonProps> = ({
onMenuClick,
}) => {
const [drawerVisible, setDrawerVisible] = useState(false);
const [messageDrawerVisible, setMessageDrawerVisible] = useState(false);
const [messageCount] = useState(3); // 模拟消息数量
const navigate = useNavigate();
const { userInfo } = useCkChatStore();
const { user } = useUserStore();
console.log(user);
const location = useLocation();
// const { userInfo } = useCkChatStore();
const { user, logout } = useUserStore();
// 处理菜单图标点击
const handleMenuClick = () => {
@@ -35,6 +45,49 @@ const NavCommon: React.FC<NavCommonProps> = ({
setDrawerVisible(false);
};
// 处理消息中心点击
const handleMessageClick = () => {
setMessageDrawerVisible(true);
};
// 处理消息抽屉关闭
const handleMessageDrawerClose = () => {
setMessageDrawerVisible(false);
};
// 处理退出登录
const handleLogout = () => {
logout(); // 清除localStorage中的token和用户状态
navigate("/login"); // 跳转到登录页面
};
// 用户菜单项
const userMenuItems = [
{
key: "userInfo",
label: (
<div style={{ fontWeight: "bold", color: "#188eee" }}>
{user.username}{user.account}
</div>
),
},
{
key: "profile",
icon: <UserSwitchOutlined style={{ fontSize: 16 }} />,
label: "个人资料",
onClick: () => {
console.log("个人资料点击");
// TODO: 跳转到个人资料页面
},
},
{
key: "logout",
icon: <LogoutOutlined style={{ fontSize: 14 }} />,
label: "退出登录",
onClick: handleLogout,
},
];
// 默认抽屉内容
const defaultDrawerContent = (
<div className={styles.drawerContent}>
@@ -52,30 +105,30 @@ const NavCommon: React.FC<NavCommonProps> = ({
</div>
</div>
</div>
<span className={styles.anticon} onClick={handleDrawerClose}>
<CloseOutlined />
</span>
</div>
<div className={styles.drawerBody}>
<div className={styles.primaryButton}>
<div className={styles.buttonIcon}>
{drawerMenuData.primaryButton.icon}
</div>
<span>{drawerMenuData.primaryButton.title}</span>
</div>
<div className={styles.menuSection}>
{menuList.map((item, index) => (
<div
key={index}
className={styles.menuItem}
onClick={() => {
if (item.path) {
navigate(item.path);
setDrawerVisible(false);
}
}}
>
<div className={styles.menuIcon}>{item.icon}</div>
<span>{item.title}</span>
</div>
))}
{menuList.map((item, index) => {
const isActive = location.pathname === item.path;
return (
<div
key={index}
className={`${styles.menuItem} ${isActive ? styles.menuItemActive : ""}`}
onClick={() => {
if (item.path) {
navigate(item.path);
setDrawerVisible(false);
}
}}
>
<div className={styles.menuIcon}>{item.icon}</div>
<span>{item.title}</span>
</div>
);
})}
</div>
</div>
<div className={styles.drawerFooter}>
@@ -113,26 +166,119 @@ const NavCommon: React.FC<NavCommonProps> = ({
<span className={styles.suanliIcon}></span>
9307.423
</span>
<Avatar
size={40}
icon={<UserOutlined />}
src={user?.avatar}
className={styles.avatar}
/>
<Dropdown
menu={{ items: userMenuItems }}
placement="bottomRight"
trigger={["click"]}
>
<div className={styles.userSection}>
<Avatar
size={40}
icon={<UserOutlined />}
src={user?.avatar}
className={styles.avatar}
/>
</div>
</Dropdown>
<div className={styles.messageButton} onClick={handleMessageClick}>
<Badge count={messageCount} size="small">
<BellOutlined style={{ fontSize: 20 }} />
</Badge>
</div>
</Space>
</div>
</Header>
<Drawer
title="菜单"
placement="left"
onClose={handleDrawerClose}
open={drawerVisible}
width={300}
className={styles.drawer}
closable={false}
>
{defaultDrawerContent}
</Drawer>
<Drawer
title="通知中心"
placement="right"
onClose={handleMessageDrawerClose}
open={messageDrawerVisible}
width={400}
className={styles.messageDrawer}
extra={
<Space>
<Button type="text" size="small">
</Button>
</Space>
}
>
<div className={styles.messageContent}>
<div className={styles.messageItem}>
<div className={styles.messageAvatar}>
<Avatar size={40} style={{ backgroundColor: "#87d068" }}>
</Avatar>
</div>
<div className={styles.messageInfo}>
<div className={styles.messageTitle}>
<span className={styles.messageType}></span>
<div className={styles.messageStatus}></div>
</div>
<div className={styles.messageText}>
19991699
</div>
<div className={styles.messageTime}>
03-05
<Button type="link" size="small">
</Button>
</div>
</div>
</div>
<div className={styles.messageItem}>
<div className={styles.messageAvatar}>
<Avatar size={40} style={{ backgroundColor: "#f56a00" }}>
E
</Avatar>
</div>
<div className={styles.messageInfo}>
<div className={styles.messageTitle}>
<span className={styles.messageType}></span>
<div className={styles.messageStatus}></div>
</div>
<div className={styles.messageText}>
Eric在@了您
</div>
<div className={styles.messageTime}>03-05</div>
</div>
</div>
<div className={styles.messageItem}>
<div className={styles.messageAvatar}>
<Avatar size={40} style={{ backgroundColor: "#1890ff" }}>
</Avatar>
</div>
<div className={styles.messageInfo}>
<div className={styles.messageTitle}>
<span className={styles.messageType}></span>
</div>
<div className={styles.messageText}></div>
<div className={styles.messageTime}>03-04</div>
<div className={styles.messageActions}>
<Button type="primary" size="small">
</Button>
<Button size="small"></Button>
</div>
</div>
</div>
</div>
</Drawer>
</>
);
};

View File

@@ -0,0 +1,43 @@
.container {
padding: 24px;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.header {
margin-bottom: 24px;
h1 {
font-size: 24px;
font-weight: 600;
color: #262626;
margin: 0 0 8px 0;
}
p {
font-size: 14px;
color: #8c8c8c;
margin: 0;
}
}
.content {
min-height: 400px;
}
.placeholder {
display: flex;
align-items: center;
justify-content: center;
height: 300px;
background: #fafafa;
border: 1px dashed #d9d9d9;
border-radius: 6px;
p {
font-size: 16px;
color: #8c8c8c;
margin: 0;
}
}

View File

@@ -0,0 +1,21 @@
import React from "react";
import styles from "./index.module.scss";
const AiTraining: React.FC = () => {
return (
<div className={styles.container}>
<div className={styles.header}>
<h1>AI模型训练</h1>
<p>AI模型训练</p>
</div>
<div className={styles.content}>
{/* 功能内容待开发 */}
<div className={styles.placeholder}>
<p>AI模型训练功能正在开发中...</p>
</div>
</div>
</div>
);
};
export default AiTraining;

View File

@@ -0,0 +1,43 @@
.container {
padding: 24px;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.header {
margin-bottom: 24px;
h1 {
font-size: 24px;
font-weight: 600;
color: #262626;
margin: 0 0 8px 0;
}
p {
font-size: 14px;
color: #8c8c8c;
margin: 0;
}
}
.content {
min-height: 400px;
}
.placeholder {
display: flex;
align-items: center;
justify-content: center;
height: 300px;
background: #fafafa;
border: 1px dashed #d9d9d9;
border-radius: 6px;
p {
font-size: 16px;
color: #8c8c8c;
margin: 0;
}
}

View File

@@ -0,0 +1,21 @@
import React from "react";
import styles from "./index.module.scss";
const AutoGreeting: React.FC = () => {
return (
<div className={styles.container}>
<div className={styles.header}>
<h1></h1>
<p></p>
</div>
<div className={styles.content}>
{/* 功能内容待开发 */}
<div className={styles.placeholder}>
<p>...</p>
</div>
</div>
</div>
);
};
export default AutoGreeting;

View File

@@ -0,0 +1,43 @@
.container {
padding: 24px;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.header {
margin-bottom: 24px;
h1 {
font-size: 24px;
font-weight: 600;
color: #262626;
margin: 0 0 8px 0;
}
p {
font-size: 14px;
color: #8c8c8c;
margin: 0;
}
}
.content {
min-height: 400px;
}
.placeholder {
display: flex;
align-items: center;
justify-content: center;
height: 300px;
background: #fafafa;
border: 1px dashed #d9d9d9;
border-radius: 6px;
p {
font-size: 16px;
color: #8c8c8c;
margin: 0;
}
}

View File

@@ -0,0 +1,21 @@
import React from "react";
import styles from "./index.module.scss";
const CommunicationRecord: React.FC = () => {
return (
<div className={styles.container}>
<div className={styles.header}>
<h1></h1>
<p></p>
</div>
<div className={styles.content}>
{/* 功能内容待开发 */}
<div className={styles.placeholder}>
<p>...</p>
</div>
</div>
</div>
);
};
export default CommunicationRecord;

View File

@@ -0,0 +1,43 @@
.container {
padding: 24px;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.header {
margin-bottom: 24px;
h1 {
font-size: 24px;
font-weight: 600;
color: #262626;
margin: 0 0 8px 0;
}
p {
font-size: 14px;
color: #8c8c8c;
margin: 0;
}
}
.content {
min-height: 400px;
}
.placeholder {
display: flex;
align-items: center;
justify-content: center;
height: 300px;
background: #fafafa;
border: 1px dashed #d9d9d9;
border-radius: 6px;
p {
font-size: 16px;
color: #8c8c8c;
margin: 0;
}
}

View File

@@ -0,0 +1,21 @@
import React from "react";
import styles from "./index.module.scss";
const ContentManagement: React.FC = () => {
return (
<div className={styles.container}>
<div className={styles.header}>
<h1></h1>
<p></p>
</div>
<div className={styles.content}>
{/* 功能内容待开发 */}
<div className={styles.placeholder}>
<p>...</p>
</div>
</div>
</div>
);
};
export default ContentManagement;

View File

@@ -0,0 +1,43 @@
.container {
padding: 24px;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.header {
margin-bottom: 24px;
h1 {
font-size: 24px;
font-weight: 600;
color: #262626;
margin: 0 0 8px 0;
}
p {
font-size: 14px;
color: #8c8c8c;
margin: 0;
}
}
.content {
min-height: 400px;
}
.placeholder {
display: flex;
align-items: center;
justify-content: center;
height: 300px;
background: #fafafa;
border: 1px dashed #d9d9d9;
border-radius: 6px;
p {
font-size: 16px;
color: #8c8c8c;
margin: 0;
}
}

View File

@@ -0,0 +1,21 @@
import React from "react";
import styles from "./index.module.scss";
const CustomerManagement: React.FC = () => {
return (
<div className={styles.container}>
<div className={styles.header}>
<h1></h1>
<p></p>
</div>
<div className={styles.content}>
{/* 功能内容待开发 */}
<div className={styles.placeholder}>
<p>...</p>
</div>
</div>
</div>
);
};
export default CustomerManagement;

View File

@@ -0,0 +1,119 @@
import React from "react";
import {
AimOutlined,
SendOutlined,
CalendarOutlined,
TagsOutlined,
UserOutlined,
MessageOutlined,
FileTextOutlined,
RobotOutlined,
UserAddOutlined,
AppstoreOutlined,
SoundOutlined,
TeamOutlined,
FolderOutlined,
BarChartOutlined,
} from "@ant-design/icons";
export interface FeatureCard {
id: string;
title: string;
description: string;
icon: React.ReactNode;
color: string;
path?: string;
isNew?: boolean;
}
export interface TabItem {
key: string;
label: string;
icon?: React.ReactNode;
active?: boolean;
}
export const featureCards: FeatureCard[] = [
{
id: "precision-send",
title: "精准群发",
description: "基于客户标签和行为数据进行精准群发",
icon: <AimOutlined />,
color: "#ff6b35",
path: "/pc/powerCenter/precision-send",
},
{
id: "sop-send",
title: "SOP群发",
description: "使用触客宝SOP标准化流程进行批量消息发送",
icon: <SendOutlined />,
color: "#4285f4",
path: "/pc/powerCenter/sop-send",
},
{
id: "moments-marketing",
title: "朋友圈营销",
description: "AI智能生成朋友圈内容提升品牌曝光度",
icon: <CalendarOutlined />,
color: "#34a853",
path: "/pc/powerCenter/moments-marketing",
},
{
id: "tag-management",
title: "标签管理",
description: "智能客户标签分类,精准用户画像分析",
icon: <TagsOutlined />,
color: "#9c27b0",
path: "/pc/powerCenter/tag-management",
},
{
id: "customer-management",
title: "客户好友管理",
description: "统一管理客户信息和好友关系,提升服务效率",
icon: <UserOutlined />,
color: "#6366f1",
path: "/pc/powerCenter/customer-management",
},
{
id: "communication-record",
title: "沟通记录",
description: "完整记录客户沟通历史,支持多维度查询分析",
icon: <MessageOutlined />,
color: "#06b6d4",
path: "/pc/powerCenter/communication-record",
},
{
id: "content-management",
title: "内容管理",
description: "素材管理、数据词汇库、关键词自动回复",
icon: <FileTextOutlined />,
color: "#f59e0b",
path: "/pc/powerCenter/content-management",
},
{
id: "ai-training",
title: "AI模型训练",
description: "自定义AI模型训练打造专属智能客服助手",
icon: <RobotOutlined />,
color: "#ec4899",
path: "/pc/powerCenter/ai-training",
isNew: true,
},
{
id: "auto-greeting",
title: "自动打招呼",
description: "智能识别新好友,自动发送个性化欢迎消息",
icon: <UserAddOutlined />,
color: "#10b981",
path: "/pc/powerCenter/auto-greeting",
},
];
export const tabItems: TabItem[] = [
{ key: "all", label: "全部功能", icon: <AppstoreOutlined /> },
{ key: "marketing", label: "营销推广", icon: <SoundOutlined /> },
{ key: "customer", label: "客户管理", icon: <TeamOutlined /> },
{ key: "ai", label: "AI智能", icon: <RobotOutlined /> },
{ key: "content", label: "内容管理", icon: <FolderOutlined /> },
{ key: "data", label: "数据分析", icon: <BarChartOutlined /> },
];

View File

@@ -0,0 +1,237 @@
.powerCenter {
padding: 24px;
background-color: #f5f5f5;
min-height: 100vh;
.header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 32px;
background: white;
padding: 24px;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
.headerLeft {
.title {
font-size: 28px;
font-weight: 600;
color: #1a1a1a;
margin: 0 0 8px 0;
}
.subtitle {
font-size: 14px;
color: #666;
margin: 0;
}
}
.headerRight {
.tabs {
.tab {
border-radius: 8px;
border: 1px solid #e0e0e0;
color: #666;
&:hover {
color: #1890ff;
border-color: #1890ff;
}
}
.activeTab {
border-radius: 8px;
background: #1890ff;
border-color: #1890ff;
box-shadow: 0 2px 4px rgba(24, 144, 255, 0.2);
}
}
}
}
.cardGrid {
.featureCard {
border-radius: 16px;
border: none;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
transition: all 0.3s ease;
height: 200px;
cursor: pointer;
&:hover {
transform: translateY(-4px);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
}
.cardContent {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
height: 100%;
justify-content: center;
.iconWrapper {
width: 64px;
height: 64px;
border-radius: 16px;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 16px;
position: relative;
.icon {
font-size: 28px;
color: white;
}
}
.cardInfo {
.cardTitle {
font-size: 16px;
font-weight: 600;
color: #1a1a1a;
margin: 0 0 8px 0;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
.newBadge {
background: linear-gradient(135deg, #ff6b6b, #ee5a24);
color: white;
font-size: 10px;
padding: 2px 6px;
border-radius: 8px;
font-weight: 500;
}
}
.cardDescription {
font-size: 12px;
color: #666;
line-height: 1.4;
margin: 0;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
}
}
}
}
}
// 响应式设计
@media (max-width: 1200px) {
.powerCenter {
.header {
flex-direction: column;
gap: 16px;
align-items: flex-start;
.headerRight {
width: 100%;
.tabs {
flex-wrap: wrap;
}
}
}
}
}
@media (max-width: 768px) {
.powerCenter {
padding: 16px;
.header {
padding: 16px;
.headerLeft {
.title {
font-size: 24px;
}
}
}
.cardGrid {
.featureCard {
height: 160px;
.cardContent {
.iconWrapper {
width: 48px;
height: 48px;
.icon {
font-size: 20px;
}
}
.cardInfo {
.cardTitle {
font-size: 14px;
}
.cardDescription {
font-size: 11px;
}
}
}
}
}
}
}
// 卡片颜色主题
.featureCard {
// 精准群发 - 橙色
&[data-color="#ff6b35"] .iconWrapper {
background: linear-gradient(135deg, #ff6b35, #f7931e);
}
// SOP群发 - 蓝色
&[data-color="#4285f4"] .iconWrapper {
background: linear-gradient(135deg, #4285f4, #1a73e8);
}
// 朋友圈营销 - 绿色
&[data-color="#34a853"] .iconWrapper {
background: linear-gradient(135deg, #34a853, #137333);
}
// 标签管理 - 紫色
&[data-color="#9c27b0"] .iconWrapper {
background: linear-gradient(135deg, #9c27b0, #7b1fa2);
}
// 客户管理 - 靛蓝
&[data-color="#6366f1"] .iconWrapper {
background: linear-gradient(135deg, #6366f1, #4f46e5);
}
// 沟通记录 - 青色
&[data-color="#06b6d4"] .iconWrapper {
background: linear-gradient(135deg, #06b6d4, #0891b2);
}
// 内容管理 - 黄色
&[data-color="#f59e0b"] .iconWrapper {
background: linear-gradient(135deg, #f59e0b, #d97706);
}
// AI训练 - 粉色
&[data-color="#ec4899"] .iconWrapper {
background: linear-gradient(135deg, #ec4899, #db2777);
}
// 自动打招呼 - 翠绿
&[data-color="#10b981"] .iconWrapper {
background: linear-gradient(135deg, #10b981, #059669);
}
}

View File

@@ -0,0 +1,103 @@
import React, { useState, useMemo } from "react";
import { Card, Row, Col, Button, Space } from "antd";
import { useNavigate } from "react-router-dom";
import { featureCards, tabItems, FeatureCard } from "./index.data";
import styles from "./index.module.scss";
const PowerCenter: React.FC = () => {
const navigate = useNavigate();
const [activeTab, setActiveTab] = useState("all");
const handleCardClick = (card: FeatureCard) => {
if (card.path) {
navigate(card.path);
}
};
const handleTabClick = (tabKey: string) => {
setActiveTab(tabKey);
};
// 根据选中的标签过滤功能卡片
const filteredCards = useMemo(() => {
if (activeTab === "all") {
return featureCards;
}
const categoryMap: { [key: string]: string[] } = {
marketing: ["precision-send", "sop-send", "moments-marketing"],
customer: ["customer-management", "tag-management"],
ai: ["ai-training", "auto-greeting"],
content: ["content-management"],
data: ["communication-record"],
};
const categoryIds = categoryMap[activeTab] || [];
return featureCards.filter(card => categoryIds.includes(card.id));
}, [activeTab]);
return (
<div className={styles.powerCenter}>
{/* 页面头部 */}
<div className={styles.header}>
<div className={styles.headerLeft}>
<h1 className={styles.title}></h1>
<p className={styles.subtitle}>AI私域营销系统 - </p>
</div>
<div className={styles.headerRight}>
<Space className={styles.tabs}>
{tabItems.map(item => (
<Button
key={item.key}
type={activeTab === item.key ? "primary" : "text"}
className={
activeTab === item.key ? styles.activeTab : styles.tab
}
onClick={() => handleTabClick(item.key)}
icon={item.icon}
>
{item.label}
</Button>
))}
</Space>
</div>
</div>
{/* 功能卡片网格 */}
<div className={styles.cardGrid}>
<Row gutter={[24, 24]}>
{filteredCards.map(card => (
<Col key={card.id} xs={24} sm={12} md={8} lg={6} xl={6}>
<Card
className={styles.featureCard}
hoverable
onClick={() => handleCardClick(card)}
bodyStyle={{ padding: "24px" }}
>
<div className={styles.cardContent}>
<div
className={styles.iconWrapper}
style={{ backgroundColor: card.color }}
>
<div className={styles.icon}>{card.icon}</div>
</div>
<div className={styles.cardInfo}>
<h3 className={styles.cardTitle}>
{card.title}
{card.isNew && (
<span className={styles.newBadge}></span>
)}
</h3>
<p className={styles.cardDescription}>{card.description}</p>
</div>
</div>
</Card>
</Col>
))}
</Row>
</div>
</div>
);
};
export default PowerCenter;

View File

@@ -0,0 +1,43 @@
.container {
padding: 24px;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.header {
margin-bottom: 24px;
h1 {
font-size: 24px;
font-weight: 600;
color: #262626;
margin: 0 0 8px 0;
}
p {
font-size: 14px;
color: #8c8c8c;
margin: 0;
}
}
.content {
min-height: 400px;
}
.placeholder {
display: flex;
align-items: center;
justify-content: center;
height: 300px;
background: #fafafa;
border: 1px dashed #d9d9d9;
border-radius: 6px;
p {
font-size: 16px;
color: #8c8c8c;
margin: 0;
}
}

View File

@@ -0,0 +1,21 @@
import React from "react";
import styles from "./index.module.scss";
const MomentsMarketing: React.FC = () => {
return (
<div className={styles.container}>
<div className={styles.header}>
<h1></h1>
<p>AI智能生成朋友圈内容</p>
</div>
<div className={styles.content}>
{/* 功能内容待开发 */}
<div className={styles.placeholder}>
<p>...</p>
</div>
</div>
</div>
);
};
export default MomentsMarketing;

View File

@@ -0,0 +1,43 @@
.container {
padding: 24px;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.header {
margin-bottom: 24px;
h1 {
font-size: 24px;
font-weight: 600;
color: #262626;
margin: 0 0 8px 0;
}
p {
font-size: 14px;
color: #8c8c8c;
margin: 0;
}
}
.content {
min-height: 400px;
}
.placeholder {
display: flex;
align-items: center;
justify-content: center;
height: 300px;
background: #fafafa;
border: 1px dashed #d9d9d9;
border-radius: 6px;
p {
font-size: 16px;
color: #8c8c8c;
margin: 0;
}
}

View File

@@ -0,0 +1,21 @@
import React from "react";
import styles from "./index.module.scss";
const PrecisionSend: React.FC = () => {
return (
<div className={styles.container}>
<div className={styles.header}>
<h1></h1>
<p></p>
</div>
<div className={styles.content}>
{/* 功能内容待开发 */}
<div className={styles.placeholder}>
<p>...</p>
</div>
</div>
</div>
);
};
export default PrecisionSend;

View File

@@ -0,0 +1,43 @@
.container {
padding: 24px;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.header {
margin-bottom: 24px;
h1 {
font-size: 24px;
font-weight: 600;
color: #262626;
margin: 0 0 8px 0;
}
p {
font-size: 14px;
color: #8c8c8c;
margin: 0;
}
}
.content {
min-height: 400px;
}
.placeholder {
display: flex;
align-items: center;
justify-content: center;
height: 300px;
background: #fafafa;
border: 1px dashed #d9d9d9;
border-radius: 6px;
p {
font-size: 16px;
color: #8c8c8c;
margin: 0;
}
}

View File

@@ -0,0 +1,21 @@
import React from "react";
import styles from "./index.module.scss";
const SopSend: React.FC = () => {
return (
<div className={styles.container}>
<div className={styles.header}>
<h1>SOP群发</h1>
<p>使SOP标准化流程进行批量消息发送</p>
</div>
<div className={styles.content}>
{/* 功能内容待开发 */}
<div className={styles.placeholder}>
<p>SOP群发功能正在开发中...</p>
</div>
</div>
</div>
);
};
export default SopSend;

View File

@@ -0,0 +1,43 @@
.container {
padding: 24px;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.header {
margin-bottom: 24px;
h1 {
font-size: 24px;
font-weight: 600;
color: #262626;
margin: 0 0 8px 0;
}
p {
font-size: 14px;
color: #8c8c8c;
margin: 0;
}
}
.content {
min-height: 400px;
}
.placeholder {
display: flex;
align-items: center;
justify-content: center;
height: 300px;
background: #fafafa;
border: 1px dashed #d9d9d9;
border-radius: 6px;
p {
font-size: 16px;
color: #8c8c8c;
margin: 0;
}
}

View File

@@ -0,0 +1,21 @@
import React from "react";
import styles from "./index.module.scss";
const TagManagement: React.FC = () => {
return (
<div className={styles.container}>
<div className={styles.header}>
<h1></h1>
<p></p>
</div>
<div className={styles.content}>
{/* 功能内容待开发 */}
<div className={styles.placeholder}>
<p>...</p>
</div>
</div>
</div>
);
};
export default TagManagement;

View File

@@ -26,26 +26,6 @@ const ChatWindow: React.FC<ChatWindowProps> = ({ contract }) => {
const onToggleProfile = () => {
setShowProfile(!showProfile);
};
const chatMenu = (
<Menu>
<Menu.Item key="profile" icon={<UserOutlined />}>
</Menu.Item>
<Menu.Item key="call" icon={<PhoneOutlined />}>
</Menu.Item>
<Menu.Item key="video" icon={<VideoCameraOutlined />}>
</Menu.Item>
<Menu.Divider />
<Menu.Item key="pin"></Menu.Item>
<Menu.Item key="mute"></Menu.Item>
<Menu.Divider />
<Menu.Item key="clear" danger>
</Menu.Item>
</Menu>
);
return (
<Layout className={styles.chatWindow}>
@@ -76,13 +56,6 @@ const ChatWindow: React.FC<ChatWindowProps> = ({ contract }) => {
className={styles.headerButton}
/>
</Tooltip>
<Dropdown overlay={chatMenu} trigger={["click"]}>
<Button
type="text"
icon={<MoreOutlined />}
className={styles.headerButton}
/>
</Dropdown>
</Space>
</Header>

View File

@@ -1,7 +1,16 @@
import CkboxPage from "@/pages/pc/ckbox";
import WeChatPage from "@/pages/pc/ckbox/weChat";
import Dashboard from "@/pages/pc/ckbox/dashboard";
import PowerCenter from "@/pages/pc/ckbox/powerCenter";
import PrecisionSend from "@/pages/pc/ckbox/powerCenter/precision-send";
import SopSend from "@/pages/pc/ckbox/powerCenter/sop-send";
import MomentsMarketing from "@/pages/pc/ckbox/powerCenter/moments-marketing";
import TagManagement from "@/pages/pc/ckbox/powerCenter/tag-management";
import CustomerManagement from "@/pages/pc/ckbox/powerCenter/customer-management";
import CommunicationRecord from "@/pages/pc/ckbox/powerCenter/communication-record";
import ContentManagement from "@/pages/pc/ckbox/powerCenter/content-management";
import AiTraining from "@/pages/pc/ckbox/powerCenter/ai-training";
import AutoGreeting from "@/pages/pc/ckbox/powerCenter/auto-greeting";
const ckboxRoutes = [
{
path: "/pc",
@@ -16,6 +25,46 @@ const ckboxRoutes = [
path: "weChat",
element: <WeChatPage />,
},
{
path: "powerCenter",
element: <PowerCenter />,
},
{
path: "powerCenter/precision-send",
element: <PrecisionSend />,
},
{
path: "powerCenter/sop-send",
element: <SopSend />,
},
{
path: "powerCenter/moments-marketing",
element: <MomentsMarketing />,
},
{
path: "powerCenter/tag-management",
element: <TagManagement />,
},
{
path: "powerCenter/customer-management",
element: <CustomerManagement />,
},
{
path: "powerCenter/communication-record",
element: <CommunicationRecord />,
},
{
path: "powerCenter/content-management",
element: <ContentManagement />,
},
{
path: "powerCenter/ai-training",
element: <AiTraining />,
},
{
path: "powerCenter/auto-greeting",
element: <AutoGreeting />,
},
],
},
];

View File

@@ -16,7 +16,6 @@ export interface User {
updateTime: string | null;
lastLoginIp: string;
lastLoginTime: number;
deviceTotal: number; // 设备总数
}
interface UserState {
@@ -28,7 +27,7 @@ interface UserState {
setToken: (token: string) => void;
setToken2: (token2: string) => void;
clearUser: () => void;
login: (token: string, userInfo: User, deviceTotal: number) => void;
login: (token: string, userInfo: User) => void;
login2: (token2: string) => void;
logout: () => void;
}
@@ -44,7 +43,7 @@ export const useUserStore = createPersistStore<UserState>(
setToken2: token2 => set({ token2 }),
clearUser: () =>
set({ user: null, token: null, token2: null, isLoggedIn: false }),
login: (token, userInfo, deviceTotal) => {
login: (token, userInfo) => {
// 只将token存储到localStorage
localStorage.setItem("token", token);
@@ -64,7 +63,6 @@ export const useUserStore = createPersistStore<UserState>(
updateTime: userInfo.updateTime,
lastLoginIp: userInfo.lastLoginIp,
lastLoginTime: userInfo.lastLoginTime,
deviceTotal: deviceTotal,
};
set({ user, token, isLoggedIn: true });

View File

@@ -0,0 +1,7 @@
import { createFromIconfontCN } from "@ant-design/icons";
const IconFont = createFromIconfontCN({
scriptUrl: "//at.alicdn.com/t/c/font_4272168_343o7l1hnhb.js",
});
export default IconFont;