Merge branch 'develop' of https://e.coding.net/g-xtcy5189/cunkebao/cunkebao_v3 into develop
3
Cunkebao/.gitignore
vendored
@@ -3,4 +3,5 @@ dist/
|
||||
build/
|
||||
yarn.lock
|
||||
.env
|
||||
.DS_Store
|
||||
.DS_Store
|
||||
dist/*
|
||||
|
||||
36
Cunkebao/dist/.vite/manifest.json
vendored
@@ -1,50 +1,50 @@
|
||||
{
|
||||
"_charts-DsEZaGAW.js": {
|
||||
"file": "assets/charts-DsEZaGAW.js",
|
||||
"_charts-B449e2xS.js": {
|
||||
"file": "assets/charts-B449e2xS.js",
|
||||
"name": "charts",
|
||||
"imports": [
|
||||
"_ui-YC29IQHT.js",
|
||||
"_vendor-Bq99rrm8.js"
|
||||
"_ui-DDu9FCjt.js",
|
||||
"_vendor-0WYR1k4q.js"
|
||||
]
|
||||
},
|
||||
"_ui-D0C0OGrH.css": {
|
||||
"file": "assets/ui-D0C0OGrH.css",
|
||||
"src": "_ui-D0C0OGrH.css"
|
||||
},
|
||||
"_ui-YC29IQHT.js": {
|
||||
"file": "assets/ui-YC29IQHT.js",
|
||||
"_ui-DDu9FCjt.js": {
|
||||
"file": "assets/ui-DDu9FCjt.js",
|
||||
"name": "ui",
|
||||
"imports": [
|
||||
"_vendor-Bq99rrm8.js"
|
||||
"_vendor-0WYR1k4q.js"
|
||||
],
|
||||
"css": [
|
||||
"assets/ui-D0C0OGrH.css"
|
||||
]
|
||||
},
|
||||
"_utils-Ft3ushmX.js": {
|
||||
"file": "assets/utils-Ft3ushmX.js",
|
||||
"_utils-DC06x9DY.js": {
|
||||
"file": "assets/utils-DC06x9DY.js",
|
||||
"name": "utils",
|
||||
"imports": [
|
||||
"_vendor-Bq99rrm8.js"
|
||||
"_vendor-0WYR1k4q.js"
|
||||
]
|
||||
},
|
||||
"_vendor-Bq99rrm8.js": {
|
||||
"file": "assets/vendor-Bq99rrm8.js",
|
||||
"_vendor-0WYR1k4q.js": {
|
||||
"file": "assets/vendor-0WYR1k4q.js",
|
||||
"name": "vendor"
|
||||
},
|
||||
"index.html": {
|
||||
"file": "assets/index-d8oC2PBC.js",
|
||||
"file": "assets/index-Bx3nG52V.js",
|
||||
"name": "index",
|
||||
"src": "index.html",
|
||||
"isEntry": true,
|
||||
"imports": [
|
||||
"_vendor-Bq99rrm8.js",
|
||||
"_ui-YC29IQHT.js",
|
||||
"_utils-Ft3ushmX.js",
|
||||
"_charts-DsEZaGAW.js"
|
||||
"_vendor-0WYR1k4q.js",
|
||||
"_ui-DDu9FCjt.js",
|
||||
"_utils-DC06x9DY.js",
|
||||
"_charts-B449e2xS.js"
|
||||
],
|
||||
"css": [
|
||||
"assets/index-D8Ews0CA.css"
|
||||
"assets/index-QrS4Cvyc.css"
|
||||
]
|
||||
}
|
||||
}
|
||||
12
Cunkebao/dist/index.html
vendored
@@ -11,13 +11,13 @@
|
||||
</style>
|
||||
<!-- 引入 uni-app web-view SDK(必须) -->
|
||||
<script type="text/javascript" src="/websdk.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-d8oC2PBC.js"></script>
|
||||
<link rel="modulepreload" crossorigin href="/assets/vendor-Bq99rrm8.js">
|
||||
<link rel="modulepreload" crossorigin href="/assets/ui-YC29IQHT.js">
|
||||
<link rel="modulepreload" crossorigin href="/assets/utils-Ft3ushmX.js">
|
||||
<link rel="modulepreload" crossorigin href="/assets/charts-DsEZaGAW.js">
|
||||
<script type="module" crossorigin src="/assets/index-Bx3nG52V.js"></script>
|
||||
<link rel="modulepreload" crossorigin href="/assets/vendor-0WYR1k4q.js">
|
||||
<link rel="modulepreload" crossorigin href="/assets/ui-DDu9FCjt.js">
|
||||
<link rel="modulepreload" crossorigin href="/assets/utils-DC06x9DY.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/index-D8Ews0CA.css">
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-QrS4Cvyc.css">
|
||||
</head>
|
||||
<body>
|
||||
<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: "微笑",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/微笑.png",
|
||||
path: "/assets/face/smile.png",
|
||||
},
|
||||
撇嘴: {
|
||||
name: "撇嘴",
|
||||
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: "发呆",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/发呆.png",
|
||||
path: "/assets/face/daze.png",
|
||||
},
|
||||
得意: {
|
||||
name: "得意",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/得意.png",
|
||||
path: "/assets/face/smug.png",
|
||||
},
|
||||
流泪: {
|
||||
name: "流泪",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/流泪.png",
|
||||
path: "/assets/face/crying.png",
|
||||
},
|
||||
害羞: {
|
||||
name: "害羞",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/害羞.png",
|
||||
path: "/assets/face/shy.png",
|
||||
},
|
||||
闭嘴: {
|
||||
name: "闭嘴",
|
||||
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: "大哭",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/大哭.png",
|
||||
path: "/assets/face/wail.png",
|
||||
},
|
||||
尴尬: {
|
||||
name: "尴尬",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/尴尬.png",
|
||||
path: "/assets/face/awkward.png",
|
||||
},
|
||||
发怒: {
|
||||
name: "发怒",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/发怒.png",
|
||||
path: "/assets/face/angry.png",
|
||||
},
|
||||
调皮: {
|
||||
name: "调皮",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/调皮.png",
|
||||
path: "/assets/face/naughty.png",
|
||||
},
|
||||
呲牙: {
|
||||
name: "呲牙",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/呲牙.png",
|
||||
path: "/assets/face/grin.png",
|
||||
},
|
||||
惊讶: {
|
||||
name: "惊讶",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/惊讶.png",
|
||||
path: "/assets/face/surprised.png",
|
||||
},
|
||||
难过: {
|
||||
name: "难过",
|
||||
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: "抓狂",
|
||||
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: "偷笑",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/偷笑.png",
|
||||
path: "/assets/face/snicker.png",
|
||||
},
|
||||
愉快: {
|
||||
name: "愉快",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/愉快.png",
|
||||
path: "/assets/face/happy.png",
|
||||
},
|
||||
白眼: {
|
||||
name: "白眼",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/白眼.png",
|
||||
path: "/assets/face/roll-eyes.png",
|
||||
},
|
||||
傲慢: {
|
||||
name: "傲慢",
|
||||
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: "惊恐",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/惊恐.png",
|
||||
path: "/assets/face/panic.png",
|
||||
},
|
||||
憨笑: {
|
||||
name: "憨笑",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/憨笑.png",
|
||||
path: "/assets/face/silly-smile.png",
|
||||
},
|
||||
悠闲: {
|
||||
name: "悠闲",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/悠闲.png",
|
||||
path: "/assets/face/leisurely.png",
|
||||
},
|
||||
咒骂: {
|
||||
name: "咒骂",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/咒骂.png",
|
||||
path: "/assets/face/curse.png",
|
||||
},
|
||||
疑问: {
|
||||
name: "疑问",
|
||||
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: "骷髅",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/骷髅.png",
|
||||
path: "/assets/face/skull.png",
|
||||
},
|
||||
敲打: {
|
||||
name: "敲打",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/敲打.png",
|
||||
path: "/assets/face/knock.png",
|
||||
},
|
||||
再见: {
|
||||
name: "再见",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/再见.png",
|
||||
path: "/assets/face/goodbye.png",
|
||||
},
|
||||
擦汗: {
|
||||
name: "擦汗",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/擦汗.png",
|
||||
path: "/assets/face/wipe-sweat.png",
|
||||
},
|
||||
抠鼻: {
|
||||
name: "抠鼻",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/抠鼻.png",
|
||||
path: "/assets/face/pick-nose.png",
|
||||
},
|
||||
鼓掌: {
|
||||
name: "鼓掌",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/鼓掌.png",
|
||||
path: "/assets/face/clap.png",
|
||||
},
|
||||
坏笑: {
|
||||
name: "坏笑",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/坏笑.png",
|
||||
path: "/assets/face/evil-smile.png",
|
||||
},
|
||||
右哼哼: {
|
||||
name: "右哼哼",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/右哼哼.png",
|
||||
path: "/assets/face/right-hum.png",
|
||||
},
|
||||
鄙视: {
|
||||
name: "鄙视",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/鄙视.png",
|
||||
path: "/assets/face/despise.png",
|
||||
},
|
||||
委屈: {
|
||||
name: "委屈",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/委屈.png",
|
||||
path: "/assets/face/wronged.png",
|
||||
},
|
||||
快哭了: {
|
||||
name: "快哭了",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/快哭了.png",
|
||||
path: "/assets/face/about-to-cry.png",
|
||||
},
|
||||
阴险: {
|
||||
name: "阴险",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/阴险.png",
|
||||
path: "/assets/face/sinister.png",
|
||||
},
|
||||
亲亲: {
|
||||
name: "亲亲",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/亲亲.png",
|
||||
path: "/assets/face/kiss.png",
|
||||
},
|
||||
可怜: {
|
||||
name: "可怜",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/可怜.png",
|
||||
path: "/assets/face/pitiful.png",
|
||||
},
|
||||
笑脸: {
|
||||
name: "笑脸",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/笑脸.png",
|
||||
path: "/assets/face/smiley.png",
|
||||
},
|
||||
生病: {
|
||||
name: "生病",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/生病.png",
|
||||
path: "/assets/face/sick.png",
|
||||
},
|
||||
脸红: {
|
||||
name: "脸红",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/脸红.png",
|
||||
path: "/assets/face/blush.png",
|
||||
},
|
||||
破涕为笑: {
|
||||
name: "破涕为笑",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/破涕为笑.png",
|
||||
path: "/assets/face/tears-to-smile.png",
|
||||
},
|
||||
恐惧: {
|
||||
name: "恐惧",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/恐惧.png",
|
||||
path: "/assets/face/fear.png",
|
||||
},
|
||||
失望: {
|
||||
name: "失望",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/失望.png",
|
||||
path: "/assets/face/disappointed.png",
|
||||
},
|
||||
无语: {
|
||||
name: "无语",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/无语.png",
|
||||
path: "/assets/face/speechless.png",
|
||||
},
|
||||
嘿哈: {
|
||||
name: "嘿哈",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/嘿哈.png",
|
||||
path: "/assets/face/hey-ha.png",
|
||||
},
|
||||
捂脸: {
|
||||
name: "捂脸",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/捂脸.png",
|
||||
path: "/assets/face/facepalm.png",
|
||||
},
|
||||
机智: {
|
||||
name: "机智",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/机智.png",
|
||||
path: "/assets/face/smart.png",
|
||||
},
|
||||
皱眉: {
|
||||
name: "皱眉",
|
||||
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: "吃瓜",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/吃瓜.png",
|
||||
path: "/assets/face/eat-melon.png",
|
||||
},
|
||||
加油: {
|
||||
name: "加油",
|
||||
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: "天啊",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/天啊.png",
|
||||
path: "/assets/face/oh-my.png",
|
||||
},
|
||||
Emm: {
|
||||
name: "Emm",
|
||||
@@ -437,28 +477,32 @@ const EMOJI_DATA: Record<EmojiName, EmojiInfo> = {
|
||||
社会社会: {
|
||||
name: "社会社会",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/社会社会.png",
|
||||
path: "/assets/face/social.png",
|
||||
},
|
||||
旺柴: {
|
||||
name: "旺柴",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/旺柴.png",
|
||||
path: "/assets/face/doge.png",
|
||||
},
|
||||
好的: {
|
||||
name: "好的",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/好的.png",
|
||||
path: "/assets/face/good.png",
|
||||
},
|
||||
打脸: {
|
||||
name: "打脸",
|
||||
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: "翻白眼",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/翻白眼.png",
|
||||
path: "/assets/face/eye-roll.png",
|
||||
},
|
||||
"666": {
|
||||
name: "666",
|
||||
@@ -468,54 +512,54 @@ const EMOJI_DATA: Record<EmojiName, EmojiInfo> = {
|
||||
让我看看: {
|
||||
name: "让我看看",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/让我看看.png",
|
||||
path: "/assets/face/let-me-see.png",
|
||||
},
|
||||
叹气: {
|
||||
name: "叹气",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/叹气.png",
|
||||
path: "/assets/face/sigh.png",
|
||||
},
|
||||
苦涩: {
|
||||
name: "苦涩",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/苦涩.png",
|
||||
path: "/assets/face/bitter.png",
|
||||
},
|
||||
裂开: {
|
||||
name: "裂开",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/裂开.png",
|
||||
path: "/assets/face/crack.png",
|
||||
},
|
||||
奸笑: {
|
||||
name: "奸笑",
|
||||
category: EmojiCategory.FACE,
|
||||
path: "/assets/face/奸笑.png",
|
||||
path: "/assets/face/sly-smile.png",
|
||||
},
|
||||
|
||||
// 手势表情
|
||||
握手: {
|
||||
name: "握手",
|
||||
category: EmojiCategory.GESTURE,
|
||||
path: "/assets/gesture/握手.png",
|
||||
path: "/assets/gesture/handshake.png",
|
||||
},
|
||||
胜利: {
|
||||
name: "胜利",
|
||||
category: EmojiCategory.GESTURE,
|
||||
path: "/assets/gesture/胜利.png",
|
||||
path: "/assets/gesture/victory.png",
|
||||
},
|
||||
抱拳: {
|
||||
name: "抱拳",
|
||||
category: EmojiCategory.GESTURE,
|
||||
path: "/assets/gesture/抱拳.png",
|
||||
path: "/assets/gesture/fist-salute.png",
|
||||
},
|
||||
勾引: {
|
||||
name: "勾引",
|
||||
category: EmojiCategory.GESTURE,
|
||||
path: "/assets/gesture/勾引.png",
|
||||
path: "/assets/gesture/beckon.png",
|
||||
},
|
||||
拳头: {
|
||||
name: "拳头",
|
||||
category: EmojiCategory.GESTURE,
|
||||
path: "/assets/gesture/拳头.png",
|
||||
path: "/assets/gesture/fist.png",
|
||||
},
|
||||
OK: {
|
||||
name: "OK",
|
||||
@@ -525,148 +569,148 @@ const EMOJI_DATA: Record<EmojiName, EmojiInfo> = {
|
||||
合十: {
|
||||
name: "合十",
|
||||
category: EmojiCategory.GESTURE,
|
||||
path: "/assets/gesture/合十.png",
|
||||
path: "/assets/gesture/pray.png",
|
||||
},
|
||||
强: {
|
||||
name: "强",
|
||||
category: EmojiCategory.GESTURE,
|
||||
path: "/assets/gesture/强.png",
|
||||
path: "/assets/gesture/strong.png",
|
||||
},
|
||||
拥抱: {
|
||||
name: "拥抱",
|
||||
category: EmojiCategory.GESTURE,
|
||||
path: "/assets/gesture/拥抱.png",
|
||||
path: "/assets/gesture/hug.png",
|
||||
},
|
||||
弱: {
|
||||
name: "弱",
|
||||
category: EmojiCategory.GESTURE,
|
||||
path: "/assets/gesture/弱.png",
|
||||
path: "/assets/gesture/weak.png",
|
||||
},
|
||||
|
||||
// 动物表情
|
||||
猪头: {
|
||||
name: "猪头",
|
||||
category: EmojiCategory.ANIMAL,
|
||||
path: "/assets/animal/猪头.png",
|
||||
path: "/assets/animal/pig.png",
|
||||
},
|
||||
跳跳: {
|
||||
name: "跳跳",
|
||||
category: EmojiCategory.ANIMAL,
|
||||
path: "/assets/animal/跳跳.png",
|
||||
path: "/assets/animal/jump.png",
|
||||
},
|
||||
发抖: {
|
||||
name: "发抖",
|
||||
category: EmojiCategory.ANIMAL,
|
||||
path: "/assets/animal/发抖.png",
|
||||
path: "/assets/animal/tremble.png",
|
||||
},
|
||||
转圈: {
|
||||
name: "转圈",
|
||||
category: EmojiCategory.ANIMAL,
|
||||
path: "/assets/animal/转圈.png",
|
||||
path: "/assets/animal/circle.png",
|
||||
},
|
||||
|
||||
// 祝福表情
|
||||
庆祝: {
|
||||
name: "庆祝",
|
||||
category: EmojiCategory.BLESSING,
|
||||
path: "/assets/blessing/庆祝.png",
|
||||
path: "/assets/blessing/celebrate.png",
|
||||
},
|
||||
礼物: {
|
||||
name: "礼物",
|
||||
category: EmojiCategory.BLESSING,
|
||||
path: "/assets/blessing/礼物.png",
|
||||
path: "/assets/blessing/gift.png",
|
||||
},
|
||||
红包: {
|
||||
name: "红包",
|
||||
category: EmojiCategory.BLESSING,
|
||||
path: "/assets/blessing/红包.png",
|
||||
path: "/assets/blessing/red-envelope.png",
|
||||
},
|
||||
發: {
|
||||
name: "發",
|
||||
category: EmojiCategory.BLESSING,
|
||||
path: "/assets/blessing/發.png",
|
||||
path: "/assets/blessing/get-rich.png",
|
||||
},
|
||||
福: {
|
||||
name: "福",
|
||||
category: EmojiCategory.BLESSING,
|
||||
path: "/assets/blessing/福.png",
|
||||
path: "/assets/blessing/fortune.png",
|
||||
},
|
||||
烟花: {
|
||||
name: "烟花",
|
||||
category: EmojiCategory.BLESSING,
|
||||
path: "/assets/blessing/烟花.png",
|
||||
path: "/assets/blessing/fireworks.png",
|
||||
},
|
||||
爆竹: {
|
||||
name: "爆竹",
|
||||
category: EmojiCategory.BLESSING,
|
||||
path: "/assets/blessing/爆竹.png",
|
||||
path: "/assets/blessing/firecrackers.png",
|
||||
},
|
||||
|
||||
// 其他表情
|
||||
嘴唇: {
|
||||
name: "嘴唇",
|
||||
category: EmojiCategory.OTHER,
|
||||
path: "/assets/other/嘴唇.png",
|
||||
path: "/assets/other/lips.png",
|
||||
},
|
||||
爱心: {
|
||||
name: "爱心",
|
||||
category: EmojiCategory.OTHER,
|
||||
path: "/assets/other/爱心.png",
|
||||
path: "/assets/other/heart.png",
|
||||
},
|
||||
心碎: {
|
||||
name: "心碎",
|
||||
category: EmojiCategory.OTHER,
|
||||
path: "/assets/other/心碎.png",
|
||||
path: "/assets/other/broken-heart.png",
|
||||
},
|
||||
啤酒: {
|
||||
name: "啤酒",
|
||||
category: EmojiCategory.OTHER,
|
||||
path: "/assets/other/啤酒.png",
|
||||
path: "/assets/other/beer.png",
|
||||
},
|
||||
咖啡: {
|
||||
name: "咖啡",
|
||||
category: EmojiCategory.OTHER,
|
||||
path: "/assets/other/咖啡.png",
|
||||
path: "/assets/other/coffee.png",
|
||||
},
|
||||
蛋糕: {
|
||||
name: "蛋糕",
|
||||
category: EmojiCategory.OTHER,
|
||||
path: "/assets/other/蛋糕.png",
|
||||
path: "/assets/other/cake.png",
|
||||
},
|
||||
凋谢: {
|
||||
name: "凋谢",
|
||||
category: EmojiCategory.OTHER,
|
||||
path: "/assets/other/凋谢.png",
|
||||
path: "/assets/other/wither.png",
|
||||
},
|
||||
菜刀: {
|
||||
name: "菜刀",
|
||||
category: EmojiCategory.OTHER,
|
||||
path: "/assets/other/菜刀.png",
|
||||
path: "/assets/other/knife.png",
|
||||
},
|
||||
炸弹: {
|
||||
name: "炸弹",
|
||||
category: EmojiCategory.OTHER,
|
||||
path: "/assets/other/炸弹.png",
|
||||
path: "/assets/other/bomb.png",
|
||||
},
|
||||
便便: {
|
||||
name: "便便",
|
||||
category: EmojiCategory.OTHER,
|
||||
path: "/assets/other/便便.png",
|
||||
path: "/assets/other/poop.png",
|
||||
},
|
||||
太阳: {
|
||||
name: "太阳",
|
||||
category: EmojiCategory.OTHER,
|
||||
path: "/assets/other/太阳.png",
|
||||
path: "/assets/other/sun.png",
|
||||
},
|
||||
月亮: {
|
||||
name: "月亮",
|
||||
category: EmojiCategory.OTHER,
|
||||
path: "/assets/other/月亮.png",
|
||||
path: "/assets/other/moon.png",
|
||||
},
|
||||
玫瑰: {
|
||||
name: "玫瑰",
|
||||
category: EmojiCategory.OTHER,
|
||||
path: "/assets/other/玫瑰.png",
|
||||
path: "/assets/other/rose.png",
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useCkChatStore } from "@/store/module/ckchat/ckchat";
|
||||
import { Form, Input, Button, Toast, Checkbox } from "antd-mobile";
|
||||
import {
|
||||
EyeInvisibleOutline,
|
||||
@@ -7,7 +6,6 @@ import {
|
||||
UserOutline,
|
||||
} from "antd-mobile-icons";
|
||||
import { useUserStore } from "@/store/module/user";
|
||||
import { useWebSocketStore } from "@/store/module/websocket/websocket";
|
||||
|
||||
import { loginWithPassword, loginWithCode, sendVerificationCode } from "./api";
|
||||
import style from "./login.module.scss";
|
||||
@@ -19,7 +17,6 @@ const Login: React.FC = () => {
|
||||
const [countdown, setCountdown] = useState(0);
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
const [agreeToTerms, setAgreeToTerms] = useState(false);
|
||||
const { setUserInfo } = useCkChatStore.getState();
|
||||
const { login, login2 } = useUserStore();
|
||||
|
||||
// 倒计时效果
|
||||
@@ -75,13 +72,9 @@ const Login: React.FC = () => {
|
||||
: loginWithCode(loginParams);
|
||||
|
||||
response.then(res => {
|
||||
const { member, kefuData, deviceTotal } = res;
|
||||
const { member, deviceTotal } = res;
|
||||
// 清空WebSocket连接状态
|
||||
useWebSocketStore.getState().clearConnectionState();
|
||||
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",
|
||||
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>
|
||||
)}
|
||||
</Card>
|
||||
|
||||
{/* 添加新标签按钮 */}
|
||||
<Button block color="primary" className={styles.addTagBtn}>
|
||||
<TagOutlined />
|
||||
添加新标签
|
||||
</Button>
|
||||
</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 |