Merge remote-tracking branch 'origin/develop' into develop

This commit is contained in:
猿小天
2023-11-24 15:13:23 +08:00
16 changed files with 379 additions and 280 deletions

View File

@@ -73,7 +73,7 @@ class DvadminWebSocket(AsyncJsonWebsocketConsumer):
unread_count = await _get_message_unread(self.user_id) unread_count = await _get_message_unread(self.user_id)
if unread_count == 0: if unread_count == 0:
# 发送连接成功 # 发送连接成功
await self.send_json(set_message('system', 'SYSTEM', '连接成功')) await self.send_json(set_message('system', 'SYSTEM', '您已上线'))
else: else:
await self.send_json( await self.send_json(
set_message('system', 'SYSTEM', "请查看您的未读消息~", set_message('system', 'SYSTEM', "请查看您的未读消息~",

View File

@@ -28,6 +28,6 @@ server {
proxy_send_timeout 600s; proxy_send_timeout 600s;
real_ip_header X-Forwarded-For; real_ip_header X-Forwarded-For;
rewrite ^/api/(.*)$ /$1 break; #重写 rewrite ^/api/(.*)$ /$1 break; #重写
proxy_pass http://177.8.0.12:8000/; # 设置代理服务器的协议和地址 proxy_pass http://177.10.0.12:8000/; # 设置代理服务器的协议和地址
} }
} }

View File

@@ -1,7 +1,7 @@
FROM registry.cn-zhangjiakou.aliyuncs.com/dvadmin-pro/dvadmin3-base-web:16.19-alpine FROM registry.cn-zhangjiakou.aliyuncs.com/dvadmin-pro/dvadmin3-base-web:16.19-alpine
WORKDIR /web/ WORKDIR /web/
COPY web/. . COPY web/. .
RUN yarn install RUN yarn install --registry=https://registry.npm.taobao.org
RUN yarn build RUN yarn build
FROM nginx:alpine FROM nginx:alpine

View File

@@ -62,7 +62,7 @@
"@typescript-eslint/parser": "^5.46.0", "@typescript-eslint/parser": "^5.46.0",
"@vitejs/plugin-vue": "^4.0.0", "@vitejs/plugin-vue": "^4.0.0",
"@vue/compiler-sfc": "^3.2.45", "@vue/compiler-sfc": "^3.2.45",
"eslint": "^8.29.0", "eslint": "^8.54.0",
"eslint-plugin-vue": "^9.8.0", "eslint-plugin-vue": "^9.8.0",
"prettier": "^2.8.1", "prettier": "^2.8.1",
"sass": "^1.56.2", "sass": "^1.56.2",

View File

@@ -59,12 +59,6 @@ onBeforeMount(() => {
setIntroduction.cssCdn(); setIntroduction.cssCdn();
// 设置批量第三方 js // 设置批量第三方 js
setIntroduction.jsCdn(); setIntroduction.jsCdn();
//websockt 模块
try {
//websocket.init(wsReceive)
} catch (e) {
console.log('websocket错误');
}
}); });
// 页面加载时 // 页面加载时
onMounted(() => { onMounted(() => {
@@ -93,6 +87,14 @@ watch(
() => route.path, () => route.path,
() => { () => {
other.useTitle(); other.useTitle();
if (!websocket.websocket) {
//websockt 模块
try {
websocket.init(wsReceive)
} catch (e) {
console.log('websocket错误');
}
}
}, },
{ {
deep: true, deep: true,

View File

@@ -120,4 +120,12 @@ export default {
copyTextSuccess: 'Copy succeeded!', copyTextSuccess: 'Copy succeeded!',
copyTextError: 'Copy failed!', copyTextError: 'Copy failed!',
}, },
upgrade: {
title: 'New version upgrade',
msg: 'It\'s a new version. Update it nowDon\'t worry, update quickly oh!',
desc: 'Tip: The update restores the default configuration',
btnOne: 'Cruel refusal',
btnTwo: 'Update now',
btnTwoLoading: 'updating',
},
}; };

View File

@@ -40,6 +40,8 @@ export default {
title4: '消息', title4: '消息',
title5: '开全屏', title5: '开全屏',
title6: '关全屏', title6: '关全屏',
retry: '重试上线',
onlinePrompt: '当前离线状态,是否重试上线?',
dropdownLarge: '大型', dropdownLarge: '大型',
dropdownDefault: '默认', dropdownDefault: '默认',
dropdownSmall: '小型', dropdownSmall: '小型',
@@ -75,7 +77,7 @@ export default {
}, },
noAccess: { noAccess: {
accessTitle: '您未被授权,没有操作权限~', accessTitle: '您未被授权,没有操作权限~',
accessMsg: '联系方式加QQ群探讨 665452019', accessMsg: '联系管理员',
accessBtn: '重新授权', accessBtn: '重新授权',
}, },
layout: { layout: {
@@ -89,10 +91,12 @@ export default {
twoIsTopBarColorGradual: '顶栏背景渐变', twoIsTopBarColorGradual: '顶栏背景渐变',
twoMenuBar: '菜单背景', twoMenuBar: '菜单背景',
twoMenuBarColor: '菜单默认字体颜色', twoMenuBarColor: '菜单默认字体颜色',
twoMenuBarActiveColor: '菜单高亮背景色',
twoIsMenuBarColorGradual: '菜单背景渐变', twoIsMenuBarColorGradual: '菜单背景渐变',
twoColumnsMenuBar: '分栏菜单背景', twoColumnsMenuBar: '分栏菜单背景',
twoColumnsMenuBarColor: '分栏菜单默认字体颜色', twoColumnsMenuBarColor: '分栏菜单默认字体颜色',
twoIsColumnsMenuBarColorGradual: '分栏菜单背景渐变', twoIsColumnsMenuBarColorGradual: '分栏菜单背景渐变',
twoIsColumnsMenuHoverPreload: '分栏菜单滑鼠悬停预加载',
threeTitle: '界面设置', threeTitle: '界面设置',
threeIsCollapse: '菜单水平折叠', threeIsCollapse: '菜单水平折叠',
threeIsUniqueOpened: '菜单手风琴', threeIsUniqueOpened: '菜单手风琴',
@@ -131,4 +135,12 @@ export default {
copyTextSuccess: '复制成功!', copyTextSuccess: '复制成功!',
copyTextError: '复制失败!', copyTextError: '复制失败!',
}, },
upgrade: {
title: '新版本升级',
msg: '新版本来啦,马上更新尝鲜吧!不用担心,更新很快的哦!',
desc: '提示:更新会还原默认配寘',
btnOne: '残忍拒绝',
btnTwo: '马上更新',
btnTwoLoading: '更新中',
},
}; };

View File

@@ -123,7 +123,7 @@ export default {
}, },
noAccess: { noAccess: {
accessTitle: '您未被授權,沒有操作許可權~', accessTitle: '您未被授權,沒有操作許可權~',
accessMsg: '聯繫方式加QQ群探討665452019', accessMsg: '請聯系管理員',
accessBtn: '重新授權', accessBtn: '重新授權',
}, },
layout: { layout: {

View File

@@ -1,7 +1,7 @@
// 定义内容 // 定义内容
export default { export default {
label: { label: {
one1: '用户名登录', one1: '账号密码登录',
two2: '手机号登录', two2: '手机号登录',
}, },
link: { link: {

View File

@@ -4,7 +4,7 @@
<el-scrollbar ref="layoutMainScrollbarRef" class="layout-main-scroll layout-backtop-header-fixed" <el-scrollbar ref="layoutMainScrollbarRef" class="layout-main-scroll layout-backtop-header-fixed"
wrap-class="layout-main-scroll" view-class="layout-main-scroll"> wrap-class="layout-main-scroll" view-class="layout-main-scroll">
<LayoutParentView /> <LayoutParentView />
<!-- <LayoutFooter v-if="isFooter" /> --> <LayoutFooter v-if="isFooter" />
</el-scrollbar> </el-scrollbar>
<el-backtop :target="setBacktopClass" /> <el-backtop :target="setBacktopClass" />
</el-main> </el-main>

View File

@@ -1,8 +1,7 @@
<template> <template>
<div class="layout-footer pb15"> <div class="layout-footer pb5 pt2">
<div class="layout-footer-warp"> <div class="layout-footer-warp">
<div> Powered by Django-Vue3-Admin </div> <div> Powered by Django-Vue3-Admin Copyright © DVAdmin团队 </div>
<div class="mt5">Copyright DVAdmin团队</div>
</div> </div>
</div> </div>
</template> </template>

View File

@@ -57,9 +57,33 @@
:class="!state.isScreenfull ? 'icon-fullscreen' : 'icon-tuichuquanping'" :class="!state.isScreenfull ? 'icon-fullscreen' : 'icon-tuichuquanping'"
></i> ></i>
</div> </div>
<div>
<span v-if="!isSocketOpen">
<el-popconfirm
width="250"
ref="onlinePopoverRef"
:confirm-button-text="$t('message.user.retry')"
:icon="InfoFilled"
trigger="hover"
icon-color="#626AEF"
:title="$t('message.user.onlinePrompt')"
@confirm="onlineConfirmEvent"
>
<template #reference>
<el-badge is-dot class="item" :class="{'online-status': isSocketOpen,'online-down':!isSocketOpen}">
<img :src="userInfos.avatar || headerImage" class="layout-navbars-breadcrumb-user-link-photo mr5" />
</el-badge>
</template>
</el-popconfirm>
</span>
</div>
<el-dropdown :show-timeout="70" :hide-timeout="50" @command="onHandleCommandClick"> <el-dropdown :show-timeout="70" :hide-timeout="50" @command="onHandleCommandClick">
<span class="layout-navbars-breadcrumb-user-link"> <span class="layout-navbars-breadcrumb-user-link">
<span v-if="isSocketOpen">
<el-badge is-dot class="item" :class="{'online-status': isSocketOpen,'online-down':!isSocketOpen}">
<img :src="userInfos.avatar || headerImage" class="layout-navbars-breadcrumb-user-link-photo mr5" /> <img :src="userInfos.avatar || headerImage" class="layout-navbars-breadcrumb-user-link-photo mr5" />
</el-badge>
</span>
{{ userInfos.username === '' ? 'common' : userInfos.username }} {{ userInfos.username === '' ? 'common' : userInfos.username }}
<el-icon class="el-icon--right"> <el-icon class="el-icon--right">
<ele-ArrowDown /> <ele-ArrowDown />
@@ -79,7 +103,7 @@
</template> </template>
<script setup lang="ts" name="layoutBreadcrumbUser"> <script setup lang="ts" name="layoutBreadcrumbUser">
import { defineAsyncComponent, ref, computed, reactive, onMounted } from 'vue'; import { defineAsyncComponent, ref, computed, reactive, onMounted, unref, watch } from 'vue';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { ElMessageBox, ElMessage } from 'element-plus'; import { ElMessageBox, ElMessage } from 'element-plus';
import screenfull from 'screenfull'; import screenfull from 'screenfull';
@@ -91,6 +115,8 @@ import other from '/@/utils/other';
import mittBus from '/@/utils/mitt'; import mittBus from '/@/utils/mitt';
import { Session, Local } from '/@/utils/storage'; import { Session, Local } from '/@/utils/storage';
import headerImage from '/@/assets/img/headerImage.png'; import headerImage from '/@/assets/img/headerImage.png';
import websocket from '/@/utils/websocket';
import { InfoFilled } from '@element-plus/icons-vue'
// 引入组件 // 引入组件
const UserNews = defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/userNews.vue')); const UserNews = defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/userNews.vue'));
const Search = defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/search.vue')); const Search = defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/search.vue'));
@@ -118,6 +144,21 @@ const layoutUserFlexNum = computed(() => {
else num = ''; else num = '';
return num; return num;
}); });
// 定义变量内容
const { isSocketOpen } = storeToRefs(useUserInfo());
// websocket状态
const onlinePopoverRef = ref()
const onlineConfirmEvent = () => {
if (!isSocketOpen.value) {
websocket.is_reonnect = true
websocket.reconnect_current = 1
websocket.reconnect()
}
// 手动隐藏弹出
unref(onlinePopoverRef).popperRef?.delayHide?.()
}
// 全屏点击时 // 全屏点击时
const onScreenfullClick = () => { const onScreenfullClick = () => {
if (!screenfull.isEnabled) { if (!screenfull.isEnabled) {
@@ -256,5 +297,29 @@ const messageCenter = messageCenterStore();
:deep(.el-badge__content.is-fixed) { :deep(.el-badge__content.is-fixed) {
top: 12px; top: 12px;
} }
.online-status{
cursor: pointer;
:deep .el-badge__content.is-fixed {
top: 30px;
font-size: 14px;
left: 5px;
height: 12px;
width: 12px;
padding: 0;
background-color: #18bc9c;
}
}
.online-down{
cursor: pointer;
:deep .el-badge__content.is-fixed {
top: 30px;
font-size: 14px;
left: 5px;
height: 12px;
width: 12px;
padding: 0;
background-color: #979b9c;
}
}
} }
</style> </style>

View File

@@ -17,7 +17,7 @@
<div class="upgrade-content"> <div class="upgrade-content">
{{ getThemeConfig.globalTitle }} {{ $t('message.upgrade.msg') }} {{ getThemeConfig.globalTitle }} {{ $t('message.upgrade.msg') }}
<div class="mt5"> <div class="mt5">
<el-link type="primary" class="font12" href="https://gitee.com/lyt-top/vue-next-admin/blob/master/CHANGELOG.md" target="_black"> <el-link type="primary" class="font12" href=https://gitee.com/huge-dream/django-vue3-admin/blob/master/CHANGELOG.md" target="_black">
CHANGELOG.md CHANGELOG.md
</el-link> </el-link>
</div> </div>
@@ -76,10 +76,10 @@ const delayShow = () => {
}; };
// 页面加载时 // 页面加载时
onMounted(() => { onMounted(() => {
// delayShow(); delayShow();
// setTimeout(() => { setTimeout(() => {
// state.btnTxt = t('message.upgrade.btnTwo'); state.btnTxt = t('message.upgrade.btnTwo');
// }, 200); }, 200);
}); });
</script> </script>

View File

@@ -19,6 +19,7 @@ export interface UserInfosState {
} }
export interface UserInfosStates { export interface UserInfosStates {
userInfos: UserInfosState; userInfos: UserInfosState;
isSocketOpen: boolean
} }
// 路由缓存列表 // 路由缓存列表

View File

@@ -26,6 +26,7 @@ export const useUserInfo = defineStore('userInfo', {
}, },
], ],
}, },
isSocketOpen: false
}), }),
actions: { actions: {
async updateUserInfos() { async updateUserInfos() {
@@ -57,6 +58,9 @@ export const useUserInfo = defineStore('userInfo', {
Session.set('userInfo', this.userInfos); Session.set('userInfo', this.userInfos);
} }
}, },
async setWebSocketState(socketState: boolean) {
this.isSocketOpen = socketState;
},
async getApiUserInfo() { async getApiUserInfo() {
return request({ return request({
url: '/api/system/user/user_info/', url: '/api/system/user/user_info/',

View File

@@ -3,7 +3,7 @@ import {Session} from "/@/utils/storage";
import {getWsBaseURL} from "/@/utils/baseUrl"; import {getWsBaseURL} from "/@/utils/baseUrl";
// @ts-ignore // @ts-ignore
import socket from '@/types/api/socket' import socket from '@/types/api/socket'
import {useUserInfo} from "/@/stores/userInfo";
const websocket: socket = { const websocket: socket = {
websocket: null, websocket: null,
connectURL: getWsBaseURL(), connectURL: getWsBaseURL(),
@@ -42,6 +42,7 @@ const websocket: socket = {
} }
websocket.websocket.onclose = (e: any) => { websocket.websocket.onclose = (e: any) => {
websocket.socket_open = false websocket.socket_open = false
useUserInfo().setWebSocketState(websocket.socket_open);
// 需要重新连接 // 需要重新连接
if (websocket.is_reonnect) { if (websocket.is_reonnect) {
websocket.reconnect_timer = setTimeout(() => { websocket.reconnect_timer = setTimeout(() => {
@@ -49,6 +50,8 @@ const websocket: socket = {
if (websocket.reconnect_current > websocket.reconnect_count) { if (websocket.reconnect_current > websocket.reconnect_count) {
clearTimeout(websocket.reconnect_timer) clearTimeout(websocket.reconnect_timer)
websocket.is_reonnect = false websocket.is_reonnect = false
websocket.socket_open = false
useUserInfo().setWebSocketState(websocket.socket_open);
return return
} }
// 记录重连次数 // 记录重连次数
@@ -60,6 +63,7 @@ const websocket: socket = {
// 连接成功 // 连接成功
websocket.websocket.onopen = function () { websocket.websocket.onopen = function () {
websocket.socket_open = true websocket.socket_open = true
useUserInfo().setWebSocketState(websocket.socket_open);
websocket.is_reonnect = true websocket.is_reonnect = true
// 开启心跳 // 开启心跳
websocket.heartbeat() websocket.heartbeat()
@@ -85,17 +89,21 @@ const websocket: socket = {
callback && callback() callback && callback()
} else { } else {
clearInterval(websocket.hearbeat_timer) clearInterval(websocket.hearbeat_timer)
message({ // message({
type: 'warning', // type: 'warning',
message: 'socket链接已断开', // message: 'socket链接已断开',
duration: 1000, // duration: 1000,
}) // })
websocket.socket_open = false
useUserInfo().setWebSocketState(websocket.socket_open);
} }
}, },
close: () => { close: () => {
websocket.is_reonnect = false websocket.is_reonnect = false
websocket.websocket.close() websocket.websocket.close()
websocket.websocket = null; websocket.websocket = null;
websocket.socket_open = false
useUserInfo().setWebSocketState(websocket.socket_open);
}, },
/** /**
* 重新连接 * 重新连接