diff --git a/README.md b/README.md index 4640cc7..f7b5000 100644 --- a/README.md +++ b/README.md @@ -45,12 +45,12 @@ - 插件市场:[戳我](https://bbs.django-vue-admin.com/plugMarket.html)👩‍👦‍👦 -- django-vue-admin交流01群:812482043 [点击链接加入群聊](https://qm.qq.com/cgi-bin/qm/qr?k=aJVwjDvH-Es4MPJQuoO32N0SucK22TE5&jump_from=webapi) +- django-vue-admin交流01群(已满):812482043 [点击链接加入群聊](https://qm.qq.com/cgi-bin/qm/qr?k=aJVwjDvH-Es4MPJQuoO32N0SucK22TE5&jump_from=webapi) - django-vue-admin交流02群:687252418 [点击链接加入群聊](https://qm.qq.com/cgi-bin/qm/qr?k=4jJN4IjWGfxJ8YJXbb_gTsuWjR34WLdc&jump_from=webapi) - 二维码 - + ## 源码地址 @@ -82,7 +82,7 @@ github地址:[https://github.com/liqianglog/django-vue-admin](https://github.c ## 准备工作 ~~~ -Python >= 3.6.0 (推荐3.8+版本) +Python >= 3.8.0 (推荐3.8+版本) nodejs >= 14.0 (推荐最新) Mysql >= 5.7.0 (可选,默认数据库sqlite3,推荐8.0版本) Redis(可选,最新版) @@ -148,14 +148,15 @@ npm run dev # 先安装docker-compose (自行百度安装),执行此命令等待安装,如有使用celery插件请打开docker-compose.yml中celery 部分注释 docker-compose up -d # 初始化后端数据(第一次执行即可) -docker exec -ti DVAdmin-django bash +docker exec -ti dvadmin-django bash python manage.py makemigrations python manage.py migrate -python manage.py init -y +python manage.py init_area +python manage.py init exit 前端地址:http://127.0.0.1:8080 -后端地址:http://127.0.0.1:8000 +后端地址:http://127.0.0.1:8080/api # 在服务器上请把127.0.0.1 换成自己公网ip 账号:superadmin 密码:admin123456 @@ -171,21 +172,25 @@ docker-compose up -d --build ## 演示图✅ -![image-01](https://gitee.com/liqianglog/pic/raw/master/master/01.png) +![image-01](https://images.gitee.com/uploads/images/2022/0530/234137_b58c8f98_5074988.png) -![image-02](https://gitee.com/liqianglog/pic/raw/master/master/02.png) +![image-02](https://images.gitee.com/uploads/images/2022/0530/234240_39834603_5074988.png) -![image-03](https://gitee.com/liqianglog/pic/raw/master/master/03.png) +![image-03](https://images.gitee.com/uploads/images/2022/0530/234339_35e728a0_5074988.png) -![image-04](https://gitee.com/liqianglog/pic/raw/master/master/04.png) +![image-04](https://images.gitee.com/uploads/images/2022/0530/234426_957036b0_5074988.png) -![image-05](https://gitee.com/liqianglog/pic/raw/master/master/05.png) +![image-05](https://images.gitee.com/uploads/images/2022/0530/234458_898be492_5074988.png) -![image-06](https://gitee.com/liqianglog/pic/raw/master/master/06.png) +![image-06](https://images.gitee.com/uploads/images/2022/0530/234521_35b40076_5074988.png) -![image-07](https://gitee.com/liqianglog/pic/raw/master/master/06.png) +![image-07](https://images.gitee.com/uploads/images/2022/0530/234615_c2325639_5074988.png) -![image-08](https://gitee.com/liqianglog/pic/raw/master/master/08.png) +![image-08](https://images.gitee.com/uploads/images/2022/0530/234639_1ed6cc93_5074988.png) + +![image-09](https://images.gitee.com/uploads/images/2022/0530/234815_cea2c53f_5074988.png) + +![image-10](https://images.gitee.com/uploads/images/2022/0530/234840_5f3e5f53_5074988.png) diff --git a/backend/application/dispatch.py b/backend/application/dispatch.py index 55c715e..ae77479 100644 --- a/backend/application/dispatch.py +++ b/backend/application/dispatch.py @@ -1,9 +1,9 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- from django.conf import settings -from django.db import ProgrammingError from django.db import connection + def is_tenants_mode(): """ 判断是否为租户模式 @@ -35,7 +35,7 @@ def _get_all_system_config(): system_config_obj = SystemConfig.objects.filter(status=True, parent_id__isnull=False).values( 'parent__key', 'key', 'value', 'form_item_type').order_by('sort') for system_config in system_config_obj: - value = system_config.get('value') or '' + value = system_config.get('value', '') if value and system_config.get('form_item_type') == 7: value = value[0].get('url') data[f"{system_config.get('parent__key')}.{system_config.get('key')}"] = value diff --git a/backend/dvadmin/system/models.py b/backend/dvadmin/system/models.py index b54ac27..396296f 100644 --- a/backend/dvadmin/system/models.py +++ b/backend/dvadmin/system/models.py @@ -187,6 +187,11 @@ class Dictionary(CoreModel): def save(self, force_insert=False, force_update=False, using=None, update_fields=None): super().save(force_insert, force_update, using, update_fields) dispatch.refresh_dictionary() # 有更新则刷新字典配置 + + def delete(self, using=None, keep_parents=False): + res = super().delete(using, keep_parents) + dispatch.refresh_dictionary() + return res class OperationLog(CoreModel): @@ -320,6 +325,11 @@ class SystemConfig(CoreModel): def save(self, force_insert=False, force_update=False, using=None, update_fields=None): super().save(force_insert, force_update, using, update_fields) dispatch.refresh_system_config() # 有更新则刷新系统配置 + + def delete(self, using=None, keep_parents=False): + res = super().delete(using, keep_parents) + dispatch.refresh_system_config() + return res class LoginLog(CoreModel): diff --git a/backend/dvadmin/system/views/user.py b/backend/dvadmin/system/views/user.py index a26dca5..28e6462 100644 --- a/backend/dvadmin/system/views/user.py +++ b/backend/dvadmin/system/views/user.py @@ -101,6 +101,7 @@ class UserUpdateSerializer(CustomModelSerializer): validators=[ CustomUniqueValidator(queryset=Users.objects.all(), message="手机号必须唯一") ], + allow_blank=True ) def save(self, **kwargs): diff --git a/backend/dvadmin/utils/filters.py b/backend/dvadmin/utils/filters.py index 3a088b9..6fc6912 100644 --- a/backend/dvadmin/utils/filters.py +++ b/backend/dvadmin/utils/filters.py @@ -165,8 +165,9 @@ class CustomDjangoFilterBackend(DjangoFilterBackend): def find_filter_lookups(self, orm_lookups, search_term_key): for lookup in orm_lookups: # if lookup.find(search_term_key) >= 0: + new_lookup = lookup.split("__")[0] # 修复条件搜索错误 bug - if lookup == search_term_key: + if new_lookup == search_term_key: return lookup return None diff --git a/backend/dvadmin/utils/import_export_mixin.py b/backend/dvadmin/utils/import_export_mixin.py index 66c6684..ee8cfdb 100644 --- a/backend/dvadmin/utils/import_export_mixin.py +++ b/backend/dvadmin/utils/import_export_mixin.py @@ -65,9 +65,7 @@ class ImportSerializerMixin: for ele in data: # 获取 unique 字段 filter_dic = {i: ele.get(i) for i in list(set(self.import_field_dict.keys()) & set(unique_list))} - print(11, ele) instance = filter_dic and queryset.filter(**filter_dic).first() - print(22, instance) if instance and not updateSupport: continue if not filter_dic: diff --git a/backend/dvadmin/utils/permission.py b/backend/dvadmin/utils/permission.py index 8d1f8f6..35e5b4b 100644 --- a/backend/dvadmin/utils/permission.py +++ b/backend/dvadmin/utils/permission.py @@ -69,33 +69,31 @@ class CustomPermission(BasePermission): # 当权限为空时,则可以访问 is_head = getattr(view, 'head', None) if is_head: - head_kwargs = getattr(view.head, 'kwargs', None) - if head_kwargs: - _permission_classes = getattr(head_kwargs, 'permission_classes', None) - if _permission_classes is None: - return True + head_kwargs = getattr(view.head, 'kwargs', {}) + _permission_classes = head_kwargs.get('permission_classes', None) + if _permission_classes == []: + return True # 判断是否是超级管理员 if request.user.is_superuser: return True else: api = request.path # 当前请求接口 method = request.method # 当前请求方法 - methodList = ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'] + methodList = ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'PATCH'] method = methodList.index(method) # ***接口白名单*** api_white_list = ApiWhiteList.objects.values(permission__api=F('url'), permission__method=F('method')) api_white_list = [ - str(item.get('permission__api').replace('{id}', '.*?')) + ":" + str(item.get('permission__method')) for - item in api_white_list if item.get('permission__api')] + str(item.get('permission__api').replace('{id}', '([a-zA-Z0-9-]+)')) + ":" + str( + item.get('permission__method')) + '$' for item in api_white_list if item.get('permission__api')] # ********# if not hasattr(request.user, "role"): return False userApiList = request.user.role.values('permission__api', 'permission__method') # 获取当前用户的角色拥有的所有接口 ApiList = [ - str(item.get('permission__api').replace('{id}', '.*?')) + ":" + str(item.get('permission__method')) for - item in - userApiList if item.get('permission__api')] - new_api_ist = api_white_list + ApiList + str(item.get('permission__api').replace('{id}', '([a-zA-Z0-9-]+)')) + ":" + str( + item.get('permission__method')) + '$' for item in userApiList if item.get('permission__api')] + new_api_ist = api_white_list + ApiList new_api = api + ":" + str(method) for item in new_api_ist: matchObj = re.match(item, new_api, re.M | re.I) diff --git a/backend/requirements.txt b/backend/requirements.txt index b7f8dc0..497fa6c 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -4,7 +4,7 @@ chardet==4.0.0 coreapi==2.3.3 coreschema==0.0.4 Django==3.2.3 -django-comment-migrate==0.1.1 +django-comment-migrate==0.1.5 django-cors-headers==3.10.1 django-filter==21.1 django-ranged-response==0.2.0 diff --git a/docker_env/celery/Dockerfile b/docker_env/celery/Dockerfile index 394a108..e233371 100644 --- a/docker_env/celery/Dockerfile +++ b/docker_env/celery/Dockerfile @@ -2,6 +2,6 @@ FROM registry.cn-zhangjiakou.aliyuncs.com/dvadmin-pro/python38-base-backend:late WORKDIR /backend COPY ./backend/ . RUN awk 'BEGIN { cmd="cp -i ./conf/env.example.py ./conf/env.py "; print "n" |cmd; }' -RUN python3 -m pip install -i https://mirrors.aliyun.com/pypi/simple/ -r requirements.txt +RUN python3 -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple/ -r requirements.txt CMD ["celery", "-A", "application", "worker", "-B", "--loglevel=info"] diff --git a/docker_env/django/Dockerfile b/docker_env/django/Dockerfile index 8fa0065..78547c7 100644 --- a/docker_env/django/Dockerfile +++ b/docker_env/django/Dockerfile @@ -2,5 +2,5 @@ FROM registry.cn-zhangjiakou.aliyuncs.com/dvadmin-pro/python38-base-backend:late WORKDIR /backend COPY ./backend/ . RUN awk 'BEGIN { cmd="cp -i ./conf/env.example.py ./conf/env.py "; print "n" |cmd; }' -RUN python3 -m pip install -i https://mirrors.aliyun.com/pypi/simple/ -r requirements.txt +RUN python3 -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple/ -r requirements.txt CMD ["daphne","-b","0.0.0.0","-p","8000","application.asgi:application"] diff --git a/docker_env/nginx/my.conf b/docker_env/nginx/my.conf index c2f3310..17c7ea1 100644 --- a/docker_env/nginx/my.conf +++ b/docker_env/nginx/my.conf @@ -21,6 +21,6 @@ server { set_real_ip_from 0.0.0.0/0; real_ip_header X-Forwarded-For; rewrite ^/api/(.*)$ /$1 break; #重写 - proxy_pass http://177.7.0.12:8000/; # 设置代理服务器的协议和地址 + proxy_pass http://177.8.0.12:8000/; # 设置代理服务器的协议和地址 } } diff --git a/web/src/api/service.js b/web/src/api/service.js index 381eaa5..4262a4c 100644 --- a/web/src/api/service.js +++ b/web/src/api/service.js @@ -42,7 +42,7 @@ export function getErrorMessage (msg) { function createService () { // 创建一个 axios 实例 const service = axios.create({ - baseURL: process.env.VUE_APP_API_URL, + baseURL: util.baseURL(), timeout: 20000, paramsSerializer: (params) => qs.stringify(params, { indices: false }) }) diff --git a/web/src/components/importExcel/index.vue b/web/src/components/importExcel/index.vue index c806624..118e77b 100644 --- a/web/src/components/importExcel/index.vue +++ b/web/src/components/importExcel/index.vue @@ -59,7 +59,7 @@ export default { // 设置上传的请求头部 headers: { Authorization: 'JWT ' + util.cookies.get('token') }, // 上传的地址 - url: process.env.VUE_APP_API + '/api/system/file/' + url: util.baseURL() + 'api/system/file/' } } }, @@ -78,7 +78,7 @@ export default { /** 下载模板操作 */ importTemplate () { downloadFile({ - url: process.env.VUE_APP_API + this.importApi, + url: util.baseURL() + this.importApi, params: {}, method: 'get' }) diff --git a/web/src/config/d2p-extends/types.js b/web/src/config/d2p-extends/types.js index 35287e7..1488415 100644 --- a/web/src/config/d2p-extends/types.js +++ b/web/src/config/d2p-extends/types.js @@ -1,3 +1,4 @@ +import util from '@/libs/util.js' export default { 'image-uploader': { form: { component: { name: 'd2p-file-uploader', props: { elProps: { listType: 'picture-card', accept: '.png,.jpeg,.jpg,.ico,.bmp,.gif' } } } }, @@ -26,6 +27,14 @@ export default { const value = row[col.key] if (value != null && value) { row[col.key] = value.split(',') + // 进行组装地址,纠正地址 + row[col.key].map((val, index) => { + if (val.startsWith('/')) { + row[col.key][index] = util.baseURL() + val.slice(1) + } else { + row[col.key][index] = !val.startsWith('http') ? util.baseURL() + val : val + } + }) } } }, @@ -56,6 +65,14 @@ export default { const value = row[col.key] if (value != null && value) { row[col.key] = value.split(',') + // 进行组装地址,纠正地址 + row[col.key].map((val, index) => { + if (val.startsWith('/')) { + row[col.key][index] = util.baseURL() + val.slice(1) + } else { + row[col.key][index] = !val.startsWith('http') ? util.baseURL() + val : val + } + }) } } }, @@ -82,6 +99,14 @@ export default { const value = row[col.key] if (value != null && value) { row[col.key] = value.split(',') + // 进行组装地址,纠正地址 + row[col.key].map((val, index) => { + if (val.startsWith('/')) { + row[col.key][index] = util.baseURL() + val.slice(1) + } else { + row[col.key][index] = !val.startsWith('http') ? util.baseURL() + val : val + } + }) } } }, @@ -112,6 +137,14 @@ export default { const value = row[col.key] if (value != null && value) { row[col.key] = value.split(',') + // 进行组装地址,纠正地址 + row[col.key].map((val, index) => { + if (val.startsWith('/')) { + row[col.key][index] = util.baseURL() + val.slice(1) + } else { + row[col.key][index] = !val.startsWith('http') ? util.baseURL() + val : val + } + }) } } } diff --git a/web/src/install.js b/web/src/install.js index 9b48ce3..4219c02 100644 --- a/web/src/install.js +++ b/web/src/install.js @@ -18,8 +18,7 @@ import XEUtils from 'xe-utils' import store from '@/store/index' import { urlPrefix as deptPrefix } from '@/views/system/dept/api' import types from '@/config/d2p-extends/types' -import { checkPlugins } from '@/views/plugins' -const uploadUrl = util.baseURL() + 'api/system/file/' +import { checkPlugins, plugins } from '@/views/plugins' /** // vxe0 @@ -32,7 +31,8 @@ const uploadUrl = util.baseURL() + 'api/system/file/' // 按如下重命名引入可与官方版共存,index.vue中标签用使用加强版 // 不传name,则d2CrudX的标签仍为,不可与官方版共存 Vue.use(d2CrudX, { name: 'd2-crud-x' }) - +// 注册dvadmin插件 +Vue.use(plugins) // // 官方版【此处为演示与官方版共存而引入,全新项目中可以用d2-crud-x完全替代官方版】 // Vue.use(d2Crud) /** @@ -167,7 +167,7 @@ Vue.use(D2pUploader, { domain: 'http://d2p.file.veryreader.com' }, form: { - action: uploadUrl, + action: util.baseURL() + 'api/system/file/', name: 'file', data: {}, // 上传附加参数 headers () { @@ -180,7 +180,7 @@ Vue.use(D2pUploader, { if (ret.data === null || ret.data === '') { throw new Error('上传失败') } - return { url: util.baseURL() + ret.data.url, key: option.data.key } + return { url: util.baseURL() + ret.data.url, key: option.data.key, id: ret.data.id } }, withCredentials: false // 是否带cookie } @@ -368,6 +368,20 @@ Vue.prototype.commonEndColumns = function (param = {}) { title: '创建时间', key: 'create_datetime', width: 160, + search: { + disabled: !showData.create_datetime.showForm, + width: 240, + component: { // 查询框组件配置,默认根据form配置生成 + name: 'el-date-picker', + props: { + type: 'daterange', + 'range-separator': '至', + 'start-placeholder': '开始', + 'end-placeholder': '结束', + valueFormat: 'yyyy-MM-dd' + } + } + }, show: showData.create_datetime.showTable, type: 'datetime', sortable: true, diff --git a/web/src/menu/index.js b/web/src/menu/index.js index 0beef46..f89ce43 100644 --- a/web/src/menu/index.js +++ b/web/src/menu/index.js @@ -76,7 +76,7 @@ export const handleRouter = function (menuData) { meta: { title: item.name, auth: true, - cache: item.cache === 1 + cache: item.cache } } result.push(obj) diff --git a/web/src/plugin/d2admin/index.js b/web/src/plugin/d2admin/index.js index e2f20b5..311c347 100644 --- a/web/src/plugin/d2admin/index.js +++ b/web/src/plugin/d2admin/index.js @@ -16,7 +16,6 @@ import pluginError from '@/plugin/error' import pluginLog from '@/plugin/log' import pluginOpen from '@/plugin/open' import tableSelector from '@/components/table-selector/index' -import { plugins } from '@/views/plugins/index.js' export default { async install (Vue, options) { // 设置为 false 以阻止 vue 在启动时生成生产提示 @@ -40,6 +39,5 @@ export default { Vue.use(pluginLog) Vue.use(pluginOpen) Vue.use(tableSelector) - Vue.use(plugins) } } diff --git a/web/src/store/modules/d2admin/modules/settings.js b/web/src/store/modules/d2admin/modules/settings.js index cd01f1d..6953a94 100644 --- a/web/src/store/modules/d2admin/modules/settings.js +++ b/web/src/store/modules/d2admin/modules/settings.js @@ -35,12 +35,13 @@ export default { */ async load ({ state, dispatch, commit }) { // store 赋值 - state.data = await dispatch('d2admin/db/get', { + const data = await dispatch('d2admin/db/get', { dbName: 'sys', path: 'settings.init', defaultValue: {}, user: true }, { root: true }) + commit('set', data) } }, mutations: { @@ -52,6 +53,16 @@ export default { */ async get (state, key, value) { return state[key] + }, + /** + * @description 赋值系统配置 + * @param {Object} state state + * @param {Object} value active + */ + async set (state, value) { + state.data = value + state.keepRecord = value['login.keep_record'] + return state.data } } } diff --git a/web/src/views/system/dept/crud.js b/web/src/views/system/dept/crud.js index 232386c..7357049 100644 --- a/web/src/views/system/dept/crud.js +++ b/web/src/views/system/dept/crud.js @@ -7,6 +7,7 @@ export const crudOptions = (vm) => { }, options: { tableType: 'vxe-table', + stripe: false, rowKey: true, // 必须设置,true or false rowId: 'id', height: '100%', // 表格高度100%, 使用toolbar必须设置 diff --git a/web/src/views/system/dictionary/index.vue b/web/src/views/system/dictionary/index.vue index 59872b6..8054851 100644 --- a/web/src/views/system/dictionary/index.vue +++ b/web/src/views/system/dictionary/index.vue @@ -1,15 +1,12 @@ - @@ -80,6 +81,10 @@ export default { dictionaryConfigure (scope) { this.drawer = true this.dictionaryRow = scope.row + }, + doAfterRowChange (row) { + this.doRefresh({ from: 'afterRowChange' }) + this.$store.dispatch('d2admin/dictionary/load') } } } diff --git a/web/src/views/system/dictionary/subDictionary/index.vue b/web/src/views/system/dictionary/subDictionary/index.vue index 649c068..684cd6b 100644 --- a/web/src/views/system/dictionary/subDictionary/index.vue +++ b/web/src/views/system/dictionary/subDictionary/index.vue @@ -44,8 +44,7 @@ export default { } }, data () { - return { - } + return {} }, methods: { getCrudOptions () { @@ -70,6 +69,10 @@ export default { }, delRequest (row) { return api.DelObj(row.id) + }, + doAfterRowChange (row) { + this.doRefresh({ from: 'afterRowChange' }) + this.$store.dispatch('d2admin/dictionary/load') } } } diff --git a/web/src/views/system/login/base.vue b/web/src/views/system/login/base.vue index f5031eb..b6d7f42 100644 --- a/web/src/views/system/login/base.vue +++ b/web/src/views/system/login/base.vue @@ -3,7 +3,7 @@