Merge branch 'yongpxu-dev' into develop
3
Cunkebao/.gitignore
vendored
@@ -3,4 +3,5 @@ dist/
|
|||||||
build/
|
build/
|
||||||
yarn.lock
|
yarn.lock
|
||||||
.env
|
.env
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
dist/*
|
||||||
|
|||||||
36
Cunkebao/dist/.vite/manifest.json
vendored
@@ -1,50 +1,50 @@
|
|||||||
{
|
{
|
||||||
"_charts-DsEZaGAW.js": {
|
"_charts-B449e2xS.js": {
|
||||||
"file": "assets/charts-DsEZaGAW.js",
|
"file": "assets/charts-B449e2xS.js",
|
||||||
"name": "charts",
|
"name": "charts",
|
||||||
"imports": [
|
"imports": [
|
||||||
"_ui-YC29IQHT.js",
|
"_ui-DDu9FCjt.js",
|
||||||
"_vendor-Bq99rrm8.js"
|
"_vendor-0WYR1k4q.js"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_ui-D0C0OGrH.css": {
|
"_ui-D0C0OGrH.css": {
|
||||||
"file": "assets/ui-D0C0OGrH.css",
|
"file": "assets/ui-D0C0OGrH.css",
|
||||||
"src": "_ui-D0C0OGrH.css"
|
"src": "_ui-D0C0OGrH.css"
|
||||||
},
|
},
|
||||||
"_ui-YC29IQHT.js": {
|
"_ui-DDu9FCjt.js": {
|
||||||
"file": "assets/ui-YC29IQHT.js",
|
"file": "assets/ui-DDu9FCjt.js",
|
||||||
"name": "ui",
|
"name": "ui",
|
||||||
"imports": [
|
"imports": [
|
||||||
"_vendor-Bq99rrm8.js"
|
"_vendor-0WYR1k4q.js"
|
||||||
],
|
],
|
||||||
"css": [
|
"css": [
|
||||||
"assets/ui-D0C0OGrH.css"
|
"assets/ui-D0C0OGrH.css"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_utils-Ft3ushmX.js": {
|
"_utils-DC06x9DY.js": {
|
||||||
"file": "assets/utils-Ft3ushmX.js",
|
"file": "assets/utils-DC06x9DY.js",
|
||||||
"name": "utils",
|
"name": "utils",
|
||||||
"imports": [
|
"imports": [
|
||||||
"_vendor-Bq99rrm8.js"
|
"_vendor-0WYR1k4q.js"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_vendor-Bq99rrm8.js": {
|
"_vendor-0WYR1k4q.js": {
|
||||||
"file": "assets/vendor-Bq99rrm8.js",
|
"file": "assets/vendor-0WYR1k4q.js",
|
||||||
"name": "vendor"
|
"name": "vendor"
|
||||||
},
|
},
|
||||||
"index.html": {
|
"index.html": {
|
||||||
"file": "assets/index-d8oC2PBC.js",
|
"file": "assets/index-Bx3nG52V.js",
|
||||||
"name": "index",
|
"name": "index",
|
||||||
"src": "index.html",
|
"src": "index.html",
|
||||||
"isEntry": true,
|
"isEntry": true,
|
||||||
"imports": [
|
"imports": [
|
||||||
"_vendor-Bq99rrm8.js",
|
"_vendor-0WYR1k4q.js",
|
||||||
"_ui-YC29IQHT.js",
|
"_ui-DDu9FCjt.js",
|
||||||
"_utils-Ft3ushmX.js",
|
"_utils-DC06x9DY.js",
|
||||||
"_charts-DsEZaGAW.js"
|
"_charts-B449e2xS.js"
|
||||||
],
|
],
|
||||||
"css": [
|
"css": [
|
||||||
"assets/index-D8Ews0CA.css"
|
"assets/index-QrS4Cvyc.css"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
12
Cunkebao/dist/index.html
vendored
@@ -11,13 +11,13 @@
|
|||||||
</style>
|
</style>
|
||||||
<!-- 引入 uni-app web-view SDK(必须) -->
|
<!-- 引入 uni-app web-view SDK(必须) -->
|
||||||
<script type="text/javascript" src="/websdk.js"></script>
|
<script type="text/javascript" src="/websdk.js"></script>
|
||||||
<script type="module" crossorigin src="/assets/index-d8oC2PBC.js"></script>
|
<script type="module" crossorigin src="/assets/index-Bx3nG52V.js"></script>
|
||||||
<link rel="modulepreload" crossorigin href="/assets/vendor-Bq99rrm8.js">
|
<link rel="modulepreload" crossorigin href="/assets/vendor-0WYR1k4q.js">
|
||||||
<link rel="modulepreload" crossorigin href="/assets/ui-YC29IQHT.js">
|
<link rel="modulepreload" crossorigin href="/assets/ui-DDu9FCjt.js">
|
||||||
<link rel="modulepreload" crossorigin href="/assets/utils-Ft3ushmX.js">
|
<link rel="modulepreload" crossorigin href="/assets/utils-DC06x9DY.js">
|
||||||
<link rel="modulepreload" crossorigin href="/assets/charts-DsEZaGAW.js">
|
<link rel="modulepreload" crossorigin href="/assets/charts-B449e2xS.js">
|
||||||
<link rel="stylesheet" crossorigin href="/assets/ui-D0C0OGrH.css">
|
<link rel="stylesheet" crossorigin href="/assets/ui-D0C0OGrH.css">
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-D8Ews0CA.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-QrS4Cvyc.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 12 KiB |
@@ -161,273 +161,313 @@ const EMOJI_DATA: Record<EmojiName, EmojiInfo> = {
|
|||||||
微笑: {
|
微笑: {
|
||||||
name: "微笑",
|
name: "微笑",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/微笑.png",
|
path: "/assets/face/smile.png",
|
||||||
},
|
},
|
||||||
撇嘴: {
|
撇嘴: {
|
||||||
name: "撇嘴",
|
name: "撇嘴",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/撇嘴.png",
|
path: "/assets/face/pout.png",
|
||||||
|
},
|
||||||
|
色: {
|
||||||
|
name: "色",
|
||||||
|
category: EmojiCategory.FACE,
|
||||||
|
path: "/assets/face/lustful.png",
|
||||||
},
|
},
|
||||||
色: { name: "色", category: EmojiCategory.FACE, path: "/assets/face/色.png" },
|
|
||||||
发呆: {
|
发呆: {
|
||||||
name: "发呆",
|
name: "发呆",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/发呆.png",
|
path: "/assets/face/daze.png",
|
||||||
},
|
},
|
||||||
得意: {
|
得意: {
|
||||||
name: "得意",
|
name: "得意",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/得意.png",
|
path: "/assets/face/smug.png",
|
||||||
},
|
},
|
||||||
流泪: {
|
流泪: {
|
||||||
name: "流泪",
|
name: "流泪",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/流泪.png",
|
path: "/assets/face/crying.png",
|
||||||
},
|
},
|
||||||
害羞: {
|
害羞: {
|
||||||
name: "害羞",
|
name: "害羞",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/害羞.png",
|
path: "/assets/face/shy.png",
|
||||||
},
|
},
|
||||||
闭嘴: {
|
闭嘴: {
|
||||||
name: "闭嘴",
|
name: "闭嘴",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/闭嘴.png",
|
path: "/assets/face/shut-up.png",
|
||||||
|
},
|
||||||
|
睡: {
|
||||||
|
name: "睡",
|
||||||
|
category: EmojiCategory.FACE,
|
||||||
|
path: "/assets/face/sleep.png",
|
||||||
},
|
},
|
||||||
睡: { name: "睡", category: EmojiCategory.FACE, path: "/assets/face/睡.png" },
|
|
||||||
大哭: {
|
大哭: {
|
||||||
name: "大哭",
|
name: "大哭",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/大哭.png",
|
path: "/assets/face/wail.png",
|
||||||
},
|
},
|
||||||
尴尬: {
|
尴尬: {
|
||||||
name: "尴尬",
|
name: "尴尬",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/尴尬.png",
|
path: "/assets/face/awkward.png",
|
||||||
},
|
},
|
||||||
发怒: {
|
发怒: {
|
||||||
name: "发怒",
|
name: "发怒",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/发怒.png",
|
path: "/assets/face/angry.png",
|
||||||
},
|
},
|
||||||
调皮: {
|
调皮: {
|
||||||
name: "调皮",
|
name: "调皮",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/调皮.png",
|
path: "/assets/face/naughty.png",
|
||||||
},
|
},
|
||||||
呲牙: {
|
呲牙: {
|
||||||
name: "呲牙",
|
name: "呲牙",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/呲牙.png",
|
path: "/assets/face/grin.png",
|
||||||
},
|
},
|
||||||
惊讶: {
|
惊讶: {
|
||||||
name: "惊讶",
|
name: "惊讶",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/惊讶.png",
|
path: "/assets/face/surprised.png",
|
||||||
},
|
},
|
||||||
难过: {
|
难过: {
|
||||||
name: "难过",
|
name: "难过",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/难过.png",
|
path: "/assets/face/sad.png",
|
||||||
|
},
|
||||||
|
囧: {
|
||||||
|
name: "囧",
|
||||||
|
category: EmojiCategory.FACE,
|
||||||
|
path: "/assets/face/embarrassed.png",
|
||||||
},
|
},
|
||||||
囧: { name: "囧", category: EmojiCategory.FACE, path: "/assets/face/囧.png" },
|
|
||||||
抓狂: {
|
抓狂: {
|
||||||
name: "抓狂",
|
name: "抓狂",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/抓狂.png",
|
path: "/assets/face/crazy.png",
|
||||||
|
},
|
||||||
|
吐: {
|
||||||
|
name: "吐",
|
||||||
|
category: EmojiCategory.FACE,
|
||||||
|
path: "/assets/face/vomit.png",
|
||||||
},
|
},
|
||||||
吐: { name: "吐", category: EmojiCategory.FACE, path: "/assets/face/吐.png" },
|
|
||||||
偷笑: {
|
偷笑: {
|
||||||
name: "偷笑",
|
name: "偷笑",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/偷笑.png",
|
path: "/assets/face/snicker.png",
|
||||||
},
|
},
|
||||||
愉快: {
|
愉快: {
|
||||||
name: "愉快",
|
name: "愉快",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/愉快.png",
|
path: "/assets/face/happy.png",
|
||||||
},
|
},
|
||||||
白眼: {
|
白眼: {
|
||||||
name: "白眼",
|
name: "白眼",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/白眼.png",
|
path: "/assets/face/roll-eyes.png",
|
||||||
},
|
},
|
||||||
傲慢: {
|
傲慢: {
|
||||||
name: "傲慢",
|
name: "傲慢",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/傲慢.png",
|
path: "/assets/face/arrogant.png",
|
||||||
|
},
|
||||||
|
困: {
|
||||||
|
name: "困",
|
||||||
|
category: EmojiCategory.FACE,
|
||||||
|
path: "/assets/face/sleepy.png",
|
||||||
},
|
},
|
||||||
困: { name: "困", category: EmojiCategory.FACE, path: "/assets/face/困.png" },
|
|
||||||
惊恐: {
|
惊恐: {
|
||||||
name: "惊恐",
|
name: "惊恐",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/惊恐.png",
|
path: "/assets/face/panic.png",
|
||||||
},
|
},
|
||||||
憨笑: {
|
憨笑: {
|
||||||
name: "憨笑",
|
name: "憨笑",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/憨笑.png",
|
path: "/assets/face/silly-smile.png",
|
||||||
},
|
},
|
||||||
悠闲: {
|
悠闲: {
|
||||||
name: "悠闲",
|
name: "悠闲",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/悠闲.png",
|
path: "/assets/face/leisurely.png",
|
||||||
},
|
},
|
||||||
咒骂: {
|
咒骂: {
|
||||||
name: "咒骂",
|
name: "咒骂",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/咒骂.png",
|
path: "/assets/face/curse.png",
|
||||||
},
|
},
|
||||||
疑问: {
|
疑问: {
|
||||||
name: "疑问",
|
name: "疑问",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/疑问.png",
|
path: "/assets/face/question.png",
|
||||||
|
},
|
||||||
|
嘘: {
|
||||||
|
name: "嘘",
|
||||||
|
category: EmojiCategory.FACE,
|
||||||
|
path: "/assets/face/shush.png",
|
||||||
|
},
|
||||||
|
晕: {
|
||||||
|
name: "晕",
|
||||||
|
category: EmojiCategory.FACE,
|
||||||
|
path: "/assets/face/dizzy.png",
|
||||||
|
},
|
||||||
|
衰: {
|
||||||
|
name: "衰",
|
||||||
|
category: EmojiCategory.FACE,
|
||||||
|
path: "/assets/face/unlucky.png",
|
||||||
},
|
},
|
||||||
嘘: { name: "嘘", category: EmojiCategory.FACE, path: "/assets/face/嘘.png" },
|
|
||||||
晕: { name: "晕", category: EmojiCategory.FACE, path: "/assets/face/晕.png" },
|
|
||||||
衰: { name: "衰", category: EmojiCategory.FACE, path: "/assets/face/衰.png" },
|
|
||||||
骷髅: {
|
骷髅: {
|
||||||
name: "骷髅",
|
name: "骷髅",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/骷髅.png",
|
path: "/assets/face/skull.png",
|
||||||
},
|
},
|
||||||
敲打: {
|
敲打: {
|
||||||
name: "敲打",
|
name: "敲打",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/敲打.png",
|
path: "/assets/face/knock.png",
|
||||||
},
|
},
|
||||||
再见: {
|
再见: {
|
||||||
name: "再见",
|
name: "再见",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/再见.png",
|
path: "/assets/face/goodbye.png",
|
||||||
},
|
},
|
||||||
擦汗: {
|
擦汗: {
|
||||||
name: "擦汗",
|
name: "擦汗",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/擦汗.png",
|
path: "/assets/face/wipe-sweat.png",
|
||||||
},
|
},
|
||||||
抠鼻: {
|
抠鼻: {
|
||||||
name: "抠鼻",
|
name: "抠鼻",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/抠鼻.png",
|
path: "/assets/face/pick-nose.png",
|
||||||
},
|
},
|
||||||
鼓掌: {
|
鼓掌: {
|
||||||
name: "鼓掌",
|
name: "鼓掌",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/鼓掌.png",
|
path: "/assets/face/clap.png",
|
||||||
},
|
},
|
||||||
坏笑: {
|
坏笑: {
|
||||||
name: "坏笑",
|
name: "坏笑",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/坏笑.png",
|
path: "/assets/face/evil-smile.png",
|
||||||
},
|
},
|
||||||
右哼哼: {
|
右哼哼: {
|
||||||
name: "右哼哼",
|
name: "右哼哼",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/右哼哼.png",
|
path: "/assets/face/right-hum.png",
|
||||||
},
|
},
|
||||||
鄙视: {
|
鄙视: {
|
||||||
name: "鄙视",
|
name: "鄙视",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/鄙视.png",
|
path: "/assets/face/despise.png",
|
||||||
},
|
},
|
||||||
委屈: {
|
委屈: {
|
||||||
name: "委屈",
|
name: "委屈",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/委屈.png",
|
path: "/assets/face/wronged.png",
|
||||||
},
|
},
|
||||||
快哭了: {
|
快哭了: {
|
||||||
name: "快哭了",
|
name: "快哭了",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/快哭了.png",
|
path: "/assets/face/about-to-cry.png",
|
||||||
},
|
},
|
||||||
阴险: {
|
阴险: {
|
||||||
name: "阴险",
|
name: "阴险",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/阴险.png",
|
path: "/assets/face/sinister.png",
|
||||||
},
|
},
|
||||||
亲亲: {
|
亲亲: {
|
||||||
name: "亲亲",
|
name: "亲亲",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/亲亲.png",
|
path: "/assets/face/kiss.png",
|
||||||
},
|
},
|
||||||
可怜: {
|
可怜: {
|
||||||
name: "可怜",
|
name: "可怜",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/可怜.png",
|
path: "/assets/face/pitiful.png",
|
||||||
},
|
},
|
||||||
笑脸: {
|
笑脸: {
|
||||||
name: "笑脸",
|
name: "笑脸",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/笑脸.png",
|
path: "/assets/face/smiley.png",
|
||||||
},
|
},
|
||||||
生病: {
|
生病: {
|
||||||
name: "生病",
|
name: "生病",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/生病.png",
|
path: "/assets/face/sick.png",
|
||||||
},
|
},
|
||||||
脸红: {
|
脸红: {
|
||||||
name: "脸红",
|
name: "脸红",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/脸红.png",
|
path: "/assets/face/blush.png",
|
||||||
},
|
},
|
||||||
破涕为笑: {
|
破涕为笑: {
|
||||||
name: "破涕为笑",
|
name: "破涕为笑",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/破涕为笑.png",
|
path: "/assets/face/tears-to-smile.png",
|
||||||
},
|
},
|
||||||
恐惧: {
|
恐惧: {
|
||||||
name: "恐惧",
|
name: "恐惧",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/恐惧.png",
|
path: "/assets/face/fear.png",
|
||||||
},
|
},
|
||||||
失望: {
|
失望: {
|
||||||
name: "失望",
|
name: "失望",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/失望.png",
|
path: "/assets/face/disappointed.png",
|
||||||
},
|
},
|
||||||
无语: {
|
无语: {
|
||||||
name: "无语",
|
name: "无语",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/无语.png",
|
path: "/assets/face/speechless.png",
|
||||||
},
|
},
|
||||||
嘿哈: {
|
嘿哈: {
|
||||||
name: "嘿哈",
|
name: "嘿哈",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/嘿哈.png",
|
path: "/assets/face/hey-ha.png",
|
||||||
},
|
},
|
||||||
捂脸: {
|
捂脸: {
|
||||||
name: "捂脸",
|
name: "捂脸",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/捂脸.png",
|
path: "/assets/face/facepalm.png",
|
||||||
},
|
},
|
||||||
机智: {
|
机智: {
|
||||||
name: "机智",
|
name: "机智",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/机智.png",
|
path: "/assets/face/smart.png",
|
||||||
},
|
},
|
||||||
皱眉: {
|
皱眉: {
|
||||||
name: "皱眉",
|
name: "皱眉",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/皱眉.png",
|
path: "/assets/face/frown.png",
|
||||||
|
},
|
||||||
|
耶: {
|
||||||
|
name: "耶",
|
||||||
|
category: EmojiCategory.FACE,
|
||||||
|
path: "/assets/face/yeah.png",
|
||||||
},
|
},
|
||||||
耶: { name: "耶", category: EmojiCategory.FACE, path: "/assets/face/耶.png" },
|
|
||||||
吃瓜: {
|
吃瓜: {
|
||||||
name: "吃瓜",
|
name: "吃瓜",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/吃瓜.png",
|
path: "/assets/face/eat-melon.png",
|
||||||
},
|
},
|
||||||
加油: {
|
加油: {
|
||||||
name: "加油",
|
name: "加油",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/加油.png",
|
path: "/assets/face/cheer-up.png",
|
||||||
},
|
},
|
||||||
|
|
||||||
汗: { name: "汗", category: EmojiCategory.FACE, path: "/assets/face/汗.png" },
|
汗: {
|
||||||
|
name: "汗",
|
||||||
|
category: EmojiCategory.FACE,
|
||||||
|
path: "/assets/face/sweat.png",
|
||||||
|
},
|
||||||
天啊: {
|
天啊: {
|
||||||
name: "天啊",
|
name: "天啊",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/天啊.png",
|
path: "/assets/face/oh-my.png",
|
||||||
},
|
},
|
||||||
Emm: {
|
Emm: {
|
||||||
name: "Emm",
|
name: "Emm",
|
||||||
@@ -437,28 +477,32 @@ const EMOJI_DATA: Record<EmojiName, EmojiInfo> = {
|
|||||||
社会社会: {
|
社会社会: {
|
||||||
name: "社会社会",
|
name: "社会社会",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/社会社会.png",
|
path: "/assets/face/social.png",
|
||||||
},
|
},
|
||||||
旺柴: {
|
旺柴: {
|
||||||
name: "旺柴",
|
name: "旺柴",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/旺柴.png",
|
path: "/assets/face/doge.png",
|
||||||
},
|
},
|
||||||
好的: {
|
好的: {
|
||||||
name: "好的",
|
name: "好的",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/好的.png",
|
path: "/assets/face/good.png",
|
||||||
},
|
},
|
||||||
打脸: {
|
打脸: {
|
||||||
name: "打脸",
|
name: "打脸",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/打脸.png",
|
path: "/assets/face/slap-face.png",
|
||||||
|
},
|
||||||
|
哇: {
|
||||||
|
name: "哇",
|
||||||
|
category: EmojiCategory.FACE,
|
||||||
|
path: "/assets/face/wow.png",
|
||||||
},
|
},
|
||||||
哇: { name: "哇", category: EmojiCategory.FACE, path: "/assets/face/哇.png" },
|
|
||||||
翻白眼: {
|
翻白眼: {
|
||||||
name: "翻白眼",
|
name: "翻白眼",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/翻白眼.png",
|
path: "/assets/face/eye-roll.png",
|
||||||
},
|
},
|
||||||
"666": {
|
"666": {
|
||||||
name: "666",
|
name: "666",
|
||||||
@@ -468,54 +512,54 @@ const EMOJI_DATA: Record<EmojiName, EmojiInfo> = {
|
|||||||
让我看看: {
|
让我看看: {
|
||||||
name: "让我看看",
|
name: "让我看看",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/让我看看.png",
|
path: "/assets/face/let-me-see.png",
|
||||||
},
|
},
|
||||||
叹气: {
|
叹气: {
|
||||||
name: "叹气",
|
name: "叹气",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/叹气.png",
|
path: "/assets/face/sigh.png",
|
||||||
},
|
},
|
||||||
苦涩: {
|
苦涩: {
|
||||||
name: "苦涩",
|
name: "苦涩",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/苦涩.png",
|
path: "/assets/face/bitter.png",
|
||||||
},
|
},
|
||||||
裂开: {
|
裂开: {
|
||||||
name: "裂开",
|
name: "裂开",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/裂开.png",
|
path: "/assets/face/crack.png",
|
||||||
},
|
},
|
||||||
奸笑: {
|
奸笑: {
|
||||||
name: "奸笑",
|
name: "奸笑",
|
||||||
category: EmojiCategory.FACE,
|
category: EmojiCategory.FACE,
|
||||||
path: "/assets/face/奸笑.png",
|
path: "/assets/face/sly-smile.png",
|
||||||
},
|
},
|
||||||
|
|
||||||
// 手势表情
|
// 手势表情
|
||||||
握手: {
|
握手: {
|
||||||
name: "握手",
|
name: "握手",
|
||||||
category: EmojiCategory.GESTURE,
|
category: EmojiCategory.GESTURE,
|
||||||
path: "/assets/gesture/握手.png",
|
path: "/assets/gesture/handshake.png",
|
||||||
},
|
},
|
||||||
胜利: {
|
胜利: {
|
||||||
name: "胜利",
|
name: "胜利",
|
||||||
category: EmojiCategory.GESTURE,
|
category: EmojiCategory.GESTURE,
|
||||||
path: "/assets/gesture/胜利.png",
|
path: "/assets/gesture/victory.png",
|
||||||
},
|
},
|
||||||
抱拳: {
|
抱拳: {
|
||||||
name: "抱拳",
|
name: "抱拳",
|
||||||
category: EmojiCategory.GESTURE,
|
category: EmojiCategory.GESTURE,
|
||||||
path: "/assets/gesture/抱拳.png",
|
path: "/assets/gesture/fist-salute.png",
|
||||||
},
|
},
|
||||||
勾引: {
|
勾引: {
|
||||||
name: "勾引",
|
name: "勾引",
|
||||||
category: EmojiCategory.GESTURE,
|
category: EmojiCategory.GESTURE,
|
||||||
path: "/assets/gesture/勾引.png",
|
path: "/assets/gesture/beckon.png",
|
||||||
},
|
},
|
||||||
拳头: {
|
拳头: {
|
||||||
name: "拳头",
|
name: "拳头",
|
||||||
category: EmojiCategory.GESTURE,
|
category: EmojiCategory.GESTURE,
|
||||||
path: "/assets/gesture/拳头.png",
|
path: "/assets/gesture/fist.png",
|
||||||
},
|
},
|
||||||
OK: {
|
OK: {
|
||||||
name: "OK",
|
name: "OK",
|
||||||
@@ -525,148 +569,148 @@ const EMOJI_DATA: Record<EmojiName, EmojiInfo> = {
|
|||||||
合十: {
|
合十: {
|
||||||
name: "合十",
|
name: "合十",
|
||||||
category: EmojiCategory.GESTURE,
|
category: EmojiCategory.GESTURE,
|
||||||
path: "/assets/gesture/合十.png",
|
path: "/assets/gesture/pray.png",
|
||||||
},
|
},
|
||||||
强: {
|
强: {
|
||||||
name: "强",
|
name: "强",
|
||||||
category: EmojiCategory.GESTURE,
|
category: EmojiCategory.GESTURE,
|
||||||
path: "/assets/gesture/强.png",
|
path: "/assets/gesture/strong.png",
|
||||||
},
|
},
|
||||||
拥抱: {
|
拥抱: {
|
||||||
name: "拥抱",
|
name: "拥抱",
|
||||||
category: EmojiCategory.GESTURE,
|
category: EmojiCategory.GESTURE,
|
||||||
path: "/assets/gesture/拥抱.png",
|
path: "/assets/gesture/hug.png",
|
||||||
},
|
},
|
||||||
弱: {
|
弱: {
|
||||||
name: "弱",
|
name: "弱",
|
||||||
category: EmojiCategory.GESTURE,
|
category: EmojiCategory.GESTURE,
|
||||||
path: "/assets/gesture/弱.png",
|
path: "/assets/gesture/weak.png",
|
||||||
},
|
},
|
||||||
|
|
||||||
// 动物表情
|
// 动物表情
|
||||||
猪头: {
|
猪头: {
|
||||||
name: "猪头",
|
name: "猪头",
|
||||||
category: EmojiCategory.ANIMAL,
|
category: EmojiCategory.ANIMAL,
|
||||||
path: "/assets/animal/猪头.png",
|
path: "/assets/animal/pig.png",
|
||||||
},
|
},
|
||||||
跳跳: {
|
跳跳: {
|
||||||
name: "跳跳",
|
name: "跳跳",
|
||||||
category: EmojiCategory.ANIMAL,
|
category: EmojiCategory.ANIMAL,
|
||||||
path: "/assets/animal/跳跳.png",
|
path: "/assets/animal/jump.png",
|
||||||
},
|
},
|
||||||
发抖: {
|
发抖: {
|
||||||
name: "发抖",
|
name: "发抖",
|
||||||
category: EmojiCategory.ANIMAL,
|
category: EmojiCategory.ANIMAL,
|
||||||
path: "/assets/animal/发抖.png",
|
path: "/assets/animal/tremble.png",
|
||||||
},
|
},
|
||||||
转圈: {
|
转圈: {
|
||||||
name: "转圈",
|
name: "转圈",
|
||||||
category: EmojiCategory.ANIMAL,
|
category: EmojiCategory.ANIMAL,
|
||||||
path: "/assets/animal/转圈.png",
|
path: "/assets/animal/circle.png",
|
||||||
},
|
},
|
||||||
|
|
||||||
// 祝福表情
|
// 祝福表情
|
||||||
庆祝: {
|
庆祝: {
|
||||||
name: "庆祝",
|
name: "庆祝",
|
||||||
category: EmojiCategory.BLESSING,
|
category: EmojiCategory.BLESSING,
|
||||||
path: "/assets/blessing/庆祝.png",
|
path: "/assets/blessing/celebrate.png",
|
||||||
},
|
},
|
||||||
礼物: {
|
礼物: {
|
||||||
name: "礼物",
|
name: "礼物",
|
||||||
category: EmojiCategory.BLESSING,
|
category: EmojiCategory.BLESSING,
|
||||||
path: "/assets/blessing/礼物.png",
|
path: "/assets/blessing/gift.png",
|
||||||
},
|
},
|
||||||
红包: {
|
红包: {
|
||||||
name: "红包",
|
name: "红包",
|
||||||
category: EmojiCategory.BLESSING,
|
category: EmojiCategory.BLESSING,
|
||||||
path: "/assets/blessing/红包.png",
|
path: "/assets/blessing/red-envelope.png",
|
||||||
},
|
},
|
||||||
發: {
|
發: {
|
||||||
name: "發",
|
name: "發",
|
||||||
category: EmojiCategory.BLESSING,
|
category: EmojiCategory.BLESSING,
|
||||||
path: "/assets/blessing/發.png",
|
path: "/assets/blessing/get-rich.png",
|
||||||
},
|
},
|
||||||
福: {
|
福: {
|
||||||
name: "福",
|
name: "福",
|
||||||
category: EmojiCategory.BLESSING,
|
category: EmojiCategory.BLESSING,
|
||||||
path: "/assets/blessing/福.png",
|
path: "/assets/blessing/fortune.png",
|
||||||
},
|
},
|
||||||
烟花: {
|
烟花: {
|
||||||
name: "烟花",
|
name: "烟花",
|
||||||
category: EmojiCategory.BLESSING,
|
category: EmojiCategory.BLESSING,
|
||||||
path: "/assets/blessing/烟花.png",
|
path: "/assets/blessing/fireworks.png",
|
||||||
},
|
},
|
||||||
爆竹: {
|
爆竹: {
|
||||||
name: "爆竹",
|
name: "爆竹",
|
||||||
category: EmojiCategory.BLESSING,
|
category: EmojiCategory.BLESSING,
|
||||||
path: "/assets/blessing/爆竹.png",
|
path: "/assets/blessing/firecrackers.png",
|
||||||
},
|
},
|
||||||
|
|
||||||
// 其他表情
|
// 其他表情
|
||||||
嘴唇: {
|
嘴唇: {
|
||||||
name: "嘴唇",
|
name: "嘴唇",
|
||||||
category: EmojiCategory.OTHER,
|
category: EmojiCategory.OTHER,
|
||||||
path: "/assets/other/嘴唇.png",
|
path: "/assets/other/lips.png",
|
||||||
},
|
},
|
||||||
爱心: {
|
爱心: {
|
||||||
name: "爱心",
|
name: "爱心",
|
||||||
category: EmojiCategory.OTHER,
|
category: EmojiCategory.OTHER,
|
||||||
path: "/assets/other/爱心.png",
|
path: "/assets/other/heart.png",
|
||||||
},
|
},
|
||||||
心碎: {
|
心碎: {
|
||||||
name: "心碎",
|
name: "心碎",
|
||||||
category: EmojiCategory.OTHER,
|
category: EmojiCategory.OTHER,
|
||||||
path: "/assets/other/心碎.png",
|
path: "/assets/other/broken-heart.png",
|
||||||
},
|
},
|
||||||
啤酒: {
|
啤酒: {
|
||||||
name: "啤酒",
|
name: "啤酒",
|
||||||
category: EmojiCategory.OTHER,
|
category: EmojiCategory.OTHER,
|
||||||
path: "/assets/other/啤酒.png",
|
path: "/assets/other/beer.png",
|
||||||
},
|
},
|
||||||
咖啡: {
|
咖啡: {
|
||||||
name: "咖啡",
|
name: "咖啡",
|
||||||
category: EmojiCategory.OTHER,
|
category: EmojiCategory.OTHER,
|
||||||
path: "/assets/other/咖啡.png",
|
path: "/assets/other/coffee.png",
|
||||||
},
|
},
|
||||||
蛋糕: {
|
蛋糕: {
|
||||||
name: "蛋糕",
|
name: "蛋糕",
|
||||||
category: EmojiCategory.OTHER,
|
category: EmojiCategory.OTHER,
|
||||||
path: "/assets/other/蛋糕.png",
|
path: "/assets/other/cake.png",
|
||||||
},
|
},
|
||||||
凋谢: {
|
凋谢: {
|
||||||
name: "凋谢",
|
name: "凋谢",
|
||||||
category: EmojiCategory.OTHER,
|
category: EmojiCategory.OTHER,
|
||||||
path: "/assets/other/凋谢.png",
|
path: "/assets/other/wither.png",
|
||||||
},
|
},
|
||||||
菜刀: {
|
菜刀: {
|
||||||
name: "菜刀",
|
name: "菜刀",
|
||||||
category: EmojiCategory.OTHER,
|
category: EmojiCategory.OTHER,
|
||||||
path: "/assets/other/菜刀.png",
|
path: "/assets/other/knife.png",
|
||||||
},
|
},
|
||||||
炸弹: {
|
炸弹: {
|
||||||
name: "炸弹",
|
name: "炸弹",
|
||||||
category: EmojiCategory.OTHER,
|
category: EmojiCategory.OTHER,
|
||||||
path: "/assets/other/炸弹.png",
|
path: "/assets/other/bomb.png",
|
||||||
},
|
},
|
||||||
便便: {
|
便便: {
|
||||||
name: "便便",
|
name: "便便",
|
||||||
category: EmojiCategory.OTHER,
|
category: EmojiCategory.OTHER,
|
||||||
path: "/assets/other/便便.png",
|
path: "/assets/other/poop.png",
|
||||||
},
|
},
|
||||||
太阳: {
|
太阳: {
|
||||||
name: "太阳",
|
name: "太阳",
|
||||||
category: EmojiCategory.OTHER,
|
category: EmojiCategory.OTHER,
|
||||||
path: "/assets/other/太阳.png",
|
path: "/assets/other/sun.png",
|
||||||
},
|
},
|
||||||
月亮: {
|
月亮: {
|
||||||
name: "月亮",
|
name: "月亮",
|
||||||
category: EmojiCategory.OTHER,
|
category: EmojiCategory.OTHER,
|
||||||
path: "/assets/other/月亮.png",
|
path: "/assets/other/moon.png",
|
||||||
},
|
},
|
||||||
玫瑰: {
|
玫瑰: {
|
||||||
name: "玫瑰",
|
name: "玫瑰",
|
||||||
category: EmojiCategory.OTHER,
|
category: EmojiCategory.OTHER,
|
||||||
path: "/assets/other/玫瑰.png",
|
path: "/assets/other/rose.png",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { useCkChatStore } from "@/store/module/ckchat/ckchat";
|
|
||||||
import { Form, Input, Button, Toast, Checkbox } from "antd-mobile";
|
import { Form, Input, Button, Toast, Checkbox } from "antd-mobile";
|
||||||
import {
|
import {
|
||||||
EyeInvisibleOutline,
|
EyeInvisibleOutline,
|
||||||
@@ -7,7 +6,6 @@ import {
|
|||||||
UserOutline,
|
UserOutline,
|
||||||
} from "antd-mobile-icons";
|
} from "antd-mobile-icons";
|
||||||
import { useUserStore } from "@/store/module/user";
|
import { useUserStore } from "@/store/module/user";
|
||||||
import { useWebSocketStore } from "@/store/module/websocket/websocket";
|
|
||||||
|
|
||||||
import { loginWithPassword, loginWithCode, sendVerificationCode } from "./api";
|
import { loginWithPassword, loginWithCode, sendVerificationCode } from "./api";
|
||||||
import style from "./login.module.scss";
|
import style from "./login.module.scss";
|
||||||
@@ -19,7 +17,6 @@ const Login: React.FC = () => {
|
|||||||
const [countdown, setCountdown] = useState(0);
|
const [countdown, setCountdown] = useState(0);
|
||||||
const [showPassword, setShowPassword] = useState(false);
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
const [agreeToTerms, setAgreeToTerms] = useState(false);
|
const [agreeToTerms, setAgreeToTerms] = useState(false);
|
||||||
const { setUserInfo } = useCkChatStore.getState();
|
|
||||||
const { login, login2 } = useUserStore();
|
const { login, login2 } = useUserStore();
|
||||||
|
|
||||||
// 倒计时效果
|
// 倒计时效果
|
||||||
@@ -75,13 +72,9 @@ const Login: React.FC = () => {
|
|||||||
: loginWithCode(loginParams);
|
: loginWithCode(loginParams);
|
||||||
|
|
||||||
response.then(res => {
|
response.then(res => {
|
||||||
const { member, kefuData, deviceTotal } = res;
|
const { member, deviceTotal } = res;
|
||||||
// 清空WebSocket连接状态
|
// 清空WebSocket连接状态
|
||||||
useWebSocketStore.getState().clearConnectionState();
|
|
||||||
login(res.token, member, deviceTotal);
|
login(res.token, member, deviceTotal);
|
||||||
const { self, token } = kefuData;
|
|
||||||
login2(token.access_token);
|
|
||||||
setUserInfo(self);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -84,16 +84,6 @@ const Mine: React.FC = () => {
|
|||||||
bgColor: "#fff7e6",
|
bgColor: "#fff7e6",
|
||||||
iconColor: "#fa8c16",
|
iconColor: "#fa8c16",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
id: "ckb",
|
|
||||||
title: "触客宝",
|
|
||||||
description: "触客宝",
|
|
||||||
icon: <PhoneOutlined />,
|
|
||||||
count: 0,
|
|
||||||
path: "/ckbox/weChat",
|
|
||||||
bgColor: "#fff7e6",
|
|
||||||
iconColor: "#fa8c16",
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
// 加载统计数据
|
// 加载统计数据
|
||||||
|
|||||||
@@ -784,12 +784,6 @@ const TrafficPoolDetail: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
{/* 添加新标签按钮 */}
|
|
||||||
<Button block color="primary" className={styles.addTagBtn}>
|
|
||||||
<TagOutlined />
|
|
||||||
添加新标签
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
//构建联系人列表标签
|
|
||||||
import { weChatGroupService, contractService } from "@/utils/db";
|
|
||||||
import { request } from "@/api/request2";
|
|
||||||
import { ContactGroupByLabel } from "@/pages/pc/ckbox/data";
|
|
||||||
|
|
||||||
export function WechatGroup(params) {
|
|
||||||
return request("/api/WechatGroup/list", params, "GET");
|
|
||||||
}
|
|
||||||
|
|
||||||
export const createContractList = async (
|
|
||||||
kfSelected: number,
|
|
||||||
countLables: ContactGroupByLabel[],
|
|
||||||
) => {
|
|
||||||
// 根据 groupType 决定查询不同的服务
|
|
||||||
const dataByLabels = [];
|
|
||||||
for (const label of countLables) {
|
|
||||||
let data;
|
|
||||||
if (label.groupType === 1) {
|
|
||||||
// groupType: 1, 查询 contractService
|
|
||||||
data = await contractService.findWhere("groupId", label.id);
|
|
||||||
// 过滤出 kfSelected 对应的联系人
|
|
||||||
if (kfSelected && kfSelected != 0) {
|
|
||||||
data = data.filter(contact => contact.wechatAccountId === kfSelected);
|
|
||||||
}
|
|
||||||
// console.log(`标签 ${label.groupName} 对应的联系人数据:`, data);
|
|
||||||
} else if (label.groupType === 2) {
|
|
||||||
// groupType: 2, 查询 weChatGroupService
|
|
||||||
data = await weChatGroupService.findWhere("groupId", label.id);
|
|
||||||
if (kfSelected && kfSelected != 0) {
|
|
||||||
data = data.filter(contact => contact.wechatAccountId === kfSelected);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.warn(`未知的 groupType: ${label.groupType}`);
|
|
||||||
data = [];
|
|
||||||
}
|
|
||||||
dataByLabels.push({
|
|
||||||
...label,
|
|
||||||
contacts: data,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return dataByLabels;
|
|
||||||
};
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
import {
|
|
||||||
ContractData,
|
|
||||||
KfUserListData,
|
|
||||||
CkAccount,
|
|
||||||
ContactGroupByLabel,
|
|
||||||
weChatGroup,
|
|
||||||
} from "@/pages/pc/ckbox/data";
|
|
||||||
|
|
||||||
// 权限片段接口
|
|
||||||
export interface PrivilegeFrag {
|
|
||||||
// 根据实际数据结构补充
|
|
||||||
[key: string]: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 租户信息接口
|
|
||||||
export interface CkTenant {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
guid: string;
|
|
||||||
thirdParty: string | null;
|
|
||||||
tenantType: number;
|
|
||||||
deployName: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 触客宝用户信息接口
|
|
||||||
export interface CkUserInfo {
|
|
||||||
account: CkAccount;
|
|
||||||
privilegeFrags: PrivilegeFrag[];
|
|
||||||
tenant: CkTenant;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 状态接口
|
|
||||||
export interface CkChatState {
|
|
||||||
userInfo: CkUserInfo | null;
|
|
||||||
isLoggedIn: boolean;
|
|
||||||
searchKeyword: string;
|
|
||||||
contractList: ContractData[];
|
|
||||||
chatSessions: any[];
|
|
||||||
kfUserList: KfUserListData[];
|
|
||||||
kfSelected: number;
|
|
||||||
getKfSelectedUser: () => KfUserListData | undefined;
|
|
||||||
countLables: ContactGroupByLabel[];
|
|
||||||
newContractList: ContactGroupByLabel[];
|
|
||||||
getContractList: () => ContractData[];
|
|
||||||
getSomeContractList: (kfSelected: number) => ContractData[];
|
|
||||||
getNewContractList: () => ContactGroupByLabel[];
|
|
||||||
setSearchKeyword: (keyword: string) => void;
|
|
||||||
clearSearchKeyword: () => void;
|
|
||||||
asyncKfSelected: (data: number) => void;
|
|
||||||
asyncWeChatGroup: (data: weChatGroup[]) => void;
|
|
||||||
asyncCountLables: (data: ContactGroupByLabel[]) => void;
|
|
||||||
getkfUserList: () => KfUserListData[];
|
|
||||||
asyncKfUserList: (data: KfUserListData[]) => void;
|
|
||||||
getKfUserInfo: (wechatAccountId: number) => KfUserListData | undefined;
|
|
||||||
asyncContractList: (data: ContractData[]) => void;
|
|
||||||
getChatSessions: () => any[];
|
|
||||||
asyncChatSessions: (data: any[]) => void;
|
|
||||||
updateChatSession: (session: ContractData | weChatGroup) => void;
|
|
||||||
deleteCtrlUser: (userId: number) => void;
|
|
||||||
updateCtrlUser: (user: KfUserListData) => void;
|
|
||||||
clearkfUserList: () => void;
|
|
||||||
addChatSession: (session: any) => void;
|
|
||||||
deleteChatSession: (sessionId: number) => void;
|
|
||||||
setUserInfo: (userInfo: CkUserInfo) => void;
|
|
||||||
clearUserInfo: () => void;
|
|
||||||
updateAccount: (account: Partial<CkAccount>) => void;
|
|
||||||
updateTenant: (tenant: Partial<CkTenant>) => void;
|
|
||||||
getAccountId: () => number | null;
|
|
||||||
getTenantId: () => number | null;
|
|
||||||
getAccountName: () => string | null;
|
|
||||||
getTenantName: () => string | null;
|
|
||||||
}
|
|
||||||
@@ -1,527 +0,0 @@
|
|||||||
import { createPersistStore } from "@/store/createPersistStore";
|
|
||||||
import { CkChatState, CkUserInfo, CkTenant } from "./ckchat.data";
|
|
||||||
import {
|
|
||||||
ContractData,
|
|
||||||
weChatGroup,
|
|
||||||
CkAccount,
|
|
||||||
KfUserListData,
|
|
||||||
ContactGroupByLabel,
|
|
||||||
} from "@/pages/pc/ckbox/data";
|
|
||||||
import { weChatGroupService, contractService } from "@/utils/db";
|
|
||||||
import { createContractList } from "@/store/module/ckchat/api";
|
|
||||||
import { useWeChatStore } from "@/store/module/weChat/weChat";
|
|
||||||
// 从weChat store获取clearCurrentContact方法
|
|
||||||
const getClearCurrentContact = () =>
|
|
||||||
useWeChatStore.getState().clearCurrentContact;
|
|
||||||
export const useCkChatStore = createPersistStore<CkChatState>(
|
|
||||||
set => ({
|
|
||||||
userInfo: null,
|
|
||||||
isLoggedIn: false,
|
|
||||||
contractList: [], //联系人列表
|
|
||||||
chatSessions: [], //聊天会话
|
|
||||||
kfUserList: [], //客服列表
|
|
||||||
countLables: [], //标签列表
|
|
||||||
newContractList: [], //联系人分组
|
|
||||||
kfSelected: 0, //选中的客服
|
|
||||||
searchKeyword: "", //搜索关键词
|
|
||||||
//客服列表
|
|
||||||
asyncKfUserList: async data => {
|
|
||||||
set({ kfUserList: data });
|
|
||||||
// await kfUserService.createManyWithServerId(data);
|
|
||||||
},
|
|
||||||
// 获取客服列表
|
|
||||||
getkfUserList: async () => {
|
|
||||||
const state = useCkChatStore.getState();
|
|
||||||
return state.kfUserList;
|
|
||||||
// return await kfUserService.findAll();
|
|
||||||
},
|
|
||||||
// 异步设置标签列表
|
|
||||||
asyncCountLables: async (data: ContactGroupByLabel[]) => {
|
|
||||||
set({ countLables: data });
|
|
||||||
// 清除getNewContractList缓存
|
|
||||||
const state = useCkChatStore.getState();
|
|
||||||
if (
|
|
||||||
state.getNewContractList &&
|
|
||||||
typeof state.getNewContractList === "function"
|
|
||||||
) {
|
|
||||||
// 触发缓存重新计算
|
|
||||||
await state.getNewContractList();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 设置搜索关键词
|
|
||||||
setSearchKeyword: (keyword: string) => {
|
|
||||||
set({ searchKeyword: keyword });
|
|
||||||
},
|
|
||||||
// 清除搜索关键词
|
|
||||||
clearSearchKeyword: () => {
|
|
||||||
set({ searchKeyword: "" });
|
|
||||||
},
|
|
||||||
asyncKfSelected: async (data: number) => {
|
|
||||||
set({ kfSelected: data });
|
|
||||||
// 清除getChatSessions、getContractList和getNewContractList缓存
|
|
||||||
const state = useCkChatStore.getState();
|
|
||||||
if (
|
|
||||||
state.getChatSessions &&
|
|
||||||
typeof state.getChatSessions === "function"
|
|
||||||
) {
|
|
||||||
// 触发缓存重新计算
|
|
||||||
state.getChatSessions();
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
state.getContractList &&
|
|
||||||
typeof state.getContractList === "function"
|
|
||||||
) {
|
|
||||||
// 触发缓存重新计算
|
|
||||||
state.getContractList();
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
state.getNewContractList &&
|
|
||||||
typeof state.getNewContractList === "function"
|
|
||||||
) {
|
|
||||||
// 触发缓存重新计算
|
|
||||||
await state.getNewContractList();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// 获取联系人分组列表 - 使用缓存避免无限循环
|
|
||||||
getNewContractList: (() => {
|
|
||||||
let cachedResult: any = null;
|
|
||||||
let lastKfSelected: number | null = null;
|
|
||||||
let lastCountLablesLength: number = 0;
|
|
||||||
let lastSearchKeyword: string = "";
|
|
||||||
|
|
||||||
return async () => {
|
|
||||||
const state = useCkChatStore.getState();
|
|
||||||
|
|
||||||
// 检查是否需要重新计算缓存
|
|
||||||
const shouldRecalculate =
|
|
||||||
cachedResult === null ||
|
|
||||||
lastKfSelected !== state.kfSelected ||
|
|
||||||
lastCountLablesLength !== (state.countLables?.length || 0) ||
|
|
||||||
lastSearchKeyword !== state.searchKeyword;
|
|
||||||
|
|
||||||
if (shouldRecalculate) {
|
|
||||||
// 使用createContractList构建联系人分组数据
|
|
||||||
let contractList = await createContractList(
|
|
||||||
state.kfSelected,
|
|
||||||
state.countLables,
|
|
||||||
);
|
|
||||||
|
|
||||||
// 根据搜索关键词筛选联系人分组
|
|
||||||
if (state.searchKeyword.trim()) {
|
|
||||||
const keyword = state.searchKeyword.toLowerCase();
|
|
||||||
contractList = contractList
|
|
||||||
.map(group => ({
|
|
||||||
...group,
|
|
||||||
contracts:
|
|
||||||
group.contracts?.filter(item => {
|
|
||||||
const nickname = (item.nickname || "").toLowerCase();
|
|
||||||
const conRemark = (item.conRemark || "").toLowerCase();
|
|
||||||
return (
|
|
||||||
nickname.includes(keyword) || conRemark.includes(keyword)
|
|
||||||
);
|
|
||||||
}) || [],
|
|
||||||
}))
|
|
||||||
.filter(group => group.contracts.length > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
cachedResult = contractList;
|
|
||||||
lastKfSelected = state.kfSelected;
|
|
||||||
lastCountLablesLength = state.countLables?.length || 0;
|
|
||||||
lastSearchKeyword = state.searchKeyword;
|
|
||||||
}
|
|
||||||
|
|
||||||
return cachedResult;
|
|
||||||
};
|
|
||||||
})(),
|
|
||||||
// 搜索好友和群组的新方法 - 从本地数据库查询并返回扁平化的搜索结果
|
|
||||||
searchContactsAndGroups: (() => {
|
|
||||||
let cachedResult: (ContractData | weChatGroup)[] = [];
|
|
||||||
let lastKfSelected: number | null = null;
|
|
||||||
let lastSearchKeyword: string = "";
|
|
||||||
|
|
||||||
return async () => {
|
|
||||||
const state = useCkChatStore.getState();
|
|
||||||
|
|
||||||
// 检查是否需要重新计算缓存
|
|
||||||
const shouldRecalculate =
|
|
||||||
lastKfSelected !== state.kfSelected ||
|
|
||||||
lastSearchKeyword !== state.searchKeyword;
|
|
||||||
|
|
||||||
if (shouldRecalculate) {
|
|
||||||
if (state.searchKeyword.trim()) {
|
|
||||||
const keyword = state.searchKeyword.toLowerCase();
|
|
||||||
|
|
||||||
// 从本地数据库查询联系人数据
|
|
||||||
let allContacts: any[] = await contractService.findAll();
|
|
||||||
|
|
||||||
// 从本地数据库查询群组数据
|
|
||||||
let allGroups: any[] = await weChatGroupService.findAll();
|
|
||||||
|
|
||||||
// 根据选中的客服筛选联系人
|
|
||||||
if (state.kfSelected !== 0) {
|
|
||||||
allContacts = allContacts.filter(
|
|
||||||
item => item.wechatAccountId === state.kfSelected,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 根据选中的客服筛选群组
|
|
||||||
if (state.kfSelected !== 0) {
|
|
||||||
allGroups = allGroups.filter(
|
|
||||||
item => item.wechatAccountId === state.kfSelected,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 搜索匹配的联系人
|
|
||||||
const matchedContacts = allContacts.filter(item => {
|
|
||||||
const nickname = (item.nickname || "").toLowerCase();
|
|
||||||
const conRemark = (item.conRemark || "").toLowerCase();
|
|
||||||
return nickname.includes(keyword) || conRemark.includes(keyword);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 搜索匹配的群组
|
|
||||||
const matchedGroups = allGroups.filter(item => {
|
|
||||||
const nickname = (item.nickname || "").toLowerCase();
|
|
||||||
const conRemark = (item.conRemark || "").toLowerCase();
|
|
||||||
return nickname.includes(keyword) || conRemark.includes(keyword);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 合并搜索结果
|
|
||||||
cachedResult = [...matchedContacts, ...matchedGroups];
|
|
||||||
} else {
|
|
||||||
cachedResult = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
lastKfSelected = state.kfSelected;
|
|
||||||
lastSearchKeyword = state.searchKeyword;
|
|
||||||
}
|
|
||||||
|
|
||||||
return cachedResult;
|
|
||||||
};
|
|
||||||
})(),
|
|
||||||
// 异步设置联系人分组列表
|
|
||||||
asyncNewContractList: async (data: any[]) => {
|
|
||||||
set({ newContractList: data });
|
|
||||||
// 清除getNewContractList缓存
|
|
||||||
const state = useCkChatStore.getState();
|
|
||||||
if (
|
|
||||||
state.getNewContractList &&
|
|
||||||
typeof state.getNewContractList === "function"
|
|
||||||
) {
|
|
||||||
// 触发缓存重新计算
|
|
||||||
await state.getNewContractList();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 异步设置会话列表
|
|
||||||
asyncChatSessions: data => {
|
|
||||||
set({ chatSessions: data });
|
|
||||||
// 清除getChatSessions缓存
|
|
||||||
const state = useCkChatStore.getState();
|
|
||||||
if (
|
|
||||||
state.getChatSessions &&
|
|
||||||
typeof state.getChatSessions === "function"
|
|
||||||
) {
|
|
||||||
// 触发缓存重新计算
|
|
||||||
state.getChatSessions();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 异步设置联系人列表
|
|
||||||
asyncContractList: async (data: ContractData[]) => {
|
|
||||||
set({ contractList: data });
|
|
||||||
await contractService.createManyWithServerId(data);
|
|
||||||
// 清除getContractList缓存
|
|
||||||
const state = useCkChatStore.getState();
|
|
||||||
if (
|
|
||||||
state.getContractList &&
|
|
||||||
typeof state.getContractList === "function"
|
|
||||||
) {
|
|
||||||
// 触发缓存重新计算
|
|
||||||
state.getContractList();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
//获取特定联系人
|
|
||||||
getSomeContractList: (kfSelected: number) => {
|
|
||||||
const state = useCkChatStore.getState();
|
|
||||||
return state.contractList.filter(
|
|
||||||
item => item.wechatAccountId === kfSelected,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
// 获取联系人列表 - 使用缓存避免无限循环
|
|
||||||
getContractList: (() => {
|
|
||||||
let cachedResult: any = null;
|
|
||||||
let lastKfSelected: number | null = null;
|
|
||||||
let lastContractListLength: number = 0;
|
|
||||||
let lastSearchKeyword: string = "";
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
const state = useCkChatStore.getState();
|
|
||||||
|
|
||||||
// 检查是否需要重新计算缓存
|
|
||||||
const shouldRecalculate =
|
|
||||||
cachedResult === null ||
|
|
||||||
lastKfSelected !== state.kfSelected ||
|
|
||||||
lastContractListLength !== state.contractList.length ||
|
|
||||||
lastSearchKeyword !== state.searchKeyword;
|
|
||||||
|
|
||||||
if (shouldRecalculate) {
|
|
||||||
let filteredContracts = state.contractList;
|
|
||||||
|
|
||||||
// 根据客服筛选
|
|
||||||
if (state.kfSelected !== 0) {
|
|
||||||
filteredContracts = filteredContracts.filter(
|
|
||||||
item => item.wechatAccountId === state.kfSelected,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 根据搜索关键词筛选
|
|
||||||
if (state.searchKeyword.trim()) {
|
|
||||||
const keyword = state.searchKeyword.toLowerCase();
|
|
||||||
filteredContracts = filteredContracts.filter(item => {
|
|
||||||
const nickname = (item.nickname || "").toLowerCase();
|
|
||||||
const conRemark = (item.conRemark || "").toLowerCase();
|
|
||||||
return nickname.includes(keyword) || conRemark.includes(keyword);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
cachedResult = filteredContracts;
|
|
||||||
lastKfSelected = state.kfSelected;
|
|
||||||
lastContractListLength = state.contractList.length;
|
|
||||||
lastSearchKeyword = state.searchKeyword;
|
|
||||||
}
|
|
||||||
|
|
||||||
return cachedResult;
|
|
||||||
};
|
|
||||||
})(),
|
|
||||||
//异步设置联系人分组
|
|
||||||
asyncWeChatGroup: async (data: weChatGroup[]) => {
|
|
||||||
await weChatGroupService.createManyWithServerId(data);
|
|
||||||
},
|
|
||||||
//获取选中的客服信息
|
|
||||||
getKfSelectedUser: () => {
|
|
||||||
const state = useCkChatStore.getState();
|
|
||||||
return state.kfUserList.find(item => item.id === state.kfSelected);
|
|
||||||
},
|
|
||||||
getKfUserInfo: (wechatAccountId: number) => {
|
|
||||||
const state = useCkChatStore.getState();
|
|
||||||
return state.kfUserList.find(item => item.id === wechatAccountId);
|
|
||||||
},
|
|
||||||
|
|
||||||
// 删除控制终端用户
|
|
||||||
deleteCtrlUser: (userId: number) => {
|
|
||||||
set(state => ({
|
|
||||||
kfUserList: state.kfUserList.filter(item => item.id !== userId),
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
// 更新控制终端用户
|
|
||||||
updateCtrlUser: (user: KfUserListData) => {
|
|
||||||
set(state => ({
|
|
||||||
kfUserList: state.kfUserList.map(item =>
|
|
||||||
item.id === user.id ? user : item,
|
|
||||||
),
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
// 清空控制终端用户列表
|
|
||||||
clearkfUserList: () => {
|
|
||||||
set({ kfUserList: [] });
|
|
||||||
},
|
|
||||||
// 获取聊天会话 - 使用缓存避免无限循环
|
|
||||||
getChatSessions: (() => {
|
|
||||||
let cachedResult: any = null;
|
|
||||||
let lastKfSelected: number | null = null;
|
|
||||||
let lastChatSessionsLength: number = 0;
|
|
||||||
let lastSearchKeyword: string = "";
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
const state = useCkChatStore.getState();
|
|
||||||
|
|
||||||
// 检查是否需要重新计算缓存
|
|
||||||
const shouldRecalculate =
|
|
||||||
cachedResult === null ||
|
|
||||||
lastKfSelected !== state.kfSelected ||
|
|
||||||
lastChatSessionsLength !== state.chatSessions.length ||
|
|
||||||
lastSearchKeyword !== state.searchKeyword;
|
|
||||||
|
|
||||||
if (shouldRecalculate) {
|
|
||||||
let filteredSessions = state.chatSessions;
|
|
||||||
|
|
||||||
// 根据客服筛选
|
|
||||||
if (state.kfSelected !== 0) {
|
|
||||||
filteredSessions = filteredSessions.filter(
|
|
||||||
item => item.wechatAccountId === state.kfSelected,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 根据搜索关键词筛选
|
|
||||||
if (state.searchKeyword.trim()) {
|
|
||||||
const keyword = state.searchKeyword.toLowerCase();
|
|
||||||
filteredSessions = filteredSessions.filter(item => {
|
|
||||||
const nickname = (item.nickname || "").toLowerCase();
|
|
||||||
const conRemark = (item.conRemark || "").toLowerCase();
|
|
||||||
return nickname.includes(keyword) || conRemark.includes(keyword);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
cachedResult = filteredSessions;
|
|
||||||
lastKfSelected = state.kfSelected;
|
|
||||||
lastChatSessionsLength = state.chatSessions.length;
|
|
||||||
lastSearchKeyword = state.searchKeyword;
|
|
||||||
}
|
|
||||||
|
|
||||||
return cachedResult;
|
|
||||||
};
|
|
||||||
})(),
|
|
||||||
// 添加聊天会话
|
|
||||||
addChatSession: (session: ContractData | weChatGroup) => {
|
|
||||||
set(state => {
|
|
||||||
// 检查是否已存在相同id的会话
|
|
||||||
const exists = state.chatSessions.some(item => item.id === session.id);
|
|
||||||
// 如果已存在则不添加,否则添加到列表中
|
|
||||||
return {
|
|
||||||
chatSessions: exists
|
|
||||||
? state.chatSessions
|
|
||||||
: [...state.chatSessions, session as ContractData | weChatGroup],
|
|
||||||
};
|
|
||||||
});
|
|
||||||
},
|
|
||||||
// 更新聊天会话
|
|
||||||
updateChatSession: (session: ContractData | weChatGroup) => {
|
|
||||||
set(state => ({
|
|
||||||
chatSessions: state.chatSessions.map(item =>
|
|
||||||
item.id === session.id ? { ...item, ...session } : item,
|
|
||||||
),
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
// 删除聊天会话
|
|
||||||
deleteChatSession: (sessionId: number) => {
|
|
||||||
set(state => ({
|
|
||||||
chatSessions: state.chatSessions.filter(item => item.id !== sessionId),
|
|
||||||
}));
|
|
||||||
//当前选中的客户清空
|
|
||||||
getClearCurrentContact();
|
|
||||||
},
|
|
||||||
// 设置用户信息
|
|
||||||
setUserInfo: (userInfo: CkUserInfo) => {
|
|
||||||
set({ userInfo, isLoggedIn: true });
|
|
||||||
},
|
|
||||||
|
|
||||||
// 清除用户信息
|
|
||||||
clearUserInfo: () => {
|
|
||||||
set({ userInfo: null, isLoggedIn: false });
|
|
||||||
},
|
|
||||||
|
|
||||||
// 更新账户信息
|
|
||||||
updateAccount: (account: Partial<CkAccount>) => {
|
|
||||||
set(state => ({
|
|
||||||
userInfo: state.userInfo
|
|
||||||
? {
|
|
||||||
...state.userInfo,
|
|
||||||
account: { ...state.userInfo.account, ...account },
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
|
|
||||||
// 更新租户信息
|
|
||||||
updateTenant: (tenant: Partial<CkTenant>) => {
|
|
||||||
set(state => ({
|
|
||||||
userInfo: state.userInfo
|
|
||||||
? {
|
|
||||||
...state.userInfo,
|
|
||||||
tenant: { ...state.userInfo.tenant, ...tenant },
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
|
|
||||||
// 获取账户ID
|
|
||||||
getAccountId: () => {
|
|
||||||
const state = useCkChatStore.getState();
|
|
||||||
return Number(state.userInfo?.account?.id) || null;
|
|
||||||
},
|
|
||||||
|
|
||||||
// 获取租户ID
|
|
||||||
getTenantId: () => {
|
|
||||||
const state = useCkChatStore.getState();
|
|
||||||
return state.userInfo?.tenant?.id || null;
|
|
||||||
},
|
|
||||||
|
|
||||||
// 获取账户名称
|
|
||||||
getAccountName: () => {
|
|
||||||
const state = useCkChatStore.getState();
|
|
||||||
return (
|
|
||||||
state.userInfo?.account?.realName ||
|
|
||||||
state.userInfo?.account?.userName ||
|
|
||||||
null
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
// 获取租户名称
|
|
||||||
getTenantName: () => {
|
|
||||||
const state = useCkChatStore.getState();
|
|
||||||
return state.userInfo?.tenant?.name || null;
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
name: "ckchat-store",
|
|
||||||
partialize: state => ({
|
|
||||||
userInfo: state.userInfo,
|
|
||||||
isLoggedIn: state.isLoggedIn,
|
|
||||||
kfUserList: state.kfUserList,
|
|
||||||
}),
|
|
||||||
onRehydrateStorage: () => state => {
|
|
||||||
// console.log("CkChat store hydrated:", state);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// 导出便捷的获取方法
|
|
||||||
export const getCkAccountId = () => useCkChatStore.getState().getAccountId();
|
|
||||||
export const getCkTenantId = () => useCkChatStore.getState().getTenantId();
|
|
||||||
export const getCkAccountName = () =>
|
|
||||||
useCkChatStore.getState().getAccountName();
|
|
||||||
export const getCkTenantName = () => useCkChatStore.getState().getTenantName();
|
|
||||||
export const getChatSessions = () =>
|
|
||||||
useCkChatStore.getState().getChatSessions();
|
|
||||||
export const addChatSession = (session: ContractData | weChatGroup) =>
|
|
||||||
useCkChatStore.getState().addChatSession(session);
|
|
||||||
export const updateChatSession = (session: ContractData | weChatGroup) =>
|
|
||||||
useCkChatStore.getState().updateChatSession(session);
|
|
||||||
export const deleteChatSession = (sessionId: string) =>
|
|
||||||
useCkChatStore.getState().deleteChatSession(sessionId);
|
|
||||||
export const getkfUserList = () => useCkChatStore.getState().kfUserList;
|
|
||||||
export const addCtrlUser = (user: KfUserListData) =>
|
|
||||||
useCkChatStore.getState().addCtrlUser(user);
|
|
||||||
export const deleteCtrlUser = (userId: number) =>
|
|
||||||
useCkChatStore.getState().deleteCtrlUser(userId);
|
|
||||||
export const updateCtrlUser = (user: KfUserListData) =>
|
|
||||||
useCkChatStore.getState().updateCtrlUser(user);
|
|
||||||
export const asyncKfUserList = (data: KfUserListData[]) =>
|
|
||||||
useCkChatStore.getState().asyncKfUserList(data);
|
|
||||||
export const asyncContractList = (data: ContractData[]) =>
|
|
||||||
useCkChatStore.getState().asyncContractList(data);
|
|
||||||
export const asyncChatSessions = (data: ContractData[]) =>
|
|
||||||
useCkChatStore.getState().asyncChatSessions(data);
|
|
||||||
export const asyncKfSelected = (data: number) =>
|
|
||||||
useCkChatStore.getState().asyncKfSelected(data);
|
|
||||||
export const asyncWeChatGroup = (data: weChatGroup[]) =>
|
|
||||||
useCkChatStore.getState().asyncWeChatGroup(data);
|
|
||||||
export const getKfSelectedUser = () =>
|
|
||||||
useCkChatStore.getState().getKfSelectedUser();
|
|
||||||
export const getKfUserInfo = (wechatAccountId: number) =>
|
|
||||||
useCkChatStore.getState().getKfUserInfo(wechatAccountId);
|
|
||||||
export const getContractList = () =>
|
|
||||||
useCkChatStore.getState().getContractList();
|
|
||||||
export const getNewContractList = () =>
|
|
||||||
useCkChatStore.getState().getNewContractList();
|
|
||||||
export const asyncCountLables = (data: ContactGroupByLabel[]) =>
|
|
||||||
useCkChatStore.getState().asyncCountLables(data);
|
|
||||||
export const asyncNewContractList = (data: any[]) =>
|
|
||||||
useCkChatStore.getState().asyncNewContractList(data);
|
|
||||||
export const getCountLables = () => useCkChatStore.getState().countLables;
|
|
||||||
export const setSearchKeyword = (keyword: string) =>
|
|
||||||
useCkChatStore.getState().setSearchKeyword(keyword);
|
|
||||||
export const clearSearchKeyword = () =>
|
|
||||||
useCkChatStore.getState().clearSearchKeyword();
|
|
||||||
export const searchContactsAndGroups = () =>
|
|
||||||
useCkChatStore.getState().searchContactsAndGroups();
|
|
||||||
useCkChatStore.getState().getKfSelectedUser();
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
import { ChatRecord, ContractData, weChatGroup } from "@/pages/pc/ckbox/data";
|
|
||||||
// 微信聊天相关的类型定义
|
|
||||||
export interface WeChatState {
|
|
||||||
// 当前选中的联系人/群组
|
|
||||||
currentContract: ContractData | weChatGroup | null;
|
|
||||||
|
|
||||||
// 当前聊天用户的消息列表(只存储当前聊天用户的消息)
|
|
||||||
currentMessages: ChatRecord[];
|
|
||||||
// 清空当前联系人
|
|
||||||
clearCurrentContact: () => void;
|
|
||||||
// 消息加载状态
|
|
||||||
messagesLoading: boolean;
|
|
||||||
isLoadingData: boolean;
|
|
||||||
currentGroupMembers: any[];
|
|
||||||
|
|
||||||
// Actions
|
|
||||||
setCurrentContact: (
|
|
||||||
contract: ContractData | weChatGroup,
|
|
||||||
isExist?: boolean,
|
|
||||||
) => void;
|
|
||||||
loadChatMessages: (Init: boolean, To?: number) => Promise<void>;
|
|
||||||
SearchMessage: (params: {
|
|
||||||
From: number;
|
|
||||||
To: number;
|
|
||||||
keyword: string;
|
|
||||||
Count?: number;
|
|
||||||
}) => Promise<void>;
|
|
||||||
// 视频消息处理方法
|
|
||||||
setVideoLoading: (messageId: number, isLoading: boolean) => void;
|
|
||||||
setVideoUrl: (messageId: number, videoUrl: string) => void;
|
|
||||||
addMessage: (message: ChatRecord) => void;
|
|
||||||
receivedMsg: (message: ChatRecord) => void;
|
|
||||||
}
|
|
||||||
@@ -1,287 +0,0 @@
|
|||||||
import { create } from "zustand";
|
|
||||||
import { persist } from "zustand/middleware";
|
|
||||||
import {
|
|
||||||
getChatMessages,
|
|
||||||
getChatroomMessages,
|
|
||||||
getGroupMembers,
|
|
||||||
} from "@/pages/pc/ckbox/api";
|
|
||||||
import { WeChatState } from "./weChat.data";
|
|
||||||
import { clearUnreadCount, updateConfig } from "@/pages/pc/ckbox/api";
|
|
||||||
import { ContractData, weChatGroup } from "@/pages/pc/ckbox/data";
|
|
||||||
import { weChatGroupService, contractService } from "@/utils/db";
|
|
||||||
import {
|
|
||||||
addChatSession,
|
|
||||||
updateChatSession,
|
|
||||||
useCkChatStore,
|
|
||||||
} from "@/store/module/ckchat/ckchat";
|
|
||||||
|
|
||||||
export const useWeChatStore = create<WeChatState>()(
|
|
||||||
persist(
|
|
||||||
(set, get) => ({
|
|
||||||
// 初始状态
|
|
||||||
currentContract: null,
|
|
||||||
currentMessages: [],
|
|
||||||
messagesLoading: false,
|
|
||||||
isLoadingData: false,
|
|
||||||
currentGroupMembers: [],
|
|
||||||
//清空当前联系人
|
|
||||||
clearCurrentContact: () => {
|
|
||||||
set({ currentContract: null, currentMessages: [] });
|
|
||||||
},
|
|
||||||
// Actions
|
|
||||||
setCurrentContact: (
|
|
||||||
contract: ContractData | weChatGroup,
|
|
||||||
isExist?: boolean,
|
|
||||||
) => {
|
|
||||||
const state = useWeChatStore.getState();
|
|
||||||
// 切换联系人时清空当前消息,等待重新加载
|
|
||||||
set({ currentMessages: [] });
|
|
||||||
clearUnreadCount([contract.id]).then(() => {
|
|
||||||
if (isExist) {
|
|
||||||
updateChatSession({ ...contract, unreadCount: 0 });
|
|
||||||
} else {
|
|
||||||
addChatSession(contract);
|
|
||||||
}
|
|
||||||
set({ currentContract: contract });
|
|
||||||
updateConfig({
|
|
||||||
id: contract.id,
|
|
||||||
config: { chat: true },
|
|
||||||
});
|
|
||||||
state.loadChatMessages(true, 4704624000000);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
loadChatMessages: async (Init: boolean, To?: number) => {
|
|
||||||
const state = useWeChatStore.getState();
|
|
||||||
const contact = state.currentContract;
|
|
||||||
set({ messagesLoading: true });
|
|
||||||
set({ isLoadingData: Init });
|
|
||||||
try {
|
|
||||||
const params: any = {
|
|
||||||
wechatAccountId: contact.wechatAccountId,
|
|
||||||
From: 1,
|
|
||||||
To: To || +new Date(),
|
|
||||||
Count: 5,
|
|
||||||
olderData: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
if ("chatroomId" in contact && contact.chatroomId) {
|
|
||||||
params.wechatChatroomId = contact.id;
|
|
||||||
const messages = await getChatroomMessages(params);
|
|
||||||
const currentGroupMembers = await getGroupMembers({
|
|
||||||
id: contact.id,
|
|
||||||
});
|
|
||||||
if (Init) {
|
|
||||||
set({ currentMessages: messages || [], currentGroupMembers });
|
|
||||||
} else {
|
|
||||||
set({
|
|
||||||
currentMessages: [
|
|
||||||
...(messages || []),
|
|
||||||
...state.currentMessages,
|
|
||||||
],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
params.wechatFriendId = contact.id;
|
|
||||||
const messages = await getChatMessages(params);
|
|
||||||
if (Init) {
|
|
||||||
set({ currentMessages: messages || [] });
|
|
||||||
} else {
|
|
||||||
set({
|
|
||||||
currentMessages: [
|
|
||||||
...(messages || []),
|
|
||||||
...state.currentMessages,
|
|
||||||
],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
set({ messagesLoading: false });
|
|
||||||
} catch (error) {
|
|
||||||
console.error("获取聊天消息失败:", error);
|
|
||||||
} finally {
|
|
||||||
set({ messagesLoading: false });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
SearchMessage: async ({
|
|
||||||
From = 1,
|
|
||||||
To = 4704624000000,
|
|
||||||
keyword = "",
|
|
||||||
Count = 20,
|
|
||||||
}: {
|
|
||||||
From: number;
|
|
||||||
To: number;
|
|
||||||
keyword: string;
|
|
||||||
Count?: number;
|
|
||||||
}) => {
|
|
||||||
const state = useWeChatStore.getState();
|
|
||||||
const contact = state.currentContract;
|
|
||||||
set({ messagesLoading: true });
|
|
||||||
|
|
||||||
try {
|
|
||||||
const params: any = {
|
|
||||||
wechatAccountId: contact.wechatAccountId,
|
|
||||||
From,
|
|
||||||
To,
|
|
||||||
keyword,
|
|
||||||
Count,
|
|
||||||
olderData: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
if ("chatroomId" in contact && contact.chatroomId) {
|
|
||||||
params.wechatChatroomId = contact.id;
|
|
||||||
const messages = await getChatroomMessages(params);
|
|
||||||
const currentGroupMembers = await getGroupMembers({
|
|
||||||
id: contact.id,
|
|
||||||
});
|
|
||||||
set({ currentMessages: messages || [], currentGroupMembers });
|
|
||||||
} else {
|
|
||||||
params.wechatFriendId = contact.id;
|
|
||||||
const messages = await getChatMessages(params);
|
|
||||||
set({ currentMessages: messages || [] });
|
|
||||||
}
|
|
||||||
set({ messagesLoading: false });
|
|
||||||
} catch (error) {
|
|
||||||
console.error("获取聊天消息失败:", error);
|
|
||||||
} finally {
|
|
||||||
set({ messagesLoading: false });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
setMessageLoading: loading => {
|
|
||||||
set({ messagesLoading: Boolean(loading) });
|
|
||||||
},
|
|
||||||
|
|
||||||
addMessage: message => {
|
|
||||||
set(state => ({
|
|
||||||
currentMessages: [...state.currentMessages, message],
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
|
|
||||||
receivedMsg: async message => {
|
|
||||||
const currentContract = useWeChatStore.getState().currentContract;
|
|
||||||
//判断群还是好友
|
|
||||||
const getMessageId =
|
|
||||||
message?.wechatChatroomId || message.wechatFriendId;
|
|
||||||
const isWechatGroup = message?.wechatChatroomId;
|
|
||||||
//当前选中聊天的群或好友
|
|
||||||
if (currentContract && currentContract.id == getMessageId) {
|
|
||||||
set(state => ({
|
|
||||||
currentMessages: [...state.currentMessages, message],
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
//更新消息列表unread数值,根据接收的++1 这样
|
|
||||||
const chatSessions = useCkChatStore.getState().chatSessions;
|
|
||||||
const session = chatSessions.find(item => item.id == getMessageId);
|
|
||||||
if (session) {
|
|
||||||
session.unreadCount = Number(session.unreadCount) + 1;
|
|
||||||
updateChatSession(session);
|
|
||||||
} else {
|
|
||||||
if (isWechatGroup) {
|
|
||||||
const [group] = await weChatGroupService.findByIds(getMessageId);
|
|
||||||
if (group) {
|
|
||||||
addChatSession({
|
|
||||||
...group,
|
|
||||||
unreadCount: 1,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const [user] = await contractService.findByIds(getMessageId);
|
|
||||||
addChatSession({
|
|
||||||
...user,
|
|
||||||
unreadCount: 1,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
updateMessage: (messageId, updates) => {
|
|
||||||
set(state => ({
|
|
||||||
currentMessages: state.currentMessages.map(msg =>
|
|
||||||
msg.id === messageId ? { ...msg, ...updates } : msg,
|
|
||||||
),
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
|
|
||||||
// 便捷选择器
|
|
||||||
getCurrentContact: () => get().currentContract,
|
|
||||||
getCurrentMessages: () => get().currentMessages,
|
|
||||||
getMessagesLoading: () => get().messagesLoading,
|
|
||||||
|
|
||||||
// 视频消息处理方法
|
|
||||||
setVideoLoading: (messageId: number, isLoading: boolean) => {
|
|
||||||
set(state => ({
|
|
||||||
currentMessages: state.currentMessages.map(msg => {
|
|
||||||
if (msg.id === messageId) {
|
|
||||||
try {
|
|
||||||
const content = JSON.parse(msg.content);
|
|
||||||
// 更新加载状态
|
|
||||||
const updatedContent = { ...content, isLoading };
|
|
||||||
return {
|
|
||||||
...msg,
|
|
||||||
content: JSON.stringify(updatedContent),
|
|
||||||
};
|
|
||||||
} catch (e) {
|
|
||||||
console.error("更新视频加载状态失败:", e);
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return msg;
|
|
||||||
}),
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
|
|
||||||
setVideoUrl: (messageId: number, videoUrl: string) => {
|
|
||||||
set(state => ({
|
|
||||||
currentMessages: state.currentMessages.map(msg => {
|
|
||||||
if (msg.id === messageId) {
|
|
||||||
try {
|
|
||||||
const content = JSON.parse(msg.content);
|
|
||||||
// 检查视频是否已经下载完毕,避免重复更新
|
|
||||||
if (content.videoUrl && content.videoUrl === videoUrl) {
|
|
||||||
console.log("视频已下载,跳过重复更新:", messageId);
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置视频URL并清除加载状态
|
|
||||||
const updatedContent = {
|
|
||||||
...content,
|
|
||||||
videoUrl,
|
|
||||||
isLoading: false,
|
|
||||||
};
|
|
||||||
return {
|
|
||||||
...msg,
|
|
||||||
content: JSON.stringify(updatedContent),
|
|
||||||
};
|
|
||||||
} catch (e) {
|
|
||||||
console.error("更新视频URL失败:", e);
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return msg;
|
|
||||||
}),
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
clearAllData: () => {
|
|
||||||
set({
|
|
||||||
currentContract: null,
|
|
||||||
currentMessages: [],
|
|
||||||
messagesLoading: false,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
name: "wechat-storage",
|
|
||||||
partialize: state => ({
|
|
||||||
// currentContract 不做持久化,登录和页面刷新时直接清空
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
// 导出便捷的选择器函数
|
|
||||||
export const useCurrentContact = () =>
|
|
||||||
useWeChatStore(state => state.currentContract);
|
|
||||||
export const useCurrentMessages = () =>
|
|
||||||
useWeChatStore(state => state.currentMessages);
|
|
||||||
export const useMessagesLoading = () =>
|
|
||||||
useWeChatStore(state => state.messagesLoading);
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
import { ChatRecord } from "@/pages/pc/ckbox/data";
|
|
||||||
export interface Messages {
|
|
||||||
friendMessage?: ChatRecord | null;
|
|
||||||
chatroomMessage?: ChatRecord | null;
|
|
||||||
seq: number;
|
|
||||||
cmdType: string;
|
|
||||||
}
|
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
//消息管理器
|
|
||||||
import { deepCopy } from "@/utils/common";
|
|
||||||
import { WebSocketMessage } from "./websocket";
|
|
||||||
import { getkfUserList, asyncKfUserList } from "@/store/module/ckchat/ckchat";
|
|
||||||
import { Messages } from "./msg.data";
|
|
||||||
|
|
||||||
import { useWeChatStore } from "@/store/module/weChat/weChat";
|
|
||||||
// 消息处理器类型定义
|
|
||||||
type MessageHandler = (message: WebSocketMessage) => void;
|
|
||||||
const setVideoUrl = useWeChatStore.getState().setVideoUrl;
|
|
||||||
const addMessage = useWeChatStore.getState().addMessage;
|
|
||||||
const receivedMsg = useWeChatStore.getState().receivedMsg;
|
|
||||||
|
|
||||||
// 消息处理器映射
|
|
||||||
const messageHandlers: Record<string, MessageHandler> = {
|
|
||||||
// 微信账号存活状态响应
|
|
||||||
CmdRequestWechatAccountsAliveStatusResp: message => {
|
|
||||||
// console.log("微信账号存活状态响应", message);
|
|
||||||
// 获取客服列表
|
|
||||||
const kfUserList = deepCopy(getkfUserList());
|
|
||||||
const wechatAccountsAliveStatus = message.wechatAccountsAliveStatus || {};
|
|
||||||
// 遍历客服列表,更新存活状态
|
|
||||||
kfUserList.forEach(kfUser => {
|
|
||||||
kfUser.isOnline = wechatAccountsAliveStatus[kfUser.id];
|
|
||||||
});
|
|
||||||
asyncKfUserList(kfUserList);
|
|
||||||
},
|
|
||||||
// 发送消息响应
|
|
||||||
CmdSendMessageResp: message => {
|
|
||||||
console.log("发送消息响应", message);
|
|
||||||
addMessage(message.friendMessage || message.chatroomMessage);
|
|
||||||
// 在这里添加具体的处理逻辑
|
|
||||||
},
|
|
||||||
CmdSendMessageResult: message => {
|
|
||||||
console.log("发送消息结果", message);
|
|
||||||
// 在这里添加具体的处理逻辑
|
|
||||||
},
|
|
||||||
// 接收消息响应
|
|
||||||
CmdReceiveMessageResp: message => {
|
|
||||||
console.log("接收消息响应", message);
|
|
||||||
addMessage(message.friendMessage || message.chatroomMessage);
|
|
||||||
// 在这里添加具体的处理逻辑
|
|
||||||
},
|
|
||||||
//收到消息
|
|
||||||
CmdNewMessage: (message: Messages) => {
|
|
||||||
// 在这里添加具体的处理逻辑
|
|
||||||
receivedMsg(message.friendMessage || message.chatroomMessage);
|
|
||||||
},
|
|
||||||
CmdFriendInfoChanged: message => {
|
|
||||||
// console.log("好友信息变更", message);
|
|
||||||
// 在这里添加具体的处理逻辑
|
|
||||||
},
|
|
||||||
|
|
||||||
// 登录响应
|
|
||||||
CmdSignInResp: message => {
|
|
||||||
console.log("登录响应", message);
|
|
||||||
// 在这里添加具体的处理逻辑
|
|
||||||
},
|
|
||||||
|
|
||||||
// 通知消息
|
|
||||||
CmdNotify: message => {
|
|
||||||
console.log("通知消息", message);
|
|
||||||
// 在这里添加具体的处理逻辑
|
|
||||||
if (message.notify == "Kicked out") {
|
|
||||||
// 被踢出时直接跳转到登录页面
|
|
||||||
window.location.href = "/login";
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
CmdDownloadVideoResult: message => {
|
|
||||||
// 在这里添加具体的处理逻辑
|
|
||||||
setVideoUrl(message.friendMessageId, message.url);
|
|
||||||
},
|
|
||||||
|
|
||||||
// 可以继续添加更多处理器...
|
|
||||||
};
|
|
||||||
|
|
||||||
// 默认处理器
|
|
||||||
const defaultHandler: MessageHandler = message => {
|
|
||||||
console.log("未知消息类型", message.cmdType, message);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 注册新的消息处理器
|
|
||||||
export const registerMessageHandler = (
|
|
||||||
cmdType: string,
|
|
||||||
handler: MessageHandler,
|
|
||||||
) => {
|
|
||||||
messageHandlers[cmdType] = handler;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 移除消息处理器
|
|
||||||
export const unregisterMessageHandler = (cmdType: string) => {
|
|
||||||
delete messageHandlers[cmdType];
|
|
||||||
};
|
|
||||||
|
|
||||||
// 获取所有已注册的消息类型
|
|
||||||
export const getRegisteredMessageTypes = (): string[] => {
|
|
||||||
return Object.keys(messageHandlers);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 消息管理核心函数
|
|
||||||
export const msgManageCore = (message: WebSocketMessage) => {
|
|
||||||
const cmdType = message.cmdType;
|
|
||||||
if (!cmdType) {
|
|
||||||
console.warn("消息缺少cmdType字段", message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取对应的处理器,如果没有则使用默认处理器
|
|
||||||
const handler = messageHandlers[cmdType] || defaultHandler;
|
|
||||||
|
|
||||||
try {
|
|
||||||
handler(message);
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`处理消息类型 ${cmdType} 时发生错误:`, error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,586 +0,0 @@
|
|||||||
import { createPersistStore } from "@/store/createPersistStore";
|
|
||||||
import { Toast } from "antd-mobile";
|
|
||||||
import { useUserStore } from "../user";
|
|
||||||
import { useCkChatStore } from "@/store/module/ckchat/ckchat";
|
|
||||||
const { getAccountId } = useCkChatStore.getState();
|
|
||||||
import { msgManageCore } from "./msgManage";
|
|
||||||
// WebSocket消息类型
|
|
||||||
export interface WebSocketMessage {
|
|
||||||
cmdType?: string;
|
|
||||||
seq?: number;
|
|
||||||
wechatAccountIds?: string[];
|
|
||||||
content?: any;
|
|
||||||
[key: string]: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
// WebSocket连接状态
|
|
||||||
export enum WebSocketStatus {
|
|
||||||
DISCONNECTED = "disconnected",
|
|
||||||
CONNECTING = "connecting",
|
|
||||||
CONNECTED = "connected",
|
|
||||||
RECONNECTING = "reconnecting",
|
|
||||||
ERROR = "error",
|
|
||||||
}
|
|
||||||
|
|
||||||
// WebSocket配置
|
|
||||||
interface WebSocketConfig {
|
|
||||||
url: string;
|
|
||||||
client: string;
|
|
||||||
accountId: number;
|
|
||||||
accessToken: string;
|
|
||||||
autoReconnect: boolean;
|
|
||||||
cmdType: string;
|
|
||||||
seq: number;
|
|
||||||
reconnectInterval: number;
|
|
||||||
maxReconnectAttempts: number;
|
|
||||||
[key: string]: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface WebSocketState {
|
|
||||||
// 连接状态
|
|
||||||
status: WebSocketStatus;
|
|
||||||
ws: WebSocket | null;
|
|
||||||
|
|
||||||
// 配置信息
|
|
||||||
config: WebSocketConfig | null;
|
|
||||||
|
|
||||||
// 消息相关
|
|
||||||
messages: WebSocketMessage[];
|
|
||||||
unreadCount: number;
|
|
||||||
|
|
||||||
// 重连相关
|
|
||||||
reconnectAttempts: number;
|
|
||||||
reconnectTimer: NodeJS.Timeout | null;
|
|
||||||
aliveStatusTimer: NodeJS.Timeout | null; // 客服用户状态查询定时器
|
|
||||||
|
|
||||||
// 方法
|
|
||||||
connect: (config: Partial<WebSocketConfig>) => void;
|
|
||||||
disconnect: () => void;
|
|
||||||
sendMessage: (message: Omit<WebSocketMessage, "id" | "timestamp">) => void;
|
|
||||||
sendCommand: (cmdType: string, data?: any) => void;
|
|
||||||
clearMessages: () => void;
|
|
||||||
markAsRead: () => void;
|
|
||||||
reconnect: () => void;
|
|
||||||
clearConnectionState: () => void; // 清空连接状态
|
|
||||||
|
|
||||||
// 内部方法
|
|
||||||
_handleOpen: () => void;
|
|
||||||
_handleMessage: (event: MessageEvent) => void;
|
|
||||||
_handleClose: (event: CloseEvent) => void;
|
|
||||||
_handleError: (event: Event) => void;
|
|
||||||
_startReconnectTimer: () => void;
|
|
||||||
_stopReconnectTimer: () => void;
|
|
||||||
_startAliveStatusTimer: () => void; // 启动客服状态查询定时器
|
|
||||||
_stopAliveStatusTimer: () => void; // 停止客服状态查询定时器
|
|
||||||
}
|
|
||||||
|
|
||||||
// 默认配置
|
|
||||||
const DEFAULT_CONFIG: WebSocketConfig = {
|
|
||||||
url: "wss://kf.quwanzhi.com:9993",
|
|
||||||
client: "kefu-client",
|
|
||||||
accountId: 0,
|
|
||||||
accessToken: "",
|
|
||||||
autoReconnect: true,
|
|
||||||
cmdType: "", // 添加默认的命令类型
|
|
||||||
seq: +new Date(), // 添加默认的序列号
|
|
||||||
reconnectInterval: 3000,
|
|
||||||
maxReconnectAttempts: 5,
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useWebSocketStore = createPersistStore<WebSocketState>(
|
|
||||||
(set, get) => ({
|
|
||||||
status: WebSocketStatus.DISCONNECTED,
|
|
||||||
ws: null,
|
|
||||||
config: null,
|
|
||||||
messages: [],
|
|
||||||
unreadCount: 0,
|
|
||||||
reconnectAttempts: 0,
|
|
||||||
reconnectTimer: null,
|
|
||||||
aliveStatusTimer: null,
|
|
||||||
|
|
||||||
// 连接WebSocket
|
|
||||||
connect: (config: Partial<WebSocketConfig>) => {
|
|
||||||
const currentState = get();
|
|
||||||
|
|
||||||
// 检查当前连接状态,避免重复连接
|
|
||||||
if (
|
|
||||||
currentState.status === WebSocketStatus.CONNECTED ||
|
|
||||||
currentState.status === WebSocketStatus.CONNECTING
|
|
||||||
) {
|
|
||||||
// console.log("WebSocket已连接或正在连接,跳过重复连接", {
|
|
||||||
// currentStatus: currentState.status,
|
|
||||||
// hasWebSocket: !!currentState.ws,
|
|
||||||
// });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果已经有WebSocket实例,先断开
|
|
||||||
if (currentState.ws) {
|
|
||||||
// console.log("断开现有WebSocket连接");
|
|
||||||
currentState.disconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 合并配置
|
|
||||||
const fullConfig: WebSocketConfig = {
|
|
||||||
...DEFAULT_CONFIG,
|
|
||||||
...config,
|
|
||||||
};
|
|
||||||
|
|
||||||
// 获取用户信息
|
|
||||||
const { token2 } = useUserStore.getState();
|
|
||||||
|
|
||||||
if (!token2) {
|
|
||||||
Toast.show({ content: "未找到有效的访问令牌", position: "top" });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 构建WebSocket URL
|
|
||||||
const params = new URLSearchParams({
|
|
||||||
client: fullConfig.client.toString(),
|
|
||||||
accountId: getAccountId().toString(),
|
|
||||||
accessToken: token2,
|
|
||||||
t: Date.now().toString(),
|
|
||||||
});
|
|
||||||
|
|
||||||
const wsUrl = fullConfig.url + "?" + params;
|
|
||||||
|
|
||||||
// 检查URL是否为localhost,如果是则不连接
|
|
||||||
if (wsUrl.includes("localhost") || wsUrl.includes("127.0.0.1")) {
|
|
||||||
// console.error("WebSocket连接被拦截:不允许连接到本地地址", wsUrl);
|
|
||||||
Toast.show({
|
|
||||||
content: "WebSocket连接被拦截:不允许连接到本地地址",
|
|
||||||
position: "top",
|
|
||||||
});
|
|
||||||
set({ status: WebSocketStatus.ERROR });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
set({
|
|
||||||
status: WebSocketStatus.CONNECTING,
|
|
||||||
config: fullConfig,
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
const ws = new WebSocket(wsUrl);
|
|
||||||
|
|
||||||
// 绑定事件处理器
|
|
||||||
ws.onopen = () => get()._handleOpen();
|
|
||||||
ws.onmessage = event => get()._handleMessage(event);
|
|
||||||
ws.onclose = event => get()._handleClose(event);
|
|
||||||
ws.onerror = event => get()._handleError(event);
|
|
||||||
|
|
||||||
set({ ws });
|
|
||||||
|
|
||||||
// console.log("WebSocket连接创建成功", wsUrl);
|
|
||||||
} catch (error) {
|
|
||||||
// console.error("WebSocket连接失败:", error);
|
|
||||||
set({ status: WebSocketStatus.ERROR });
|
|
||||||
Toast.show({ content: "WebSocket连接失败", position: "top" });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// 断开连接
|
|
||||||
disconnect: () => {
|
|
||||||
const currentState = get();
|
|
||||||
|
|
||||||
if (currentState.ws) {
|
|
||||||
currentState.ws.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
currentState._stopReconnectTimer();
|
|
||||||
currentState._stopAliveStatusTimer();
|
|
||||||
|
|
||||||
set({
|
|
||||||
status: WebSocketStatus.DISCONNECTED,
|
|
||||||
ws: null,
|
|
||||||
reconnectAttempts: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
// console.log("WebSocket连接已断开");
|
|
||||||
},
|
|
||||||
|
|
||||||
// 发送消息
|
|
||||||
sendMessage: message => {
|
|
||||||
const currentState = get();
|
|
||||||
|
|
||||||
if (
|
|
||||||
currentState.status !== WebSocketStatus.CONNECTED ||
|
|
||||||
!currentState.ws
|
|
||||||
) {
|
|
||||||
Toast.show({ content: "WebSocket未连接", position: "top" });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const fullMessage: WebSocketMessage = {
|
|
||||||
...message,
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
currentState.ws.send(JSON.stringify(fullMessage));
|
|
||||||
// console.log("消息发送成功:", fullMessage);
|
|
||||||
} catch (error) {
|
|
||||||
// console.error("消息发送失败:", error);
|
|
||||||
Toast.show({ content: "消息发送失败", position: "top" });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// 发送命令
|
|
||||||
sendCommand: (cmdType: string, data?: any) => {
|
|
||||||
const currentState = get();
|
|
||||||
|
|
||||||
if (
|
|
||||||
currentState.status !== WebSocketStatus.CONNECTED ||
|
|
||||||
!currentState.ws
|
|
||||||
) {
|
|
||||||
Toast.show({
|
|
||||||
content: "WebSocket未连接,正在重新连接...",
|
|
||||||
position: "top",
|
|
||||||
});
|
|
||||||
|
|
||||||
// 重置连接状态并发起重新连接
|
|
||||||
set({ status: WebSocketStatus.DISCONNECTED });
|
|
||||||
if (currentState.config) {
|
|
||||||
currentState.connect(currentState.config);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const command = {
|
|
||||||
cmdType,
|
|
||||||
...data,
|
|
||||||
seq: +new Date(),
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
currentState.ws.send(JSON.stringify(command));
|
|
||||||
// console.log("命令发送成功:", command);
|
|
||||||
} catch (error) {
|
|
||||||
// console.error("命令发送失败:", error);
|
|
||||||
Toast.show({ content: "命令发送失败", position: "top" });
|
|
||||||
|
|
||||||
// 发送失败时也尝试重新连接
|
|
||||||
set({ status: WebSocketStatus.DISCONNECTED });
|
|
||||||
if (currentState.config) {
|
|
||||||
currentState.connect(currentState.config);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// 清除消息
|
|
||||||
clearMessages: () => {
|
|
||||||
set({ messages: [], unreadCount: 0 });
|
|
||||||
},
|
|
||||||
|
|
||||||
// 标记为已读
|
|
||||||
markAsRead: () => {
|
|
||||||
set({ unreadCount: 0 });
|
|
||||||
},
|
|
||||||
|
|
||||||
// 重连
|
|
||||||
reconnect: () => {
|
|
||||||
const currentState = get();
|
|
||||||
|
|
||||||
if (currentState.config) {
|
|
||||||
// 检查是否允许重连
|
|
||||||
if (!currentState.config.autoReconnect) {
|
|
||||||
// console.log("自动重连已禁用,不再尝试重连");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
currentState.connect(currentState.config);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// 清空连接状态(用于退出登录时)
|
|
||||||
clearConnectionState: () => {
|
|
||||||
const currentState = get();
|
|
||||||
|
|
||||||
// 断开现有连接
|
|
||||||
if (currentState.ws) {
|
|
||||||
currentState.ws.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 停止所有定时器
|
|
||||||
currentState._stopReconnectTimer();
|
|
||||||
currentState._stopAliveStatusTimer();
|
|
||||||
|
|
||||||
// 重置所有状态
|
|
||||||
set({
|
|
||||||
status: WebSocketStatus.DISCONNECTED,
|
|
||||||
ws: null,
|
|
||||||
config: null,
|
|
||||||
messages: [],
|
|
||||||
unreadCount: 0,
|
|
||||||
reconnectAttempts: 0,
|
|
||||||
reconnectTimer: null,
|
|
||||||
aliveStatusTimer: null,
|
|
||||||
});
|
|
||||||
|
|
||||||
// console.log("WebSocket连接状态已清空");
|
|
||||||
},
|
|
||||||
|
|
||||||
// 内部方法:处理连接打开
|
|
||||||
_handleOpen: () => {
|
|
||||||
const currentState = get();
|
|
||||||
|
|
||||||
set({
|
|
||||||
status: WebSocketStatus.CONNECTED,
|
|
||||||
reconnectAttempts: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
// console.log("WebSocket连接成功");
|
|
||||||
const { token2 } = useUserStore.getState();
|
|
||||||
// 发送登录命令
|
|
||||||
if (currentState.config) {
|
|
||||||
currentState.sendCommand("CmdSignIn", {
|
|
||||||
accessToken: token2,
|
|
||||||
accountId: Number(getAccountId()),
|
|
||||||
client: currentState.config?.client || "kefu-client",
|
|
||||||
seq: +new Date(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Toast.show({ content: "WebSocket连接成功", position: "top" });
|
|
||||||
|
|
||||||
// 启动客服状态查询定时器
|
|
||||||
currentState._startAliveStatusTimer();
|
|
||||||
},
|
|
||||||
|
|
||||||
// 内部方法:处理消息接收
|
|
||||||
_handleMessage: (event: MessageEvent) => {
|
|
||||||
try {
|
|
||||||
const data = JSON.parse(event.data);
|
|
||||||
// console.log("收到WebSocket消息:", data);
|
|
||||||
|
|
||||||
// 处理特定的通知消息
|
|
||||||
if (data.cmdType === "CmdNotify") {
|
|
||||||
// 处理Auth failed通知
|
|
||||||
if (data.notify === "Auth failed" || data.notify === "Kicked out") {
|
|
||||||
// console.error(`WebSocket ${data.notify},断开连接`);
|
|
||||||
Toast.show({
|
|
||||||
content: `WebSocket ${data.notify},断开连接`,
|
|
||||||
position: "top",
|
|
||||||
});
|
|
||||||
|
|
||||||
// 禁用自动重连
|
|
||||||
if (get().config) {
|
|
||||||
set({
|
|
||||||
config: {
|
|
||||||
...get().config!,
|
|
||||||
autoReconnect: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 停止客服状态查询定时器
|
|
||||||
get()._stopAliveStatusTimer();
|
|
||||||
|
|
||||||
// 断开连接
|
|
||||||
get().disconnect();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentState = get();
|
|
||||||
const newMessage: WebSocketMessage = {
|
|
||||||
id: Date.now().toString(),
|
|
||||||
type: data.type || "message",
|
|
||||||
content: data,
|
|
||||||
timestamp: Date.now(),
|
|
||||||
sender: data.sender,
|
|
||||||
receiver: data.receiver,
|
|
||||||
};
|
|
||||||
|
|
||||||
set({
|
|
||||||
messages: [...currentState.messages, newMessage],
|
|
||||||
unreadCount: currentState.unreadCount + 1,
|
|
||||||
});
|
|
||||||
//消息处理器
|
|
||||||
msgManageCore(data);
|
|
||||||
|
|
||||||
// 可以在这里添加消息处理逻辑
|
|
||||||
// 比如播放提示音、显示通知等
|
|
||||||
} catch (error) {
|
|
||||||
// console.error("解析WebSocket消息失败:", error);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// 内部方法:处理连接关闭
|
|
||||||
_handleClose: (event: CloseEvent) => {
|
|
||||||
const currentState = get();
|
|
||||||
|
|
||||||
// console.log("WebSocket连接关闭:", event.code, event.reason);
|
|
||||||
|
|
||||||
set({
|
|
||||||
status: WebSocketStatus.DISCONNECTED,
|
|
||||||
ws: null,
|
|
||||||
});
|
|
||||||
|
|
||||||
// 自动重连逻辑
|
|
||||||
if (
|
|
||||||
currentState.config?.autoReconnect &&
|
|
||||||
currentState.reconnectAttempts <
|
|
||||||
(currentState.config?.maxReconnectAttempts || 5)
|
|
||||||
) {
|
|
||||||
// console.log("尝试自动重连...");
|
|
||||||
currentState._startReconnectTimer();
|
|
||||||
} else if (!currentState.config?.autoReconnect) {
|
|
||||||
// console.log("自动重连已禁用,不再尝试重连");
|
|
||||||
// 重置重连计数
|
|
||||||
set({ reconnectAttempts: 0 });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// 内部方法:处理连接错误
|
|
||||||
_handleError: (event: Event) => {
|
|
||||||
// console.error("WebSocket连接错误:", event);
|
|
||||||
|
|
||||||
set({ status: WebSocketStatus.ERROR });
|
|
||||||
|
|
||||||
Toast.show({ content: "WebSocket连接错误", position: "top" });
|
|
||||||
},
|
|
||||||
|
|
||||||
// 内部方法:启动重连定时器
|
|
||||||
_startReconnectTimer: () => {
|
|
||||||
const currentState = get();
|
|
||||||
|
|
||||||
currentState._stopReconnectTimer();
|
|
||||||
|
|
||||||
set({
|
|
||||||
status: WebSocketStatus.RECONNECTING,
|
|
||||||
reconnectAttempts: currentState.reconnectAttempts + 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
const timer = setTimeout(() => {
|
|
||||||
// console.log(
|
|
||||||
// `尝试重连 (${currentState.reconnectAttempts + 1}/${currentState.config?.maxReconnectAttempts})`,
|
|
||||||
// );
|
|
||||||
currentState.reconnect();
|
|
||||||
}, currentState.config?.reconnectInterval || 3000);
|
|
||||||
|
|
||||||
set({ reconnectTimer: timer });
|
|
||||||
},
|
|
||||||
|
|
||||||
// 内部方法:停止重连定时器
|
|
||||||
_stopReconnectTimer: () => {
|
|
||||||
const currentState = get();
|
|
||||||
|
|
||||||
if (currentState.reconnectTimer) {
|
|
||||||
clearTimeout(currentState.reconnectTimer);
|
|
||||||
set({ reconnectTimer: null });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// 内部方法:启动客服状态查询定时器
|
|
||||||
_startAliveStatusTimer: () => {
|
|
||||||
const currentState = get();
|
|
||||||
|
|
||||||
// 先停止现有定时器
|
|
||||||
currentState._stopAliveStatusTimer();
|
|
||||||
|
|
||||||
// 获取客服用户列表
|
|
||||||
const { kfUserList } = useCkChatStore.getState();
|
|
||||||
|
|
||||||
// 如果没有客服用户,不启动定时器
|
|
||||||
if (!kfUserList || kfUserList.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 启动定时器,每5秒查询一次
|
|
||||||
const timer = setInterval(() => {
|
|
||||||
const state = get();
|
|
||||||
// 检查连接状态
|
|
||||||
if (state.status === WebSocketStatus.CONNECTED) {
|
|
||||||
const { kfUserList: currentKfUserList } = useCkChatStore.getState();
|
|
||||||
if (currentKfUserList && currentKfUserList.length > 0) {
|
|
||||||
state.sendCommand("CmdRequestWechatAccountsAliveStatus", {
|
|
||||||
wechatAccountIds: currentKfUserList.map(v => v.id),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 如果连接断开,停止定时器
|
|
||||||
state._stopAliveStatusTimer();
|
|
||||||
}
|
|
||||||
}, 5 * 1000);
|
|
||||||
|
|
||||||
set({ aliveStatusTimer: timer });
|
|
||||||
},
|
|
||||||
|
|
||||||
// 内部方法:停止客服状态查询定时器
|
|
||||||
_stopAliveStatusTimer: () => {
|
|
||||||
const currentState = get();
|
|
||||||
|
|
||||||
if (currentState.aliveStatusTimer) {
|
|
||||||
clearInterval(currentState.aliveStatusTimer);
|
|
||||||
set({ aliveStatusTimer: null });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
name: "websocket-store",
|
|
||||||
partialize: state => ({
|
|
||||||
// 只持久化必要的状态,不持久化WebSocket实例
|
|
||||||
status: state.status,
|
|
||||||
config: state.config,
|
|
||||||
messages: state.messages.slice(-100), // 只保留最近100条消息
|
|
||||||
unreadCount: state.unreadCount,
|
|
||||||
reconnectAttempts: state.reconnectAttempts,
|
|
||||||
// 注意:定时器不需要持久化,重新连接时会重新创建
|
|
||||||
}),
|
|
||||||
onRehydrateStorage: () => state => {
|
|
||||||
// 页面刷新后,如果之前是连接状态,尝试重新连接
|
|
||||||
if (state && state.status === WebSocketStatus.CONNECTED && state.config) {
|
|
||||||
// console.log("页面刷新后恢复WebSocket连接", {
|
|
||||||
// persistedConfig: state.config,
|
|
||||||
// currentDefaultConfig: DEFAULT_CONFIG,
|
|
||||||
// });
|
|
||||||
|
|
||||||
// 使用最新的默认配置,而不是持久化的配置
|
|
||||||
const freshConfig = {
|
|
||||||
...DEFAULT_CONFIG,
|
|
||||||
client: state.config.client,
|
|
||||||
accountId: state.config.accountId,
|
|
||||||
accessToken: state.config.accessToken,
|
|
||||||
autoReconnect: state.config.autoReconnect,
|
|
||||||
};
|
|
||||||
|
|
||||||
// console.log("使用刷新后的配置重连:", freshConfig);
|
|
||||||
|
|
||||||
// 延迟一下再重连,确保页面完全加载
|
|
||||||
// 同时检查当前状态,避免重复连接
|
|
||||||
setTimeout(() => {
|
|
||||||
// 重新获取最新的状态,而不是使用闭包中的state
|
|
||||||
const currentState = useWebSocketStore.getState();
|
|
||||||
// console.log("页面刷新后检查状态", {
|
|
||||||
// status: currentState.status,
|
|
||||||
// hasWs: !!currentState.ws,
|
|
||||||
// });
|
|
||||||
|
|
||||||
// 强制重置状态为disconnected,因为页面刷新后WebSocket实例已失效
|
|
||||||
if (
|
|
||||||
currentState.status === WebSocketStatus.CONNECTED &&
|
|
||||||
!currentState.ws
|
|
||||||
) {
|
|
||||||
// console.log("检测到状态不一致,重置为disconnected");
|
|
||||||
useWebSocketStore.setState({
|
|
||||||
status: WebSocketStatus.DISCONNECTED,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 重新获取状态进行连接
|
|
||||||
const latestState = useWebSocketStore.getState();
|
|
||||||
if (
|
|
||||||
latestState.status === WebSocketStatus.DISCONNECTED ||
|
|
||||||
latestState.status === WebSocketStatus.ERROR
|
|
||||||
) {
|
|
||||||
// console.log("页面刷新后开始重连");
|
|
||||||
latestState.connect(freshConfig);
|
|
||||||
} else {
|
|
||||||
// console.log("WebSocket已连接或正在连接,跳过页面刷新重连", {
|
|
||||||
// status: latestState.status,
|
|
||||||
// });
|
|
||||||
}
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
121
Touchkebao/emoji-mapping.json
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
{
|
||||||
|
"face": {
|
||||||
|
"微笑": "smile",
|
||||||
|
"撇嘴": "pout",
|
||||||
|
"色": "lust",
|
||||||
|
"发呆": "daze",
|
||||||
|
"得意": "proud",
|
||||||
|
"流泪": "cry",
|
||||||
|
"害羞": "shy",
|
||||||
|
"闭嘴": "shut-up",
|
||||||
|
"睡": "sleep",
|
||||||
|
"大哭": "sob",
|
||||||
|
"尴尬": "awkward",
|
||||||
|
"发怒": "angry",
|
||||||
|
"调皮": "naughty",
|
||||||
|
"呲牙": "grin",
|
||||||
|
"惊讶": "surprised",
|
||||||
|
"难过": "sad",
|
||||||
|
"囧": "embarrassed",
|
||||||
|
"抓狂": "crazy",
|
||||||
|
"吐": "vomit",
|
||||||
|
"偷笑": "snicker",
|
||||||
|
"愉快": "happy",
|
||||||
|
"白眼": "roll-eyes",
|
||||||
|
"傲慢": "arrogant",
|
||||||
|
"困": "sleepy",
|
||||||
|
"惊恐": "panic",
|
||||||
|
"憨笑": "silly-smile",
|
||||||
|
"悠闲": "relaxed",
|
||||||
|
"咒骂": "curse",
|
||||||
|
"疑问": "question",
|
||||||
|
"嘘": "shush",
|
||||||
|
"晕": "dizzy",
|
||||||
|
"衰": "unlucky",
|
||||||
|
"骷髅": "skull",
|
||||||
|
"敲打": "knock",
|
||||||
|
"再见": "goodbye",
|
||||||
|
"擦汗": "wipe-sweat",
|
||||||
|
"抠鼻": "pick-nose",
|
||||||
|
"鼓掌": "clap",
|
||||||
|
"坏笑": "evil-smile",
|
||||||
|
"右哼哼": "right-hum",
|
||||||
|
"鄙视": "despise",
|
||||||
|
"委屈": "wronged",
|
||||||
|
"快哭了": "about-to-cry",
|
||||||
|
"阴险": "sinister",
|
||||||
|
"亲亲": "kiss",
|
||||||
|
"可怜": "pitiful",
|
||||||
|
"笑脸": "smiley",
|
||||||
|
"生病": "sick",
|
||||||
|
"脸红": "blush",
|
||||||
|
"破涕为笑": "smile-through-tears",
|
||||||
|
"恐惧": "fear",
|
||||||
|
"失望": "disappointed",
|
||||||
|
"无语": "speechless",
|
||||||
|
"嘿哈": "hey-ha",
|
||||||
|
"捂脸": "facepalm",
|
||||||
|
"机智": "smart",
|
||||||
|
"皱眉": "frown",
|
||||||
|
"耶": "yeah",
|
||||||
|
"吃瓜": "eat-melon",
|
||||||
|
"加油": "cheer-up",
|
||||||
|
"汗": "sweat",
|
||||||
|
"天啊": "oh-my-god",
|
||||||
|
"Emm": "emm",
|
||||||
|
"社会社会": "social",
|
||||||
|
"旺柴": "doge",
|
||||||
|
"好的": "ok",
|
||||||
|
"打脸": "slap-face",
|
||||||
|
"哇": "wow",
|
||||||
|
"翻白眼": "eye-roll",
|
||||||
|
"666": "666",
|
||||||
|
"让我看看": "let-me-see",
|
||||||
|
"叹气": "sigh",
|
||||||
|
"苦涩": "bitter",
|
||||||
|
"裂开": "crack",
|
||||||
|
"奸笑": "wicked-smile"
|
||||||
|
},
|
||||||
|
"gesture": {
|
||||||
|
"握手": "handshake",
|
||||||
|
"胜利": "victory",
|
||||||
|
"抱拳": "fist-salute",
|
||||||
|
"勾引": "beckon",
|
||||||
|
"拳头": "fist",
|
||||||
|
"OK": "ok",
|
||||||
|
"合十": "pray",
|
||||||
|
"强": "strong",
|
||||||
|
"拥抱": "hug",
|
||||||
|
"弱": "weak"
|
||||||
|
},
|
||||||
|
"animal": {
|
||||||
|
"猪头": "pig-head",
|
||||||
|
"跳跳": "jump",
|
||||||
|
"发抖": "shiver",
|
||||||
|
"转圈": "spin"
|
||||||
|
},
|
||||||
|
"blessing": {
|
||||||
|
"庆祝": "celebrate",
|
||||||
|
"礼物": "gift",
|
||||||
|
"红包": "red-envelope",
|
||||||
|
"發": "fortune",
|
||||||
|
"福": "blessing",
|
||||||
|
"烟花": "fireworks",
|
||||||
|
"爆竹": "firecrackers"
|
||||||
|
},
|
||||||
|
"other": {
|
||||||
|
"嘴唇": "lips",
|
||||||
|
"爱心": "heart",
|
||||||
|
"心碎": "broken-heart",
|
||||||
|
"啤酒": "beer",
|
||||||
|
"咖啡": "coffee",
|
||||||
|
"蛋糕": "cake",
|
||||||
|
"凋谢": "wither",
|
||||||
|
"菜刀": "knife",
|
||||||
|
"炸弹": "bomb",
|
||||||
|
"便便": "poop",
|
||||||
|
"太阳": "sun",
|
||||||
|
"月亮": "moon",
|
||||||
|
"玫瑰": "rose"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 9.8 KiB After Width: | Height: | Size: 9.8 KiB |
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 9.8 KiB |
|
Before Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 5.8 KiB |
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 5.9 KiB |
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 5.8 KiB |
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.0 KiB |