feat(db): 添加Dexie数据库支持并重构数据存储结构
添加Dexie作为IndexedDB封装库,实现本地数据存储功能 重构数据接口定义和存储模块结构,优化类型定义 统一数据接口文件位置,增强代码可维护性
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
"antd-mobile-icons": "^0.3.0",
|
||||
"axios": "^1.6.7",
|
||||
"dayjs": "^1.11.13",
|
||||
"dexie": "^4.2.0",
|
||||
"echarts": "^5.6.0",
|
||||
"echarts-for-react": "^3.0.2",
|
||||
"react": "^18.2.0",
|
||||
|
||||
8
Cunkebao/pnpm-lock.yaml
generated
8
Cunkebao/pnpm-lock.yaml
generated
@@ -26,6 +26,9 @@ importers:
|
||||
dayjs:
|
||||
specifier: ^1.11.13
|
||||
version: 1.11.13
|
||||
dexie:
|
||||
specifier: ^4.2.0
|
||||
version: 4.2.0
|
||||
echarts:
|
||||
specifier: ^5.6.0
|
||||
version: 5.6.0
|
||||
@@ -1064,6 +1067,9 @@ packages:
|
||||
engines: {node: '>=0.10'}
|
||||
hasBin: true
|
||||
|
||||
dexie@4.2.0:
|
||||
resolution: {integrity: sha512-OSeyyWOUetDy9oFWeddJgi83OnRA3hSFh3RrbltmPgqHszE9f24eUCVLI4mPg0ifsWk0lQTdnS+jyGNrPMvhDA==}
|
||||
|
||||
dir-glob@3.0.1:
|
||||
resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -3397,6 +3403,8 @@ snapshots:
|
||||
detect-libc@1.0.3:
|
||||
optional: true
|
||||
|
||||
dexie@4.2.0: {}
|
||||
|
||||
dir-glob@3.0.1:
|
||||
dependencies:
|
||||
path-type: 4.0.0
|
||||
|
||||
@@ -43,16 +43,15 @@ import {
|
||||
FilePptOutlined,
|
||||
PlayCircleFilled,
|
||||
} from "@ant-design/icons";
|
||||
import { ChatRecord, ContractData } from "@/pages/pc/ckbox/data";
|
||||
import { ChatRecord, ContractData, GroupData } from "@/pages/pc/ckbox/data";
|
||||
import { clearUnreadCount, getMessages } from "@/pages/pc/ckbox/api";
|
||||
import styles from "./ChatWindow.module.scss";
|
||||
import { useWebSocketStore, WebSocketMessage } from "@/store/module/websocket";
|
||||
import { formatWechatTime } from "@/utils/common";
|
||||
const { Header, Content, Footer, Sider } = Layout;
|
||||
const { TextArea } = Input;
|
||||
|
||||
interface ChatWindowProps {
|
||||
contract: ContractData;
|
||||
contract: ContractData | GroupData;
|
||||
onSendMessage: (message: string) => void;
|
||||
showProfile?: boolean;
|
||||
onToggleProfile?: () => void;
|
||||
@@ -797,6 +796,7 @@ const ChatWindow: React.FC<ChatWindowProps> = ({
|
||||
label: "视频通话",
|
||||
},
|
||||
{
|
||||
key: "divider1",
|
||||
type: "divider",
|
||||
},
|
||||
{
|
||||
@@ -808,6 +808,7 @@ const ChatWindow: React.FC<ChatWindowProps> = ({
|
||||
label: "消息免打扰",
|
||||
},
|
||||
{
|
||||
key: "divider2",
|
||||
type: "divider",
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import React from "react";
|
||||
import { List, Avatar, Badge } from "antd";
|
||||
import { UserOutlined, TeamOutlined } from "@ant-design/icons";
|
||||
import dayjs from "dayjs";
|
||||
import { ContractData, GroupData } from "@/pages/pc/ckbox/data";
|
||||
import styles from "./MessageList.module.scss";
|
||||
import { formatWechatTime } from "@/utils/common";
|
||||
interface MessageListProps {
|
||||
chatSessions: ContractData[];
|
||||
currentChat: ContractData;
|
||||
chatSessions: ContractData[] | GroupData[];
|
||||
currentChat: ContractData | GroupData;
|
||||
onChatSelect: (chat: ContractData | GroupData) => void;
|
||||
}
|
||||
|
||||
@@ -19,9 +18,10 @@ const MessageList: React.FC<MessageListProps> = ({
|
||||
return (
|
||||
<div className={styles.messageList}>
|
||||
<List
|
||||
dataSource={chatSessions}
|
||||
dataSource={chatSessions as ContractData[]}
|
||||
renderItem={session => (
|
||||
<List.Item
|
||||
key={session.id}
|
||||
className={`${styles.messageItem} ${
|
||||
currentChat?.id === session.id ? styles.active : ""
|
||||
}`}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import React, { useState, useCallback, useEffect } from "react";
|
||||
import { List, Avatar, Collapse, Button } from "antd";
|
||||
import type { CollapseProps } from "antd";
|
||||
import { ContractData } from "@/pages/pc/ckbox/data";
|
||||
import styles from "./WechatFriends.module.scss";
|
||||
import { useCkChatStore } from "@/store/module/ckchat";
|
||||
import { ContractData, GroupData } from "@/pages/pc/ckbox/data";
|
||||
|
||||
interface WechatFriendsProps {
|
||||
contracts: ContractData[];
|
||||
onContactClick: (contract: ContractData) => void;
|
||||
selectedContactId?: ContractData;
|
||||
contracts: ContractData[] | GroupData[];
|
||||
onContactClick: (contract: ContractData | GroupData) => void;
|
||||
selectedContactId?: ContractData | GroupData;
|
||||
}
|
||||
|
||||
const ContactListSimple: React.FC<WechatFriendsProps> = ({
|
||||
@@ -174,7 +174,7 @@ const ContactListSimple: React.FC<WechatFriendsProps> = ({
|
||||
<div className={styles.header}>全部好友</div>
|
||||
<List
|
||||
className={styles.list}
|
||||
dataSource={contracts}
|
||||
dataSource={contracts as ContractData[]}
|
||||
renderItem={renderContactItem}
|
||||
/>
|
||||
</>
|
||||
|
||||
@@ -6,17 +6,17 @@ import {
|
||||
ChromeOutlined,
|
||||
MessageOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { ContractData } from "@/pages/pc/ckbox/data";
|
||||
import { ContractData, GroupData } from "@/pages/pc/ckbox/data";
|
||||
import WechatFriends from "./WechatFriends";
|
||||
import MessageList from "./MessageList/index";
|
||||
import styles from "./SidebarMenu.module.scss";
|
||||
import { getChatSessions } from "@/store/module/ckchat";
|
||||
|
||||
interface SidebarMenuProps {
|
||||
contracts: ContractData[];
|
||||
currentChat: ContractData;
|
||||
onContactClick: (contract: ContractData) => void;
|
||||
onChatSelect: (chat: ContractData) => void;
|
||||
contracts: ContractData[] | GroupData[];
|
||||
currentChat: ContractData | GroupData;
|
||||
onContactClick: (contract: ContractData | GroupData) => void;
|
||||
onChatSelect: (chat: ContractData | GroupData) => void;
|
||||
loading?: boolean;
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ const SidebarMenu: React.FC<SidebarMenuProps> = ({
|
||||
case "contracts":
|
||||
return (
|
||||
<WechatFriends
|
||||
contracts={getFilteredContacts()}
|
||||
contracts={getFilteredContacts() as ContractData[]}
|
||||
onContactClick={onContactClick}
|
||||
selectedContactId={currentChat}
|
||||
/>
|
||||
|
||||
@@ -1,3 +1,45 @@
|
||||
//终端用户数据接口
|
||||
export interface KfUserListData {
|
||||
id: number;
|
||||
tenantId: number;
|
||||
wechatId: string;
|
||||
nickname: string;
|
||||
alias: string;
|
||||
avatar: string;
|
||||
gender: number;
|
||||
region: string;
|
||||
signature: string;
|
||||
bindQQ: string;
|
||||
bindEmail: string;
|
||||
bindMobile: string;
|
||||
createTime: string;
|
||||
currentDeviceId: number;
|
||||
isDeleted: boolean;
|
||||
deleteTime: string;
|
||||
groupId: number;
|
||||
memo: string;
|
||||
wechatVersion: string;
|
||||
labels: string[];
|
||||
lastUpdateTime: string;
|
||||
isOnline?: boolean;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
// 账户信息接口
|
||||
export interface CkAccount {
|
||||
id: number;
|
||||
realName: string;
|
||||
nickname: string | null;
|
||||
memo: string | null;
|
||||
avatar: string;
|
||||
userName: string;
|
||||
secret: string;
|
||||
accountType: number;
|
||||
departmentId: number;
|
||||
useGoogleSecretKey: boolean;
|
||||
hasVerifyGoogleSecret: boolean;
|
||||
}
|
||||
|
||||
//群聊数据接口
|
||||
export interface GroupData {
|
||||
id?: number;
|
||||
@@ -10,7 +52,9 @@ export interface GroupData {
|
||||
nickname: string;
|
||||
chatroomAvatar: string;
|
||||
groupId: number;
|
||||
config: any;
|
||||
config?: {
|
||||
chat: boolean;
|
||||
};
|
||||
unreadCount: number;
|
||||
notice: string;
|
||||
selfDisplyName: string;
|
||||
|
||||
@@ -2,7 +2,6 @@ import React, { useState, useEffect } from "react";
|
||||
import { Layout, Button, Space, message, Tooltip } from "antd";
|
||||
import { InfoCircleOutlined, MessageOutlined } from "@ant-design/icons";
|
||||
import dayjs from "dayjs";
|
||||
import { ContractData } from "./data";
|
||||
import ChatWindow from "./components/ChatWindow/index";
|
||||
import SidebarMenu from "./components/SidebarMenu/index";
|
||||
import VerticalUserList from "./components/VerticalUserList";
|
||||
@@ -11,13 +10,14 @@ import styles from "./index.module.scss";
|
||||
import { addChatSession } from "@/store/module/ckchat";
|
||||
const { Header, Content, Sider } = Layout;
|
||||
import { chatInitAPIdata } from "./main";
|
||||
import { KfUserListData } from "@/store/module/ckchat.data";
|
||||
import { KfUserListData, GroupData, ContractData } from "@/pages/pc/ckbox/data";
|
||||
|
||||
const CkboxPage: React.FC = () => {
|
||||
const [messageApi, contextHolder] = message.useMessage();
|
||||
const [contracts, setContacts] = useState<any[]>([]);
|
||||
const [currentChat, setCurrentChat] = useState<ContractData | null>(null);
|
||||
const [activeVerticalUserId, setActiveVerticalUserId] = useState(0);
|
||||
const [currentChat, setCurrentChat] = useState<ContractData | GroupData>(
|
||||
null,
|
||||
);
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [showProfile, setShowProfile] = useState(true);
|
||||
@@ -53,7 +53,7 @@ const CkboxPage: React.FC = () => {
|
||||
});
|
||||
}, []);
|
||||
|
||||
const handleContactClick = (contract: ContractData) => {
|
||||
const handleContactClick = (contract: ContractData | GroupData) => {
|
||||
addChatSession(contract);
|
||||
setCurrentChat(contract);
|
||||
};
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
} from "./api";
|
||||
const { sendCommand } = useWebSocketStore.getState();
|
||||
import { useUserStore } from "@/store/module/user";
|
||||
import { KfUserListData } from "@/store/module/ckchat.data";
|
||||
import { ContractData, GroupData, KfUserListData } from "@/pages/pc/ckbox/data";
|
||||
const { login2 } = useUserStore.getState();
|
||||
//获取触客宝基础信息
|
||||
export const chatInitAPIdata = async () => {
|
||||
@@ -24,11 +24,6 @@ export const chatInitAPIdata = async () => {
|
||||
//获取联系人列表
|
||||
const contractList = await getAllContactList();
|
||||
|
||||
//构建联系人列表标签
|
||||
const newContractList = await createContractList(contractList);
|
||||
|
||||
// 会话列表分组
|
||||
asyncNewContractList(newContractList);
|
||||
//获取联系人列表
|
||||
asyncContractList(contractList);
|
||||
|
||||
@@ -46,6 +41,12 @@ export const chatInitAPIdata = async () => {
|
||||
//获取群列表
|
||||
const groupList = await getAllGroupList();
|
||||
|
||||
//构建联系人列表标签
|
||||
const newContractList = await createContractList(contractList, groupList);
|
||||
console.log("分组信息", newContractList);
|
||||
|
||||
// 会话列表分组
|
||||
asyncNewContractList(newContractList);
|
||||
//获取消息会话列表并按lastUpdateTime排序
|
||||
const filterUserSessions = contractList?.filter(
|
||||
v => v?.config && v.config?.chat,
|
||||
@@ -92,7 +93,10 @@ export const chatInitAPIdata = async () => {
|
||||
};
|
||||
|
||||
//构建联系人列表标签
|
||||
export const createContractList = async (contractList: any[]) => {
|
||||
export const createContractList = async (
|
||||
contractList: ContractData[],
|
||||
groupList: GroupData[],
|
||||
) => {
|
||||
const LablesRes = await Promise.all(
|
||||
[1, 2].map(item =>
|
||||
WechatGroup({
|
||||
@@ -106,7 +110,7 @@ export const createContractList = async (contractList: any[]) => {
|
||||
|
||||
// 根据countLables中的groupName整理contractList数据
|
||||
// 返回按标签分组的联系人数组,包括未分组标签(在数组最后)
|
||||
return organizeContactsByLabels(contractList, countLables);
|
||||
return organizeContactsByLabels(countLables, contractList, groupList);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -116,8 +120,9 @@ export const createContractList = async (contractList: any[]) => {
|
||||
* @returns 按标签分组的联系人
|
||||
*/
|
||||
export const organizeContactsByLabels = (
|
||||
contractList: any[],
|
||||
countLables: any[],
|
||||
contractList: ContractData[],
|
||||
groupList: GroupData[],
|
||||
) => {
|
||||
// 创建结果对象,用于存储按标签分组的联系人
|
||||
const result: { [key: string]: any[] } = {};
|
||||
|
||||
@@ -1,46 +1,4 @@
|
||||
import { ContractData } from "../../pages/pc/ckbox/data";
|
||||
|
||||
//终端用户数据接口
|
||||
export interface KfUserListData {
|
||||
id: number;
|
||||
tenantId: number;
|
||||
wechatId: string;
|
||||
nickname: string;
|
||||
alias: string;
|
||||
avatar: string;
|
||||
gender: number;
|
||||
region: string;
|
||||
signature: string;
|
||||
bindQQ: string;
|
||||
bindEmail: string;
|
||||
bindMobile: string;
|
||||
createTime: string;
|
||||
currentDeviceId: number;
|
||||
isDeleted: boolean;
|
||||
deleteTime: string;
|
||||
groupId: number;
|
||||
memo: string;
|
||||
wechatVersion: string;
|
||||
labels: string[];
|
||||
lastUpdateTime: string;
|
||||
isOnline?: boolean;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
// 账户信息接口
|
||||
export interface CkAccount {
|
||||
id: number;
|
||||
realName: string;
|
||||
nickname: string | null;
|
||||
memo: string | null;
|
||||
avatar: string;
|
||||
userName: string;
|
||||
secret: string;
|
||||
accountType: number;
|
||||
departmentId: number;
|
||||
useGoogleSecretKey: boolean;
|
||||
hasVerifyGoogleSecret: boolean;
|
||||
}
|
||||
import { ContractData, KfUserListData, CkAccount } from "@/pages/pc/ckbox/data";
|
||||
|
||||
// 权限片段接口
|
||||
export interface PrivilegeFrag {
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import { createPersistStore } from "@/store/createPersistStore";
|
||||
|
||||
import { CkChatState, CkUserInfo, CkTenant } from "./ckchat.data";
|
||||
import {
|
||||
CkChatState,
|
||||
CkUserInfo,
|
||||
ContractData,
|
||||
GroupData,
|
||||
CkAccount,
|
||||
CkTenant,
|
||||
KfUserListData,
|
||||
} from "./ckchat.data";
|
||||
import { ContractData, GroupData } from "@/pages/pc/ckbox/data";
|
||||
} from "@/pages/pc/ckbox/data";
|
||||
|
||||
export const useCkChatStore = createPersistStore<CkChatState>(
|
||||
set => ({
|
||||
|
||||
508
Cunkebao/src/utils/db-examples.ts
Normal file
508
Cunkebao/src/utils/db-examples.ts
Normal file
@@ -0,0 +1,508 @@
|
||||
// 数据库使用示例
|
||||
import {
|
||||
db,
|
||||
userService,
|
||||
messageService,
|
||||
chatRoomService,
|
||||
settingService,
|
||||
userBusinessService,
|
||||
messageBusinessService,
|
||||
settingBusinessService,
|
||||
DatabaseUtils,
|
||||
type User,
|
||||
type Message,
|
||||
type ChatRoom,
|
||||
type Setting,
|
||||
} from "./db";
|
||||
|
||||
// ============= 基础 CRUD 操作示例 =============
|
||||
|
||||
// 1. 用户操作示例
|
||||
export const userExamples = {
|
||||
// 创建用户
|
||||
async createUser() {
|
||||
const userId = await userService.create({
|
||||
name: "张三",
|
||||
email: "zhangsan@example.com",
|
||||
status: "active",
|
||||
});
|
||||
console.log("创建用户成功,ID:", userId);
|
||||
return userId;
|
||||
},
|
||||
|
||||
// 批量创建用户
|
||||
async createMultipleUsers() {
|
||||
const userIds = await userService.createMany([
|
||||
{ name: "李四", email: "lisi@example.com", status: "active" },
|
||||
{ name: "王五", email: "wangwu@example.com", status: "inactive" },
|
||||
{ name: "赵六", email: "zhaoliu@example.com", status: "active" },
|
||||
]);
|
||||
console.log("批量创建用户成功,IDs:", userIds);
|
||||
return userIds;
|
||||
},
|
||||
|
||||
// 查询用户
|
||||
async findUsers() {
|
||||
// 查询所有用户
|
||||
const allUsers = await userService.findAll();
|
||||
console.log("所有用户:", allUsers);
|
||||
|
||||
// 根据ID查询
|
||||
const user = await userService.findById(1);
|
||||
console.log("ID为1的用户:", user);
|
||||
|
||||
// 条件查询
|
||||
const activeUsers = await userService.findWhere("status", "active");
|
||||
console.log("活跃用户:", activeUsers);
|
||||
|
||||
return { allUsers, user, activeUsers };
|
||||
},
|
||||
|
||||
// 分页查询
|
||||
async paginateUsers() {
|
||||
const result = await userService.paginate(1, 5); // 第1页,每页5条
|
||||
console.log("分页结果:", result);
|
||||
return result;
|
||||
},
|
||||
|
||||
// 更新用户
|
||||
async updateUser() {
|
||||
const count = await userService.update(1, {
|
||||
name: "张三(已更新)",
|
||||
status: "inactive",
|
||||
});
|
||||
console.log("更新用户数量:", count);
|
||||
return count;
|
||||
},
|
||||
|
||||
// 删除用户
|
||||
async deleteUser() {
|
||||
await userService.delete(1);
|
||||
console.log("删除用户成功");
|
||||
},
|
||||
};
|
||||
|
||||
// 2. 消息操作示例
|
||||
export const messageExamples = {
|
||||
// 创建消息
|
||||
async createMessage() {
|
||||
const messageId = await messageService.create({
|
||||
userId: 1,
|
||||
content: "这是一条测试消息",
|
||||
type: "text",
|
||||
isRead: false,
|
||||
});
|
||||
console.log("创建消息成功,ID:", messageId);
|
||||
return messageId;
|
||||
},
|
||||
|
||||
// 查询未读消息
|
||||
async findUnreadMessages() {
|
||||
const unreadMessages = await messageService.findWhere("isRead", false);
|
||||
console.log("未读消息:", unreadMessages);
|
||||
return unreadMessages;
|
||||
},
|
||||
|
||||
// 标记消息为已读
|
||||
async markMessageAsRead() {
|
||||
const count = await messageService.update(1, { isRead: true });
|
||||
console.log("标记已读消息数量:", count);
|
||||
return count;
|
||||
},
|
||||
|
||||
// 获取最近消息
|
||||
async getRecentMessages() {
|
||||
const recentMessages = await messageService.findAllSorted(
|
||||
"createdAt",
|
||||
"desc",
|
||||
);
|
||||
console.log("最近消息:", recentMessages.slice(0, 10));
|
||||
return recentMessages.slice(0, 10);
|
||||
},
|
||||
};
|
||||
|
||||
// 3. 设置操作示例
|
||||
export const settingExamples = {
|
||||
// 保存设置
|
||||
async saveSetting() {
|
||||
const settingId = await settingService.create({
|
||||
key: "theme",
|
||||
value: "dark",
|
||||
category: "appearance",
|
||||
});
|
||||
console.log("保存设置成功,ID:", settingId);
|
||||
return settingId;
|
||||
},
|
||||
|
||||
// 批量保存设置
|
||||
async saveMultipleSettings() {
|
||||
const settingIds = await settingService.createMany([
|
||||
{ key: "language", value: "zh-CN", category: "general" },
|
||||
{ key: "fontSize", value: 14, category: "appearance" },
|
||||
{ key: "autoSave", value: true, category: "editor" },
|
||||
]);
|
||||
console.log("批量保存设置成功,IDs:", settingIds);
|
||||
return settingIds;
|
||||
},
|
||||
|
||||
// 查询设置
|
||||
async getSettings() {
|
||||
// 查询所有设置
|
||||
const allSettings = await settingService.findAll();
|
||||
console.log("所有设置:", allSettings);
|
||||
|
||||
// 按分类查询
|
||||
const appearanceSettings = await settingService.findWhere(
|
||||
"category",
|
||||
"appearance",
|
||||
);
|
||||
console.log("外观设置:", appearanceSettings);
|
||||
|
||||
return { allSettings, appearanceSettings };
|
||||
},
|
||||
};
|
||||
|
||||
// ============= 业务方法示例 =============
|
||||
|
||||
// 4. 用户业务操作示例
|
||||
export const userBusinessExamples = {
|
||||
// 根据邮箱查找用户
|
||||
async findUserByEmail() {
|
||||
const user = await userBusinessService.findByEmail("zhangsan@example.com");
|
||||
console.log("根据邮箱查找的用户:", user);
|
||||
return user;
|
||||
},
|
||||
|
||||
// 查找活跃用户
|
||||
async findActiveUsers() {
|
||||
const activeUsers = await userBusinessService.findActiveUsers();
|
||||
console.log("活跃用户列表:", activeUsers);
|
||||
return activeUsers;
|
||||
},
|
||||
|
||||
// 搜索用户
|
||||
async searchUsers() {
|
||||
const users = await userBusinessService.searchByName("张");
|
||||
console.log("搜索结果:", users);
|
||||
return users;
|
||||
},
|
||||
|
||||
// 更新用户状态
|
||||
async updateUserStatus() {
|
||||
const count = await userBusinessService.updateStatus(1, "active");
|
||||
console.log("更新用户状态数量:", count);
|
||||
return count;
|
||||
},
|
||||
};
|
||||
|
||||
// 5. 消息业务操作示例
|
||||
export const messageBusinessExamples = {
|
||||
// 查找用户消息
|
||||
async findUserMessages() {
|
||||
const messages = await messageBusinessService.findByUserId(1);
|
||||
console.log("用户消息:", messages);
|
||||
return messages;
|
||||
},
|
||||
|
||||
// 查找未读消息
|
||||
async findUnreadMessages() {
|
||||
const unreadMessages = await messageBusinessService.findUnreadMessages();
|
||||
console.log("未读消息:", unreadMessages);
|
||||
return unreadMessages;
|
||||
},
|
||||
|
||||
// 标记消息为已读
|
||||
async markAsRead() {
|
||||
const count = await messageBusinessService.markAsRead(1);
|
||||
console.log("标记已读数量:", count);
|
||||
return count;
|
||||
},
|
||||
|
||||
// 标记用户所有消息为已读
|
||||
async markAllAsRead() {
|
||||
const count = await messageBusinessService.markAllAsRead(1);
|
||||
console.log("标记用户所有消息已读数量:", count);
|
||||
return count;
|
||||
},
|
||||
|
||||
// 获取最近消息
|
||||
async getRecentMessages() {
|
||||
const messages = await messageBusinessService.getRecentMessages(20);
|
||||
console.log("最近20条消息:", messages);
|
||||
return messages;
|
||||
},
|
||||
};
|
||||
|
||||
// 6. 设置业务操作示例
|
||||
export const settingBusinessExamples = {
|
||||
// 获取设置值
|
||||
async getSetting() {
|
||||
const theme = await settingBusinessService.getSetting("theme");
|
||||
console.log("主题设置:", theme);
|
||||
return theme;
|
||||
},
|
||||
|
||||
// 设置值
|
||||
async setSetting() {
|
||||
const settingId = await settingBusinessService.setSetting(
|
||||
"theme",
|
||||
"light",
|
||||
"appearance",
|
||||
);
|
||||
console.log("设置主题成功,ID:", settingId);
|
||||
return settingId;
|
||||
},
|
||||
|
||||
// 获取分类设置
|
||||
async getSettingsByCategory() {
|
||||
const settings =
|
||||
await settingBusinessService.getSettingsByCategory("appearance");
|
||||
console.log("外观设置:", settings);
|
||||
return settings;
|
||||
},
|
||||
|
||||
// 删除设置
|
||||
async deleteSetting() {
|
||||
await settingBusinessService.deleteSetting("oldSetting");
|
||||
console.log("删除设置成功");
|
||||
},
|
||||
};
|
||||
|
||||
// ============= 数据库工具示例 =============
|
||||
|
||||
// 7. 数据库工具操作示例
|
||||
export const databaseUtilsExamples = {
|
||||
// 获取数据库统计信息
|
||||
async getStats() {
|
||||
const stats = await DatabaseUtils.getStats();
|
||||
console.log("数据库统计:", stats);
|
||||
return stats;
|
||||
},
|
||||
|
||||
// 健康检查
|
||||
async healthCheck() {
|
||||
const health = await DatabaseUtils.healthCheck();
|
||||
console.log("数据库健康状态:", health);
|
||||
return health;
|
||||
},
|
||||
|
||||
// 导出数据
|
||||
async exportData() {
|
||||
const jsonData = await DatabaseUtils.exportData();
|
||||
console.log("导出的数据长度:", jsonData.length);
|
||||
return jsonData;
|
||||
},
|
||||
|
||||
// 备份到文件
|
||||
async backupToFile() {
|
||||
await DatabaseUtils.backupToFile();
|
||||
console.log("备份文件下载成功");
|
||||
},
|
||||
|
||||
// 从文件恢复(需要文件输入)
|
||||
async restoreFromFile(file: File) {
|
||||
try {
|
||||
await DatabaseUtils.restoreFromFile(file);
|
||||
console.log("数据恢复成功");
|
||||
} catch (error) {
|
||||
console.error("数据恢复失败:", error);
|
||||
}
|
||||
},
|
||||
|
||||
// 清空所有数据
|
||||
async clearAllData() {
|
||||
const confirmed = confirm("确定要清空所有数据吗?此操作不可恢复!");
|
||||
if (confirmed) {
|
||||
await DatabaseUtils.clearAllData();
|
||||
console.log("所有数据已清空");
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// ============= 高级查询示例 =============
|
||||
|
||||
// 8. 高级查询示例
|
||||
export const advancedQueryExamples = {
|
||||
// 复杂条件查询
|
||||
async complexQuery() {
|
||||
// 查询活跃用户的未读消息
|
||||
const activeUsers = await db.users
|
||||
.where("status")
|
||||
.equals("active")
|
||||
.toArray();
|
||||
const activeUserIds = activeUsers.map(user => user.id!);
|
||||
const unreadMessages = await db.messages
|
||||
.where("userId")
|
||||
.anyOf(activeUserIds)
|
||||
.and(msg => !msg.isRead)
|
||||
.toArray();
|
||||
|
||||
console.log("活跃用户的未读消息:", unreadMessages);
|
||||
return unreadMessages;
|
||||
},
|
||||
|
||||
// 统计查询
|
||||
async statisticsQuery() {
|
||||
const stats = {
|
||||
totalUsers: await db.users.count(),
|
||||
activeUsers: await db.users.where("status").equals("active").count(),
|
||||
totalMessages: await db.messages.count(),
|
||||
unreadMessages: await db.messages.where("isRead").equals(false).count(),
|
||||
messagesThisWeek: await db.messages
|
||||
.where("createdAt")
|
||||
.above(new Date(Date.now() - 7 * 24 * 60 * 60 * 1000))
|
||||
.count(),
|
||||
};
|
||||
|
||||
console.log("统计信息:", stats);
|
||||
return stats;
|
||||
},
|
||||
|
||||
// 事务操作
|
||||
async transactionExample() {
|
||||
try {
|
||||
await db.transaction("rw", [db.users, db.messages], async () => {
|
||||
// 创建用户
|
||||
const userId = await db.users.add({
|
||||
name: "事务用户",
|
||||
email: "transaction@example.com",
|
||||
status: "active",
|
||||
});
|
||||
|
||||
// 为该用户创建欢迎消息
|
||||
await db.messages.add({
|
||||
userId,
|
||||
content: "欢迎使用我们的应用!",
|
||||
type: "text",
|
||||
isRead: false,
|
||||
});
|
||||
|
||||
console.log("事务执行成功");
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("事务执行失败:", error);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// ============= 完整使用流程示例 =============
|
||||
|
||||
// 9. 完整的应用场景示例
|
||||
export const fullScenarioExample = {
|
||||
// 模拟用户注册和使用流程
|
||||
async simulateUserFlow() {
|
||||
console.log("=== 开始模拟用户流程 ===");
|
||||
|
||||
try {
|
||||
// 1. 用户注册
|
||||
const userId = await userBusinessService.create({
|
||||
name: "新用户",
|
||||
email: "newuser@example.com",
|
||||
status: "active",
|
||||
});
|
||||
console.log("1. 用户注册成功,ID:", userId);
|
||||
|
||||
// 2. 设置用户偏好
|
||||
await settingBusinessService.setSetting(
|
||||
"theme",
|
||||
"dark",
|
||||
"user-" + userId,
|
||||
);
|
||||
await settingBusinessService.setSetting(
|
||||
"language",
|
||||
"zh-CN",
|
||||
"user-" + userId,
|
||||
);
|
||||
console.log("2. 用户偏好设置完成");
|
||||
|
||||
// 3. 发送欢迎消息
|
||||
const messageId = await messageBusinessService.create({
|
||||
userId,
|
||||
content: "欢迎加入我们的平台!",
|
||||
type: "text",
|
||||
isRead: false,
|
||||
});
|
||||
console.log("3. 欢迎消息发送成功,ID:", messageId);
|
||||
|
||||
// 4. 用户查看消息
|
||||
const userMessages = await messageBusinessService.findByUserId(userId);
|
||||
console.log("4. 用户消息列表:", userMessages);
|
||||
|
||||
// 5. 标记消息为已读
|
||||
await messageBusinessService.markAsRead(messageId);
|
||||
console.log("5. 消息已标记为已读");
|
||||
|
||||
// 6. 获取用户统计信息
|
||||
const userStats = {
|
||||
totalMessages: await messageService.countWhere("userId", userId),
|
||||
unreadMessages: await db.messages
|
||||
.where("userId")
|
||||
.equals(userId)
|
||||
.and(msg => !msg.isRead)
|
||||
.count(),
|
||||
};
|
||||
console.log("6. 用户统计信息:", userStats);
|
||||
|
||||
console.log("=== 用户流程模拟完成 ===");
|
||||
return { userId, messageId, userStats };
|
||||
} catch (error) {
|
||||
console.error("用户流程模拟失败:", error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// 导出所有示例
|
||||
export const allExamples = {
|
||||
userExamples,
|
||||
messageExamples,
|
||||
settingExamples,
|
||||
userBusinessExamples,
|
||||
messageBusinessExamples,
|
||||
settingBusinessExamples,
|
||||
databaseUtilsExamples,
|
||||
advancedQueryExamples,
|
||||
fullScenarioExample,
|
||||
};
|
||||
|
||||
// 快速测试函数
|
||||
export async function quickTest() {
|
||||
console.log("=== 开始快速测试 ===");
|
||||
|
||||
try {
|
||||
// 健康检查
|
||||
const health = await DatabaseUtils.healthCheck();
|
||||
console.log("数据库健康状态:", health);
|
||||
|
||||
// 创建测试数据
|
||||
const userId = await userBusinessService.create({
|
||||
name: "测试用户",
|
||||
email: "test@example.com",
|
||||
status: "active",
|
||||
});
|
||||
|
||||
const messageId = await messageBusinessService.create({
|
||||
userId,
|
||||
content: "测试消息",
|
||||
type: "text",
|
||||
isRead: false,
|
||||
});
|
||||
|
||||
// 查询测试
|
||||
const user = await userBusinessService.findById(userId);
|
||||
const message = await messageBusinessService.findById(messageId);
|
||||
|
||||
console.log("创建的用户:", user);
|
||||
console.log("创建的消息:", message);
|
||||
|
||||
// 统计信息
|
||||
const stats = await DatabaseUtils.getStats();
|
||||
console.log("数据库统计:", stats);
|
||||
|
||||
console.log("=== 快速测试完成 ===");
|
||||
return { userId, messageId, stats };
|
||||
} catch (error) {
|
||||
console.error("快速测试失败:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
446
Cunkebao/src/utils/db.ts
Normal file
446
Cunkebao/src/utils/db.ts
Normal file
@@ -0,0 +1,446 @@
|
||||
import Dexie, { Table } from "dexie";
|
||||
import { KfUserListData, GroupData, ContractData } from "@/pages/pc/ckbox/data";
|
||||
|
||||
// 定义数据库表结构接口
|
||||
export interface BaseEntity {
|
||||
id?: number;
|
||||
createdAt?: Date;
|
||||
updatedAt?: Date;
|
||||
}
|
||||
|
||||
export interface User extends BaseEntity {
|
||||
name: string;
|
||||
email: string;
|
||||
avatar?: string;
|
||||
status: "active" | "inactive";
|
||||
}
|
||||
|
||||
export interface Message extends BaseEntity {
|
||||
userId: number;
|
||||
content: string;
|
||||
type: "text" | "image" | "file";
|
||||
isRead: boolean;
|
||||
}
|
||||
|
||||
export interface ChatRoom extends BaseEntity {
|
||||
name: string;
|
||||
description?: string;
|
||||
memberIds: number[];
|
||||
lastMessageAt?: Date;
|
||||
}
|
||||
|
||||
export interface Setting extends BaseEntity {
|
||||
key: string;
|
||||
value: any;
|
||||
category: string;
|
||||
}
|
||||
|
||||
// 数据库类
|
||||
class AppDatabase extends Dexie {
|
||||
users!: Table<User>;
|
||||
messages!: Table<Message>;
|
||||
chatRooms!: Table<ChatRoom>;
|
||||
settings!: Table<Setting>;
|
||||
|
||||
constructor() {
|
||||
super("CunkebaoDatabase");
|
||||
|
||||
this.version(1).stores({
|
||||
users: "++id, name, email, status, createdAt",
|
||||
messages: "++id, userId, type, isRead, createdAt",
|
||||
chatRooms: "++id, name, lastMessageAt, createdAt",
|
||||
settings: "++id, key, category, createdAt",
|
||||
});
|
||||
|
||||
// 自动添加时间戳
|
||||
this.users.hook("creating", (primKey, obj, trans) => {
|
||||
obj.createdAt = new Date();
|
||||
obj.updatedAt = new Date();
|
||||
});
|
||||
|
||||
this.users.hook("updating", (modifications, primKey, obj, trans) => {
|
||||
modifications.updatedAt = new Date();
|
||||
});
|
||||
|
||||
this.messages.hook("creating", (primKey, obj, trans) => {
|
||||
obj.createdAt = new Date();
|
||||
obj.updatedAt = new Date();
|
||||
});
|
||||
|
||||
this.chatRooms.hook("creating", (primKey, obj, trans) => {
|
||||
obj.createdAt = new Date();
|
||||
obj.updatedAt = new Date();
|
||||
});
|
||||
|
||||
this.settings.hook("creating", (primKey, obj, trans) => {
|
||||
obj.createdAt = new Date();
|
||||
obj.updatedAt = new Date();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 创建数据库实例
|
||||
export const db = new AppDatabase();
|
||||
|
||||
// 通用数据库操作类
|
||||
export class DatabaseService<T extends BaseEntity> {
|
||||
constructor(private table: Table<T>) {}
|
||||
|
||||
// 基础 CRUD 操作
|
||||
async create(
|
||||
data: Omit<T, "id" | "createdAt" | "updatedAt">,
|
||||
): Promise<number> {
|
||||
return await this.table.add(data as T);
|
||||
}
|
||||
|
||||
async createMany(
|
||||
dataList: Omit<T, "id" | "createdAt" | "updatedAt">[],
|
||||
): Promise<number[]> {
|
||||
return await this.table.bulkAdd(dataList as T[], { allKeys: true });
|
||||
}
|
||||
|
||||
async findById(id: number): Promise<T | undefined> {
|
||||
return await this.table.get(id);
|
||||
}
|
||||
|
||||
async findAll(): Promise<T[]> {
|
||||
return await this.table.toArray();
|
||||
}
|
||||
|
||||
async findByIds(ids: number[]): Promise<T[]> {
|
||||
return await this.table.where("id").anyOf(ids).toArray();
|
||||
}
|
||||
|
||||
async update(
|
||||
id: number,
|
||||
data: Partial<Omit<T, "id" | "createdAt">>,
|
||||
): Promise<number> {
|
||||
return await this.table.update(id, data);
|
||||
}
|
||||
|
||||
async updateMany(
|
||||
updates: { id: number; data: Partial<Omit<T, "id" | "createdAt">> }[],
|
||||
): Promise<number> {
|
||||
return await this.table.bulkUpdate(
|
||||
updates.map(u => ({ key: u.id, changes: u.data })),
|
||||
);
|
||||
}
|
||||
|
||||
async delete(id: number): Promise<void> {
|
||||
await this.table.delete(id);
|
||||
}
|
||||
|
||||
async deleteMany(ids: number[]): Promise<void> {
|
||||
await this.table.bulkDelete(ids);
|
||||
}
|
||||
|
||||
async deleteAll(): Promise<void> {
|
||||
await this.table.clear();
|
||||
}
|
||||
|
||||
// 分页查询
|
||||
async paginate(
|
||||
page: number = 1,
|
||||
limit: number = 10,
|
||||
): Promise<{ data: T[]; total: number; page: number; limit: number }> {
|
||||
const offset = (page - 1) * limit;
|
||||
const total = await this.table.count();
|
||||
const data = await this.table.offset(offset).limit(limit).toArray();
|
||||
|
||||
return { data, total, page, limit };
|
||||
}
|
||||
|
||||
// 条件查询
|
||||
async findWhere(field: keyof T, value: any): Promise<T[]> {
|
||||
return await this.table
|
||||
.where(field as string)
|
||||
.equals(value)
|
||||
.toArray();
|
||||
}
|
||||
|
||||
async findWhereIn(field: keyof T, values: any[]): Promise<T[]> {
|
||||
return await this.table
|
||||
.where(field as string)
|
||||
.anyOf(values)
|
||||
.toArray();
|
||||
}
|
||||
|
||||
async findWhereNot(field: keyof T, value: any): Promise<T[]> {
|
||||
return await this.table
|
||||
.where(field as string)
|
||||
.notEqual(value)
|
||||
.toArray();
|
||||
}
|
||||
|
||||
// 排序查询
|
||||
async findAllSorted(
|
||||
field: keyof T,
|
||||
direction: "asc" | "desc" = "asc",
|
||||
): Promise<T[]> {
|
||||
const collection = this.table.orderBy(field as string);
|
||||
return direction === "desc"
|
||||
? await collection.reverse().toArray()
|
||||
: await collection.toArray();
|
||||
}
|
||||
|
||||
// 搜索功能
|
||||
async search(field: keyof T, keyword: string): Promise<T[]> {
|
||||
return await this.table
|
||||
.where(field as string)
|
||||
.startsWithIgnoreCase(keyword)
|
||||
.toArray();
|
||||
}
|
||||
|
||||
// 统计功能
|
||||
async count(): Promise<number> {
|
||||
return await this.table.count();
|
||||
}
|
||||
|
||||
async countWhere(field: keyof T, value: any): Promise<number> {
|
||||
return await this.table
|
||||
.where(field as string)
|
||||
.equals(value)
|
||||
.count();
|
||||
}
|
||||
|
||||
// 存在性检查
|
||||
async exists(id: number): Promise<boolean> {
|
||||
const item = await this.table.get(id);
|
||||
return !!item;
|
||||
}
|
||||
|
||||
async existsWhere(field: keyof T, value: any): Promise<boolean> {
|
||||
const count = await this.table
|
||||
.where(field as string)
|
||||
.equals(value)
|
||||
.count();
|
||||
return count > 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 创建各表的服务实例
|
||||
export const userService = new DatabaseService(db.users);
|
||||
export const messageService = new DatabaseService(db.messages);
|
||||
export const chatRoomService = new DatabaseService(db.chatRooms);
|
||||
export const settingService = new DatabaseService(db.settings);
|
||||
|
||||
// 专门的业务方法
|
||||
export class UserService extends DatabaseService<User> {
|
||||
constructor() {
|
||||
super(db.users);
|
||||
}
|
||||
|
||||
async findByEmail(email: string): Promise<User | undefined> {
|
||||
return await db.users.where("email").equals(email).first();
|
||||
}
|
||||
|
||||
async findActiveUsers(): Promise<User[]> {
|
||||
return await db.users.where("status").equals("active").toArray();
|
||||
}
|
||||
|
||||
async searchByName(name: string): Promise<User[]> {
|
||||
return await db.users.where("name").startsWithIgnoreCase(name).toArray();
|
||||
}
|
||||
|
||||
async updateStatus(
|
||||
id: number,
|
||||
status: "active" | "inactive",
|
||||
): Promise<number> {
|
||||
return await this.update(id, { status });
|
||||
}
|
||||
}
|
||||
|
||||
export class MessageService extends DatabaseService<Message> {
|
||||
constructor() {
|
||||
super(db.messages);
|
||||
}
|
||||
|
||||
async findByUserId(userId: number): Promise<Message[]> {
|
||||
return await db.messages.where("userId").equals(userId).toArray();
|
||||
}
|
||||
|
||||
async findUnreadMessages(): Promise<Message[]> {
|
||||
return await db.messages.where("isRead").equals(false).toArray();
|
||||
}
|
||||
|
||||
async markAsRead(id: number): Promise<number> {
|
||||
return await this.update(id, { isRead: true });
|
||||
}
|
||||
|
||||
async markAllAsRead(userId: number): Promise<number> {
|
||||
const messages = await db.messages
|
||||
.where("userId")
|
||||
.equals(userId)
|
||||
.and(msg => !msg.isRead)
|
||||
.toArray();
|
||||
const updates = messages.map(msg => ({
|
||||
id: msg.id!,
|
||||
data: { isRead: true },
|
||||
}));
|
||||
return await this.updateMany(updates);
|
||||
}
|
||||
|
||||
async getRecentMessages(limit: number = 50): Promise<Message[]> {
|
||||
return await db.messages
|
||||
.orderBy("createdAt")
|
||||
.reverse()
|
||||
.limit(limit)
|
||||
.toArray();
|
||||
}
|
||||
}
|
||||
|
||||
export class SettingService extends DatabaseService<Setting> {
|
||||
constructor() {
|
||||
super(db.settings);
|
||||
}
|
||||
|
||||
async getSetting(key: string): Promise<any> {
|
||||
const setting = await db.settings.where("key").equals(key).first();
|
||||
return setting?.value;
|
||||
}
|
||||
|
||||
async setSetting(
|
||||
key: string,
|
||||
value: any,
|
||||
category: string = "general",
|
||||
): Promise<number> {
|
||||
const existing = await db.settings.where("key").equals(key).first();
|
||||
if (existing) {
|
||||
return await this.update(existing.id!, { value });
|
||||
} else {
|
||||
return await this.create({ key, value, category });
|
||||
}
|
||||
}
|
||||
|
||||
async getSettingsByCategory(category: string): Promise<Setting[]> {
|
||||
return await db.settings.where("category").equals(category).toArray();
|
||||
}
|
||||
|
||||
async deleteSetting(key: string): Promise<void> {
|
||||
await db.settings.where("key").equals(key).delete();
|
||||
}
|
||||
}
|
||||
|
||||
// 数据库工具类
|
||||
export class DatabaseUtils {
|
||||
// 数据导出
|
||||
static async exportData(): Promise<string> {
|
||||
const data = {
|
||||
users: await db.users.toArray(),
|
||||
messages: await db.messages.toArray(),
|
||||
chatRooms: await db.chatRooms.toArray(),
|
||||
settings: await db.settings.toArray(),
|
||||
exportedAt: new Date().toISOString(),
|
||||
};
|
||||
return JSON.stringify(data, null, 2);
|
||||
}
|
||||
|
||||
// 数据导入
|
||||
static async importData(jsonData: string): Promise<void> {
|
||||
try {
|
||||
const data = JSON.parse(jsonData);
|
||||
|
||||
await db.transaction(
|
||||
"rw",
|
||||
[db.users, db.messages, db.chatRooms, db.settings],
|
||||
async () => {
|
||||
if (data.users) await db.users.bulkPut(data.users);
|
||||
if (data.messages) await db.messages.bulkPut(data.messages);
|
||||
if (data.chatRooms) await db.chatRooms.bulkPut(data.chatRooms);
|
||||
if (data.settings) await db.settings.bulkPut(data.settings);
|
||||
},
|
||||
);
|
||||
} catch (error) {
|
||||
throw new Error("导入数据失败: " + error);
|
||||
}
|
||||
}
|
||||
|
||||
// 清空所有数据
|
||||
static async clearAllData(): Promise<void> {
|
||||
await db.transaction(
|
||||
"rw",
|
||||
[db.users, db.messages, db.chatRooms, db.settings],
|
||||
async () => {
|
||||
await db.users.clear();
|
||||
await db.messages.clear();
|
||||
await db.chatRooms.clear();
|
||||
await db.settings.clear();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// 获取数据库统计信息
|
||||
static async getStats(): Promise<{
|
||||
users: number;
|
||||
messages: number;
|
||||
chatRooms: number;
|
||||
settings: number;
|
||||
totalSize: number;
|
||||
}> {
|
||||
const [users, messages, chatRooms, settings] = await Promise.all([
|
||||
db.users.count(),
|
||||
db.messages.count(),
|
||||
db.chatRooms.count(),
|
||||
db.settings.count(),
|
||||
]);
|
||||
|
||||
// 估算数据库大小(简单估算)
|
||||
const totalSize = users + messages + chatRooms + settings;
|
||||
|
||||
return { users, messages, chatRooms, settings, totalSize };
|
||||
}
|
||||
|
||||
// 数据库健康检查
|
||||
static async healthCheck(): Promise<{
|
||||
status: "healthy" | "error";
|
||||
message: string;
|
||||
}> {
|
||||
try {
|
||||
await db.users.limit(1).toArray();
|
||||
return { status: "healthy", message: "数据库连接正常" };
|
||||
} catch (error) {
|
||||
return { status: "error", message: "数据库连接异常: " + error };
|
||||
}
|
||||
}
|
||||
|
||||
// 数据备份到文件
|
||||
static async backupToFile(): Promise<void> {
|
||||
const data = await this.exportData();
|
||||
const blob = new Blob([data], { type: "application/json" });
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.download = `cunkebao-backup-${new Date().toISOString().split("T")[0]}.json`;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
// 从文件恢复数据
|
||||
static async restoreFromFile(file: File): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = async e => {
|
||||
try {
|
||||
const jsonData = e.target?.result as string;
|
||||
await this.importData(jsonData);
|
||||
resolve();
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
};
|
||||
reader.onerror = () => reject(new Error("文件读取失败"));
|
||||
reader.readAsText(file);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 创建业务服务实例
|
||||
export const userBusinessService = new UserService();
|
||||
export const messageBusinessService = new MessageService();
|
||||
export const settingBusinessService = new SettingService();
|
||||
|
||||
// 默认导出数据库实例
|
||||
export default db;
|
||||
Reference in New Issue
Block a user