diff --git a/backend/dvadmin/system/migrations/0004_systemconfig_title_en_systemconfig_title_zh_tw.py b/backend/dvadmin/system/migrations/0004_systemconfig_title_en_systemconfig_title_zh_tw.py new file mode 100644 index 0000000..190bdc7 --- /dev/null +++ b/backend/dvadmin/system/migrations/0004_systemconfig_title_en_systemconfig_title_zh_tw.py @@ -0,0 +1,23 @@ +# Generated by Django 4.2.14 on 2026-04-09 10:48 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('system', '0003_fill_menu_i18n_name'), + ] + + operations = [ + migrations.AddField( + model_name='systemconfig', + name='title_en', + field=models.CharField(blank=True, help_text='标题(English)', max_length=50, null=True, verbose_name='标题(English)'), + ), + migrations.AddField( + model_name='systemconfig', + name='title_zh_tw', + field=models.CharField(blank=True, help_text='标题(繁體中文)', max_length=50, null=True, verbose_name='标题(繁體中文)'), + ), + ] diff --git a/backend/dvadmin/system/migrations/0005_fill_systemconfig_title_i18n.py b/backend/dvadmin/system/migrations/0005_fill_systemconfig_title_i18n.py new file mode 100644 index 0000000..e6bdbc5 --- /dev/null +++ b/backend/dvadmin/system/migrations/0005_fill_systemconfig_title_i18n.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +""" +填充系统配置标题多语言翻译 +用法: python manage.py migrate system 0005 +""" +from django.db import migrations + + +def fill_systemconfig_title_i18n(apps, schema_editor): + SystemConfig = apps.get_model('system', 'SystemConfig') + + updated_count = 0 + for config in SystemConfig.objects.all(): + update_fields = [] + if not config.title_en: + config.title_en = config.title + update_fields.append('title_en') + if not config.title_zh_tw: + config.title_zh_tw = config.title + update_fields.append('title_zh_tw') + if update_fields: + config.save(update_fields=update_fields) + updated_count += 1 + print(f"Updated {updated_count} system config translations") + + +def reverse_func(apps, schema_editor): + pass + + +class Migration(migrations.Migration): + + dependencies = [ + ('system', '0004_systemconfig_title_en_systemconfig_title_zh_tw'), + ] + + operations = [ + migrations.RunPython(fill_systemconfig_title_i18n, reverse_func), + ] diff --git a/backend/dvadmin/system/migrations/0005_fill_systemconfig_title_i18n.sql b/backend/dvadmin/system/migrations/0005_fill_systemconfig_title_i18n.sql new file mode 100644 index 0000000..a8edd47 --- /dev/null +++ b/backend/dvadmin/system/migrations/0005_fill_systemconfig_title_i18n.sql @@ -0,0 +1,8 @@ +/* +Fill SystemConfig title_i18n fields from existing title values +SQL reference (Django uses RunPython instead): + +UPDATE `dvadmin_system_config` +SET `title_en` = `title`, `title_zh_tw` = `title` +WHERE `title_en` IS NULL AND `title_zh_tw` IS NULL; +*/ diff --git a/backend/dvadmin/system/migrations/0006_fill_systemconfig_title_translations.py b/backend/dvadmin/system/migrations/0006_fill_systemconfig_title_translations.py new file mode 100644 index 0000000..1480274 --- /dev/null +++ b/backend/dvadmin/system/migrations/0006_fill_systemconfig_title_translations.py @@ -0,0 +1,67 @@ +# -*- coding: utf-8 -*- +""" +填充系统配置标题多语言翻译 +""" +from django.db import migrations + + +def fill_translations(apps, schema_editor): + SystemConfig = apps.get_model('system', 'SystemConfig') + + translations = { + '基础配置': {'en': 'Basic Config', 'zh_tw': '基礎配置'}, + '系统配置': {'en': 'System Config', 'zh_tw': '系統配置'}, + '登录页配置': {'en': 'Login Page Config', 'zh_tw': '登錄頁配置'}, + '文件存储配置': {'en': 'File Storage Config', 'zh_tw': '文件存儲配置'}, + '网站标题': {'en': 'Site Title', 'zh_tw': '網站標題'}, + '网站名称': {'en': 'Site Name', 'zh_tw': '網站名稱'}, + '登录网站logo': {'en': 'Login Logo', 'zh_tw': '登錄網站logo'}, + '登录页背景图': {'en': 'Login Background', 'zh_tw': '登錄頁背景圖'}, + '版权信息': {'en': 'Copyright', 'zh_tw': '版權信息'}, + '备案信息': {'en': 'Filing Info', 'zh_tw': '備案信息'}, + '帮助链接': {'en': 'Help Link', 'zh_tw': '幫助鏈接'}, + '隐私链接': {'en': 'Privacy Link', 'zh_tw': '隱私鏈接'}, + '条款链接': {'en': 'Terms Link', 'zh_tw': '條款鏈接'}, + '网页标题': {'en': 'Page Title', 'zh_tw': '網頁標題'}, + '网站小图标': {'en': 'Site Favicon', 'zh_tw': '網站小圖標'}, + '开启验证码': {'en': 'Enable Captcha', 'zh_tw': '開啟驗證碼'}, + '创建用户默认密码': {'en': 'Default Password', 'zh_tw': '創建用戶默認密碼'}, + '存储引擎': {'en': 'Storage Engine', 'zh_tw': '存儲引擎'}, + '文件是否备份': {'en': 'File Backup', 'zh_tw': '文件是否備份'}, + '阿里云-AccessKey': {'en': 'Aliyun AccessKey', 'zh_tw': '阿裡雲-AccessKey'}, + '阿里云-Secret': {'en': 'Aliyun Secret', 'zh_tw': '阿裡雲-Secret'}, + '阿里云-Endpoint': {'en': 'Aliyun Endpoint', 'zh_tw': '阿裡雲-Endpoint'}, + '阿里云-上传路径': {'en': 'Aliyun Upload Path', 'zh_tw': '阿裡雲-上傳路徑'}, + '阿里云-Bucket': {'en': 'Aliyun Bucket', 'zh_tw': '阿裡雲-Bucket'}, + '阿里云-cdn地址': {'en': 'Aliyun CDN URL', 'zh_tw': '阿裡雲-cdn地址'}, + '腾讯云-SecretId': {'en': 'Tencent SecretId', 'zh_tw': '騰訊雲-SecretId'}, + '腾讯云-SecretKey': {'en': 'Tencent SecretKey', 'zh_tw': '騰訊雲-SecretKey'}, + '腾讯云-Region': {'en': 'Tencent Region', 'zh_tw': '騰訊雲-Region'}, + '腾讯云-Bucket': {'en': 'Tencent Bucket', 'zh_tw': '騰訊雲-Bucket'}, + '腾讯云-上传路径': {'en': 'Tencent Upload Path', 'zh_tw': '騰訊雲-上傳路徑'}, + } + + updated = 0 + for config in SystemConfig.objects.all(): + if config.title in translations: + trans = translations[config.title] + config.title_en = trans['en'] + config.title_zh_tw = trans['zh_tw'] + config.save(update_fields=['title_en', 'title_zh_tw']) + updated += 1 + print(f"Updated {updated} config translations") + + +def reverse_func(apps, schema_editor): + pass + + +class Migration(migrations.Migration): + + dependencies = [ + ('system', '0005_fill_systemconfig_title_i18n'), + ] + + operations = [ + migrations.RunPython(fill_translations, reverse_func), + ] diff --git a/backend/dvadmin/system/models.py b/backend/dvadmin/system/models.py index da9a0a9..6aaec3d 100644 --- a/backend/dvadmin/system/models.py +++ b/backend/dvadmin/system/models.py @@ -567,6 +567,8 @@ class SystemConfig(CoreModel): help_text="父级", ) title = models.CharField(max_length=50, verbose_name="标题", help_text="标题") + title_en = models.CharField(max_length=50, verbose_name="标题(English)", blank=True, null=True, help_text="标题(English)") + title_zh_tw = models.CharField(max_length=50, verbose_name="标题(繁體中文)", blank=True, null=True, help_text="标题(繁體中文)") key = models.CharField(max_length=100, verbose_name="键", help_text="键", db_index=True) value = models.JSONField(max_length=100, verbose_name="值", help_text="值", null=True, blank=True) sort = models.IntegerField(default=0, verbose_name="排序", help_text="排序", blank=True) diff --git a/backend/dvadmin/system/views/system_config.py b/backend/dvadmin/system/views/system_config.py index 8168b50..65194a5 100644 --- a/backend/dvadmin/system/views/system_config.py +++ b/backend/dvadmin/system/views/system_config.py @@ -50,6 +50,32 @@ class SystemConfigSerializer(CustomModelSerializer): 系统配置-序列化器 """ form_item_type_label = serializers.CharField(source='get_form_item_type_display', read_only=True) + title_i18n = serializers.SerializerMethodField() + + def get_title_i18n(self, obj): + lang = None + req = getattr(self, 'request', None) + if req: + user = getattr(req, 'user', None) + if user and getattr(user, 'is_authenticated', False): + lang = getattr(user, 'language', None) + _qp_lang = req.query_params.get('language', None) + if _qp_lang: + lang = _qp_lang[0] if isinstance(_qp_lang, list) else _qp_lang + if not lang: + meta_lang = req.META.get('HTTP_ACCEPT_LANGUAGE', '') + # Parse Accept-Language header (e.g., "zh-TW,zh;q=0.9,en;q=0.8") + if 'en' in meta_lang: + lang = 'en' + elif 'zh-tw' in meta_lang or 'zh-hant' in meta_lang: + lang = 'zh-tw' + elif meta_lang.startswith('zh'): + lang = 'zh-cn' + if lang == 'en': + return obj.title_en or obj.title + elif lang == 'zh-tw': + return obj.title_zh_tw or obj.title + return obj.title class Meta: model = SystemConfig @@ -63,12 +89,38 @@ class SystemConfigChinldernSerializer(CustomModelSerializer): """ children = serializers.SerializerMethodField() form_item_type_label = serializers.CharField(source='get_form_item_type_display', read_only=True) + title_i18n = serializers.SerializerMethodField() def get_children(self, instance): queryset = SystemConfig.objects.filter(parent=instance) - serializer = SystemConfigSerializer(queryset, many=True) + serializer = SystemConfigSerializer(queryset, many=True, request=self.request) return serializer.data + def get_title_i18n(self, obj): + lang = None + req = getattr(self, 'request', None) + if req: + user = getattr(req, 'user', None) + if user and getattr(user, 'is_authenticated', False): + lang = getattr(user, 'language', None) + _qp_lang = req.query_params.get('language', None) + if _qp_lang: + lang = _qp_lang[0] if isinstance(_qp_lang, list) else _qp_lang + if not lang: + meta_lang = req.META.get('HTTP_ACCEPT_LANGUAGE', '') + # Parse Accept-Language header (e.g., "zh-TW,zh;q=0.9,en;q=0.8") + if 'en' in meta_lang: + lang = 'en' + elif 'zh-tw' in meta_lang or 'zh-hant' in meta_lang: + lang = 'zh-tw' + elif meta_lang.startswith('zh'): + lang = 'zh-cn' + if lang == 'en': + return obj.title_en or obj.title + elif lang == 'zh-tw': + return obj.title_zh_tw or obj.title + return obj.title + class Meta: model = SystemConfig fields = "__all__" diff --git a/web/package.json b/web/package.json index b34579a..c7e98b4 100644 --- a/web/package.json +++ b/web/package.json @@ -18,7 +18,6 @@ "@fast-crud/fast-extends": "^1.21.2", "@fast-crud/ui-element": "^1.21.2", "@fast-crud/ui-interface": "^1.21.2", - "@great-dream/dvadmin3-celery-web": "^3.1.3", "@iconify/vue": "^4.1.2", "@meetjs/vant4-kit": "^1.0.1", "@types/lodash": "^4.17.7", diff --git a/web/src/components/fileSelector/index.vue b/web/src/components/fileSelector/index.vue index 86192c9..8a4eb74 100644 --- a/web/src/components/fileSelector/index.vue +++ b/web/src/components/fileSelector/index.vue @@ -3,7 +3,7 @@
@@ -93,34 +93,34 @@
- - - - + + + + - - - - + + + +
-
- 一共选中 {{ data?.length || 0 }} 个文件 + {{ t('message.pages.fileSelector.selected', { count: data?.length || 0 }) }}
@@ -133,10 +133,10 @@ :data="{ upload_method: 1 }" :show-file-list="true" :accept="AcceptList[tabsActived % 4]" :on-success="() => { listRequest(); listRequestAll(); uploadRef.clearFiles(); }" v-if="props.showUploadButton"> - 上传{{ TypeLabel[tabsActived % 4] }} + {{ t('message.pages.fileSelector.upload.button', { type: TypeLabel[tabsActived % 4] }) }} - 网络{{ TypeLabel[tabsActived % 4] }} + {{ t('message.pages.fileSelector.upload.netButton', { type: TypeLabel[tabsActived % 4] }) }} @@ -144,7 +144,7 @@
- +
@@ -164,10 +164,10 @@
- - + +