From d7edbde434e6b0c31d5af99d3b5be5dfbe4c0b03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E7=BB=A7=E9=A3=8E?= <1914007838@qq.com> Date: Mon, 24 Jun 2024 22:35:17 +0800 Subject: [PATCH 01/14] =?UTF-8?q?[feat]=E5=A2=9E=E5=8A=A0=E7=94=9F?= =?UTF-8?q?=E4=BA=A7=E7=8E=AF=E5=A2=83=E4=B8=AD=E5=89=8D=E7=AB=AF=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E6=9B=B4=E6=96=B0=E7=89=88=E6=9C=AC=E5=90=8E=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E7=AB=AF=E8=87=AA=E5=8A=A8=E5=8D=87=E7=BA=A7=E4=B8=BA?= =?UTF-8?q?=E6=9C=80=E6=96=B0=E5=89=8D=E7=AB=AF=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/.gitignore | 1 + web/src/router/index.ts | 3 +++ web/src/utils/loading.ts | 4 +++ web/src/utils/upgrade.ts | 54 ++++++++++++++++++++++++++++++++++++++++ web/vite.config.ts | 3 +++ 5 files changed, 65 insertions(+) create mode 100644 web/src/utils/upgrade.ts diff --git a/web/.gitignore b/web/.gitignore index aa1baf2..455b613 100644 --- a/web/.gitignore +++ b/web/.gitignore @@ -21,3 +21,4 @@ pnpm-debug.log* *.njsproj *.sln *.sw? +public/version diff --git a/web/src/router/index.ts b/web/src/router/index.ts index 3f564eb..53aa3d9 100644 --- a/web/src/router/index.ts +++ b/web/src/router/index.ts @@ -13,6 +13,7 @@ import {initBackEndControlRoutes, setRouters} from '/@/router/backEnd'; import {useFrontendMenuStore} from "/@/stores/frontendMenu"; import {useTagsViewRoutes} from "/@/stores/tagsViewRoutes"; import {toRaw} from "vue"; +import {checkVersion} from "/@/utils/upgrade"; /** * 1、前端控制路由时:isRequestRoutes 为 false,需要写 roles,需要走 setFilterRoute 方法。 @@ -95,6 +96,8 @@ export function formatTwoStageRoutes(arr: any) { // 路由加载前 router.beforeEach(async (to, from, next) => { + // 检查浏览器本地版本与线上版本是否一致,判断是否需要刷新页面进行更新 + await checkVersion() NProgress.configure({showSpinner: false}); if (to.meta.title) NProgress.start(); const token = Session.get('token'); diff --git a/web/src/utils/loading.ts b/web/src/utils/loading.ts index 5fd020c..9dce4bd 100644 --- a/web/src/utils/loading.ts +++ b/web/src/utils/loading.ts @@ -1,5 +1,7 @@ import { nextTick } from 'vue'; import '/@/theme/loading.scss'; +import { showUpgrade } from "/@/utils/upgrade"; + /** * 页面全局 Loading @@ -9,6 +11,8 @@ import '/@/theme/loading.scss'; export const NextLoading = { // 创建 loading start: () => { + // 显示升级提示 + showUpgrade() const bodys: Element = document.body; const div = document.createElement('div'); div.setAttribute('class', 'loading-next'); diff --git a/web/src/utils/upgrade.ts b/web/src/utils/upgrade.ts new file mode 100644 index 0000000..6ca9e67 --- /dev/null +++ b/web/src/utils/upgrade.ts @@ -0,0 +1,54 @@ +import axios from "axios"; +import * as process from "process"; +import {Local, Session} from '/@/utils/storage'; +import {ElNotification} from "element-plus"; +import fs from "fs"; + +// 是否显示升级提示信息框 +export const IS_SHOW_UPGRADE_SESSION_KEY = 'isShowUpgrade'; +const versionKey = 'DVADMIN3_VERSION' + +export function showUpgrade () { + const isShowUpgrade = Session.get(IS_SHOW_UPGRADE_SESSION_KEY) ?? false + if (isShowUpgrade) { + Session.remove(IS_SHOW_UPGRADE_SESSION_KEY) + ElNotification({ + title: '新版本升级', + message: "检测到系统新版本,正在更新中!不用担心,更新很快的哦!", + type: 'success', + duration: 5000, + }); + } +} + +// 生产环境前端版本校验, +export async function checkVersion(){ + if (process.env.NODE_ENV === 'development') { + // 开发环境无需校验前端版本 + return + } + // 获取线上版本号 t为时间戳,防止缓存 + await axios.get(`/version?t=${new Date().getTime()}`).then(res => { + const {status, data} = res || {} + if (status === 200) { + // 获取当前版本号 + const localVersion = Local.get(versionKey) + // 将当前版本号持久缓存至本地 + Local.set(versionKey, data) + // 当用户本地存在版本号并且和线上版本号不一致时,进行页面刷新操作 + if (localVersion && localVersion !== data) { + // 本地缓存版本号和线上版本号不一致,弹出升级提示框 + // 此处无法直接使用消息框进行提醒,因为 window.location.reload()会导致消息框消失,将在loading页面判断是否需要显示升级提示框 + Session.set(IS_SHOW_UPGRADE_SESSION_KEY, true) + window.location.reload() + + } + } + }) +} + +export function generateVersionFile (){ + // 生成版本文件到public目录下version文件中 + const version = `${process.env.npm_package_version}.${new Date().getTime()}`; + fs.writeFileSync('public/version', version); +} diff --git a/web/vite.config.ts b/web/vite.config.ts index c86cfc4..d91057f 100644 --- a/web/vite.config.ts +++ b/web/vite.config.ts @@ -3,6 +3,7 @@ import { resolve } from 'path'; import { defineConfig, loadEnv, ConfigEnv } from 'vite'; import vueSetupExtend from 'vite-plugin-vue-setup-extend'; import vueJsx from '@vitejs/plugin-vue-jsx' +import { generateVersionFile } from "./src/utils/upgrade"; const pathResolve = (dir: string) => { return resolve(__dirname, '.', dir); @@ -17,6 +18,8 @@ const alias: Record = { const viteConfig = defineConfig((mode: ConfigEnv) => { const env = loadEnv(mode.mode, process.cwd()); + // 当Vite构建时,生成版本文件 + generateVersionFile() return { plugins: [vue(), vueJsx(), vueSetupExtend()], root: process.cwd(), From 0529c2747a6cb074db497889dfdbca99d5898eb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E7=BB=A7=E9=A3=8E?= <1914007838@qq.com> Date: Tue, 25 Jun 2024 10:16:11 +0800 Subject: [PATCH 02/14] =?UTF-8?q?[fix]=E4=BC=98=E5=8C=96=E5=89=8D=E7=AB=AF?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E5=86=99=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/.gitignore | 3 ++- web/src/utils/upgrade.ts | 13 +++++++------ web/vite.config.ts | 2 +- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/web/.gitignore b/web/.gitignore index 455b613..8cef918 100644 --- a/web/.gitignore +++ b/web/.gitignore @@ -21,4 +21,5 @@ pnpm-debug.log* *.njsproj *.sln *.sw? -public/version +# 构建版本文件,无需上传git +public/version-build diff --git a/web/src/utils/upgrade.ts b/web/src/utils/upgrade.ts index 6ca9e67..a571863 100644 --- a/web/src/utils/upgrade.ts +++ b/web/src/utils/upgrade.ts @@ -5,8 +5,9 @@ import {ElNotification} from "element-plus"; import fs from "fs"; // 是否显示升级提示信息框 -export const IS_SHOW_UPGRADE_SESSION_KEY = 'isShowUpgrade'; -const versionKey = 'DVADMIN3_VERSION' +const IS_SHOW_UPGRADE_SESSION_KEY = 'isShowUpgrade'; +const VERSION_KEY = 'DVADMIN3_VERSION' +const VERSION_FILE_NAME = 'version-build' export function showUpgrade () { const isShowUpgrade = Session.get(IS_SHOW_UPGRADE_SESSION_KEY) ?? false @@ -28,13 +29,13 @@ export async function checkVersion(){ return } // 获取线上版本号 t为时间戳,防止缓存 - await axios.get(`/version?t=${new Date().getTime()}`).then(res => { + await axios.get(`/${VERSION_FILE_NAME}?t=${new Date().getTime()}`).then(res => { const {status, data} = res || {} if (status === 200) { // 获取当前版本号 - const localVersion = Local.get(versionKey) + const localVersion = Local.get(VERSION_KEY) // 将当前版本号持久缓存至本地 - Local.set(versionKey, data) + Local.set(VERSION_KEY, data) // 当用户本地存在版本号并且和线上版本号不一致时,进行页面刷新操作 if (localVersion && localVersion !== data) { // 本地缓存版本号和线上版本号不一致,弹出升级提示框 @@ -50,5 +51,5 @@ export async function checkVersion(){ export function generateVersionFile (){ // 生成版本文件到public目录下version文件中 const version = `${process.env.npm_package_version}.${new Date().getTime()}`; - fs.writeFileSync('public/version', version); + fs.writeFileSync(`public/${VERSION_FILE_NAME}`, version); } diff --git a/web/vite.config.ts b/web/vite.config.ts index d91057f..ed6dec2 100644 --- a/web/vite.config.ts +++ b/web/vite.config.ts @@ -3,7 +3,7 @@ import { resolve } from 'path'; import { defineConfig, loadEnv, ConfigEnv } from 'vite'; import vueSetupExtend from 'vite-plugin-vue-setup-extend'; import vueJsx from '@vitejs/plugin-vue-jsx' -import { generateVersionFile } from "./src/utils/upgrade"; +import { generateVersionFile } from "/@/utils/upgrade"; const pathResolve = (dir: string) => { return resolve(__dirname, '.', dir); From 38ad2db7a75250550e9972c481eede14ef8d4ddf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E7=BB=A7=E9=A3=8E?= <1914007838@qq.com> Date: Tue, 25 Jun 2024 10:17:36 +0800 Subject: [PATCH 03/14] =?UTF-8?q?[feat]=E5=A2=9E=E5=8A=A0nginx=E4=B8=8D?= =?UTF-8?q?=E7=BC=93=E5=AD=98index.html=E9=85=8D=E7=BD=AE=EF=BC=8C?= =?UTF-8?q?=E6=90=AD=E9=85=8D=E5=89=8D=E7=AB=AF=E7=89=88=E6=9C=AC=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E5=8D=87=E7=BA=A7=E5=8A=9F=E8=83=BD=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=EF=BC=88=E4=B8=8D=E5=8A=A0=E6=AD=A4=E9=85=8D=E7=BD=AE=E5=8F=AF?= =?UTF-8?q?=E8=83=BD=E4=BC=9A=E5=AF=BC=E8=87=B4=E7=94=A8=E6=88=B7=E5=88=B7?= =?UTF-8?q?=E6=96=B0=E5=90=8E=E8=BF=98=E6=98=AF=E6=97=A7=E7=89=88=E6=9C=AC?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker_env/nginx/my-80.conf | 4 ++++ docker_env/nginx/my.conf | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/docker_env/nginx/my-80.conf b/docker_env/nginx/my-80.conf index e50bdc4..98d33bd 100644 --- a/docker_env/nginx/my-80.conf +++ b/docker_env/nginx/my-80.conf @@ -7,6 +7,10 @@ server { index index.html index.htm; root /usr/share/nginx/html; try_files $uri $uri/ /index.html; + # 禁止缓存html文件,避免前端页面不及时更新,需要用户手动刷新的情况 + if ($request_uri ~* "^/$|^/index.html|^/index.htm") { + add_header Cache-Control "no-store"; + } } location ~ ^/api/ { diff --git a/docker_env/nginx/my.conf b/docker_env/nginx/my.conf index 178d979..dd6b333 100644 --- a/docker_env/nginx/my.conf +++ b/docker_env/nginx/my.conf @@ -11,6 +11,10 @@ server { real_ip_header X-Forwarded-For; root /usr/share/nginx/html; index index.html index.php index.htm; + # 禁止缓存html文件,避免前端页面不及时更新,需要用户手动刷新的情况 + if ($request_uri ~* "^/$|^/index.html|^/index.htm") { + add_header Cache-Control "no-store"; + } } location /api/ { From 5b60f60b7053b451b8c8152657de5d6394d53108 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E7=BB=A7=E9=A3=8E?= <1914007838@qq.com> Date: Tue, 25 Jun 2024 23:53:54 +0800 Subject: [PATCH 04/14] =?UTF-8?q?[fix]=E6=9B=B4=E6=96=B0django-cors-header?= =?UTF-8?q?s=E7=89=88=E6=9C=AC=E8=87=B34.4.0,=20=E7=8E=B0=E5=9C=A84.3.0?= =?UTF-8?q?=E7=89=88=E6=9C=AC=E7=9A=84CorsMiddleware=E4=B8=AD=E9=97=B4?= =?UTF-8?q?=E4=BB=B6=E4=BC=9A=E5=AF=BC=E8=87=B4SessionMiddleware=E4=B8=AD?= =?UTF-8?q?=E6=97=A0=E6=B3=95=E8=AE=BE=E7=BD=AEsession?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/requirements.txt b/backend/requirements.txt index 6309e7c..2e7c6cb 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -1,6 +1,6 @@ Django==4.2.7 django-comment-migrate==0.1.7 -django-cors-headers==4.3.0 +django-cors-headers==4.4.0 django-filter==23.3 django-ranged-response==0.2.0 djangorestframework==3.14.0 From 259c51b23cefd07a41bca1652a00c45db42f5bdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E7=BB=A7=E9=A3=8E?= <1914007838@qq.com> Date: Wed, 26 Jun 2024 00:00:14 +0800 Subject: [PATCH 05/14] =?UTF-8?q?[fix]=E6=9B=B4=E6=96=B0django-cors-header?= =?UTF-8?q?s=E7=89=88=E6=9C=AC=E8=87=B34.4.0,=20=E7=8E=B0=E5=9C=A84.3.0?= =?UTF-8?q?=E7=89=88=E6=9C=AC=E7=9A=84CorsMiddleware=E4=B8=AD=E9=97=B4?= =?UTF-8?q?=E4=BB=B6=E4=BC=9A=E5=AF=BC=E8=87=B4SessionMiddleware=E4=B8=AD?= =?UTF-8?q?=E6=97=A0=E6=B3=95=E8=AE=BE=E7=BD=AEsession?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/requirements.txt b/backend/requirements.txt index 6309e7c..2e7c6cb 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -1,6 +1,6 @@ Django==4.2.7 django-comment-migrate==0.1.7 -django-cors-headers==4.3.0 +django-cors-headers==4.4.0 django-filter==23.3 django-ranged-response==0.2.0 djangorestframework==3.14.0 From 7234d2b3e90041c953c823607a4645c35052e2a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=A8=E5=AD=90-=E6=9D=8E?= <1537080775@qq.com> Date: Wed, 26 Jun 2024 05:53:20 +0000 Subject: [PATCH 06/14] update docker_env/django/Dockerfile. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 木子-李 <1537080775@qq.com> --- docker_env/django/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker_env/django/Dockerfile b/docker_env/django/Dockerfile index 427ed0c..cad6fed 100644 --- a/docker_env/django/Dockerfile +++ b/docker_env/django/Dockerfile @@ -6,4 +6,4 @@ RUN awk 'BEGIN { cmd="cp -i ./conf/env.example.py ./conf/env.py "; print "n" | RUN sed -i "s|DATABASE_HOST = '127.0.0.1'|DATABASE_HOST = '177.10.0.1'|g" ./conf/env.py RUN sed -i "s|REDIS_HOST = '127.0.0.1'|REDIS_HOST = '177.10.0.1'|g" ./conf/env.py RUN python3 -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple/ -r requirements.txt -CMD ["/backend/docker_start.sh"] +CMD ["sh","docker_start.sh"] From b6a4be25f2aee186188bd782beb0e1937830d0b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8C=97=E9=A3=8E=E5=8D=97=E9=87=8C?= Date: Wed, 26 Jun 2024 09:04:54 +0000 Subject: [PATCH 07/14] =?UTF-8?q?=E6=B7=BB=E5=8A=A0copy=EF=BC=8CImport?= =?UTF-8?q?=E5=8F=8AImport=E6=8C=89=E9=92=AE=EF=BC=8C=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E6=8B=BC=E6=8E=A5=E7=9A=84URL=E5=9C=B0=E5=9D=80=E4=B8=BAmenu?= =?UTF-8?q?=5Fobj.component=5Fname=20=E5=8E=9F=E6=9D=A5URL=E5=9C=B0?= =?UTF-8?q?=E5=9D=80=E6=98=AF=E6=8B=BC=E6=8E=A5=E5=89=8D=E7=AB=AF=E7=9A=84?= =?UTF-8?q?=E8=B7=AF=E7=94=B1=E5=9C=B0=E5=9D=80=EF=BC=8C=E8=BF=99=E4=B8=8E?= =?UTF-8?q?=E5=AE=9E=E9=99=85=E5=90=8E=E7=AB=AF=E5=9C=B0=E5=9D=80=E4=B8=8D?= =?UTF-8?q?=E5=8C=B9=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 北风南里 --- backend/dvadmin/system/views/menu_button.py | 22 ++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/backend/dvadmin/system/views/menu_button.py b/backend/dvadmin/system/views/menu_button.py index f6cc411..6ad1a0c 100644 --- a/backend/dvadmin/system/views/menu_button.py +++ b/backend/dvadmin/system/views/menu_button.py @@ -16,6 +16,8 @@ from dvadmin.utils.serializers import CustomModelSerializer from dvadmin.utils.viewset import CustomModelViewSet + + class MenuButtonSerializer(CustomModelSerializer): """ 菜单按钮-序列化器 @@ -92,17 +94,15 @@ class MenuButtonViewSet(CustomModelViewSet): """ menu_obj = Menu.objects.filter(id=request.data['menu']).first() result_list = [ - {'menu': menu_obj.id, 'name': '新增', 'value': f'{menu_obj.component_name}:Create', 'api': f'/api{menu_obj.web_path}/', - 'method': 1}, - {'menu': menu_obj.id, 'name': '删除', 'value': f'{menu_obj.component_name}:Delete', 'api': f'/api{menu_obj.web_path}/{{id}}/', - 'method': 3}, - {'menu': menu_obj.id, 'name': '修改', 'value': f'{menu_obj.component_name}:Update', 'api': f'/api{menu_obj.web_path}/{{id}}/', - 'method': 2}, - {'menu': menu_obj.id, 'name': '查询', 'value': f'{menu_obj.component_name}:Search', 'api': f'/api{menu_obj.web_path}/', - 'method': 0}, - {'menu': menu_obj.id, 'name': '详情', 'value': f'{menu_obj.component_name}:Retrieve', 'api': f'/api{menu_obj.web_path}/{{id}}/', - 'method': 0}] + {'menu': menu_obj.id, 'name': '新增', 'value': f'{menu_obj.component_name}:Create', 'api': f'/api{menu_obj.component_name}/', 'method': 1}, + {'menu': menu_obj.id, 'name': '删除', 'value': f'{menu_obj.component_name}:Delete', 'api': f'/api{menu_obj.component_name}/{{id}}/', 'method': 3}, + {'menu': menu_obj.id, 'name': '编辑', 'value': f'{menu_obj.component_name}:Update', 'api': f'/api{menu_obj.component_name}/{{id}}/', 'method': 2}, + {'menu': menu_obj.id, 'name': '查询', 'value': f'{menu_obj.component_name}:Search', 'api': f'/api{menu_obj.component_name}/', 'method': 0}, + {'menu': menu_obj.id, 'name': '详情', 'value': f'{menu_obj.component_name}:Retrieve', 'api': f'/api{menu_obj.component_name}/{{id}}/', 'method': 0}, + {'menu': menu_obj.id, 'name': '复制', 'value': f'{menu_obj.component_name}:Copy', 'api': f'/api{menu_obj.component_name}/', 'method': 1}, + {'menu': menu_obj.id, 'name': '导入', 'value': f'{menu_obj.component_name}:Import', 'api': f'/api{menu_obj.component_name}/import_data/', 'method': 1}, + {'menu': menu_obj.id, 'name': '导出', 'value': f'{menu_obj.component_name}:Import', 'api': f'/api{menu_obj.component_name}/export_data/', 'method': 1},] serializer = self.get_serializer(data=result_list, many=True) serializer.is_valid(raise_exception=True) serializer.save() - return SuccessResponse(serializer.data, msg="批量创建成功") + return SuccessResponse(serializer.data, msg="批量创建成功") \ No newline at end of file From 9383508a858b083a82a90ca4593423c823a5e324 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A5=BD=E5=A5=87=E5=AE=9D=E5=AE=9D?= Date: Fri, 28 Jun 2024 10:13:11 +0800 Subject: [PATCH 08/14] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=82=AE=E7=AE=B1?= =?UTF-8?q?=E5=92=8C=E6=89=8B=E6=9C=BA=E5=8F=B7=E7=99=BB=E5=BD=95=E6=94=AF?= =?UTF-8?q?=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/dvadmin/system/views/login.py | 12 ++++++++++-- web/public/version-build | 1 + web/src/i18n/pages/login/zh-cn.ts | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) create mode 100644 web/public/version-build diff --git a/backend/dvadmin/system/views/login.py b/backend/dvadmin/system/views/login.py index e04743c..f3adcb8 100644 --- a/backend/dvadmin/system/views/login.py +++ b/backend/dvadmin/system/views/login.py @@ -4,6 +4,7 @@ from datetime import datetime, timedelta from captcha.views import CaptchaStore, captcha_image from django.contrib import auth from django.contrib.auth import login +from django.db.models import Q from django.shortcuts import redirect from django.utils.translation import gettext_lazy as _ from drf_yasg import openapi @@ -83,11 +84,18 @@ class LoginSerializer(TokenObtainPairSerializer): else: self.image_code and self.image_code.delete() raise CustomValidationError("图片验证码错误") - - user = Users.objects.get(username=attrs['username']) + try: + user = Users.objects.get( + Q(username=attrs['username']) | Q(email=attrs['username']) | Q(mobile=attrs['username'])) + except Users.DoesNotExist: + raise CustomValidationError("您登录的账号不存在") + except Users.MultipleObjectsReturned: + raise CustomValidationError("您登录的账号存在多个,请联系管理员检查登录账号唯一性") if not user.is_active: raise CustomValidationError("账号已被锁定,联系管理员解锁") try: + # 必须重置用户名为username,否则使用邮箱手机号登录会提示密码错误 + attrs['username'] = user.username data = super().validate(attrs) data["name"] = self.user.name data["userId"] = self.user.id diff --git a/web/public/version-build b/web/public/version-build new file mode 100644 index 0000000..f6571be --- /dev/null +++ b/web/public/version-build @@ -0,0 +1 @@ +3.0.3.1719538291017 \ No newline at end of file diff --git a/web/src/i18n/pages/login/zh-cn.ts b/web/src/i18n/pages/login/zh-cn.ts index 6ab9813..07fd82e 100644 --- a/web/src/i18n/pages/login/zh-cn.ts +++ b/web/src/i18n/pages/login/zh-cn.ts @@ -9,7 +9,7 @@ export default { two4: '友情链接', }, account: { - accountPlaceholder1: '请输入登录账号', + accountPlaceholder1: '请输入登录账号/邮箱/手机号', accountPlaceholder2: '请输入登录密码', accountPlaceholder3: '请输入验证码', accountBtnText: '登 录', From 7a21f44eab58e50565d14172341fca7c6a69cb84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A5=BD=E5=A5=87=E5=AE=9D=E5=AE=9D?= <11259906+haoqibb@user.noreply.gitee.com> Date: Fri, 28 Jun 2024 02:15:47 +0000 Subject: [PATCH 09/14] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=96=87=E4=BB=B6=20we?= =?UTF-8?q?b/public/version-build?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/public/version-build | 1 - 1 file changed, 1 deletion(-) delete mode 100644 web/public/version-build diff --git a/web/public/version-build b/web/public/version-build deleted file mode 100644 index f6571be..0000000 --- a/web/public/version-build +++ /dev/null @@ -1 +0,0 @@ -3.0.3.1719538291017 \ No newline at end of file From 5cb7ec500c9b9a641d306866142b2de381a7bc97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A5=BD=E5=A5=87=E5=AE=9D=E5=AE=9D?= <11259906+haoqibb@user.noreply.gitee.com> Date: Fri, 28 Jun 2024 02:18:39 +0000 Subject: [PATCH 10/14] =?UTF-8?q?update=20backend/dvadmin/system/views/log?= =?UTF-8?q?in.py.=20=E4=BF=AE=E5=A4=8D=E7=99=BB=E5=BD=95=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E6=AC=A1=E6=95=B0=E5=86=99=E5=85=A5=E7=9A=84BUG?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 好奇宝宝 <11259906+haoqibb@user.noreply.gitee.com> --- backend/dvadmin/system/views/login.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/dvadmin/system/views/login.py b/backend/dvadmin/system/views/login.py index e04743c..165cf55 100644 --- a/backend/dvadmin/system/views/login.py +++ b/backend/dvadmin/system/views/login.py @@ -114,8 +114,8 @@ class LoginSerializer(TokenObtainPairSerializer): user.login_error_count += 1 if user.login_error_count >= 5: user.is_active = False + user.save() raise CustomValidationError("账号已被锁定,联系管理员解锁") - user.save() count = 5 - user.login_error_count raise CustomValidationError(f"账号/密码错误;重试{count}次后将被锁定~") From 087d478094ed745b7fa631d6603b352ec46eadce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=B0=8F=E6=B6=9B?= <1537080775@qq.com> Date: Fri, 28 Jun 2024 15:19:57 +0800 Subject: [PATCH 11/14] =?UTF-8?q?feat(role=5Fper=5F20240628):=20=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E6=9D=83=E9=99=90=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1、修复首次打开权限配置,按钮小齿轮数据权限不显示bug 2、优化自定义数据权限:在input里面显示当前配置用户已有的权限 3、优化自定义数据权限:在树形选择器中,禁用掉当前登录用户没有权限的部门 --- .../views/role_menu_button_permission.py | 142 ++++++------------ .../views/system/login/component/account.vue | 4 +- .../role/components/PermissionComNew/api.ts | 5 +- .../components/PermissionComNew/index.vue | 22 ++- 4 files changed, 70 insertions(+), 103 deletions(-) diff --git a/backend/dvadmin/system/views/role_menu_button_permission.py b/backend/dvadmin/system/views/role_menu_button_permission.py index d44fab2..4c933f4 100644 --- a/backend/dvadmin/system/views/role_menu_button_permission.py +++ b/backend/dvadmin/system/views/role_menu_button_permission.py @@ -256,86 +256,45 @@ class RoleMenuButtonPermissionViewSet(CustomModelViewSet): is_superuser = request.user.is_superuser if is_superuser: data = [ - { - "value": 0, - "label": '仅本人数据权限' - }, - { - "value": 1, - "label": '本部门及以下数据权限' - }, - { - "value": 2, - "label": '本部门数据权限' - }, - { - "value": 3, - "label": '全部数据权限' - }, - { - "value": 4, - "label": '自定义数据权限' - } + {"value": 0, "label": '仅本人数据权限'}, + {"value": 1, "label": '本部门及以下数据权限'}, + {"value": 2, "label": '本部门数据权限'}, + {"value": 3, "label": '全部数据权限'}, + {"value": 4, "label": '自定义数据权限'} ] return DetailResponse(data=data) else: - data = [] + params = request.query_params + data = [{"value": 0, "label": '仅本人数据权限'}] role_list = request.user.role.values_list('id', flat=True) - if params := request.query_params: - if menu_button_id := params.get('menu_button', None): - role_queryset = RoleMenuButtonPermission.objects.filter( - role__in=role_list, menu_button__id=menu_button_id - ).values_list('data_range', flat=True) - data_range_list = list(set(role_queryset)) - for item in data_range_list: - if item == 0: - data = [{ - "value": 0, - "label": '仅本人数据权限' - }] - elif item == 1: - data = [{ - "value": 0, - "label": '仅本人数据权限' - }, { - "value": 1, - "label": '本部门及以下数据权限' - }, - { - "value": 2, - "label": '本部门数据权限' - }] - elif item == 2: - data = [{ - "value": 0, - "label": '仅本人数据权限' - }, - { - "value": 2, - "label": '本部门数据权限' - }] - elif item == 3: - data = [{ - "value": 0, - "label": '仅本人数据权限' - }, - { - "value": 3, - "label": '全部数据权限' - }, ] - elif item == 4: - data = [{ - "value": 0, - "label": '仅本人数据权限' - }, - { - "value": 4, - "label": '自定义数据权限' - }] - else: - data = [] - return DetailResponse(data=data) - return ErrorResponse(msg="参数错误") + # 权限页面进入初始化获取所有的数据权限范围 + role_queryset = RoleMenuButtonPermission.objects.filter( + role__in=role_list + ).values_list('data_range', flat=True) + # 通过按钮小齿轮获取指定按钮的权限 + if menu_button_id := params.get('menu_button', None): + role_queryset = RoleMenuButtonPermission.objects.filter( + role__in=role_list, menu_button__id=menu_button_id + ).values_list('data_range', flat=True) + + data_range_list = list(set(role_queryset)) + for item in data_range_list: + if item == 0: + data = data + elif item == 1: + data.extend([ + {"value": 1, "label": '本部门及以下数据权限'}, + {"value": 2, "label": '本部门数据权限'} + ]) + elif item == 2: + data.extend([{"value": 2, "label": '本部门数据权限'}]) + elif item == 3: + data.extend([{"value": 3, "label": '全部数据权限'}]) + elif item == 4: + data.extend([{"value": 4, "label": '自定义数据权限'}]) + else: + data = [] + return DetailResponse(data=data) @action(methods=['get'], detail=False, permission_classes=[IsAuthenticated]) def role_to_dept_all(self, request): @@ -344,23 +303,22 @@ class RoleMenuButtonPermissionViewSet(CustomModelViewSet): :param request: :return: """ - params = request.query_params is_superuser = request.user.is_superuser - if is_superuser: - queryset = Dept.objects.values('id', 'name', 'parent') - else: - if not params: - return ErrorResponse(msg="参数错误") - menu_button = params.get('menu_button') - if menu_button is None: - return ErrorResponse(msg="参数错误") - role_list = request.user.role.values_list('id', flat=True) - queryset = RoleMenuButtonPermission.objects.filter(role__in=role_list, menu_button=None).values( - dept_id=F('dept__id'), - name=F('dept__name'), - parent=F('dept__parent') - ) - return DetailResponse(data=queryset) + params = request.query_params + role_id = params.get('role') + menu_button_id = params.get('menu_button') + dept_checked = RoleMenuButtonPermission.objects.filter( + role_id=role_id, menu_button_id=menu_button_id + ).values_list('dept', flat=True) + dept_list = Dept.objects.values('id', 'name', 'parent') + data = { + 'depts': [], + 'dept_checked': dept_checked + } + for dept in dept_list: + dept["disabled"] = not (is_superuser | dept["id"] in dept_checked) + data['depts'].append(dept) + return DetailResponse(data=data) @action(methods=['get'], detail=False, permission_classes=[IsAuthenticated]) def menu_to_button(self, request): diff --git a/web/src/views/system/login/component/account.vue b/web/src/views/system/login/component/account.vue index ee4e64f..def80eb 100644 --- a/web/src/views/system/login/component/account.vue +++ b/web/src/views/system/login/component/account.vue @@ -80,8 +80,8 @@ export default defineComponent({ const state = reactive({ isShowPassword: false, ruleForm: { - username: '', - password: '', + username: 'superadmin', + password: 'admin123456', captcha: '', captchaKey: '', captchaImgBase: '', diff --git a/web/src/views/system/role/components/PermissionComNew/api.ts b/web/src/views/system/role/components/PermissionComNew/api.ts index eee6d2a..68793c0 100644 --- a/web/src/views/system/role/components/PermissionComNew/api.ts +++ b/web/src/views/system/role/components/PermissionComNew/api.ts @@ -40,10 +40,11 @@ export function getDataPermissionRangeAll() { method: 'get', }) } -export function getDataPermissionDept() { +export function getDataPermissionDept(query:object) { return request({ url: '/api/system/role_menu_button_permission/role_to_dept_all/', - method: 'get' + method: 'get', + params:query }) } diff --git a/web/src/views/system/role/components/PermissionComNew/index.vue b/web/src/views/system/role/components/PermissionComNew/index.vue index 7770b05..3babb8e 100644 --- a/web/src/views/system/role/components/PermissionComNew/index.vue +++ b/web/src/views/system/role/components/PermissionComNew/index.vue @@ -124,6 +124,7 @@ watch( (val) => { drawerVisible.value = val; getMenuBtnPermission() + getDataPermissionRangeLable() } ); @@ -144,9 +145,10 @@ let menuCurrent = ref>({}); let menuBtnCurrent = ref(-1); let dialogVisible = ref(false); let dataPermissionRange = ref([]); +let dataPermissionRangeLabel = ref([]); const formatDataRange = computed(() => { return function (datarange: number) { - const findItem = dataPermissionRange.value.find((i) => i.value === datarange); + const findItem = dataPermissionRangeLabel.value.find((i) => i.value === datarange); return findItem?.label || '' } }) @@ -158,6 +160,11 @@ const getMenuBtnPermission = async () => { const resMenu = await getRolePremission({ role: props.roleId }) menuData.value = resMenu.data } +// 获取按钮的数据权限下拉选项 +const getDataPermissionRangeLable = async () => { + const resRange = await getDataPermissionRange({ role: props.roleId }) + dataPermissionRangeLabel.value = resRange.data; +} const fetchData = async (btnId) => { try { @@ -170,9 +177,9 @@ const fetchData = async (btnId) => { } }; -const handleCollapseChange = (val: number) => { - collapseCurrent.value = [val]; -}; +// const handleCollapseChange = (val: number) => { +// collapseCurrent.value = [val]; +// }; /** * 设置按钮数据权限 @@ -194,9 +201,10 @@ const handleColumnChange = (val: boolean, record: MenusType, btnType: string) => const handlePermissionRangeChange = async (val: number) => { if (val === 4) { - const res = await getDataPermissionDept(); - const data = XEUtils.toArrayTree(res.data, { parentKey: 'parent', strict: false }); - deptData.value = data; + const res = await getDataPermissionDept({ role: props.roleId,menu_button:menuBtnCurrent.value }); + const depts = XEUtils.toArrayTree(res.data.depts, { parentKey: 'parent', strict: false }); + deptData.value = depts; + customDataPermission.value = res.data.dept_checked; } }; From 1b8a502d6695fc121f07c89c491b6786e195ea4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=B0=8F=E6=B6=9B?= <1537080775@qq.com> Date: Fri, 28 Jun 2024 15:36:38 +0800 Subject: [PATCH 12/14] =?UTF-8?q?fix(role=5Fper=5F20240628):=20=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E6=9D=83=E9=99=90=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1、修复第一次选择自定义数据权限无法设置的bug --- backend/dvadmin/system/views/role_menu_button_permission.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/backend/dvadmin/system/views/role_menu_button_permission.py b/backend/dvadmin/system/views/role_menu_button_permission.py index 4c933f4..a3e8500 100644 --- a/backend/dvadmin/system/views/role_menu_button_permission.py +++ b/backend/dvadmin/system/views/role_menu_button_permission.py @@ -313,10 +313,11 @@ class RoleMenuButtonPermissionViewSet(CustomModelViewSet): dept_list = Dept.objects.values('id', 'name', 'parent') data = { 'depts': [], - 'dept_checked': dept_checked + 'dept_checked': [] if dept_checked.exists() else dept_checked } + for dept in dept_list: - dept["disabled"] = not (is_superuser | dept["id"] in dept_checked) + dept["disabled"] = False if is_superuser else dept["id"] not in dept_checked data['depts'].append(dept) return DetailResponse(data=data) From 0779cc1a842a071736996f74a356bfe2bec09a66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=B0=8F=E6=B6=9B?= <1537080775@qq.com> Date: Fri, 28 Jun 2024 15:46:44 +0800 Subject: [PATCH 13/14] =?UTF-8?q?fix(role=5Fper=5F20240628):=20=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E6=9D=83=E9=99=90=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1、修复第一次选择自定义数据权限无法设置的bug --- .../dvadmin/system/views/role_menu_button_permission.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/backend/dvadmin/system/views/role_menu_button_permission.py b/backend/dvadmin/system/views/role_menu_button_permission.py index 4c933f4..b53c31d 100644 --- a/backend/dvadmin/system/views/role_menu_button_permission.py +++ b/backend/dvadmin/system/views/role_menu_button_permission.py @@ -307,16 +307,17 @@ class RoleMenuButtonPermissionViewSet(CustomModelViewSet): params = request.query_params role_id = params.get('role') menu_button_id = params.get('menu_button') - dept_checked = RoleMenuButtonPermission.objects.filter( + dept_checked = RoleMenuButtonPermission.objects.get( role_id=role_id, menu_button_id=menu_button_id - ).values_list('dept', flat=True) + ).dept.all().values_list('id', flat=True) dept_list = Dept.objects.values('id', 'name', 'parent') data = { 'depts': [], - 'dept_checked': dept_checked + 'dept_checked': dept_checked if dept_checked.exists() else [] } + for dept in dept_list: - dept["disabled"] = not (is_superuser | dept["id"] in dept_checked) + dept["disabled"] = False if is_superuser else dept["id"] not in dept_checked data['depts'].append(dept) return DetailResponse(data=data) From 8c7e8aee9fa4b00450a7bc40272136dff2f5da1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=B0=8F=E6=B6=9B?= <1537080775@qq.com> Date: Fri, 28 Jun 2024 16:22:16 +0800 Subject: [PATCH 14/14] =?UTF-8?q?feat(role=5Fper=5F20240628):=20=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E6=9D=83=E9=99=90=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1、点击权限小齿轮,自动带出默认值 --- .../role/components/PermissionComNew/index.vue | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/web/src/views/system/role/components/PermissionComNew/index.vue b/web/src/views/system/role/components/PermissionComNew/index.vue index 3babb8e..29a8ab3 100644 --- a/web/src/views/system/role/components/PermissionComNew/index.vue +++ b/web/src/views/system/role/components/PermissionComNew/index.vue @@ -29,7 +29,7 @@ :label="btn.value">
{{ btn.data_range !== null ? `${btn.name}(${formatDataRange(btn.data_range)})` : btn.name }} - + @@ -166,7 +166,7 @@ const getDataPermissionRangeLable = async () => { dataPermissionRangeLabel.value = resRange.data; } -const fetchData = async (btnId) => { +const fetchData = async (btnId:number) => { try { const resRange = await getDataPermissionRange({menu_button:btnId}); if (resRange?.code === 2000) { @@ -186,11 +186,13 @@ const fetchData = async (btnId) => { * @param record 当前菜单 * @param btnType 按钮类型 */ -const handleSettingClick = (record: MenusType, btnId: number) => { +const handleSettingClick = (record: MenusType, btn: MenusType['btns'][number]) => { menuCurrent.value = record; - menuBtnCurrent.value = btnId; + menuBtnCurrent.value = btn.id; dialogVisible.value = true; - fetchData(btnId) + dataPermission.value =btn.data_range; + handlePermissionRangeChange(btn.data_range) + fetchData( btn.id) }; const handleColumnChange = (val: boolean, record: MenusType, btnType: string) => {