新增邮件和用户列表右键菜单
This commit is contained in:
@@ -20,7 +20,7 @@
|
|||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"dexie": "^4.0.11",
|
"dexie": "^4.0.11",
|
||||||
"echarts": "^5.6.0",
|
"echarts": "^5.6.0",
|
||||||
"element-plus": "^2.9.11",
|
"element-plus": "^2.13.1",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"path": "^0.12.7",
|
"path": "^0.12.7",
|
||||||
|
|||||||
81
mail-vue/pnpm-lock.yaml
generated
81
mail-vue/pnpm-lock.yaml
generated
@@ -36,8 +36,8 @@ importers:
|
|||||||
specifier: ^5.6.0
|
specifier: ^5.6.0
|
||||||
version: 5.6.0
|
version: 5.6.0
|
||||||
element-plus:
|
element-plus:
|
||||||
specifier: ^2.9.11
|
specifier: ^2.13.1
|
||||||
version: 2.11.1(vue@3.5.20)
|
version: 2.13.1(vue@3.5.20)
|
||||||
lodash-es:
|
lodash-es:
|
||||||
specifier: ^4.17.21
|
specifier: ^4.17.21
|
||||||
version: 4.17.21
|
version: 4.17.21
|
||||||
@@ -1085,8 +1085,8 @@ packages:
|
|||||||
'@types/trusted-types@2.0.7':
|
'@types/trusted-types@2.0.7':
|
||||||
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
|
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
|
||||||
|
|
||||||
'@types/web-bluetooth@0.0.16':
|
'@types/web-bluetooth@0.0.20':
|
||||||
resolution: {integrity: sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==}
|
resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==}
|
||||||
|
|
||||||
'@types/web-bluetooth@0.0.21':
|
'@types/web-bluetooth@0.0.21':
|
||||||
resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==}
|
resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==}
|
||||||
@@ -1144,6 +1144,9 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
vue: ^3.5.0
|
vue: ^3.5.0
|
||||||
|
|
||||||
|
'@vueuse/core@10.11.1':
|
||||||
|
resolution: {integrity: sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww==}
|
||||||
|
|
||||||
'@vueuse/core@12.8.2':
|
'@vueuse/core@12.8.2':
|
||||||
resolution: {integrity: sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ==}
|
resolution: {integrity: sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ==}
|
||||||
|
|
||||||
@@ -1152,8 +1155,8 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
vue: ^3.5.0
|
vue: ^3.5.0
|
||||||
|
|
||||||
'@vueuse/core@9.13.0':
|
'@vueuse/metadata@10.11.1':
|
||||||
resolution: {integrity: sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==}
|
resolution: {integrity: sha512-IGa5FXd003Ug1qAZmyE8wF3sJ81xGLSqTqtQ6jaVfkeZ4i5kS2mwQF61yhVqojRnenVew5PldLyRgvdl4YYuSw==}
|
||||||
|
|
||||||
'@vueuse/metadata@12.8.2':
|
'@vueuse/metadata@12.8.2':
|
||||||
resolution: {integrity: sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A==}
|
resolution: {integrity: sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A==}
|
||||||
@@ -1161,8 +1164,8 @@ packages:
|
|||||||
'@vueuse/metadata@14.1.0':
|
'@vueuse/metadata@14.1.0':
|
||||||
resolution: {integrity: sha512-7hK4g015rWn2PhKcZ99NyT+ZD9sbwm7SGvp7k+k+rKGWnLjS/oQozoIZzWfCewSUeBmnJkIb+CNr7Zc/EyRnnA==}
|
resolution: {integrity: sha512-7hK4g015rWn2PhKcZ99NyT+ZD9sbwm7SGvp7k+k+rKGWnLjS/oQozoIZzWfCewSUeBmnJkIb+CNr7Zc/EyRnnA==}
|
||||||
|
|
||||||
'@vueuse/metadata@9.13.0':
|
'@vueuse/shared@10.11.1':
|
||||||
resolution: {integrity: sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==}
|
resolution: {integrity: sha512-LHpC8711VFZlDaYUXEBbFBCQ7GS3dVU9mjOhhMhXP6txTV4EhYQg/KGnQuvt/sPAtoUKq7VVUnL6mVtFoL42sA==}
|
||||||
|
|
||||||
'@vueuse/shared@12.8.2':
|
'@vueuse/shared@12.8.2':
|
||||||
resolution: {integrity: sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w==}
|
resolution: {integrity: sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w==}
|
||||||
@@ -1172,9 +1175,6 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
vue: ^3.5.0
|
vue: ^3.5.0
|
||||||
|
|
||||||
'@vueuse/shared@9.13.0':
|
|
||||||
resolution: {integrity: sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==}
|
|
||||||
|
|
||||||
acorn@8.15.0:
|
acorn@8.15.0:
|
||||||
resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==}
|
resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==}
|
||||||
engines: {node: '>=0.4.0'}
|
engines: {node: '>=0.4.0'}
|
||||||
@@ -1349,6 +1349,9 @@ packages:
|
|||||||
dayjs@1.11.18:
|
dayjs@1.11.18:
|
||||||
resolution: {integrity: sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA==}
|
resolution: {integrity: sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA==}
|
||||||
|
|
||||||
|
dayjs@1.11.19:
|
||||||
|
resolution: {integrity: sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==}
|
||||||
|
|
||||||
debug@4.4.1:
|
debug@4.4.1:
|
||||||
resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==}
|
resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==}
|
||||||
engines: {node: '>=6.0'}
|
engines: {node: '>=6.0'}
|
||||||
@@ -1406,10 +1409,10 @@ packages:
|
|||||||
electron-to-chromium@1.5.211:
|
electron-to-chromium@1.5.211:
|
||||||
resolution: {integrity: sha512-IGBvimJkotaLzFnwIVgW9/UD/AOJ2tByUmeOrtqBfACSbAw5b1G0XpvdaieKyc7ULmbwXVx+4e4Be8pOPBrYkw==}
|
resolution: {integrity: sha512-IGBvimJkotaLzFnwIVgW9/UD/AOJ2tByUmeOrtqBfACSbAw5b1G0XpvdaieKyc7ULmbwXVx+4e4Be8pOPBrYkw==}
|
||||||
|
|
||||||
element-plus@2.11.1:
|
element-plus@2.13.1:
|
||||||
resolution: {integrity: sha512-weYFIniyNXTAe9vJZnmZpYzurh4TDbdKhBsJwhbzuo0SDZ8PLwHVll0qycJUxc6SLtH+7A9F7dvdDh5CnqeIVA==}
|
resolution: {integrity: sha512-eG4BDBGdAsUGN6URH1PixzZb0ngdapLivIk1meghS1uEueLvQ3aljSKrCt5x6sYb6mUk8eGtzTQFgsPmLavQcA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
vue: ^3.2.0
|
vue: ^3.3.0
|
||||||
|
|
||||||
entities@4.5.0:
|
entities@4.5.0:
|
||||||
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
|
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
|
||||||
@@ -1452,9 +1455,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
|
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
escape-html@1.0.3:
|
|
||||||
resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
|
|
||||||
|
|
||||||
escape-string-regexp@5.0.0:
|
escape-string-regexp@5.0.0:
|
||||||
resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==}
|
resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
@@ -3522,7 +3522,7 @@ snapshots:
|
|||||||
|
|
||||||
'@types/trusted-types@2.0.7': {}
|
'@types/trusted-types@2.0.7': {}
|
||||||
|
|
||||||
'@types/web-bluetooth@0.0.16': {}
|
'@types/web-bluetooth@0.0.20': {}
|
||||||
|
|
||||||
'@types/web-bluetooth@0.0.21': {}
|
'@types/web-bluetooth@0.0.21': {}
|
||||||
|
|
||||||
@@ -3611,6 +3611,16 @@ snapshots:
|
|||||||
'@vueuse/shared': 14.1.0(vue@3.5.20)
|
'@vueuse/shared': 14.1.0(vue@3.5.20)
|
||||||
vue: 3.5.20
|
vue: 3.5.20
|
||||||
|
|
||||||
|
'@vueuse/core@10.11.1(vue@3.5.20)':
|
||||||
|
dependencies:
|
||||||
|
'@types/web-bluetooth': 0.0.20
|
||||||
|
'@vueuse/metadata': 10.11.1
|
||||||
|
'@vueuse/shared': 10.11.1(vue@3.5.20)
|
||||||
|
vue-demi: 0.14.10(vue@3.5.20)
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@vue/composition-api'
|
||||||
|
- vue
|
||||||
|
|
||||||
'@vueuse/core@12.8.2':
|
'@vueuse/core@12.8.2':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/web-bluetooth': 0.0.21
|
'@types/web-bluetooth': 0.0.21
|
||||||
@@ -3627,21 +3637,18 @@ snapshots:
|
|||||||
'@vueuse/shared': 14.1.0(vue@3.5.20)
|
'@vueuse/shared': 14.1.0(vue@3.5.20)
|
||||||
vue: 3.5.20
|
vue: 3.5.20
|
||||||
|
|
||||||
'@vueuse/core@9.13.0(vue@3.5.20)':
|
'@vueuse/metadata@10.11.1': {}
|
||||||
dependencies:
|
|
||||||
'@types/web-bluetooth': 0.0.16
|
|
||||||
'@vueuse/metadata': 9.13.0
|
|
||||||
'@vueuse/shared': 9.13.0(vue@3.5.20)
|
|
||||||
vue-demi: 0.14.10(vue@3.5.20)
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- '@vue/composition-api'
|
|
||||||
- vue
|
|
||||||
|
|
||||||
'@vueuse/metadata@12.8.2': {}
|
'@vueuse/metadata@12.8.2': {}
|
||||||
|
|
||||||
'@vueuse/metadata@14.1.0': {}
|
'@vueuse/metadata@14.1.0': {}
|
||||||
|
|
||||||
'@vueuse/metadata@9.13.0': {}
|
'@vueuse/shared@10.11.1(vue@3.5.20)':
|
||||||
|
dependencies:
|
||||||
|
vue-demi: 0.14.10(vue@3.5.20)
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@vue/composition-api'
|
||||||
|
- vue
|
||||||
|
|
||||||
'@vueuse/shared@12.8.2':
|
'@vueuse/shared@12.8.2':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -3653,13 +3660,6 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
vue: 3.5.20
|
vue: 3.5.20
|
||||||
|
|
||||||
'@vueuse/shared@9.13.0(vue@3.5.20)':
|
|
||||||
dependencies:
|
|
||||||
vue-demi: 0.14.10(vue@3.5.20)
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- '@vue/composition-api'
|
|
||||||
- vue
|
|
||||||
|
|
||||||
acorn@8.15.0: {}
|
acorn@8.15.0: {}
|
||||||
|
|
||||||
ajv@8.17.1:
|
ajv@8.17.1:
|
||||||
@@ -3859,6 +3859,8 @@ snapshots:
|
|||||||
|
|
||||||
dayjs@1.11.18: {}
|
dayjs@1.11.18: {}
|
||||||
|
|
||||||
|
dayjs@1.11.19: {}
|
||||||
|
|
||||||
debug@4.4.1:
|
debug@4.4.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
ms: 2.1.3
|
ms: 2.1.3
|
||||||
@@ -3907,7 +3909,7 @@ snapshots:
|
|||||||
|
|
||||||
electron-to-chromium@1.5.211: {}
|
electron-to-chromium@1.5.211: {}
|
||||||
|
|
||||||
element-plus@2.11.1(vue@3.5.20):
|
element-plus@2.13.1(vue@3.5.20):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@ctrl/tinycolor': 3.6.1
|
'@ctrl/tinycolor': 3.6.1
|
||||||
'@element-plus/icons-vue': 2.3.2(vue@3.5.20)
|
'@element-plus/icons-vue': 2.3.2(vue@3.5.20)
|
||||||
@@ -3915,10 +3917,9 @@ snapshots:
|
|||||||
'@popperjs/core': '@sxzz/popperjs-es@2.11.7'
|
'@popperjs/core': '@sxzz/popperjs-es@2.11.7'
|
||||||
'@types/lodash': 4.17.20
|
'@types/lodash': 4.17.20
|
||||||
'@types/lodash-es': 4.17.12
|
'@types/lodash-es': 4.17.12
|
||||||
'@vueuse/core': 9.13.0(vue@3.5.20)
|
'@vueuse/core': 10.11.1(vue@3.5.20)
|
||||||
async-validator: 4.2.5
|
async-validator: 4.2.5
|
||||||
dayjs: 1.11.18
|
dayjs: 1.11.19
|
||||||
escape-html: 1.0.3
|
|
||||||
lodash: 4.17.21
|
lodash: 4.17.21
|
||||||
lodash-es: 4.17.21
|
lodash-es: 4.17.21
|
||||||
lodash-unified: 1.0.3(@types/lodash-es@4.17.12)(lodash-es@4.17.21)(lodash@4.17.21)
|
lodash-unified: 1.0.3(@types/lodash-es@4.17.12)(lodash-es@4.17.21)(lodash@4.17.21)
|
||||||
@@ -4044,8 +4045,6 @@ snapshots:
|
|||||||
|
|
||||||
escalade@3.2.0: {}
|
escalade@3.2.0: {}
|
||||||
|
|
||||||
escape-html@1.0.3: {}
|
|
||||||
|
|
||||||
escape-string-regexp@5.0.0: {}
|
escape-string-regexp@5.0.0: {}
|
||||||
|
|
||||||
estree-walker@1.0.1: {}
|
estree-walker@1.0.1: {}
|
||||||
|
|||||||
@@ -37,12 +37,14 @@
|
|||||||
v-if="!loading && emailList.length > 0"
|
v-if="!loading && emailList.length > 0"
|
||||||
:key="keyCount"
|
:key="keyCount"
|
||||||
>
|
>
|
||||||
<template #default="{ data: item, index }">
|
<template #default="{ data: item, index }" >
|
||||||
<div :class="'email-row ' + props.type"
|
<div :class="'email-row ' + props.type"
|
||||||
:data-checked="item.checked"
|
:data-checked="item.checked"
|
||||||
@click="jumpDetails(item)"
|
@click="jumpDetails(item)"
|
||||||
v-if="!item.expand"
|
v-if="!item.expand"
|
||||||
:key="item.emailId"
|
:key="item.emailId"
|
||||||
|
@contextmenu="handleContextmenu($event, item)"
|
||||||
|
:style="item.rightChecked ? 'background: #FDF6EC' : ''"
|
||||||
>
|
>
|
||||||
<el-checkbox :class=" props.type === 'all-email' ? 'all-email-checkbox' : 'checkbox'"
|
<el-checkbox :class=" props.type === 'all-email' ? 'all-email-checkbox' : 'checkbox'"
|
||||||
v-model="item.checked" @click.stop></el-checkbox>
|
v-model="item.checked" @click.stop></el-checkbox>
|
||||||
@@ -133,16 +135,89 @@
|
|||||||
:showUserInfo="showUserInfo"
|
:showUserInfo="showUserInfo"
|
||||||
:type="type"/>
|
:type="type"/>
|
||||||
<div class="empty" v-if="noLoading && emailList.length === 0 && !loading">
|
<div class="empty" v-if="noLoading && emailList.length === 0 && !loading">
|
||||||
<el-empty :image-size="isMobile ? 120 : 0" :description="$t('noMessagesFound')"/>
|
<el-empty :image-size="isMobile ? 120 : null" :description="$t('noMessagesFound')"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<el-dropdown
|
||||||
|
ref="dropdownRef"
|
||||||
|
@visible-change="visibleChange"
|
||||||
|
:virtual-ref="triggerRef"
|
||||||
|
:show-arrow="false"
|
||||||
|
:popper-options="{
|
||||||
|
modifiers: [{ name: 'offset', options: { offset: [0, 0] } }],
|
||||||
|
}"
|
||||||
|
virtual-triggering
|
||||||
|
trigger="contextmenu"
|
||||||
|
placement="bottom-start"
|
||||||
|
>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item v-if="props.type === 'email'" @click="emailRead()" >
|
||||||
|
<template #default>
|
||||||
|
<div class="right-dropdown-item">
|
||||||
|
<Icon icon="fluent:mail-read-20-regular" width="20" height="20" />
|
||||||
|
<span>{{t('markAsRead')}}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item v-if="props.type === 'email'" @click="openReply(rightClickEmail)">
|
||||||
|
<template #default>
|
||||||
|
<div class="right-dropdown-item">
|
||||||
|
<Icon icon="la:reply" width="20" height="20" />
|
||||||
|
<span>{{t('reply')}}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item v-if="['email','send', 'star'].includes(props.type)" @click="starChange(rightClickEmail)">
|
||||||
|
<template #default>
|
||||||
|
<div class="right-dropdown-item">
|
||||||
|
<Icon icon="solar:star-line-duotone" width="19" height="19"/>
|
||||||
|
<span>{{t('star')}}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item v-if="props.type === 'all-email'" @click="handleSearch('user', rightClickEmail.userEmail)">
|
||||||
|
<template #default>
|
||||||
|
<div class="right-dropdown-item">
|
||||||
|
<Icon icon="iconoir:search" width="20" height="20" />
|
||||||
|
<span>{{t('searchUser')}}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item v-if="props.type === 'all-email' " @click="handleSearch('account', rightClickEmail.toEmail)">
|
||||||
|
<template #default>
|
||||||
|
<div class="right-dropdown-item">
|
||||||
|
<Icon icon="iconoir:search" width="20" height="20" />
|
||||||
|
<span>{{t('searchEmail')}}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item v-if="props.type === 'all-email' " @click="handleSearch('name', rightClickEmail.name)">
|
||||||
|
<template #default>
|
||||||
|
<div class="right-dropdown-item">
|
||||||
|
<Icon icon="iconoir:search" width="20" height="20" />
|
||||||
|
<span>{{t('searchSender')}}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item @click="rightDelete(rightClickEmail.emailId)">
|
||||||
|
<template #default>
|
||||||
|
<div class="right-dropdown-item">
|
||||||
|
<Icon icon="uiw:delete" width="16" height="20" style="margin-left: 1px;margin-right: 3px" />
|
||||||
|
<span>{{t('delete')}}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import {Icon} from "@iconify/vue";
|
import {Icon} from "@iconify/vue";
|
||||||
import skeletonBlock from "@/components/email-scroll/skeleton/index.vue"
|
import skeletonBlock from "@/components/email-scroll/skeleton/index.vue"
|
||||||
import {computed, onActivated, reactive, ref, watch, nextTick} from "vue";
|
import {computed, onActivated, reactive, ref, watch, nextTick, onMounted, onUnmounted } from "vue";
|
||||||
import {useEmailStore} from "@/store/email.js";
|
import {useEmailStore} from "@/store/email.js";
|
||||||
import {useUiStore} from "@/store/ui.js";
|
import {useUiStore} from "@/store/ui.js";
|
||||||
import {useSettingStore} from "@/store/setting.js";
|
import {useSettingStore} from "@/store/setting.js";
|
||||||
@@ -191,7 +266,7 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
type: {
|
type: {
|
||||||
type: String,
|
type: String,
|
||||||
default: ''
|
default: 'email'
|
||||||
},
|
},
|
||||||
showFirstLoading: {
|
showFirstLoading: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
@@ -203,7 +278,7 @@ const props = defineProps({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['jump', 'refresh-before', 'delete-draft'])
|
const emit = defineEmits(['jump', 'refresh-before', 'delete-draft', 'right-search'])
|
||||||
const {t} = useI18n()
|
const {t} = useI18n()
|
||||||
const settingStore = useSettingStore()
|
const settingStore = useSettingStore()
|
||||||
const uiStore = useUiStore();
|
const uiStore = useUiStore();
|
||||||
@@ -225,7 +300,26 @@ let reqLock = false
|
|||||||
let isMobile = ref(innerWidth < 1367)
|
let isMobile = ref(innerWidth < 1367)
|
||||||
let skeletonRows = 0
|
let skeletonRows = 0
|
||||||
const timePaddingRight = ref('');
|
const timePaddingRight = ref('');
|
||||||
const keyCount = ref(0)
|
const keyCount = ref(0);
|
||||||
|
const dropdownRef = ref(null);
|
||||||
|
const dropdownCloseLock = ref(false);
|
||||||
|
const dropdownShow = ref(false);
|
||||||
|
const rightClickEmail = ref({});
|
||||||
|
const checkedEmailCount = ref(0);
|
||||||
|
let timer = null
|
||||||
|
const position = ref(
|
||||||
|
DOMRect.fromRect({
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const triggerRef = ref({
|
||||||
|
getBoundingClientRect() {
|
||||||
|
return position.value;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const queryParam = reactive({
|
const queryParam = reactive({
|
||||||
size: 50
|
size: 50
|
||||||
});
|
});
|
||||||
@@ -234,6 +328,7 @@ defineExpose({
|
|||||||
refreshList,
|
refreshList,
|
||||||
deleteEmail,
|
deleteEmail,
|
||||||
addItem,
|
addItem,
|
||||||
|
handleList,
|
||||||
emailList,
|
emailList,
|
||||||
firstLoad,
|
firstLoad,
|
||||||
latestEmail,
|
latestEmail,
|
||||||
@@ -248,6 +343,18 @@ onActivated(() => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
timer = setInterval(() => {
|
||||||
|
emailList.forEach(email => {
|
||||||
|
email.formatCreateTime = fromNow(email.createTime);
|
||||||
|
})
|
||||||
|
}, 1000 * 60);
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
clearInterval(timer)
|
||||||
|
})
|
||||||
|
|
||||||
getEmailList()
|
getEmailList()
|
||||||
|
|
||||||
window.onresize = () => {
|
window.onresize = () => {
|
||||||
@@ -323,6 +430,7 @@ watch(() => arrivedState.bottom, (isBottom) => {
|
|||||||
watch(
|
watch(
|
||||||
() => emailList.map(item => item.checked),
|
() => emailList.map(item => item.checked),
|
||||||
() => {
|
() => {
|
||||||
|
checkedEmailCount.value = emailList.length
|
||||||
if (emailList.length > 0) {
|
if (emailList.length > 0) {
|
||||||
updateCheckStatus();
|
updateCheckStatus();
|
||||||
}
|
}
|
||||||
@@ -353,6 +461,50 @@ watch(() => emailStore.addStarEmailId, () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
window.addEventListener('wheel', (event) => {
|
||||||
|
if (dropdownShow.value) {
|
||||||
|
dropdownRef.value.handleClose();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function openReply(email) {
|
||||||
|
uiStore.writerRef.openReply(email)
|
||||||
|
}
|
||||||
|
|
||||||
|
function visibleChange(e) {
|
||||||
|
dropdownShow.value = e;
|
||||||
|
dropdownCloseLock.value = true;
|
||||||
|
setTimeout(() => {
|
||||||
|
dropdownCloseLock.value = false;
|
||||||
|
},1500)
|
||||||
|
|
||||||
|
if (!e && rightClickEmail.value.rightChecked) {
|
||||||
|
rightClickEmail.value.rightChecked = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleContextmenu = (event, email) => {
|
||||||
|
|
||||||
|
if (props.type === 'draft') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rightClickEmail.value.rightChecked) {
|
||||||
|
rightClickEmail.value.rightChecked = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const { clientX, clientY } = event
|
||||||
|
position.value = DOMRect.fromRect({
|
||||||
|
x: clientX,
|
||||||
|
y: clientY,
|
||||||
|
})
|
||||||
|
event.preventDefault();
|
||||||
|
dropdownRef.value?.handleOpen();
|
||||||
|
|
||||||
|
rightClickEmail.value = email;
|
||||||
|
rightClickEmail.value.rightChecked = true
|
||||||
|
}
|
||||||
|
|
||||||
function updateHasScrollbar() {
|
function updateHasScrollbar() {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
const doc = document.querySelector('.virtual');
|
const doc = document.querySelector('.virtual');
|
||||||
@@ -407,7 +559,6 @@ function cleanSpace(text) {
|
|||||||
.trim();
|
.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function starChange(email) {
|
function starChange(email) {
|
||||||
|
|
||||||
if (!email.isStar) {
|
if (!email.isStar) {
|
||||||
@@ -451,7 +602,45 @@ const handleRead = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleDelete = () => {
|
function emailRead() {
|
||||||
|
const emailIds = getSelectedMailsIds();
|
||||||
|
props.emailRead(emailIds)
|
||||||
|
}
|
||||||
|
|
||||||
|
function rightDelete(emailId) {
|
||||||
|
|
||||||
|
if (props.type === 'all-email') {
|
||||||
|
ElMessageBox.confirm(t('delOneEmailConfirm'), {
|
||||||
|
confirmButtonText: t('confirm'),
|
||||||
|
cancelButtonText: t('cancel'),
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
props.emailDelete([emailId]).then(() => {
|
||||||
|
ElMessage({
|
||||||
|
message: t('delSuccessMsg'),
|
||||||
|
type: 'success',
|
||||||
|
plain: true
|
||||||
|
})
|
||||||
|
emailStore.deleteIds = [emailId];
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
props.emailDelete([emailId]).then(() => {
|
||||||
|
ElMessage({
|
||||||
|
message: t('delSuccessMsg'),
|
||||||
|
type: 'success',
|
||||||
|
plain: true
|
||||||
|
})
|
||||||
|
emailStore.deleteIds = [emailId];
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSearch(type, value) {
|
||||||
|
emit('right-search', type, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDelete() {
|
||||||
ElMessageBox.confirm(t('delEmailsConfirm'), {
|
ElMessageBox.confirm(t('delEmailsConfirm'), {
|
||||||
confirmButtonText: t('confirm'),
|
confirmButtonText: t('confirm'),
|
||||||
cancelButtonText: t('cancel'),
|
cancelButtonText: t('cancel'),
|
||||||
@@ -551,13 +740,24 @@ function getSelectedDraftsIds() {
|
|||||||
|
|
||||||
function updateCheckStatus() {
|
function updateCheckStatus() {
|
||||||
const checkedCount = emailList.filter(item => item.checked).length;
|
const checkedCount = emailList.filter(item => item.checked).length;
|
||||||
|
checkedEmailCount.value = checkedCount;
|
||||||
checkAll.value = checkedCount === emailList.length;
|
checkAll.value = checkedCount === emailList.length;
|
||||||
isIndeterminate.value = checkedCount > 0 && checkedCount < emailList.length;
|
isIndeterminate.value = checkedCount > 0 && checkedCount < emailList.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
function jumpDetails(email) {
|
function jumpDetails(email) {
|
||||||
const sel = window.getSelection();
|
|
||||||
if (sel && !sel.isCollapsed) return;
|
if (dropdownShow.value) {
|
||||||
|
dropdownRef.value.handleClose();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dropdownCloseLock.value) {
|
||||||
|
const sel = window.getSelection();
|
||||||
|
if (sel.toString().trim()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
emit('jump', email)
|
emit('jump', email)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1054,6 +1254,26 @@ function loadData() {
|
|||||||
bottom: 1px;
|
bottom: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.right-dropdown-item {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-dropdown-menu__item:last-child) {
|
||||||
|
padding-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-dropdown-menu__item:first-child) {
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-dropdown-menu__item) {
|
||||||
|
padding-right: 14px;
|
||||||
|
padding-left: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
.unread {
|
.unread {
|
||||||
height: 6px;
|
height: 6px;
|
||||||
width: 6px;
|
width: 6px;
|
||||||
|
|||||||
@@ -12,10 +12,10 @@ const en = {
|
|||||||
SystemSettings: 'System Settings',
|
SystemSettings: 'System Settings',
|
||||||
noMoreData: 'No more data',
|
noMoreData: 'No more data',
|
||||||
noMessagesFound: 'No messages found',
|
noMessagesFound: 'No messages found',
|
||||||
addAccount: 'Add Account',
|
addAccount: 'Add Email Address',
|
||||||
emailAccount: 'Email',
|
emailAccount: 'Email',
|
||||||
account: 'Account',
|
account: 'Address',
|
||||||
userAccount: 'User Accounts',
|
userAccount: 'User Email Address',
|
||||||
deleteUser: 'Delete Account',
|
deleteUser: 'Delete Account',
|
||||||
deleteUserBtn: 'Delete',
|
deleteUserBtn: 'Delete',
|
||||||
changePassword: 'Change Password',
|
changePassword: 'Change Password',
|
||||||
@@ -35,7 +35,7 @@ const en = {
|
|||||||
delAccountMsg: 'This will permanently delete your account and data. It cannot be reactivated',
|
delAccountMsg: 'This will permanently delete your account and data. It cannot be reactivated',
|
||||||
totalReceived: 'Total Received',
|
totalReceived: 'Total Received',
|
||||||
totalSent: 'Total Sent',
|
totalSent: 'Total Sent',
|
||||||
totalMailboxes: 'Total Accounts',
|
totalMailboxes: 'Total Email Addresses',
|
||||||
totalUsers: 'Total Users',
|
totalUsers: 'Total Users',
|
||||||
deleted: 'Deleted',
|
deleted: 'Deleted',
|
||||||
selectDeleted: 'Deleted',
|
selectDeleted: 'Deleted',
|
||||||
@@ -52,7 +52,7 @@ const en = {
|
|||||||
tabEmailAddress: 'Email',
|
tabEmailAddress: 'Email',
|
||||||
tabReceived: 'Received',
|
tabReceived: 'Received',
|
||||||
tabSent: 'Sent',
|
tabSent: 'Sent',
|
||||||
tabMailboxes: 'Accounts',
|
tabMailboxes: 'Addresses',
|
||||||
tabRegisteredAt: 'Registered at',
|
tabRegisteredAt: 'Registered at',
|
||||||
tabStatus: 'Status',
|
tabStatus: 'Status',
|
||||||
tabRole: 'Role',
|
tabRole: 'Role',
|
||||||
@@ -73,9 +73,9 @@ const en = {
|
|||||||
unauthorized: 'Unauthorized',
|
unauthorized: 'Unauthorized',
|
||||||
unlimited: 'Unlimited',
|
unlimited: 'Unlimited',
|
||||||
sendCount: 'Send email : ',
|
sendCount: 'Send email : ',
|
||||||
accountCount: 'Add account : ',
|
accountCount: 'Add Address : ',
|
||||||
action: 'Action',
|
action: 'Action',
|
||||||
chgPwd: 'Pwd',
|
chgPwd: 'Password',
|
||||||
perm: 'Role',
|
perm: 'Role',
|
||||||
btnBan: 'Ban',
|
btnBan: 'Ban',
|
||||||
admin: 'Admin',
|
admin: 'Admin',
|
||||||
@@ -136,8 +136,8 @@ const en = {
|
|||||||
websiteSetting: 'Website',
|
websiteSetting: 'Website',
|
||||||
websiteReg: 'Sign Up',
|
websiteReg: 'Sign Up',
|
||||||
loginDomain: 'Sign-In Box Domain',
|
loginDomain: 'Sign-In Box Domain',
|
||||||
multipleEmail: 'Multiple Accounts',
|
multipleEmail: 'Multiple Email Address',
|
||||||
multipleEmailDesc: 'Enable this feature to allow users to add multiple accounts',
|
multipleEmailDesc: 'Enable this feature to allow users to add multiple email',
|
||||||
customization: 'Customization',
|
customization: 'Customization',
|
||||||
websiteTitle: 'Title',
|
websiteTitle: 'Title',
|
||||||
loginBoxOpacity: 'Login Box Opacity',
|
loginBoxOpacity: 'Login Box Opacity',
|
||||||
@@ -161,7 +161,7 @@ const en = {
|
|||||||
rules: 'Rules',
|
rules: 'Rules',
|
||||||
turnstileSetting: 'Turnstile',
|
turnstileSetting: 'Turnstile',
|
||||||
signUpVerification: 'Sign Up Verification',
|
signUpVerification: 'Sign Up Verification',
|
||||||
addEmailVerification: 'Add Account Verification',
|
addEmailVerification: 'Add Email Verification',
|
||||||
about: 'About',
|
about: 'About',
|
||||||
version: 'Version',
|
version: 'Version',
|
||||||
community: 'Community',
|
community: 'Community',
|
||||||
@@ -207,6 +207,7 @@ const en = {
|
|||||||
emptyCountMsg: 'Available count cannot be empty',
|
emptyCountMsg: 'Available count cannot be empty',
|
||||||
addSuccessMsg: 'Addition successful',
|
addSuccessMsg: 'Addition successful',
|
||||||
delConfirm: 'Confirm deleting {msg}?',
|
delConfirm: 'Confirm deleting {msg}?',
|
||||||
|
delUsersConfirm: 'Confirm deletion of selected users?',
|
||||||
emptyRoleNameMsg: 'Role name cannot be empty',
|
emptyRoleNameMsg: 'Role name cannot be empty',
|
||||||
saveSuccessMsg: 'Saved successfully',
|
saveSuccessMsg: 'Saved successfully',
|
||||||
changeRoleTitle: 'Change Role',
|
changeRoleTitle: 'Change Role',
|
||||||
@@ -237,7 +238,8 @@ const en = {
|
|||||||
sendSuccessMsg: 'Send successful',
|
sendSuccessMsg: 'Send successful',
|
||||||
sendFailMsg: 'Send failed',
|
sendFailMsg: 'Send failed',
|
||||||
saveDraftConfirm: 'Save draft?',
|
saveDraftConfirm: 'Save draft?',
|
||||||
delEmailsConfirm: 'Confirm batch delete these emails?',
|
delEmailsConfirm: 'Confirm deletion of selected emails?',
|
||||||
|
delOneEmailConfirm: 'Confirm deletion of this email?',
|
||||||
sending: 'Sending email...',
|
sending: 'Sending email...',
|
||||||
sendingErrorMsg: 'Sending in progress',
|
sendingErrorMsg: 'Sending in progress',
|
||||||
networkErrorMsg: 'Network error. Check your internet',
|
networkErrorMsg: 'Network error. Check your internet',
|
||||||
@@ -312,7 +314,18 @@ const en = {
|
|||||||
mustNotContainDesc: 'Separate with commas',
|
mustNotContainDesc: 'Separate with commas',
|
||||||
setSuccess: 'Settings saved successfully',
|
setSuccess: 'Settings saved successfully',
|
||||||
details: 'Details',
|
details: 'Details',
|
||||||
userDetails: 'User Details'
|
userDetails: 'User Details',
|
||||||
|
markAsRead: 'Mark as Read',
|
||||||
|
star: 'Star',
|
||||||
|
setRole: 'Set Role',
|
||||||
|
adminDeleteUser: 'Delete User',
|
||||||
|
banUser: 'Ban User',
|
||||||
|
enableUser: 'Enable User',
|
||||||
|
restoreUser: 'Restore User',
|
||||||
|
searchUser: 'Search by user',
|
||||||
|
searchEmail: 'Search by Email',
|
||||||
|
searchSender: 'Search by Sender',
|
||||||
|
userEmail: 'Email Address'
|
||||||
}
|
}
|
||||||
|
|
||||||
export default en
|
export default en
|
||||||
|
|||||||
@@ -207,6 +207,7 @@ const zh = {
|
|||||||
emptyCountMsg: '可用次数不能为空',
|
emptyCountMsg: '可用次数不能为空',
|
||||||
addSuccessMsg: '添加成功',
|
addSuccessMsg: '添加成功',
|
||||||
delConfirm: '确认删除{msg}吗?',
|
delConfirm: '确认删除{msg}吗?',
|
||||||
|
delUsersConfirm: '确定删除选中的用户吗?',
|
||||||
emptyRoleNameMsg: '身份名不能为空',
|
emptyRoleNameMsg: '身份名不能为空',
|
||||||
saveSuccessMsg: '保存成功',
|
saveSuccessMsg: '保存成功',
|
||||||
changeRoleTitle: '修改身份',
|
changeRoleTitle: '修改身份',
|
||||||
@@ -237,7 +238,8 @@ const zh = {
|
|||||||
sendSuccessMsg: '发送成功',
|
sendSuccessMsg: '发送成功',
|
||||||
sendFailMsg: '发送失败',
|
sendFailMsg: '发送失败',
|
||||||
saveDraftConfirm: '是否保存草稿?',
|
saveDraftConfirm: '是否保存草稿?',
|
||||||
delEmailsConfirm: '确认批量删除这些邮件吗?',
|
delEmailsConfirm: '确认删除选中的邮件吗?',
|
||||||
|
delOneEmailConfirm: '确认删除这个邮件吗?',
|
||||||
sending: '邮件正在发送中',
|
sending: '邮件正在发送中',
|
||||||
sendingErrorMsg: '邮件正在发送中',
|
sendingErrorMsg: '邮件正在发送中',
|
||||||
networkErrorMsg: '网络错误,请检查网络连接',
|
networkErrorMsg: '网络错误,请检查网络连接',
|
||||||
@@ -312,6 +314,17 @@ const zh = {
|
|||||||
mustNotContainDesc: '输入多个值用,分开',
|
mustNotContainDesc: '输入多个值用,分开',
|
||||||
setSuccess: '设置成功',
|
setSuccess: '设置成功',
|
||||||
details: '详情',
|
details: '详情',
|
||||||
userDetails: '用户详情'
|
userDetails: '用户详情',
|
||||||
|
markAsRead: '标为已读',
|
||||||
|
star: '星标',
|
||||||
|
setRole: '设置权限',
|
||||||
|
adminDeleteUser: '删除用户',
|
||||||
|
banUser: '封禁用户',
|
||||||
|
enableUser: '启动用户',
|
||||||
|
restoreUser: '恢复用户',
|
||||||
|
searchUser: '搜索用户',
|
||||||
|
searchEmail: '搜索邮箱',
|
||||||
|
searchSender: '搜索发件人',
|
||||||
|
userEmail: '用户邮箱'
|
||||||
}
|
}
|
||||||
export default zh
|
export default zh
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export function emailLatest(emailId, accountId, allReceive) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function emailRead(emailIds) {
|
export function emailRead(emailIds) {
|
||||||
return http.put('/email/read', {emailIds}, {noMsg: true})
|
return http.put('/email/read', {emailIds})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function emailSend(form,progress) {
|
export function emailSend(form,progress) {
|
||||||
|
|||||||
@@ -56,6 +56,14 @@ export function fromNow(date) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function updateNow(date) {
|
||||||
|
if (isToday) {
|
||||||
|
if (diffSeconds < 60) return `Just now`;
|
||||||
|
if (diffMinutes < 60) return `${diffMinutes} min ago`;
|
||||||
|
if (diffHours < 2) return `${diffHours} hour${diffHours > 1 ? 's' : ''} ago`;
|
||||||
|
return d.format('hh:mm A');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function formatDetailDate(time) {
|
export function formatDetailDate(time) {
|
||||||
const d = dayjs.utc(time).tz(timeZone);
|
const d = dayjs.utc(time).tz(timeZone);
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
:item-height="65"
|
:item-height="65"
|
||||||
@jump="jumpContent"
|
@jump="jumpContent"
|
||||||
@refresh-before="refreshBefore"
|
@refresh-before="refreshBefore"
|
||||||
|
@right-search="rightSearch"
|
||||||
:type="'all-email'"
|
:type="'all-email'"
|
||||||
|
|
||||||
>
|
>
|
||||||
@@ -227,6 +228,12 @@ function batchDelete() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function rightSearch(type, value) {
|
||||||
|
params.searchType = type;
|
||||||
|
searchValue.value = value;
|
||||||
|
search();
|
||||||
|
}
|
||||||
|
|
||||||
function refreshBefore() {
|
function refreshBefore() {
|
||||||
searchValue.value = null
|
searchValue.value = null
|
||||||
params.timeSort = 0
|
params.timeSort = 0
|
||||||
@@ -337,7 +344,7 @@ async function latest() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e.code === 401) {
|
if (e.code === 401 || e.code === 403) {
|
||||||
settingStore.settings.autoRefresh = AutoRefreshEnum.DISABLED;
|
settingStore.settings.autoRefresh = AutoRefreshEnum.DISABLED;
|
||||||
}
|
}
|
||||||
console.error(e)
|
console.error(e)
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<emailScroll ref="scroll"
|
<emailScroll ref="scroll"
|
||||||
:allow-star="false"
|
:allow-star="false"
|
||||||
:cancel-success="cancelStar"
|
|
||||||
:getEmailList="getEmailList"
|
:getEmailList="getEmailList"
|
||||||
:emailDelete="emailDelete"
|
:emailDelete="emailDelete"
|
||||||
:star-add="starAdd"
|
:star-add="starAdd"
|
||||||
@@ -15,7 +14,7 @@
|
|||||||
:type="'draft'"
|
:type="'draft'"
|
||||||
>
|
>
|
||||||
<template #name="props">
|
<template #name="props">
|
||||||
<span class="send-email">{{ props.email.receiveEmail.join(',') || '(' + $t('noRecipient') + ')' }}</span>
|
<span class="send-email">{{ props.email.receiveEmail?.join(',') || '(' + $t('noRecipient') + ')' }}</span>
|
||||||
</template>
|
</template>
|
||||||
<template #subject="props">
|
<template #subject="props">
|
||||||
{{ props.email.subject || '(' + $t('noSubject') + ')' }}
|
{{ props.email.subject || '(' + $t('noSubject') + ')' }}
|
||||||
@@ -27,8 +26,7 @@
|
|||||||
import emailScroll from "@/components/email-scroll/index.vue"
|
import emailScroll from "@/components/email-scroll/index.vue"
|
||||||
import {emailDelete} from "@/request/email.js";
|
import {emailDelete} from "@/request/email.js";
|
||||||
import {starAdd, starCancel} from "@/request/star.js";
|
import {starAdd, starCancel} from "@/request/star.js";
|
||||||
import {useEmailStore} from "@/store/email.js";
|
import {defineOptions, ref, watch, toRaw} from "vue";
|
||||||
import {defineOptions, onMounted, ref, watch, toRaw} from "vue";
|
|
||||||
import {useUiStore} from "@/store/ui.js";
|
import {useUiStore} from "@/store/ui.js";
|
||||||
import {userDraftStore} from "@/store/draft.js";
|
import {userDraftStore} from "@/store/draft.js";
|
||||||
import db from "@/db/db.js"
|
import db from "@/db/db.js"
|
||||||
@@ -40,7 +38,6 @@ defineOptions({
|
|||||||
const draftStore = userDraftStore();
|
const draftStore = userDraftStore();
|
||||||
const uiStore = useUiStore();
|
const uiStore = useUiStore();
|
||||||
const scroll = ref({})
|
const scroll = ref({})
|
||||||
const emailStore = useEmailStore();
|
|
||||||
|
|
||||||
watch(() => draftStore.setDraft, async () => {
|
watch(() => draftStore.setDraft, async () => {
|
||||||
|
|
||||||
@@ -68,6 +65,7 @@ watch(() => draftStore.setDraft, async () => {
|
|||||||
watch(() => draftStore.refreshList, async () => {
|
watch(() => draftStore.refreshList, async () => {
|
||||||
const {list} = await getEmailList();
|
const {list} = await getEmailList();
|
||||||
scroll.value.emailList.length = 0
|
scroll.value.emailList.length = 0
|
||||||
|
scroll.value.handleList(list);
|
||||||
scroll.value.emailList.push(...list)
|
scroll.value.emailList.push(...list)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -90,15 +88,6 @@ async function jumpContent(email) {
|
|||||||
uiStore.writerRef.openDraft(email);
|
uiStore.writerRef.openDraft(email);
|
||||||
}
|
}
|
||||||
|
|
||||||
function cancelStar(email) {
|
|
||||||
emailStore.cancelStarEmailId = email.emailId
|
|
||||||
scroll.value.deleteEmail([email.emailId])
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
emailStore.starScroll = scroll
|
|
||||||
})
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
.send-email {
|
.send-email {
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ async function latest() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e.code === 401) {
|
if (e.code === 401 || e.code === 403) {
|
||||||
settingStore.settings.autoRefresh = AutoRefreshEnum.DISABLED;
|
settingStore.settings.autoRefresh = AutoRefreshEnum.DISABLED;
|
||||||
}
|
}
|
||||||
console.error(e)
|
console.error(e)
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
:star-cancel="starCancel"
|
:star-cancel="starCancel"
|
||||||
@jump="jumpContent"
|
@jump="jumpContent"
|
||||||
:time-sort="params.timeSort"
|
:time-sort="params.timeSort"
|
||||||
|
:type="'send'"
|
||||||
>
|
>
|
||||||
<template #first>
|
<template #first>
|
||||||
<Icon class="icon" @click="changeTimeSort" icon="material-symbols-light:timer-arrow-down-outline"
|
<Icon class="icon" @click="changeTimeSort" icon="material-symbols-light:timer-arrow-down-outline"
|
||||||
@@ -29,7 +30,6 @@ import {starAdd, starCancel} from "@/request/star.js";
|
|||||||
import {defineOptions, onMounted, reactive, ref, watch} from "vue";
|
import {defineOptions, onMounted, reactive, ref, watch} from "vue";
|
||||||
import router from "@/router/index.js";
|
import router from "@/router/index.js";
|
||||||
import {Icon} from "@iconify/vue";
|
import {Icon} from "@iconify/vue";
|
||||||
import {AccountAllReceiveEnum} from "@/enums/account-enum.js";
|
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'send'
|
name: 'send'
|
||||||
|
|||||||
@@ -754,7 +754,7 @@ defineOptions({
|
|||||||
name: 'sys-setting'
|
name: 'sys-setting'
|
||||||
})
|
})
|
||||||
|
|
||||||
const currentVersion = 'v2.7.0'
|
const currentVersion = 'v2.8.0'
|
||||||
const hasUpdate = ref(false)
|
const hasUpdate = ref(false)
|
||||||
let getUpdateErrorCount = 1;
|
let getUpdateErrorCount = 1;
|
||||||
const {t, locale} = useI18n();
|
const {t, locale} = useI18n();
|
||||||
|
|||||||
@@ -38,6 +38,8 @@
|
|||||||
:preserve-expanded-content="preserveExpanded"
|
:preserve-expanded-content="preserveExpanded"
|
||||||
style="width: 100%;"
|
style="width: 100%;"
|
||||||
ref="tableRef"
|
ref="tableRef"
|
||||||
|
@cell-contextmenu="handleContextmenu"
|
||||||
|
:cell-class-name="cellClassName"
|
||||||
>
|
>
|
||||||
<el-table-column :width="expandWidth" type="selection" :selectable="row => row.type !== 0" />
|
<el-table-column :width="expandWidth" type="selection" :selectable="row => row.type !== 0" />
|
||||||
<el-table-column show-overflow-tooltip :tooltip-formatter="tableRowFormatter" :label="$t('tabEmailAddress')"
|
<el-table-column show-overflow-tooltip :tooltip-formatter="tableRowFormatter" :label="$t('tabEmailAddress')"
|
||||||
@@ -81,20 +83,21 @@
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column :label="$t('tabSetting')" :width="settingWidth">
|
<el-table-column :label="$t('tabSetting')" :width="settingWidth">
|
||||||
<template #default="props">
|
<template #default="props">
|
||||||
<el-dropdown>
|
<el-button size="small" type="primary" v-if="(props.row.type === 0 && userStore.user.type !== 0)" >{{ $t('action') }}</el-button>
|
||||||
|
<el-dropdown v-else >
|
||||||
<el-button size="small" type="primary">{{ $t('action') }}</el-button>
|
<el-button size="small" type="primary">{{ $t('action') }}</el-button>
|
||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
<el-dropdown-menu>
|
<el-dropdown-menu>
|
||||||
<el-dropdown-item @click="openSetPwd(props.row)" v-if="(props.row.type !== 0 || userStore.user.type === 0)">{{ $t('chgPwd') }}</el-dropdown-item>
|
<el-dropdown-item @click="openSetPwd(props.row)" >{{ $t('chgPwd') }}</el-dropdown-item>
|
||||||
<el-dropdown-item @click="openSetType(props.row)">{{ $t('perm') }}</el-dropdown-item>
|
<el-dropdown-item @click="openSetType(props.row)" >{{ $t('perm') }}</el-dropdown-item>
|
||||||
<template v-if="props.row.type !== 0">
|
<template v-if="props.row.type !== 0">
|
||||||
<el-dropdown-item v-if="props.row.isDel !== 1" @click="setStatus(props.row)">
|
<el-dropdown-item v-if="props.row.isDel !== 1" @click="setStatus(props.row)">
|
||||||
{{ setStatusName(props.row) }}
|
{{ setStatusName(props.row) }}
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
<el-dropdown-item v-else @click="restore(props.row)">{{ $t('restore') }}</el-dropdown-item>
|
<el-dropdown-item v-else @click="restore(props.row)">{{ $t('restore') }}</el-dropdown-item>
|
||||||
</template>
|
</template>
|
||||||
<el-dropdown-item @click="openAccountList(props.row.userId)" v-if="(props.row.type !== 0 || userStore.user.type === 0)" >{{ $t('account') }}</el-dropdown-item>
|
<el-dropdown-item @click="openAccountList(props.row.userId)" >{{ $t('account') }}</el-dropdown-item>
|
||||||
<el-dropdown-item @click="openDetails(props.row)" v-if="(props.row.type !== 0 || userStore.user.type === 0)" >{{ $t('details') }}</el-dropdown-item>
|
<el-dropdown-item @click="openDetails(props.row)" >{{ $t('details') }}</el-dropdown-item>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
</template>
|
</template>
|
||||||
</el-dropdown>
|
</el-dropdown>
|
||||||
@@ -286,6 +289,78 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
<el-dropdown
|
||||||
|
:show-timeout="0"
|
||||||
|
:hide-timeout="0"
|
||||||
|
ref="dropdownRef"
|
||||||
|
@visible-change="visibleChange"
|
||||||
|
:virtual-ref="triggerRef"
|
||||||
|
:show-arrow="false"
|
||||||
|
:popper-options="{
|
||||||
|
modifiers: [{ name: 'offset', options: { offset: [0, 0] } }],
|
||||||
|
}"
|
||||||
|
virtual-triggering
|
||||||
|
trigger="contextmenu"
|
||||||
|
placement="bottom-start"
|
||||||
|
>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item @click="openSetPwd(rightClickUser)">
|
||||||
|
<template #default>
|
||||||
|
<div class="right-dropdown-item">
|
||||||
|
<icon icon="fluent:fingerprint-20-filled" width="22" height="22" />
|
||||||
|
<span>{{t('changePassword')}}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item @click="openSetType(rightClickUser)">
|
||||||
|
<template #default>
|
||||||
|
<div class="right-dropdown-item">
|
||||||
|
<icon icon="fluent:lock-closed-16-regular" width="21" height="21" />
|
||||||
|
<span>{{ t('setRole') }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item v-if="rightClickUser.type !== 0">
|
||||||
|
<template #default>
|
||||||
|
<div class="right-dropdown-item" v-if="rightClickUser.isDel !== 1" @click="setStatus(rightClickUser)" >
|
||||||
|
<Icon icon="ion:reload" v-if="rightClickUser.status" style="margin-left: 1px;margin-right: 1px" width="19" height="19" />
|
||||||
|
<Icon icon="ion:ban-outline" v-else style="margin-left: 1px;margin-right: 1px" width="19" height="19" />
|
||||||
|
<span>{{ setRightStatusName(rightClickUser) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="right-dropdown-item" v-else @click="restore(rightClickUser)">
|
||||||
|
<Icon icon="ion:reload" style="margin-left: 1px;margin-right: 1px" width="19" height="19" />
|
||||||
|
<span>{{ t('restoreUser') }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item @click="openAccountList(rightClickUser.userId)" >
|
||||||
|
<template #default>
|
||||||
|
<div class="right-dropdown-item" >
|
||||||
|
<Icon icon="hugeicons:mailbox-01" width="20" height="20" />
|
||||||
|
<span>{{ t('userEmail') }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item @click="openDetails(rightClickUser)" >
|
||||||
|
<template #default>
|
||||||
|
<div class="right-dropdown-item" >
|
||||||
|
<Icon icon="si:user-alt-2-line" width="20" height="20" />
|
||||||
|
<span>{{ t('userDetails') }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item v-if="rightClickUser.type !== 0" @click="delOneUser(rightClickUser)" >
|
||||||
|
<template #default>
|
||||||
|
<div class="right-dropdown-item" >
|
||||||
|
<Icon icon="uiw:delete" width="18" height="18" style="margin-left: 1px;margin-right: 1px" />
|
||||||
|
<span>{{ t('adminDeleteUser') }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -344,7 +419,21 @@ const total = ref(0)
|
|||||||
const first = ref(true)
|
const first = ref(true)
|
||||||
const scrollbarRef = ref(null)
|
const scrollbarRef = ref(null)
|
||||||
const accountLoading = ref(false)
|
const accountLoading = ref(false)
|
||||||
|
const dropdownRef = ref(null);
|
||||||
|
const dropdownShow = ref(false);
|
||||||
|
const rightClickUser = ref({});
|
||||||
|
const position = ref(
|
||||||
|
DOMRect.fromRect({
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const triggerRef = ref({
|
||||||
|
getBoundingClientRect() {
|
||||||
|
return position.value;
|
||||||
|
}
|
||||||
|
})
|
||||||
const domainList = settingStore.domainList
|
const domainList = settingStore.domainList
|
||||||
|
|
||||||
const addForm = reactive({
|
const addForm = reactive({
|
||||||
@@ -425,6 +514,43 @@ const filterItem = reactive({
|
|||||||
receive: ['normal', 'del']
|
receive: ['normal', 'del']
|
||||||
})
|
})
|
||||||
|
|
||||||
|
window.addEventListener('wheel', (event) => {
|
||||||
|
if (dropdownShow.value) {
|
||||||
|
dropdownRef.value.handleClose();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function visibleChange(e) {
|
||||||
|
dropdownShow.value = e;
|
||||||
|
if (!e) {
|
||||||
|
rightClickUser.value.checkedClass = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function cellClassName({ row }) {
|
||||||
|
return row.checkedClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleContextmenu = (row, column, cell, event) => {
|
||||||
|
|
||||||
|
if (row.type === 0 && userStore.user.type !== 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rightClickUser.value.checkedClass = '';
|
||||||
|
|
||||||
|
const { clientX, clientY } = event
|
||||||
|
position.value = DOMRect.fromRect({
|
||||||
|
x: clientX,
|
||||||
|
y: clientY,
|
||||||
|
})
|
||||||
|
event.preventDefault()
|
||||||
|
dropdownRef.value?.handleOpen()
|
||||||
|
|
||||||
|
row.checkedClass = 'checked-row';
|
||||||
|
rightClickUser.value = row;
|
||||||
|
}
|
||||||
|
|
||||||
function deleteAccount(account) {
|
function deleteAccount(account) {
|
||||||
ElMessageBox.confirm(t('delConfirm', {msg: account.email}), {
|
ElMessageBox.confirm(t('delConfirm', {msg: account.email}), {
|
||||||
confirmButtonText: t('confirm'),
|
confirmButtonText: t('confirm'),
|
||||||
@@ -540,6 +666,12 @@ function setStatusName(user) {
|
|||||||
if (user.status === 1) return t('enable')
|
if (user.status === 1) return t('enable')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setRightStatusName(user) {
|
||||||
|
if (user.isDel === 1) return t('adminDeleteUser')
|
||||||
|
if (user.status === 0) return t('banUser')
|
||||||
|
if (user.status === 1) return t('enableUser')
|
||||||
|
}
|
||||||
|
|
||||||
const tableRowFormatter = (data) => {
|
const tableRowFormatter = (data) => {
|
||||||
return data.row.email
|
return data.row.email
|
||||||
}
|
}
|
||||||
@@ -683,7 +815,7 @@ function delUser(user) {
|
|||||||
if (userIds.length === 0) {
|
if (userIds.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ElMessageBox.confirm(t('delConfirm', {msg: user.email}), {
|
ElMessageBox.confirm(t('delUsersConfirm'), {
|
||||||
confirmButtonText: t('confirm'),
|
confirmButtonText: t('confirm'),
|
||||||
cancelButtonText: t('cancel'),
|
cancelButtonText: t('cancel'),
|
||||||
type: 'warning'
|
type: 'warning'
|
||||||
@@ -699,6 +831,23 @@ function delUser(user) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function delOneUser(user) {
|
||||||
|
ElMessageBox.confirm(t('delConfirm', {msg: user.email}), {
|
||||||
|
confirmButtonText: t('confirm'),
|
||||||
|
cancelButtonText: t('cancel'),
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
userDelete([user.userId]).then(() => {
|
||||||
|
ElMessage({
|
||||||
|
message: t('delSuccessMsg'),
|
||||||
|
type: "success",
|
||||||
|
plain: true
|
||||||
|
})
|
||||||
|
getUserList(true)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function restore(user) {
|
function restore(user) {
|
||||||
|
|
||||||
const type = ref(0)
|
const type = ref(0)
|
||||||
@@ -707,14 +856,14 @@ function restore(user) {
|
|||||||
confirmButtonText: t('confirm'),
|
confirmButtonText: t('confirm'),
|
||||||
cancelButtonText: t('cancel'),
|
cancelButtonText: t('cancel'),
|
||||||
message: () => h('div', [
|
message: () => h('div', [
|
||||||
h('div', {class: 'mb-2'}, t('restoreConfirm', {msg: user.email})),
|
h('div', {class: 'mb-2'}, t('restoreConfirm', {msg: user.email}))
|
||||||
h(ElRadioGroup, {
|
// h(ElRadioGroup, {
|
||||||
modelValue: type.value,
|
// modelValue: type.value,
|
||||||
'onUpdate:modelValue': (val) => (type.value = val),
|
// 'onUpdate:modelValue': (val) => (type.value = val),
|
||||||
}, [
|
// }, [
|
||||||
h(ElRadio, {label: 'option1', value: 0}, t('normalRestore')),
|
// h(ElRadio, {label: 'option1', value: 0}, t('normalRestore')),
|
||||||
h(ElRadio, {label: 'option2', value: 1}, t('allRestore')),
|
// h(ElRadio, {label: 'option2', value: 1}, t('allRestore')),
|
||||||
])
|
// ])
|
||||||
]),
|
]),
|
||||||
type: 'warning'
|
type: 'warning'
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
@@ -730,18 +879,7 @@ function restore(user) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setStatus(user) {
|
function setStatus(user) {
|
||||||
|
httpSetStatus(user);
|
||||||
if (user.status === 0) {
|
|
||||||
ElMessageBox.confirm(t('banRestore', {msg: user.email}), {
|
|
||||||
confirmButtonText: t('confirm'),
|
|
||||||
cancelButtonText: t('cancel'),
|
|
||||||
type: 'warning'
|
|
||||||
}).then(() => {
|
|
||||||
httpSetStatus(user)
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
httpSetStatus(user)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function httpSetStatus(user) {
|
function httpSetStatus(user) {
|
||||||
@@ -866,7 +1004,7 @@ function getUserList(loading = true) {
|
|||||||
newParams.isDel = 1
|
newParams.isDel = 1
|
||||||
}
|
}
|
||||||
userList(newParams).then(data => {
|
userList(newParams).then(data => {
|
||||||
users.value = data.list
|
users.value = data.list.map(item => ({...item, checkedClass: ''}))
|
||||||
total.value = data.total
|
total.value = data.total
|
||||||
scrollbarRef.value?.setScrollTop(0);
|
scrollbarRef.value?.setScrollTop(0);
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
@@ -917,6 +1055,10 @@ function adjustWidth() {
|
|||||||
</style>
|
</style>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
:deep(.el-table .checked-row) {
|
||||||
|
background: var(--el-color-warning-light-9);
|
||||||
|
}
|
||||||
|
|
||||||
.user-box {
|
.user-box {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@@ -994,7 +1136,7 @@ function adjustWidth() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.details {
|
.details {
|
||||||
padding: 10px 10px 10px 10px;
|
padding: 0 10px 10px 10px;
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
.details-item-title {
|
.details-item-title {
|
||||||
@@ -1093,6 +1235,10 @@ function adjustWidth() {
|
|||||||
top: 6px;
|
top: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.right-dropdown-item {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
const en = {
|
const en = {
|
||||||
IncorrectPwd: 'Incorrect password',
|
IncorrectPwd: 'Incorrect password',
|
||||||
addAccountDisabled: 'Add account feature is disabled',
|
addAccountDisabled: 'Add Email Address feature is disabled',
|
||||||
regDisabled: 'Sign up is disabled',
|
regDisabled: 'Sign up is disabled',
|
||||||
emptyEmail: 'Email cannot be empty',
|
emptyEmail: 'Email cannot be empty',
|
||||||
notEmail: 'Invalid email',
|
notEmail: 'Invalid email',
|
||||||
notExistDomain: 'Email domain does not exist',
|
notExistDomain: 'Email domain does not exist',
|
||||||
isDelAccount: 'This Email has been deleted',
|
isDelAccount: 'This Email has been deleted',
|
||||||
isRegAccount: 'This Email is already registered',
|
isRegAccount: 'This Email is already registered',
|
||||||
accountLimit: 'Account limit reached',
|
accountLimit: 'Email address limit reached',
|
||||||
delMyAccount: 'Cannot delete your own account',
|
delMyAccount: 'Cannot delete your own email',
|
||||||
noUserAccount: 'This email does not belong to the current user',
|
noUserAccount: 'This email does not belong to the current user',
|
||||||
usernameLengthLimit: 'Username length exceeds the limit',
|
usernameLengthLimit: 'Username length exceeds the limit',
|
||||||
noOsSendPic: 'Cannot send body images: object storage not configured',
|
noOsSendPic: 'Cannot send body images: object storage not configured',
|
||||||
@@ -64,13 +64,13 @@ const en = {
|
|||||||
emailExistDatabase: 'Email already exists in the database',
|
emailExistDatabase: 'Email already exists in the database',
|
||||||
notConfigOss: 'Object storage not configured',
|
notConfigOss: 'Object storage not configured',
|
||||||
perms: {
|
perms: {
|
||||||
"邮件": "Email",
|
"邮件": "Emails",
|
||||||
"邮件发送": "Send Email",
|
"邮件发送": "Send Email",
|
||||||
"邮件删除": "Delete Email",
|
"邮件删除": "Delete Email",
|
||||||
"邮箱侧栏": "Account",
|
"邮箱侧栏": "Email Address",
|
||||||
"邮箱查看": "View Account",
|
"邮箱查看": "View Email",
|
||||||
"邮箱添加": "Add Account",
|
"邮箱添加": "Add Email",
|
||||||
"邮箱删除": "Delete Account",
|
"邮箱删除": "Delete Email",
|
||||||
"个人设置": "Settings",
|
"个人设置": "Settings",
|
||||||
"用户注销": "Delete User",
|
"用户注销": "Delete User",
|
||||||
"分析页": "Analytics",
|
"分析页": "Analytics",
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ const requirePerms = [
|
|||||||
'/account/delete',
|
'/account/delete',
|
||||||
'/account/add',
|
'/account/add',
|
||||||
'/my/delete',
|
'/my/delete',
|
||||||
|
'/analysis/echarts',
|
||||||
'/role/add',
|
'/role/add',
|
||||||
'/role/list',
|
'/role/list',
|
||||||
'/role/delete',
|
'/role/delete',
|
||||||
@@ -36,6 +37,8 @@ const requirePerms = [
|
|||||||
'/role/setDefault',
|
'/role/setDefault',
|
||||||
'/allEmail/list',
|
'/allEmail/list',
|
||||||
'/allEmail/delete',
|
'/allEmail/delete',
|
||||||
|
'/allEmail/batchDelete',
|
||||||
|
'/allEmail/latest',
|
||||||
'/setting/setBackground',
|
'/setting/setBackground',
|
||||||
'/setting/deleteBackground',
|
'/setting/deleteBackground',
|
||||||
'/setting/set',
|
'/setting/set',
|
||||||
|
|||||||
@@ -449,7 +449,7 @@ const emailService = {
|
|||||||
let count = 0
|
let count = 0
|
||||||
let list = []
|
let list = []
|
||||||
|
|
||||||
while ((count < 10) && list.length === 0) {
|
while ((count < 6) && list.length === 0) {
|
||||||
list = await orm(c).select({...email}).from(email)
|
list = await orm(c).select({...email}).from(email)
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
account,
|
account,
|
||||||
@@ -467,7 +467,7 @@ const emailService = {
|
|||||||
.orderBy(desc(email.emailId))
|
.orderBy(desc(email.emailId))
|
||||||
.limit(20);
|
.limit(20);
|
||||||
|
|
||||||
await sleep(3000);
|
await sleep(5000);
|
||||||
count++
|
count++
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -556,24 +556,24 @@ const emailService = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (userEmail) {
|
if (userEmail) {
|
||||||
conditions.push(sql`${user.email} COLLATE NOCASE LIKE ${userEmail + '%'}`);
|
conditions.push(sql`${user.email} COLLATE NOCASE LIKE ${'%'+ userEmail + '%'}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (accountEmail) {
|
if (accountEmail) {
|
||||||
conditions.push(
|
conditions.push(
|
||||||
or(
|
or(
|
||||||
sql`${email.toEmail} COLLATE NOCASE LIKE ${accountEmail + '%'}`,
|
sql`${email.toEmail} COLLATE NOCASE LIKE ${'%'+ accountEmail + '%'}`,
|
||||||
sql`${email.sendEmail} COLLATE NOCASE LIKE ${accountEmail + '%'}`,
|
sql`${email.sendEmail} COLLATE NOCASE LIKE ${'%'+ accountEmail + '%'}`,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name) {
|
if (name) {
|
||||||
conditions.push(sql`${email.name} COLLATE NOCASE LIKE ${name + '%'}`);
|
conditions.push(sql`${email.name} COLLATE NOCASE LIKE ${'%'+ name + '%'}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (subject) {
|
if (subject) {
|
||||||
conditions.push(sql`${email.subject} COLLATE NOCASE LIKE ${subject + '%'}`);
|
conditions.push(sql`${email.subject} COLLATE NOCASE LIKE ${'%'+ subject + '%'}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
conditions.push(ne(email.status, emailConst.status.SAVING));
|
conditions.push(ne(email.status, emailConst.status.SAVING));
|
||||||
@@ -633,7 +633,7 @@ const emailService = {
|
|||||||
let count = 0
|
let count = 0
|
||||||
let list = []
|
let list = []
|
||||||
|
|
||||||
while ((count < 10) && list.length === 0) {
|
while ((count < 6) && list.length === 0) {
|
||||||
list = await orm(c).select({...email, userEmail: user.email}).from(email)
|
list = await orm(c).select({...email, userEmail: user.email}).from(email)
|
||||||
.leftJoin(user, eq(email.userId, user.userId))
|
.leftJoin(user, eq(email.userId, user.userId))
|
||||||
.where(
|
.where(
|
||||||
@@ -645,7 +645,7 @@ const emailService = {
|
|||||||
.orderBy(desc(email.emailId))
|
.orderBy(desc(email.emailId))
|
||||||
.limit(20);
|
.limit(20);
|
||||||
|
|
||||||
await sleep(3000);
|
await sleep(5000);
|
||||||
count++
|
count++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ const userService = {
|
|||||||
|
|
||||||
|
|
||||||
if (email) {
|
if (email) {
|
||||||
conditions.push(sql`${user.email} COLLATE NOCASE LIKE ${email + '%'}`);
|
conditions.push(sql`${user.email} COLLATE NOCASE LIKE ${'%'+ email + '%'}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -250,6 +250,7 @@ const userService = {
|
|||||||
|
|
||||||
const { password, userId } = params;
|
const { password, userId } = params;
|
||||||
await this.resetPassword(c, { password }, userId);
|
await this.resetPassword(c, { password }, userId);
|
||||||
|
await c.env.kv.delete(KvConst.AUTH_INFO + userId);
|
||||||
},
|
},
|
||||||
|
|
||||||
async setStatus(c, params) {
|
async setStatus(c, params) {
|
||||||
|
|||||||
Reference in New Issue
Block a user