diff --git a/backend/application/dispatch.py b/backend/application/dispatch.py index 3bd364c..101b3ab 100644 --- a/backend/application/dispatch.py +++ b/backend/application/dispatch.py @@ -2,6 +2,10 @@ # -*- coding: utf-8 -*- from django.conf import settings from django.db import connection +from django.core.cache import cache +from dvadmin.utils.validator import CustomValidationError + +dispatch_db_type = getattr(settings, 'DISPATCH_DB_TYPE', 'memory') # redis def is_tenants_mode(): @@ -68,6 +72,9 @@ def init_dictionary(): :return: """ try: + if dispatch_db_type == 'redis': + cache.set(f"init_dictionary", _get_all_dictionary()) + return if is_tenants_mode(): from django_tenants.utils import tenant_context, get_tenant_model @@ -88,7 +95,9 @@ def init_system_config(): :return: """ try: - + if dispatch_db_type == 'redis': + cache.set(f"init_system_config", _get_all_system_config()) + return if is_tenants_mode(): from django_tenants.utils import tenant_context, get_tenant_model @@ -107,6 +116,9 @@ def refresh_dictionary(): 刷新字典配置 :return: """ + if dispatch_db_type == 'redis': + cache.set(f"init_dictionary", _get_all_dictionary()) + return if is_tenants_mode(): from django_tenants.utils import tenant_context, get_tenant_model @@ -122,6 +134,9 @@ def refresh_system_config(): 刷新系统配置 :return: """ + if dispatch_db_type == 'redis': + cache.set(f"init_system_config", _get_all_system_config()) + return if is_tenants_mode(): from django_tenants.utils import tenant_context, get_tenant_model @@ -141,6 +156,11 @@ def get_dictionary_config(schema_name=None): :param schema_name: 对应字典配置的租户schema_name值 :return: """ + if dispatch_db_type == 'redis': + init_dictionary_data = cache.get(f"init_dictionary") + if not init_dictionary_data: + refresh_dictionary() + return cache.get(f"init_dictionary") or {} if not settings.DICTIONARY_CONFIG: refresh_dictionary() if is_tenants_mode(): @@ -157,6 +177,12 @@ def get_dictionary_values(key, schema_name=None): :param schema_name: 对应字典配置的租户schema_name值 :return: """ + if dispatch_db_type == 'redis': + dictionary_config = cache.get(f"init_dictionary") + if not dictionary_config: + refresh_dictionary() + dictionary_config = cache.get(f"init_dictionary") + return dictionary_config.get(key) dictionary_config = get_dictionary_config(schema_name) return dictionary_config.get(key) @@ -169,8 +195,8 @@ def get_dictionary_label(key, name, schema_name=None): :param schema_name: 对应字典配置的租户schema_name值 :return: """ - children = get_dictionary_values(key, schema_name) or [] - for ele in children: + res = get_dictionary_values(key, schema_name) or [] + for ele in res.get('children'): if ele.get("value") == str(name): return ele.get("label") return "" @@ -187,6 +213,11 @@ def get_system_config(schema_name=None): :param schema_name: 对应字典配置的租户schema_name值 :return: """ + if dispatch_db_type == 'redis': + init_dictionary_data = cache.get(f"init_system_config") + if not init_dictionary_data: + refresh_system_config() + return cache.get(f"init_system_config") or {} if not settings.SYSTEM_CONFIG: refresh_system_config() if is_tenants_mode(): @@ -203,10 +234,32 @@ def get_system_config_values(key, schema_name=None): :param schema_name: 对应系统配置的租户schema_name值 :return: """ + if dispatch_db_type == 'redis': + system_config = cache.get(f"init_system_config") + if not system_config: + refresh_system_config() + system_config = cache.get(f"init_system_config") + return system_config.get(key) system_config = get_system_config(schema_name) return system_config.get(key) +def get_system_config_values_to_dict(key, schema_name=None): + """ + 获取系统配置数据并转换为字典 **仅限于数组类型系统配置 + :param key: 对应系统配置的key值(字典编号) + :param schema_name: 对应系统配置的租户schema_name值 + :return: + """ + values_dict = {} + config_values = get_system_config_values(key, schema_name) + if not isinstance(config_values, list): + raise CustomValidationError("该方式仅限于数组类型系统配置") + for ele in get_system_config_values(key, schema_name): + values_dict[ele.get('key')] = ele.get('value') + return values_dict + + def get_system_config_label(key, name, schema_name=None): """ 获取获取系统配置label值 diff --git a/backend/dvadmin/system/models.py b/backend/dvadmin/system/models.py index d9a4274..5d500b7 100644 --- a/backend/dvadmin/system/models.py +++ b/backend/dvadmin/system/models.py @@ -73,6 +73,7 @@ class Users(CoreModel, AbstractUser): help_text="关联部门", ) login_error_count = models.IntegerField(default=0, verbose_name="登录错误次数", help_text="登录错误次数") + pwd_change_count = models.IntegerField(default=0,blank=True, verbose_name="密码修改次数", help_text="密码修改次数") objects = CustomUserManager() def set_password(self, raw_password): @@ -407,6 +408,18 @@ class FileList(CoreModel): mime_type = models.CharField(max_length=100, blank=True, verbose_name="Mime类型", help_text="Mime类型") size = models.CharField(max_length=36, blank=True, verbose_name="文件大小", help_text="文件大小") md5sum = models.CharField(max_length=36, blank=True, verbose_name="文件md5", help_text="文件md5") + UPLOAD_METHOD_CHOIDES = ( + (0, '默认上传'), + (1, '文件选择器上传'), + ) + upload_method = models.SmallIntegerField(default=0, blank=True, null=True, choices=UPLOAD_METHOD_CHOIDES, verbose_name='上传方式', help_text='上传方式') + FILE_TYPE_CHOIDES = ( + (0, '图片'), + (1, '视频'), + (2, '音频'), + (3, '其他'), + ) + file_type = models.SmallIntegerField(default=3, choices=FILE_TYPE_CHOIDES, blank=True, null=True, verbose_name='文件类型', help_text='文件类型') def save(self, *args, **kwargs): if not self.md5sum: # file is new diff --git a/backend/dvadmin/system/views/file_list.py b/backend/dvadmin/system/views/file_list.py index c595699..eb97270 100644 --- a/backend/dvadmin/system/views/file_list.py +++ b/backend/dvadmin/system/views/file_list.py @@ -1,12 +1,15 @@ import hashlib import mimetypes +import django_filters +from django.conf import settings +from django.db import connection from rest_framework import serializers from rest_framework.decorators import action from application import dispatch from dvadmin.system.models import FileList -from dvadmin.utils.json_response import DetailResponse +from dvadmin.utils.json_response import DetailResponse, SuccessResponse from dvadmin.utils.serializers import CustomModelSerializer from dvadmin.utils.viewset import CustomModelViewSet @@ -15,8 +18,21 @@ class FileSerializer(CustomModelSerializer): url = serializers.SerializerMethodField(read_only=True) def get_url(self, instance): - base_url = f"{self.request.scheme}://{self.request.get_host()}/" - return base_url + (instance.file_url or (f'media/{str(instance.url)}')) + 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: + model = FileList + fields = "__all__" class Meta: model = FileList @@ -35,6 +51,8 @@ class FileSerializer(CustomModelSerializer): validated_data['md5sum'] = md5.hexdigest() validated_data['engine'] = file_engine validated_data['mime_type'] = file.content_type + ft = {'image':0,'video':1,'audio':2}.get(file.content_type.split('/')[0], None) + validated_data['file_type'] = 3 if ft is None else ft if file_backup: validated_data['url'] = file if file_engine == 'oss': @@ -64,6 +82,22 @@ class FileSerializer(CustomModelSerializer): return super().create(validated_data) +class FileAllSerializer(CustomModelSerializer): + + class Meta: + model = FileList + fields = ['id', 'name'] + + +class FileFilter(django_filters.FilterSet): + name = django_filters.CharFilter(field_name="name", lookup_expr="icontains", help_text="文件名") + mime_type = django_filters.CharFilter(field_name="mime_type", lookup_expr="icontains", help_text="文件类型") + + class Meta: + model = FileList + fields = ['name', 'mime_type', 'upload_method', 'file_type'] + + class FileViewSet(CustomModelViewSet): """ 文件管理接口 @@ -75,5 +109,22 @@ class FileViewSet(CustomModelViewSet): """ queryset = FileList.objects.all() serializer_class = FileSerializer - filter_fields = ['name', ] - permission_classes = [] \ No newline at end of file + filter_class = FileFilter + permission_classes = [] + + @action(methods=['GET'], detail=False) + def get_all(self, request): + data1 = self.get_serializer(self.get_queryset(), many=True).data + data2 = [] + if dispatch.is_tenants_mode(): + from django_tenants.utils import schema_context + with schema_context('public'): + data2 = self.get_serializer(FileList.objects.all(), many=True).data + return DetailResponse(data=data2+data1) + + def list(self, request, *args, **kwargs): + if self.request.query_params.get('system', 'False') == 'True' and dispatch.is_tenants_mode(): + from django_tenants.utils import schema_context + with schema_context('public'): + return super().list(request, *args, **kwargs) + return super().list(request, *args, **kwargs) diff --git a/backend/dvadmin/system/views/login.py b/backend/dvadmin/system/views/login.py index 1996906..3b1209d 100644 --- a/backend/dvadmin/system/views/login.py +++ b/backend/dvadmin/system/views/login.py @@ -4,12 +4,15 @@ 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.contrib.auth.hashers import check_password, make_password from django.db.models import Q from django.shortcuts import redirect from django.utils.translation import gettext_lazy as _ from drf_yasg import openapi from drf_yasg.utils import swagger_auto_schema from rest_framework import serializers +from rest_framework.decorators import action +from rest_framework.permissions import IsAuthenticated from rest_framework.views import APIView from rest_framework_simplejwt.serializers import TokenObtainPairSerializer from rest_framework_simplejwt.views import TokenObtainPairView @@ -97,16 +100,17 @@ class LoginSerializer(TokenObtainPairSerializer): # 必须重置用户名为username,否则使用邮箱手机号登录会提示密码错误 attrs['username'] = user.username data = super().validate(attrs) + data["username"] = self.user.username data["name"] = self.user.name data["userId"] = self.user.id data["avatar"] = self.user.avatar data['user_type'] = self.user.user_type + data['pwd_change_count'] = self.user.pwd_change_count dept = getattr(self.user, 'dept', None) if dept: data['dept_info'] = { 'dept_id': dept.id, 'dept_name': dept.name, - } role = getattr(self.user, 'role', None) if role: diff --git a/backend/dvadmin/system/views/menu.py b/backend/dvadmin/system/views/menu.py index c0c6b65..33ed979 100644 --- a/backend/dvadmin/system/views/menu.py +++ b/backend/dvadmin/system/views/menu.py @@ -120,11 +120,11 @@ class MenuViewSet(CustomModelViewSet): """用于前端获取当前角色的路由""" user = request.user if user.is_superuser: - queryset = self.queryset.filter(status=1).order_by("id") + queryset = self.queryset.filter(status=1).order_by("sort") else: role_list = user.role.values_list('id', flat=True) menu_list = RoleMenuPermission.objects.filter(role__in=role_list).values_list('menu_id', flat=True) - queryset = Menu.objects.filter(id__in=menu_list).order_by("id") + queryset = Menu.objects.filter(id__in=menu_list).order_by("sort") serializer = WebRouterSerializer(queryset, many=True, request=request) data = serializer.data return SuccessResponse(data=data, total=len(data), msg="获取成功") diff --git a/backend/dvadmin/system/views/user.py b/backend/dvadmin/system/views/user.py index 68de06b..16fcbe9 100644 --- a/backend/dvadmin/system/views/user.py +++ b/backend/dvadmin/system/views/user.py @@ -286,6 +286,7 @@ class UserViewSet(CustomModelViewSet): "dept": user.dept_id, "is_superuser": user.is_superuser, "role": user.role.values_list('id', flat=True), + "pwd_change_count":user.pwd_change_count } if hasattr(connection, 'tenant'): result['tenant_id'] = connection.tenant and connection.tenant.id @@ -319,7 +320,6 @@ class UserViewSet(CustomModelViewSet): """密码修改""" data = request.data old_pwd = data.get("oldPassword") - print(old_pwd) new_pwd = data.get("newPassword") new_pwd2 = data.get("newPassword2") if old_pwd is None or new_pwd is None or new_pwd2 is None: @@ -335,12 +335,28 @@ class UserViewSet(CustomModelViewSet): old_pwd_md5 = hashlib.md5(old_pwd_md5.encode(encoding='UTF-8')).hexdigest() verify_password = check_password(str(old_pwd_md5), request.user.password) if verify_password: - request.user.password = make_password(hashlib.md5(new_pwd.encode(encoding='UTF-8')).hexdigest()) + # request.user.password = make_password(hashlib.md5(new_pwd.encode(encoding='UTF-8')).hexdigest()) + request.user.password = make_password(new_pwd) + request.user.pwd_change_count += 1 request.user.save() return DetailResponse(data=None, msg="修改成功") else: return ErrorResponse(msg="旧密码不正确") + @action(methods=["post"], detail=False, permission_classes=[IsAuthenticated]) + def login_change_password(self, request, *args, **kwargs): + """初次登录进行密码修改""" + data = request.data + new_pwd = data.get("password") + new_pwd2 = data.get("password_regain") + if new_pwd != new_pwd2: + return ErrorResponse(msg="两次密码不匹配") + else: + request.user.password = make_password(hashlib.md5(new_pwd.encode(encoding='UTF-8')).hexdigest()) + request.user.pwd_change_count += 1 + request.user.save() + return DetailResponse(data=None, msg="修改成功") + @action(methods=["PUT"], detail=True, permission_classes=[IsAuthenticated]) def reset_to_default_password(self, request,pk): """恢复默认密码""" diff --git a/web/src/assets/login-bg.png b/web/src/assets/login-bg.png new file mode 100644 index 0000000..9fd3aa4 Binary files /dev/null and b/web/src/assets/login-bg.png differ diff --git a/web/src/components/fileSelector/fileItem.vue b/web/src/components/fileSelector/fileItem.vue new file mode 100644 index 0000000..73ef6f0 --- /dev/null +++ b/web/src/components/fileSelector/fileItem.vue @@ -0,0 +1,84 @@ + + + \ No newline at end of file diff --git a/web/src/components/fileSelector/index.vue b/web/src/components/fileSelector/index.vue new file mode 100644 index 0000000..55b1fa2 --- /dev/null +++ b/web/src/components/fileSelector/index.vue @@ -0,0 +1,476 @@ + + + + + \ No newline at end of file diff --git a/web/src/components/fileSelector/types.ts b/web/src/components/fileSelector/types.ts new file mode 100644 index 0000000..e428015 --- /dev/null +++ b/web/src/components/fileSelector/types.ts @@ -0,0 +1,7 @@ +export const SHOW = { + IMAGE: 0b1000, // 图片 + VIDEO: 0b0100, // 视频 + AUDIO: 0b0010, // 音频 + OTHER: 0b0001, // 其他 + ALL: 0b1111, // 全部 +}; diff --git a/web/src/i18n/pages/login/en.ts b/web/src/i18n/pages/login/en.ts index 28c6983..ad93b86 100644 --- a/web/src/i18n/pages/login/en.ts +++ b/web/src/i18n/pages/login/en.ts @@ -3,6 +3,7 @@ export default { label: { one1: 'User name login', two2: 'Mobile number', + changePwd: 'Change The Password', }, link: { one3: 'Third party login', diff --git a/web/src/i18n/pages/login/zh-cn.ts b/web/src/i18n/pages/login/zh-cn.ts index 07fd82e..ccd60cb 100644 --- a/web/src/i18n/pages/login/zh-cn.ts +++ b/web/src/i18n/pages/login/zh-cn.ts @@ -3,6 +3,7 @@ export default { label: { one1: '账号密码登录', two2: '手机号登录', + changePwd: '密码修改', }, link: { one3: '第三方登录', @@ -12,6 +13,8 @@ export default { accountPlaceholder1: '请输入登录账号/邮箱/手机号', accountPlaceholder2: '请输入登录密码', accountPlaceholder3: '请输入验证码', + accountPlaceholder4:'请输入新密码', + accountPlaceholder5:'请再次输入新密码', accountBtnText: '登 录', }, mobile: { diff --git a/web/src/i18n/pages/login/zh-tw.ts b/web/src/i18n/pages/login/zh-tw.ts index 13ad013..e3cd9e6 100644 --- a/web/src/i18n/pages/login/zh-tw.ts +++ b/web/src/i18n/pages/login/zh-tw.ts @@ -3,6 +3,7 @@ export default { label: { one1: '用戶名登入', two2: '手機號登入', + changePwd: '密码修改', }, link: { one3: '協力廠商登入', diff --git a/web/src/router/backEnd.ts b/web/src/router/backEnd.ts index 0839c49..a12db74 100644 --- a/web/src/router/backEnd.ts +++ b/web/src/router/backEnd.ts @@ -44,7 +44,7 @@ export async function initBackEndControlRoutes() { if (!Session.get('token')) return false; // 触发初始化用户信息 pinia // https://gitee.com/lyt-top/vue-next-admin/issues/I5F1HP - await useUserInfo().setUserInfos(); + await useUserInfo().getApiUserInfo(); // 获取路由菜单数据 const res = await getBackEndControlRoutes(); // 无登录权限时,添加判断 diff --git a/web/src/router/index.ts b/web/src/router/index.ts index f98dcf3..0815e9e 100644 --- a/web/src/router/index.ts +++ b/web/src/router/index.ts @@ -28,6 +28,8 @@ import {checkVersion} from "/@/utils/upgrade"; const storesThemeConfig = useThemeConfig(pinia); const {themeConfig} = storeToRefs(storesThemeConfig); const {isRequestRoutes} = themeConfig.value; +import {useUserInfo} from "/@/stores/userInfo"; +const { userInfos } = storeToRefs(useUserInfo()); /** * 创建一个可以被 Vue 应用程序使用的路由实例 @@ -111,7 +113,10 @@ router.beforeEach(async (to, from, next) => { next(`/login?redirect=${to.path}¶ms=${JSON.stringify(to.query ? to.query : to.params)}`); Session.clear(); NProgress.done(); - } else if (token && to.path === '/login') { + }else if (token && to.path === '/login' && userInfos.value.pwd_change_count===0 ) { + next('/login'); + NProgress.done(); + } else if (token && to.path === '/login' && userInfos.value.pwd_change_count>0) { next('/home'); NProgress.done(); }else if(token && frameOutRoutes.includes(to.path) ){ diff --git a/web/src/stores/interface/index.ts b/web/src/stores/interface/index.ts index 82dc5c6..aeb8420 100644 --- a/web/src/stores/interface/index.ts +++ b/web/src/stores/interface/index.ts @@ -12,6 +12,7 @@ export interface UserInfosState { email: string; mobile: string; gender: string; + pwd_change_count:null|number; dept_info: { dept_id: number; dept_name: string; diff --git a/web/src/stores/userInfo.ts b/web/src/stores/userInfo.ts index 9be64a8..04d296a 100644 --- a/web/src/stores/userInfo.ts +++ b/web/src/stores/userInfo.ts @@ -15,6 +15,7 @@ export const useUserInfo = defineStore('userInfo', { email: '', mobile: '', gender: '', + pwd_change_count:null, dept_info: { dept_id: 0, dept_name: '', @@ -29,16 +30,19 @@ export const useUserInfo = defineStore('userInfo', { isSocketOpen: false }), actions: { - async updateUserInfos() { - let userInfos: any = await this.getApiUserInfo(); - this.userInfos.username = userInfos.data.name; - this.userInfos.avatar = userInfos.data.avatar; - this.userInfos.name = userInfos.data.name; - this.userInfos.email = userInfos.data.email; - this.userInfos.mobile = userInfos.data.mobile; - this.userInfos.gender = userInfos.data.gender; - this.userInfos.dept_info = userInfos.data.dept_info; - this.userInfos.role_info = userInfos.data.role_info; + async setPwdChangeCount(count: number) { + this.userInfos.pwd_change_count = count; + }, + async updateUserInfos(userInfos:any) { + this.userInfos.username = userInfos.name; + this.userInfos.avatar = userInfos.avatar; + this.userInfos.name = userInfos.name; + this.userInfos.email = userInfos.email; + this.userInfos.mobile = userInfos.mobile; + this.userInfos.gender = userInfos.gender; + this.userInfos.dept_info = userInfos.dept_info; + this.userInfos.role_info = userInfos.role_info; + this.userInfos.pwd_change_count = userInfos.pwd_change_count; Session.set('userInfo', this.userInfos); }, async setUserInfos() { @@ -55,6 +59,7 @@ export const useUserInfo = defineStore('userInfo', { this.userInfos.gender = userInfos.data.gender; this.userInfos.dept_info = userInfos.data.dept_info; this.userInfos.role_info = userInfos.data.role_info; + this.userInfos.pwd_change_count = userInfos.data.pwd_change_count; Session.set('userInfo', this.userInfos); } }, @@ -65,7 +70,18 @@ export const useUserInfo = defineStore('userInfo', { return request({ url: '/api/system/user/user_info/', method: 'get', - }); + }).then((res:any)=>{ + this.userInfos.username = res.data.name; + this.userInfos.avatar = res.data.avatar; + this.userInfos.name = res.data.name; + this.userInfos.email = res.data.email; + this.userInfos.mobile = res.data.mobile; + this.userInfos.gender = res.data.gender; + this.userInfos.dept_info = res.data.dept_info; + this.userInfos.role_info = res.data.role_info; + this.userInfos.pwd_change_count = res.data.pwd_change_count; + Session.set('userInfo', this.userInfos); + }) }, }, }); diff --git a/web/src/utils/service.ts b/web/src/utils/service.ts index afb30cc..4207572 100644 --- a/web/src/utils/service.ts +++ b/web/src/utils/service.ts @@ -215,6 +215,7 @@ export const downloadFile = function ({ url, params, method, filename = '文件 // headers: {Accept: 'application/vnd.openxmlformats-officedocument'} }).then((res: any) => { // console.log(res.headers['content-type']); // 根据content-type不同来判断是否异步下载 + // if (res.headers && res.headers['Content-type'] === 'application/json') return successMessage('导入任务已创建,请前往‘下载中心’等待下载'); if (res.headers['content-type'] === 'application/json') return successMessage('导入任务已创建,请前往‘下载中心’等待下载'); const xlsxName = window.decodeURI(res.headers['content-disposition'].split('=')[1]) const fileName = xlsxName || `${filename}.xlsx` diff --git a/web/src/views/system/fileList/crud.tsx b/web/src/views/system/fileList/crud.tsx index 5c6a0d5..5dc388e 100644 --- a/web/src/views/system/fileList/crud.tsx +++ b/web/src/views/system/fileList/crud.tsx @@ -1,7 +1,19 @@ import * as api from './api'; -import { UserPageQuery, AddReq, DelReq, EditReq, CrudExpose, CrudOptions, CreateCrudOptionsProps, CreateCrudOptionsRet } from '@fast-crud/fast-crud'; +import { + UserPageQuery, + AddReq, + DelReq, + EditReq, + CrudExpose, + CrudOptions, + CreateCrudOptionsProps, + CreateCrudOptionsRet, + dict +} from '@fast-crud/fast-crud'; +import fileSelector from '/@/components/fileSelector/index.vue'; +import { shallowRef } from 'vue'; -export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet { +export const createCrudOptions = function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet { const pageRequest = async (query: UserPageQuery) => { return await api.GetList(query); }; @@ -20,7 +32,8 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp actionbar: { buttons: { add: { - show: false, + show: true, + click: () => context.openAddHandle?.() }, }, }, @@ -30,11 +43,22 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp editRequest, delRequest, }, + tabs: { + show: true, + name: 'file_type', + type: '', + options: [ + { value: 0, label: '图片' }, + { value: 1, label: '视频' }, + { value: 2, label: '音频' }, + { value: 3, label: '其他' }, + ] + }, rowHandle: { //固定右侧 fixed: 'right', width: 200, - show:false, + show: false, buttons: { view: { show: false, @@ -95,23 +119,34 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp show: true, }, type: 'input', - column:{ - minWidth: 120, + column: { + minWidth: 200, }, form: { component: { placeholder: '请输入文件名称', + clearable: true }, }, }, + preview: { + title: '预览', + column: { + minWidth: 120, + align: 'center' + }, + form: { + show: false + } + }, url: { title: '文件地址', type: 'file-uploader', search: { disabled: true, }, - column:{ - minWidth: 200, + column: { + minWidth: 360, }, }, md5sum: { @@ -119,13 +154,99 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp search: { disabled: true, }, - column:{ - minWidth: 120, + column: { + minWidth: 300, }, form: { - disabled: false, + disabled: false }, }, + mime_type: { + title: '文件类型', + type: 'input', + form: { + show: false, + }, + column: { + minWidth: 160 + } + }, + file_type: { + title: '文件类型', + type: 'dict-select', + dict: dict({ + data: [ + { label: '图片', value: 0, color: 'success' }, + { label: '视频', value: 1, color: 'warning' }, + { label: '音频', value: 2, color: 'danger' }, + { label: '其他', value: 3, color: 'primary' }, + ] + }), + column: { + show: false + }, + search: { + show: true + }, + form: { + show: false, + component: { + placeholder: '请选择文件类型' + } + } + }, + size: { + title: '文件大小', + column: { + minWidth: 120 + }, + form: { + show: false + } + }, + upload_method: { + title: '上传方式', + type: 'dict-select', + dict: dict({ + data: [ + { label: '默认上传', value: 0, color: 'primary' }, + { label: '文件选择器上传', value: 1, color: 'warning' }, + ] + }), + column: { + minWidth: 140 + }, + search: { + show: true + } + }, + create_datetime: { + title: '创建时间', + column: { + minWidth: 160 + }, + form: { + show: false + } + }, + // fileselectortest: { + // title: '文件选择器测试', + // type: 'file-selector', + // width: 200, + // form: { + // component: { + // name: shallowRef(fileSelector), + // vModel: 'modelValue', + // tabsShow: 0b0100, + // itemSize: 100, + // multiple: false, + // selectable: true, + // showInput: true, + // inputType: 'video', + // valueKey: 'url', + // } + // } + // } }, }, }; diff --git a/web/src/views/system/fileList/index.vue b/web/src/views/system/fileList/index.vue index 119c73a..7141809 100644 --- a/web/src/views/system/fileList/index.vue +++ b/web/src/views/system/fileList/index.vue @@ -1,13 +1,85 @@ + \ No newline at end of file diff --git a/web/src/views/system/login/api.ts b/web/src/views/system/login/api.ts index 71b0844..7f83556 100644 --- a/web/src/views/system/login/api.ts +++ b/web/src/views/system/login/api.ts @@ -13,6 +13,15 @@ export function login(params: object) { data: params }); } + +export function loginChangePwd(data: object) { + return request({ + url: '/api/system/user/login_change_password/', + method: 'post', + data: data + }); +} + export function getUserInfo() { return request({ url: '/api/system/user/user_info/', diff --git a/web/src/views/system/login/component/account.vue b/web/src/views/system/login/component/account.vue index ee4e64f..64d870c 100644 --- a/web/src/views/system/login/component/account.vue +++ b/web/src/views/system/login/component/account.vue @@ -45,6 +45,12 @@ + +
+ + 申请试用 + +
+ + diff --git a/web/src/views/system/login/index.vue b/web/src/views/system/login/index.vue index dbaf061..0f4d9c8 100644 --- a/web/src/views/system/login/index.vue +++ b/web/src/views/system/login/index.vue @@ -5,51 +5,52 @@
{{ getSystemConfig['login.site_title'] || getThemeConfig.globalViceTitle }} - {{ +
-
- -
-
- - + +
-

Copyright © {{ getSystemConfig['login.copyright'] || '2021-2024 django-vue-admin.com' }} 版权所有

-

+

Copyright © {{ getSystemConfig['login.copyright'] || '2021-2024 北京信码新创科技有限公司' }} 版权所有

+

{{ getSystemConfig['login.keep_record'] || - '晋ICP备18005113号-3' }} + '京ICP备2021031018号' }} | - 帮助 |

-
- +
+