.env CHANGED
@@ -8,3 +8,6 @@ VITE_GLOB_OPEN_LONG_REPLY=false
8
 
9
  # When you want to use PWA
10
  VITE_GLOB_APP_PWA=false
 
 
 
 
8
 
9
  # When you want to use PWA
10
  VITE_GLOB_APP_PWA=false
11
+
12
+ # 触发弹层提示所需聊天次数
13
+ VITE_GLOB_CHAT_TIP_THRESHOLD=6
.eslintrc.cjs CHANGED
@@ -1,4 +1,7 @@
1
  module.exports = {
2
  root: true,
3
  extends: ['@antfu'],
 
 
 
4
  }
 
1
  module.exports = {
2
  root: true,
3
  extends: ['@antfu'],
4
+ rules: {
5
+ "no-tabs": "off"
6
+ }
7
  }
src/hooks/useChatTip/TipNode.vue ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <script setup lang="ts">
2
+ import { NButton, NModal } from 'naive-ui'
3
+ const { show } = defineProps<{
4
+ show: boolean
5
+ }>()
6
+
7
+ const emit = defineEmits<{
8
+ (event: 'increase'): void
9
+ }>()
10
+ </script>
11
+
12
+ <template>
13
+ <NModal :show="show">
14
+ <div class="card">
15
+ <div class="title">
16
+ ❤️ ❤️️如果你觉得这个项目对你有帮助,并且情况允许的话,可以给我一点点支持,非常感谢支持~
17
+ </div>
18
+ <div class="pics">
19
+ <div class="item">
20
+ <img src="./qr-wx.png" alt="qr-img">
21
+ <div class="color-green">
22
+ 微信
23
+ </div>
24
+ </div>
25
+ <div class="item">
26
+ <img src="./qr-zfb.png" alt="qr-img">
27
+ <div class="color-blue">
28
+ 支付宝
29
+ </div>
30
+ </div>
31
+ </div>
32
+ <div class="foot">
33
+ <NButton type="success" size="large" block style="font-size: 18px;" @click="emit('increase')">
34
+ 知道了🐶
35
+ </NButton>
36
+ </div>
37
+ </div>
38
+ </NModal>
39
+ </template>
40
+
41
+ <style scoped>
42
+ .card {
43
+ border-radius: 6px;
44
+ background-color: #fff;
45
+ text-align: center;
46
+ max-width: 660px;
47
+ padding: 32px;
48
+ max-height: 60vh;
49
+ overflow: auto;
50
+ }
51
+
52
+ .title {
53
+ font-size: 24px;
54
+ }
55
+
56
+ .pics {
57
+ display: flex;
58
+ }
59
+
60
+ .item {
61
+ display: flex;
62
+ flex-direction: column;
63
+ align-items: center;
64
+ border: 1px solid #e5e5e5;
65
+ border-radius: 12px;
66
+ overflow: hidden;
67
+ margin: 12px 24px;
68
+ padding: 12px;
69
+ padding-bottom: 8px;
70
+
71
+ > img {
72
+ width: 300px;
73
+ }
74
+ }
75
+
76
+ .foot {
77
+ max-width: 320px;
78
+ margin: 0 auto;
79
+ margin-top: 32px;
80
+ }
81
+
82
+ .color-green {
83
+ color: rgb(82, 158, 70);
84
+ font-size: 18px;
85
+ font-weight: bold;
86
+ }
87
+
88
+ .color-blue {
89
+ color: rgb(70, 157, 225);
90
+ font-size: 18px;
91
+ font-weight: bold;
92
+ }
93
+
94
+ @media screen and (max-width: 768px) {
95
+ .pics {
96
+ display: block;
97
+ }
98
+ }
99
+ </style>
src/hooks/useChatTip/common.ts ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ export const TIP_THRESHOLD = strNum2Num(import.meta.env.VITE_GLOB_CHAT_TIP_THRESHOLD) || 6
2
+
3
+ export function strNum2Num(str: string) {
4
+ const n = parseInt(str, 10)
5
+ return isNaN(n) ? 0 : n
6
+ }
src/hooks/useChatTip/qr-wx.png ADDED
src/hooks/useChatTip/qr-zfb.png ADDED
src/hooks/useChatTip/useChatTip.ts ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { onMounted, ref } from 'vue'
2
+ import { TIP_THRESHOLD, strNum2Num } from './common.js'
3
+
4
+ const countCacheKey = '__chat_tip_count__'
5
+
6
+ export function useChatTip() {
7
+ const tipCount = ref(0)
8
+
9
+ onMounted(() => {
10
+ forceSyncCount()
11
+ })
12
+
13
+ /** 从localStorage中同步count */
14
+ function forceSyncCount() {
15
+ const cacheCount = strNum2Num(localStorage.getItem(countCacheKey) || '0')
16
+
17
+ if (cacheCount !== tipCount.value)
18
+ tipCount.value = cacheCount
19
+ }
20
+
21
+ function increase() {
22
+ tipCount.value++
23
+
24
+ if (tipCount.value > TIP_THRESHOLD)
25
+ tipCount.value = 0
26
+
27
+ localStorage.setItem(countCacheKey, String(tipCount.value))
28
+ }
29
+
30
+ return {
31
+ increase,
32
+ count: tipCount,
33
+ }
34
+ }
src/views/chat/index.vue CHANGED
@@ -5,6 +5,9 @@ import { useRoute } from 'vue-router'
5
  import { storeToRefs } from 'pinia'
6
  import { NAutoComplete, NButton, NInput, useDialog, useMessage } from 'naive-ui'
7
  import html2canvas from 'html2canvas'
 
 
 
8
  import { Message } from './components'
9
  import { useScroll } from './hooks/useScroll'
10
  import { useChat } from './hooks/useChat'
@@ -27,6 +30,8 @@ const ms = useMessage()
27
 
28
  const chatStore = useChatStore()
29
 
 
 
30
  useCopyCode()
31
 
32
  const { isMobile } = useBasicLayout()
@@ -155,6 +160,8 @@ async function onConversation() {
155
  }
156
 
157
  await fetchChatAPIOnce()
 
 
158
  }
159
  catch (error: any) {
160
  const errorMessage = error?.message ?? t('common.wrong')
@@ -556,5 +563,10 @@ onUnmounted(() => {
556
  </div>
557
  </div>
558
  </footer>
 
 
 
 
 
559
  </div>
560
  </template>
 
5
  import { storeToRefs } from 'pinia'
6
  import { NAutoComplete, NButton, NInput, useDialog, useMessage } from 'naive-ui'
7
  import html2canvas from 'html2canvas'
8
+ import { useChatTip } from '../../hooks/useChatTip/useChatTip.js'
9
+ import TipNode from '../../hooks/useChatTip/TipNode.vue'
10
+ import { TIP_THRESHOLD } from '../../hooks/useChatTip/common.js'
11
  import { Message } from './components'
12
  import { useScroll } from './hooks/useScroll'
13
  import { useChat } from './hooks/useChat'
 
30
 
31
  const chatStore = useChatStore()
32
 
33
+ const tipCount = useChatTip()
34
+
35
  useCopyCode()
36
 
37
  const { isMobile } = useBasicLayout()
 
160
  }
161
 
162
  await fetchChatAPIOnce()
163
+
164
+ tipCount.increase()
165
  }
166
  catch (error: any) {
167
  const errorMessage = error?.message ?? t('common.wrong')
 
563
  </div>
564
  </div>
565
  </footer>
566
+
567
+ <TipNode
568
+ :show="tipCount.count.value === TIP_THRESHOLD"
569
+ @increase="tipCount.increase"
570
+ />
571
  </div>
572
  </template>