Merge remote-tracking branch 'origin/dev' into dev
						commit
						22879cb170
					
				|  | @ -2,4 +2,5 @@ | |||
| # python manage.py makemigrations | ||||
| # python manage.py migrate | ||||
| # python manage.py init -y | ||||
| gunicorn -c gunicorn_conf.py application.asgi:application | ||||
| #gunicorn -c gunicorn_conf.py application.asgi:application | ||||
| uvicorn application.asgi:application --port 8000 --host 0.0.0.0 --workers 4 | ||||
|  |  | |||
|  | @ -318,33 +318,33 @@ class FileList(CoreModel): | |||
|         # 保存到File model中 | ||||
|         instance = FileList() | ||||
|         instance.name = file_name | ||||
|         instance.engine = dispatch.get_system_config_values("fileStorageConfig.file_engine") or 'local' | ||||
|         instance.engine = dispatch.get_system_config_values("file_storage.file_engine") or 'local' | ||||
|         instance.file_url = os.path.join(file_path, file_name) | ||||
|         instance.mime_type = mime_type | ||||
|         instance.creator = request.user | ||||
|         instance.modifier = request.user.id | ||||
|         instance.dept_belong_id = request.user.dept_id | ||||
| 
 | ||||
|         file_backup = dispatch.get_system_config_values("fileStorageConfig.file_backup") | ||||
|         file_engine = dispatch.get_system_config_values("fileStorageConfig.file_engine") or 'local' | ||||
|         file_backup = dispatch.get_system_config_values("file_storage.file_backup") | ||||
|         file_engine = dispatch.get_system_config_values("file_storage.file_engine") or 'local' | ||||
|         if file_backup: | ||||
|             instance.url = os.path.join(file_path.replace('media/', ''), file_name) | ||||
|         if file_engine == 'oss': | ||||
|             from dvadmin_cloud_storage.views.aliyun import ali_oss_upload | ||||
|             file = File(open(os.path.join(BASE_DIR, file_path, file_name))) | ||||
|             file_path = ali_oss_upload(file) | ||||
|             if file_path: | ||||
|                 instance.file_url = file_path | ||||
|             else: | ||||
|                 raise ValueError("上传失败") | ||||
|             with open(os.path.join(BASE_DIR, file_path, file_name), 'rb') as file: | ||||
|                 file_path = ali_oss_upload(file, file_name=os.path.join(file_path.replace('media/', ''), file_name)) | ||||
|                 if file_path: | ||||
|                     instance.file_url = file_path | ||||
|                 else: | ||||
|                     raise ValueError("上传失败") | ||||
|         elif file_engine == 'cos': | ||||
|             from dvadmin_cloud_storage.views.tencent import tencent_cos_upload | ||||
|             file = File(open(os.path.join(BASE_DIR, file_path, file_name))) | ||||
|             file_path = tencent_cos_upload(file) | ||||
|             if file_path: | ||||
|                 instance.file_url = file_path | ||||
|             else: | ||||
|                 raise ValueError("上传失败") | ||||
|             with open(os.path.join(BASE_DIR, file_path, file_name), 'rb') as file: | ||||
|                 file_path = tencent_cos_upload(file, file_name=os.path.join(file_path.replace('media/', ''), file_name)) | ||||
|                 if file_path: | ||||
|                     instance.file_url = file_path | ||||
|                 else: | ||||
|                     raise ValueError("上传失败") | ||||
|         else: | ||||
|             instance.url = os.path.join(file_path.replace('media/', ''), file_name) | ||||
|         instance.save() | ||||
|  |  | |||
|  | @ -215,38 +215,39 @@ class DataVViewSet(GenericViewSet): | |||
|         CHINA_PROVINCES = [ | ||||
|             {'name': '北京', 'code': '110000'}, | ||||
|             {'name': '天津', 'code': '120000'}, | ||||
|             {'name': '河北', 'code': '130000'}, | ||||
|             {'name': '山西', 'code': '140000'}, | ||||
|             {'name': '河北省', 'code': '130000'}, | ||||
|             {'name': '山西省', 'code': '140000'}, | ||||
|             {'name': '内蒙古', 'code': '150000'}, | ||||
|             {'name': '辽宁', 'code': '210000'}, | ||||
|             {'name': '吉林', 'code': '220000'}, | ||||
|             {'name': '黑龙江', 'code': '230000'}, | ||||
|             {'name': '辽宁省', 'code': '210000'}, | ||||
|             {'name': '吉林省', 'code': '220000'}, | ||||
|             {'name': '黑龙江省', 'code': '230000'}, | ||||
|             {'name': '上海', 'code': '310000'}, | ||||
|             {'name': '江苏', 'code': '320000'}, | ||||
|             {'name': '浙江', 'code': '330000'}, | ||||
|             {'name': '安徽', 'code': '340000'}, | ||||
|             {'name': '福建', 'code': '350000'}, | ||||
|             {'name': '江西', 'code': '360000'}, | ||||
|             {'name': '山东', 'code': '370000'}, | ||||
|             {'name': '河南', 'code': '410000'}, | ||||
|             {'name': '湖北', 'code': '420000'}, | ||||
|             {'name': '湖南', 'code': '430000'}, | ||||
|             {'name': '广东', 'code': '440000'}, | ||||
|             {'name': '江苏省', 'code': '320000'}, | ||||
|             {'name': '浙江省', 'code': '330000'}, | ||||
|             {'name': '安徽省', 'code': '340000'}, | ||||
|             {'name': '福建省', 'code': '350000'}, | ||||
|             {'name': '江西省', 'code': '360000'}, | ||||
|             {'name': '山东省', 'code': '370000'}, | ||||
|             {'name': '河南省', 'code': '410000'}, | ||||
|             {'name': '湖北省', 'code': '420000'}, | ||||
|             {'name': '湖南省', 'code': '430000'}, | ||||
|             {'name': '广东省', 'code': '440000'}, | ||||
|             {'name': '广西', 'code': '450000'}, | ||||
|             {'name': '海南', 'code': '460000'}, | ||||
|             {'name': '海南省', 'code': '460000'}, | ||||
|             {'name': '重庆', 'code': '500000'}, | ||||
|             {'name': '四川', 'code': '510000'}, | ||||
|             {'name': '贵州', 'code': '520000'}, | ||||
|             {'name': '云南', 'code': '530000'}, | ||||
|             {'name': '四川省', 'code': '510000'}, | ||||
|             {'name': '贵州省', 'code': '520000'}, | ||||
|             {'name': '云南省', 'code': '530000'}, | ||||
|             {'name': '西藏', 'code': '540000'}, | ||||
|             {'name': '陕西', 'code': '610000'}, | ||||
|             {'name': '甘肃', 'code': '620000'}, | ||||
|             {'name': '青海', 'code': '630000'}, | ||||
|             {'name': '陕西省', 'code': '610000'}, | ||||
|             {'name': '甘肃省', 'code': '620000'}, | ||||
|             {'name': '青海省', 'code': '630000'}, | ||||
|             {'name': '宁夏', 'code': '640000'}, | ||||
|             {'name': '新疆', 'code': '650000'}, | ||||
|             {'name': '台湾', 'code': '710000'}, | ||||
|             {'name': '香港', 'code': '810000'}, | ||||
|             {'name': '澳门', 'code': '820000'}, | ||||
|             {'name': '钓鱼岛', 'code': '900000'}, | ||||
|             {'name': '未知区域', 'code': '000000'}, | ||||
|         ] | ||||
|         provinces = [x['name'] for x in CHINA_PROVINCES] | ||||
|  |  | |||
|  | @ -1,17 +1,20 @@ | |||
| import base64 | ||||
| import datetime | ||||
| import hashlib | ||||
| import json | ||||
| import os | ||||
| import random | ||||
| from pathlib import PurePosixPath | ||||
| 
 | ||||
| from django.http import HttpResponse | ||||
| from django.views.decorators.csrf import csrf_exempt | ||||
| from rest_framework import serializers | ||||
| from rest_framework.decorators import action | ||||
| from application.settings import BASE_DIR | ||||
| from application import dispatch | ||||
| from dvadmin.system.models import FileList | ||||
| from application import dispatch, settings | ||||
| from dvadmin.system.models import FileList, media_file_name | ||||
| from dvadmin.system.views.ueditor_settings import ueditor_upload_settings, ueditor_settings | ||||
| from dvadmin.utils.json_response import DetailResponse | ||||
| from dvadmin.utils.serializers import CustomModelSerializer | ||||
| from dvadmin.utils.string_util import format_bytes | ||||
| from dvadmin.utils.viewset import CustomModelViewSet | ||||
|  | @ -21,6 +24,16 @@ class FileSerializer(CustomModelSerializer): | |||
|     url = serializers.SerializerMethodField(read_only=True) | ||||
| 
 | ||||
|     def get_url(self, instance): | ||||
|         if self.request.query_params.get('prefix'): | ||||
|             if settings.ENVIRONMENT in ['local']: | ||||
|                 prefix = 'http://127.0.0.1:8000' | ||||
|             elif settings.ENVIRONMENT in ['test']: | ||||
|                 prefix = 'http://{host}/api'.format(host=self.request.get_host()) | ||||
|             else: | ||||
|                 prefix = 'https://{host}/api'.format(host=self.request.get_host()) | ||||
|             if instance.file_url: | ||||
|                 return instance.file_url if instance.file_url.startswith('http') else f"{prefix}/{instance.file_url}" | ||||
|             return (f'{prefix}/media/{str(instance.url)}') | ||||
|         return instance.file_url or (f'media/{str(instance.url)}') | ||||
| 
 | ||||
|     class Meta: | ||||
|  | @ -28,8 +41,8 @@ class FileSerializer(CustomModelSerializer): | |||
|         fields = "__all__" | ||||
| 
 | ||||
|     def create(self, validated_data): | ||||
|         file_engine = dispatch.get_system_config_values("fileStorageConfig.file_engine") or 'local' | ||||
|         file_backup = dispatch.get_system_config_values("fileStorageConfig.file_backup") | ||||
|         file_engine = dispatch.get_system_config_values("file_storage.file_engine") or 'local' | ||||
|         file_backup = dispatch.get_system_config_values("file_storage.file_backup") | ||||
|         file = self.initial_data.get('file') | ||||
|         file_size = file.size | ||||
|         validated_data['name'] = file.name | ||||
|  | @ -44,14 +57,18 @@ class FileSerializer(CustomModelSerializer): | |||
|             validated_data['url'] = file | ||||
|         if file_engine == 'oss': | ||||
|             from dvadmin_cloud_storage.views.aliyun import ali_oss_upload | ||||
|             file_path = ali_oss_upload(file) | ||||
|             h = validated_data['md5sum'] | ||||
|             basename, ext = os.path.splitext(file.name) | ||||
|             file_path = ali_oss_upload(file, file_name=PurePosixPath("files", h[:1], h[1:2], h + ext.lower())) | ||||
|             if file_path: | ||||
|                 validated_data['file_url'] = file_path | ||||
|             else: | ||||
|                 raise ValueError("上传失败") | ||||
|         elif file_engine == 'cos': | ||||
|             from dvadmin_cloud_storage.views.tencent import tencent_cos_upload | ||||
|             file_path = tencent_cos_upload(file) | ||||
|             h = validated_data['md5sum'] | ||||
|             basename, ext = os.path.splitext(file.name) | ||||
|             file_path = tencent_cos_upload(file, file_name=PurePosixPath("files", h[:1], h[1:2], h + ext.lower())) | ||||
|             if file_path: | ||||
|                 validated_data['file_url'] = file_path | ||||
|             else: | ||||
|  | @ -83,6 +100,12 @@ class FileViewSet(CustomModelViewSet): | |||
|     filter_fields = ['name', ] | ||||
|     permission_classes = [] | ||||
| 
 | ||||
|     def create(self, request, *args, **kwargs): | ||||
|         serializer = self.get_serializer(data=request.data, request=request) | ||||
|         serializer.is_valid(raise_exception=True) | ||||
|         self.perform_create(serializer) | ||||
|         return DetailResponse(data=serializer.data, msg="新增成功") | ||||
| 
 | ||||
|     @csrf_exempt | ||||
|     @action(methods=["GET", "POST"], detail=False, permission_classes=[]) | ||||
|     def ueditor(self, request): | ||||
|  | @ -138,16 +161,17 @@ class FileViewSet(CustomModelViewSet): | |||
|     # 涂鸦功能上传处理 | ||||
|     def save_scrawl_file(self, request, file_path, file_name): | ||||
|         import base64 | ||||
|         instance = None | ||||
|         try: | ||||
|             content = request.data.get(ueditor_upload_settings.get("scrawlFieldName", "upfile")) | ||||
|             f = open(os.path.join(BASE_DIR, file_path, file_name), 'wb') | ||||
|             f.write(base64.b64decode(content)) | ||||
|             f.close() | ||||
|             state = "SUCCESS" | ||||
|             FileList.save_file(request, file_path, file_name,mime_type='image/png') | ||||
|             instance = FileList.save_file(request, file_path, file_name, mime_type='image/png') | ||||
|         except Exception as e: | ||||
|             state = f"写入图片文件错误:{e}" | ||||
|         return state | ||||
|         return state, instance | ||||
| 
 | ||||
|     def upload_file(self, request): | ||||
|         """上传文件""" | ||||
|  | @ -213,20 +237,21 @@ class FileViewSet(CustomModelViewSet): | |||
|         # 取得输出文件的路径 | ||||
|         format_file_name, output_path = self.get_output_path(path_format_var) | ||||
|         # 所有检测完成后写入文件 | ||||
|         file_instance = None | ||||
|         if state == "SUCCESS": | ||||
|             if action == "uploadscrawl": | ||||
|                 state = self.save_scrawl_file(request, file_path=output_path, | ||||
|                                               file_name=format_file_name) | ||||
|                 state, file_instance = self.save_scrawl_file(request, file_path=output_path, | ||||
|                                                              file_name=format_file_name) | ||||
|             else: | ||||
|                 file = request.FILES.get(upload_field_name, None) | ||||
|                 # 保存到文件中,如果保存错误,需要返回ERROR | ||||
|                 state = self.save_upload_file(file, os.path.join(BASE_DIR, output_path, format_file_name)) | ||||
|                 # 保存到附件管理中 | ||||
|                 FileList.save_file(request, output_path, format_file_name, mime_type=file.content_type) | ||||
|                 file_instance = FileList.save_file(request, output_path, format_file_name, mime_type=file.content_type) | ||||
| 
 | ||||
|         # 返回数据 | ||||
|         return_info = { | ||||
|             'url': os.path.join(output_path, format_file_name),  # 保存后的文件名称 | ||||
|             'url': file_instance.file_url if file_instance else os.path.join(output_path, format_file_name),  # 保存后的文件名称 | ||||
|             'original': upload_file_name,  # 原始文件名 | ||||
|             'type': upload_original_ext, | ||||
|             'state': state,  # 上传状态,成功时返回SUCCESS,其他任何值将原样返回至图片上传框中 | ||||
|  |  | |||
|  | @ -249,16 +249,16 @@ class UserViewSet(CustomModelViewSet): | |||
|     serializer_class = UserSerializer | ||||
|     create_serializer_class = UserCreateSerializer | ||||
|     update_serializer_class = UserUpdateSerializer | ||||
|     # filter_fields = ["name", "username", "gender", "is_active", "dept", "user_type"] | ||||
|     filter_fields = { | ||||
|         "name": ["icontains"], | ||||
|         "mobile": ["icontains"], | ||||
|         "username": ["icontains"], | ||||
|         "gender": ["icontains"], | ||||
|         "is_active": ["icontains"], | ||||
|         "dept": ["exact"], | ||||
|         "user_type": ["exact"], | ||||
|     } | ||||
|     filter_fields = ["^name", "~username", "^mobile", "is_active", "dept", "user_type", "$dept__name"] | ||||
|     # filter_fields = { | ||||
|     #     "name": ["icontains"], | ||||
|     #     "mobile": ["iregex"], | ||||
|     #     "username": ["icontains"], | ||||
|     #     "is_active": ["icontains"], | ||||
|     #     "dept": ["exact"], | ||||
|     #     "user_type": ["exact"], | ||||
|     #     "dept__name": ["icontains"], | ||||
|     # } | ||||
|     search_fields = ["username", "name", "gender", "dept__name", "role__name"] | ||||
|     # 导出 | ||||
|     export_field_label = { | ||||
|  |  | |||
|  | @ -150,6 +150,7 @@ class CustomDjangoFilterBackend(DjangoFilterBackend): | |||
|         "$": "iregex", | ||||
|         "~": "icontains", | ||||
|     } | ||||
|     filter_fields = "__all__" | ||||
| 
 | ||||
|     def construct_search(self, field_name, lookup_expr=None): | ||||
|         lookup = self.lookup_prefixes.get(field_name[0]) | ||||
|  | @ -157,14 +158,16 @@ class CustomDjangoFilterBackend(DjangoFilterBackend): | |||
|             field_name = field_name[1:] | ||||
|         else: | ||||
|             lookup = lookup_expr | ||||
|         if field_name.endswith(lookup): | ||||
|             return field_name | ||||
|         return LOOKUP_SEP.join([field_name, lookup]) | ||||
|         if lookup: | ||||
|             if field_name.endswith(lookup): | ||||
|                 return field_name | ||||
|             return LOOKUP_SEP.join([field_name, lookup]) | ||||
|         return field_name | ||||
| 
 | ||||
|     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] | ||||
|             new_lookup = LOOKUP_SEP.join(lookup.split(LOOKUP_SEP)[:-1]) if len(lookup.split(LOOKUP_SEP)) > 1 else lookup | ||||
|             # 修复条件搜索错误 bug | ||||
|             if new_lookup == search_term_key: | ||||
|                 return lookup | ||||
|  | @ -189,7 +192,13 @@ class CustomDjangoFilterBackend(DjangoFilterBackend): | |||
|             utils.deprecate( | ||||
|                 "`%s.filter_fields` attribute should be renamed `filterset_fields`." % view.__class__.__name__ | ||||
|             ) | ||||
|             filterset_fields = getattr(view, "filter_fields", None) | ||||
|             self.filter_fields = getattr(view, "filter_fields", None) | ||||
|             if isinstance(self.filter_fields, (list, tuple)): | ||||
|                 filterset_fields = [ | ||||
|                     field[1:] if field[0] in self.lookup_prefixes.keys() else field for field in self.filter_fields | ||||
|                 ] | ||||
|             else: | ||||
|                 filterset_fields = self.filter_fields | ||||
| 
 | ||||
|         if filterset_class: | ||||
|             filterset_model = filterset_class._meta.model | ||||
|  | @ -327,18 +336,22 @@ class CustomDjangoFilterBackend(DjangoFilterBackend): | |||
|             return queryset | ||||
|         if filterset.__class__.__name__ == "AutoFilterSet": | ||||
|             queryset = filterset.queryset | ||||
|             orm_lookups = [] | ||||
|             for search_field in filterset.filters: | ||||
|                 if isinstance(filterset.filters[search_field], CharFilter): | ||||
|                     orm_lookups.append( | ||||
|                         self.construct_search(six.text_type(search_field), filterset.filters[search_field].lookup_expr) | ||||
|                     ) | ||||
|                 else: | ||||
|                     orm_lookups.append(search_field) | ||||
|             filter_fields = filterset.filters if self.filter_fields == "__all__" else self.filter_fields | ||||
|             orm_lookup_dict = dict( | ||||
|                 zip( | ||||
|                     [field for field in filter_fields], | ||||
|                     [filterset.filters[lookup].lookup_expr for lookup in filterset.filters.keys()], | ||||
|                 ) | ||||
|             ) | ||||
|             orm_lookups = [ | ||||
|                 self.construct_search(lookup, lookup_expr) for lookup, lookup_expr in orm_lookup_dict.items() | ||||
|             ] | ||||
|             # print(orm_lookups) | ||||
|             conditions = [] | ||||
|             queries = [] | ||||
|             for search_term_key in filterset.data.keys(): | ||||
|                 orm_lookup = self.find_filter_lookups(orm_lookups, search_term_key) | ||||
|                 # print(search_term_key, orm_lookup) | ||||
|                 if not orm_lookup: | ||||
|                     continue | ||||
|                 query = Q(**{orm_lookup: filterset.data[search_term_key]}) | ||||
|  |  | |||
|  | @ -118,7 +118,6 @@ class SoftDeleteModel(models.Model): | |||
|             ] | ||||
|             relations["self"] = f"{tree_model_field[0]}_id" if len(tree_model_field) == 1 else None | ||||
|         relations["foreign"] = related_fields | ||||
|         print(f"{relations=}", flush=True) | ||||
|         return relations | ||||
| 
 | ||||
|     def _is_cascade(self, relation): | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ certifi==2021.5.30 | |||
| chardet==4.0.0 | ||||
| coreapi==2.3.3 | ||||
| coreschema==0.0.4 | ||||
| Django==3.2.12 | ||||
| Django==3.2.19 | ||||
| django-comment-migrate==0.1.7 | ||||
| django-cors-headers==3.10.1 | ||||
| django-filter==22.1 | ||||
|  | @ -11,8 +11,8 @@ django-ranged-response==0.2.0 | |||
| django-redis==5.2.0 | ||||
| django-restql==0.15.3 | ||||
| django-simple-captcha==0.5.17 | ||||
| django-tenants==3.4.8 | ||||
| django-timezone-field==4.2.3 | ||||
| django-tenants==3.5.0 | ||||
| django-timezone-field==5.0 | ||||
| djangorestframework==3.14.0 | ||||
| djangorestframework-simplejwt==5.2.2 | ||||
| packaging==23.0 | ||||
|  | @ -29,7 +29,7 @@ pyparsing==2.4.7 | |||
| pyPEG2==2.15.2 | ||||
| pypinyin==0.48.0 | ||||
| pytz==2021.1 | ||||
| requests==2.28.2 | ||||
| requests==2.28.0 | ||||
| ruamel.yaml==0.17.10 | ||||
| ruamel.yaml.clib==0.2.4 | ||||
| six==1.16.0 | ||||
|  |  | |||
|  | @ -6,4 +6,6 @@ VUE_APP_TITLE=企业级后台管理系统 | |||
| VUE_APP_PM_ENABLED = true | ||||
| # 后端接口地址及端口(域名) | ||||
| VUE_APP_API = "http://127.0.0.1:8000" | ||||
| 
 | ||||
| VUE_APP_VERSION = '2.0.4' | ||||
| # 文件存储引擎 | ||||
| VUE_APP_FILE_ENGINE = 'local'  # oss、cos、local | ||||
|  |  | |||
|  | @ -13,3 +13,5 @@ VUE_APP_SCOURCE_LINK=FALSE | |||
| VUE_APP_PUBLIC_PATH=/ | ||||
| # 启用权限管理 | ||||
| VUE_APP_PM_ENABLED = true | ||||
| # 文件存储引擎 | ||||
| VUE_APP_FILE_ENGINE = 'local'  # oss、cos、local | ||||
|  |  | |||
|  | @ -13,3 +13,5 @@ VUE_APP_SCOURCE_LINK=FALSE | |||
| VUE_APP_PUBLIC_PATH=/ | ||||
| # 启用权限管理 | ||||
| VUE_APP_PM_ENABLED = true | ||||
| # 文件存储引擎 | ||||
| VUE_APP_FILE_ENGINE = 'local'  # oss、cos、local | ||||
|  |  | |||
|  | @ -56,6 +56,9 @@ | |||
|       <div style="text-align: center" v-if="_elProps.listType ===  'avatar'"> | ||||
|         <img  style="max-width: 100%;" :src="dialogImageUrl" alt=""> | ||||
|       </div> | ||||
|       <div style="text-align: center" v-else-if="_elProps.listType ===  'picture-card'"> | ||||
|         <img  style="max-width: 100%;" :src="dialogImageUrl" alt=""> | ||||
|       </div> | ||||
|       <div style="text-align: center" v-else> | ||||
|         <div id="player" v-if="dialogVisible"> | ||||
|           <div class="player-container"> | ||||
|  |  | |||
|  | @ -15,7 +15,7 @@ | |||
|       <el-row style="margin-bottom: 0" :gutter="5" v-for="(field, index) in currentForm.data" :key="index"> | ||||
|         <el-col :span="elProps.index.span" v-if="elProps.index"> | ||||
|           <div style="text-align: center"> | ||||
|             {{index+1}} | ||||
|             {{ index + 1 }} | ||||
|           </div> | ||||
|         </el-col> | ||||
|         <el-col :span="elProps.fields[key].span" v-for="(_,key) in elProps.fields" :key="key"> | ||||
|  | @ -34,20 +34,23 @@ | |||
|                 :value="item.value"> | ||||
|               </el-option> | ||||
|             </el-select> | ||||
|             <el-input-number style="width: 100%" v-else-if="elProps.fields[key].type === 'number'" controls-position="right" v-model="field[key]"></el-input-number> | ||||
|             <div v-else-if="elProps.fields[key].type === 'image'" style="height: 30px;width: 30px;"> | ||||
|                 <d2p-file-uploader v-model="field[key]" :elProps="elProps.fields[key].elProps || { listType: 'picture-card', accept: '.png,.jpeg,.jpg,.ico,.bmp,.gif', limit: 1 }"></d2p-file-uploader> | ||||
|             <el-input-number style="width: 100%" v-else-if="elProps.fields[key].type === 'number'" | ||||
|                              controls-position="right" v-model="field[key]"></el-input-number> | ||||
|             <div class="d2p-images" v-else-if="elProps.fields[key].type === 'image'"> | ||||
|               <d2p-file-uploader v-model="field[key]" | ||||
|                                  :elProps="elProps.fields[key].elProps || { listType: 'picture-card', accept: '.png,.jpeg,.jpg,.ico,.bmp,.gif', limit: 1 }"></d2p-file-uploader> | ||||
|             </div> | ||||
|             <!--     富文本     --> | ||||
|             <span v-else-if="elProps.fields[key].type === 'ueditor'"> | ||||
|               <values-popover v-model="field[key]" :elProps="{ type: 'ueditor' }" @previewClick="previewClick(index,key)"></values-popover> | ||||
|               <values-popover v-model="field[key]" :elProps="{ type: 'ueditor' }" | ||||
|                               @previewClick="previewClick(index,key)"></values-popover> | ||||
|             </span> | ||||
|             <!--     多对多     --> | ||||
|             <span v-else-if="elProps.fields[key].type === 'many_to_many'"> | ||||
|               <values-popover | ||||
|                 v-model="field[key]" | ||||
|                 :dict="elProps.fields[key].dict" | ||||
|                 :elProps="{ type: elProps.fields[key].value?.type || 'strList',  rowKey: elProps.fields[key].value?.rowKey || 'title', label: elProps.value?.title || '答复选项内容' }" | ||||
|                 :elProps="{ type: elProps.fields[key].value?.type || 'manyToMany',  rowKey: elProps.fields[key].value?.rowKey || 'title', label: elProps.value?.title || '答复选项内容' }" | ||||
|                 @listClick="manyToManyClick(index,key)"> | ||||
|               </values-popover> | ||||
|             </span> | ||||
|  | @ -56,9 +59,11 @@ | |||
|         </el-col> | ||||
|         <el-col :span="4"> | ||||
|           <el-form-item> | ||||
|           <el-button @click.prevent="topDomain(index)" :disabled="index === 0" type="primary" circle icon="el-icon-top"></el-button> | ||||
|           <el-button @click.prevent="bottomDomain(index)" :disabled="index === currentForm.data.length - 1" type="primary" circle icon="el-icon-bottom"></el-button> | ||||
|           <el-button @click.prevent="removeDomain(index)" type="danger" circle icon="el-icon-delete"></el-button> | ||||
|             <el-button @click.prevent="topDomain(index)" :disabled="index === 0" type="primary" circle | ||||
|                        icon="el-icon-top"></el-button> | ||||
|             <el-button @click.prevent="bottomDomain(index)" :disabled="index === currentForm.data.length - 1" | ||||
|                        type="primary" circle icon="el-icon-bottom"></el-button> | ||||
|             <el-button @click.prevent="removeDomain(index)" type="danger" circle icon="el-icon-delete"></el-button> | ||||
|           </el-form-item> | ||||
|         </el-col> | ||||
|       </el-row> | ||||
|  | @ -102,6 +107,7 @@ | |||
| </template> | ||||
| <script> | ||||
| import util from '@/libs/util.js' | ||||
| 
 | ||||
| export default { | ||||
|   name: 'foreignKeyCrudForm', | ||||
|   model: { | ||||
|  | @ -213,21 +219,21 @@ export default { | |||
|       ueditorConfig: { | ||||
|         serverUrl: util.baseURL() + 'api/system/file/ueditor/', | ||||
|         headers: { Authorization: 'JWT ' + util.cookies.get('token') }, | ||||
|         imageUrlPrefix: util.baseURL(), | ||||
|         imageUrlPrefix: util.baseFileURL(), | ||||
|         // 涂鸦图片上传 | ||||
|         scrawlUrlPrefix: util.baseURL(), | ||||
|         scrawlUrlPrefix: util.baseFileURL(), | ||||
|         // 截图工具上传 | ||||
|         snapscreenUrlPrefix: util.baseURL(), | ||||
|         snapscreenUrlPrefix: util.baseFileURL(), | ||||
|         // 抓取远程图片路径前缀 | ||||
|         catcherUrlPrefix: util.baseURL(), | ||||
|         catcherUrlPrefix: util.baseFileURL(), | ||||
|         // 视频访问路径前缀 | ||||
|         videoUrlPrefix: util.baseURL(), | ||||
|         videoUrlPrefix: util.baseFileURL(), | ||||
|         // 文件访问路径前缀 | ||||
|         fileUrlPrefix: util.baseURL(), | ||||
|         fileUrlPrefix: util.baseFileURL(), | ||||
|         // 列出指定目录下的图片 | ||||
|         imageManagerUrlPrefix: util.baseURL(), | ||||
|         imageManagerUrlPrefix: util.baseFileURL(), | ||||
|         // 列出指定目录下的文件 | ||||
|         fileManagerUrlPrefix: util.baseURL() | ||||
|         fileManagerUrlPrefix: util.baseFileURL() | ||||
|         // 传入ueditor的配置 | ||||
|         // 文档参考: http://fex.baidu.com/ueditor/#start-config | ||||
|       }, | ||||
|  | @ -346,8 +352,37 @@ export default { | |||
|   } | ||||
| } | ||||
| </script> | ||||
| <style scoped> | ||||
| ::v-deep .d2p-file-uploader .el-upload--picture-card{ | ||||
| <style lang="scss"> | ||||
| .d2p-images { | ||||
|   height: 30px; | ||||
|   width: 30px; | ||||
| 
 | ||||
|   .el-upload-list__item-thumbnail { | ||||
|     height: 60px !important; | ||||
|     width: 60px !important; | ||||
|   } | ||||
| 
 | ||||
|   .el-upload-list__item { | ||||
|     width: 60px !important; | ||||
|     height: 60px !important; | ||||
|     line-height: 0 !important; | ||||
| 
 | ||||
|     img { | ||||
|       height: 60px !important; | ||||
|       width: 60px !important; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   .el-upload-list__item-actions { | ||||
|     height: 60px !important; | ||||
|     width: 60px !important; | ||||
|     line-height: 0 !important; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| </style> | ||||
| <style lang="scss" scoped> | ||||
| ::v-deep .d2p-file-uploader .el-upload--picture-card { | ||||
|   width: 50px; | ||||
|   height: 50px; | ||||
| } | ||||
|  |  | |||
|  | @ -0,0 +1,6 @@ | |||
| <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32" fill="#999"> | ||||
|   <path opacity=".25" d="M16 0 A16 16 0 0 0 16 32 A16 16 0 0 0 16 0 M16 4 A12 12 0 0 1 16 28 A12 12 0 0 1 16 4"/> | ||||
|   <path d="M16 0 A16 16 0 0 1 32 16 L28 16 A12 12 0 0 0 16 4z"> | ||||
|     <animateTransform attributeName="transform" type="rotate" from="0 16 16" to="360 16 16" dur="0.8s" repeatCount="indefinite" /> | ||||
|   </path> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 422 B | 
|  | @ -40,7 +40,18 @@ | |||
|           <el-table-column fixed type="index" label="#" width="50"/> | ||||
|           <span v-for="(item,index) in _elProps.tableConfig.columns" :key="index" > | ||||
|             <el-table-column :prop="item.prop" :label="item.label" :width="item.width" | ||||
|                            v-if="item.show !== false"/> | ||||
|                            v-if="item.show !== false"> | ||||
|               <template slot-scope="scope"> | ||||
|                 <span v-if="item.type === 'image'"> | ||||
|                   <el-image :src="baseURL + scope.row[item.prop]" style="height: 30px;width: 30px;"> | ||||
|                     <div slot="placeholder" class="image-slot"> | ||||
|                       <img src="./loading-spin.svg"> | ||||
|                     </div> | ||||
|                   </el-image> | ||||
|                 </span> | ||||
|                 <span v-else>{{ scope.row[item.prop] }}</span> | ||||
|               </template> | ||||
|             </el-table-column> | ||||
|           </span> | ||||
|         </el-table> | ||||
|         <el-pagination style="margin-top: 10px;max-width: 200px" background | ||||
|  | @ -80,6 +91,7 @@ | |||
| import { request } from '@/api/service' | ||||
| import XEUtils from 'xe-utils' | ||||
| import { d2CrudPlus } from 'd2-crud-plus' | ||||
| import util from '@/libs/util' | ||||
| 
 | ||||
| export default { | ||||
|   name: 'selector-table-input', | ||||
|  | @ -147,7 +159,8 @@ export default { | |||
|       search: null, | ||||
|       tableData: [], | ||||
|       multipleSelection: [], | ||||
|       collapseTags: false | ||||
|       collapseTags: false, | ||||
|       baseURL: util.baseURL() | ||||
|     } | ||||
|   }, | ||||
|   computed: { | ||||
|  |  | |||
|  | @ -306,7 +306,7 @@ export default { | |||
|     }, | ||||
|     itemClosed (item) { | ||||
|       const newNodes = lodash.without(this.selected, item) | ||||
|       console.log('new value', item, newNodes) | ||||
|       // console.log('new value', item, newNodes) | ||||
|       this.$set(this, 'selected', newNodes) | ||||
|       this.doValueInputChanged(newNodes) | ||||
|     }, | ||||
|  |  | |||
|  | @ -36,6 +36,30 @@ | |||
|         </el-descriptions-item> | ||||
|       </el-descriptions> | ||||
|     </div> | ||||
|     <div v-if="elProps.type === 'manyToMany'"> | ||||
|      <el-popover | ||||
|         placement="right" | ||||
|         width="300" | ||||
|         trigger="hover" | ||||
|         v-if="value.length > 0" | ||||
|         @show="showEvents" | ||||
|         @hide="show=false"> | ||||
|         <el-descriptions class="margin-top" :column="1" size="mini" border> | ||||
|           <el-descriptions-item v-for="(item,index) in value" :key="index" labelStyle="width: 60px;"> | ||||
|             <template slot="label"> | ||||
|               选项{{ index + 1 }} | ||||
|             </template> | ||||
|             {{ item[dict.label] }} | ||||
|           </el-descriptions-item> | ||||
|         </el-descriptions> | ||||
|         <el-button type="primary" plain size="mini" slot="reference" @click="listClick"><span> {{ value.length }} {{ elProps.unit }}</span> | ||||
|         </el-button> | ||||
|       </el-popover> | ||||
|       <el-button v-else type="primary" plain size="mini" slot="reference" @click="listClick"><span> {{ | ||||
|           value.length | ||||
|         }} {{ elProps.unit }}</span> | ||||
|       </el-button> | ||||
|     </div> | ||||
|     <div v-else-if="elProps.type === 'ueditor'"> | ||||
|       <el-popover | ||||
|         placement="right" | ||||
|  |  | |||
|  | @ -1,4 +1,5 @@ | |||
| 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' } } } }, | ||||
|  | @ -244,21 +245,21 @@ export default { | |||
|           config: { | ||||
|             serverUrl: util.baseURL() + 'api/system/file/ueditor/', | ||||
|             headers: { Authorization: 'JWT ' + util.cookies.get('token') }, | ||||
|             imageUrlPrefix: util.baseURL(), | ||||
|             imageUrlPrefix: util.baseFileURL(), | ||||
|             // 涂鸦图片上传 | ||||
|             scrawlUrlPrefix: util.baseURL(), | ||||
|             scrawlUrlPrefix: util.baseFileURL(), | ||||
|             // 截图工具上传 | ||||
|             snapscreenUrlPrefix: util.baseURL(), | ||||
|             snapscreenUrlPrefix: util.baseFileURL(), | ||||
|             // 抓取远程图片路径前缀 | ||||
|             catcherUrlPrefix: util.baseURL(), | ||||
|             catcherUrlPrefix: util.baseFileURL(), | ||||
|             // 视频访问路径前缀 | ||||
|             videoUrlPrefix: util.baseURL(), | ||||
|             videoUrlPrefix: util.baseFileURL(), | ||||
|             // 文件访问路径前缀 | ||||
|             fileUrlPrefix: util.baseURL(), | ||||
|             fileUrlPrefix: util.baseFileURL(), | ||||
|             // 列出指定目录下的图片 | ||||
|             imageManagerUrlPrefix: util.baseURL(), | ||||
|             imageManagerUrlPrefix: util.baseFileURL(), | ||||
|             // 列出指定目录下的文件 | ||||
|             fileManagerUrlPrefix: util.baseURL() | ||||
|             fileManagerUrlPrefix: util.baseFileURL() | ||||
|             // 传入ueditor的配置 | ||||
|             // 文档参考: http://fex.baidu.com/ueditor/#start-config | ||||
|           } | ||||
|  |  | |||
|  | @ -3,7 +3,6 @@ import db from './util.db' | |||
| import log from './util.log' | ||||
| import dayjs from 'dayjs' | ||||
| import filterParams from './util.params' | ||||
| 
 | ||||
| const util = { | ||||
|   cookies, | ||||
|   db, | ||||
|  | @ -63,6 +62,12 @@ util.baseURL = function () { | |||
|   return baseURL | ||||
| } | ||||
| 
 | ||||
| util.baseFileURL = function () { | ||||
|   if (process.env.VUE_APP_FILE_ENGINE && (process.env.VUE_APP_FILE_ENGINE === 'oss' || process.env.VUE_APP_FILE_ENGINE === 'cos')) { | ||||
|     return '' | ||||
|   } | ||||
|   return util.baseURL() | ||||
| } | ||||
| util.wsBaseURL = function () { | ||||
|   var baseURL = process.env.VUE_APP_API | ||||
|   var param = baseURL.split('/')[3] || '' | ||||
|  |  | |||
|  | @ -56,11 +56,11 @@ router.beforeEach(async (to, from, next) => { | |||
|       }) | ||||
|       await store.dispatch('d2admin/user/set', res.data, { root: true }) | ||||
|       await store.dispatch('d2admin/account/load') | ||||
|       await store.dispatch('d2admin/permission/load', routes) | ||||
|       store.dispatch('d2admin/dept/load') | ||||
|       store.dispatch('d2admin/settings/init') | ||||
|     } | ||||
|     if (!store.state.d2admin.menu || store.state.d2admin.menu.aside.length === 0) { | ||||
|       await store.dispatch('d2admin/permission/load', routes) | ||||
|       await store.dispatch('d2admin/dept/load') | ||||
|       // 动态添加路由 | ||||
|       getMenu().then(ret => { | ||||
|         // 校验路由是否有效 | ||||
|  | @ -87,8 +87,11 @@ router.beforeEach(async (to, from, next) => { | |||
|     } else { | ||||
|       const childrenPath = window.qiankunActiveRule || [] | ||||
|       if (to.name) { | ||||
|         if (to.meta.openInNewWindow && (from.query.newWindow && to.query.newWindow !== '1' || from.path === '/')) { | ||||
|           to.query.newWindow = '1' | ||||
|         } | ||||
|         // 有 name 属性,说明是主应用的路由 | ||||
|         if (to.meta.openInNewWindow && !to.query.newWindow) { | ||||
|         if (to.meta.openInNewWindow && !to.query.newWindow && !from.query.newWindow && from.path !== '/') { | ||||
|           // 在新窗口中打开路由 | ||||
|           const { href } = router.resolve({ | ||||
|             path: to.path + '?newWindow=1' | ||||
|  |  | |||
|  | @ -97,6 +97,11 @@ export const crudOptions = (vm) => { | |||
|         //   label: 'name', // 数据字典中label字段的属性名 | ||||
|         //   children: 'children' // 数据字典中children字段的属性名 | ||||
|         // }, | ||||
|         valueResolve (row, key) { | ||||
|           if (row.pcode === null) { | ||||
|             row.pcode = undefined | ||||
|           } | ||||
|         }, | ||||
|         form: { | ||||
|           component: { | ||||
|             showAllLevels: false, // 仅显示最后一级 | ||||
|  |  | |||
|  | @ -82,7 +82,9 @@ export const crudOptions = (vm) => { | |||
|       }, | ||||
|       type: 'select', | ||||
|       dict: { | ||||
|         data: vm.dictionary('system_button') | ||||
|         data: vm.dictionary('system_button'), | ||||
|         label: 'label', | ||||
|         value: 'label' | ||||
|       }, | ||||
|       form: { | ||||
|         rules: [ // 表单校验规则 | ||||
|  | @ -107,7 +109,7 @@ export const crudOptions = (vm) => { | |||
|             // console.log('component.dictOptions', component.dictOptions) | ||||
|             const obj = component.dictOptions.find(item => { | ||||
|               // console.log(item.label, value) | ||||
|               return item.value === value | ||||
|               return item.label === value | ||||
|             }) | ||||
|             if (obj && obj.value) { | ||||
|               form.name = obj.label | ||||
|  |  | |||
|  | @ -92,6 +92,23 @@ export const crudOptions = (vm) => { | |||
|           disabled: true | ||||
|         } | ||||
|       }, | ||||
|       { | ||||
|         title: '部门名称', | ||||
|         key: 'dept__name', | ||||
|         treeNode: true, // 设置为树形列 | ||||
|         search: { | ||||
|           disabled: false, | ||||
|           component: { | ||||
|             props: { | ||||
|               clearable: true | ||||
|             } | ||||
|           } | ||||
|         }, | ||||
|         show: false, | ||||
|         form: { | ||||
|           disabled: true | ||||
|         } | ||||
|       }, | ||||
|       { | ||||
|         title: '账号', | ||||
|         key: 'username', | ||||
|  | @ -174,7 +191,7 @@ export const crudOptions = (vm) => { | |||
|         title: '部门', | ||||
|         key: 'dept', | ||||
|         search: { | ||||
|           disabled: true | ||||
|           disabled: false | ||||
|         }, | ||||
|         minWidth: 140, | ||||
|         type: 'tree-selector', | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 猿小天
						猿小天