优化全局注入dict_data

This commit is contained in:
XIE7654
2025-07-16 13:56:02 +08:00
parent baa650ea64
commit 3fe272ffca
11 changed files with 219 additions and 24 deletions

View File

@@ -0,0 +1,24 @@
# Generated by Django 5.2.1 on 2025-07-16 03:18
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("ai", "0002_alter_chatrole_knowledge_alter_chatrole_tools"),
]
operations = [
migrations.AddField(
model_name="aimodel",
name="model_type",
field=models.CharField(
blank=True,
db_comment="模型类型",
max_length=32,
null=True,
verbose_name="模型类型",
),
),
]

View File

@@ -46,6 +46,7 @@ class AIModel(CoreModel):
on_delete=models.CASCADE,
db_comment='API 秘钥编号', verbose_name="API 秘钥编号"
)
model_type = models.CharField(max_length=32, db_comment="模型类型", verbose_name="模型类型", blank=True, null=True)
platform = models.CharField(max_length=32, db_comment="模型平台", verbose_name="模型平台")
model = models.CharField(max_length=64, db_comment="模型标识", verbose_name="模型标识")

View File

@@ -219,38 +219,124 @@ class Command(BaseCommand):
field_name = field.name
field_label = getattr(field, 'verbose_name', field_name)
if field_name == 'status':
return "{ component: 'RadioGroup', componentProps: { buttonStyle: 'solid', options: [{ label: $t('common.enabled'), value: 1 }, { label: $t('common.disabled'), value: 0 }], optionType: 'button' }, defaultValue: 1, fieldName: 'status', label: $t('system.status') },"
return f''' {{
component: 'RadioGroup',
componentProps: {{
buttonStyle: 'solid',
options: [
{{ label: $t('common.enabled'), value: 1 }},
{{ label: $t('common.disabled'), value: 0 }},
],
optionType: 'button',
}},
defaultValue: 1,
fieldName: 'status',
label: $t('system.status'),
}},'''
if isinstance(field, models.CharField):
return f"{{ component: 'Input', fieldName: '{field_name}', label: '{field_label}', rules: z.string().min(1, $t('ui.formRules.required', ['{field_label}'])).max(100, $t('ui.formRules.maxLength', ['{field_label}', 100])) }},"
return f''' {{
component: 'Input',
fieldName: '{field_name}',
label: '{field_label}',
rules: z.string().min(1, $t('ui.formRules.required', ['{field_label}'])).max(100, $t('ui.formRules.maxLength', ['{field_label}', 100])),
}},'''
elif isinstance(field, models.TextField):
return f"{{ component: 'Input', componentProps: {{ rows: 3, showCount: true }}, fieldName: '{field_name}', label: '{field_label}', rules: z.string().max(500, $t('ui.formRules.maxLength', ['{field_label}', 500])).optional() }},"
return f''' {{
component: 'Input',
componentProps: {{ rows: 3, showCount: true }},
fieldName: '{field_name}',
label: '{field_label}',
rules: z.string().max(500, $t('ui.formRules.maxLength', ['{field_label}', 500])).optional(),
}},'''
elif isinstance(field, models.IntegerField):
return f"{{ component: 'InputNumber', fieldName: '{field_name}', label: '{field_label}' }},"
return f''' {{
component: 'InputNumber',
fieldName: '{field_name}',
label: '{field_label}',
}},'''
elif isinstance(field, models.BooleanField):
return f"{{ component: 'RadioGroup', componentProps: {{ buttonStyle: 'solid', options: [{{ label: '开启', value: 1 }}, {{ label: '关闭', value: 0 }}], optionType: 'button' }}, defaultValue: 1, fieldName: '{field_name}', label: '{field_label}' }},"
return f''' {{
component: 'RadioGroup',
componentProps: {{
buttonStyle: 'solid',
options: [
{{ label: '开启', value: 1 }},
{{ label: '关闭', value: 0 }},
],
optionType: 'button',
}},
defaultValue: 1,
fieldName: '{field_name}',
label: '{field_label}',
}},'''
else:
return f"{{ component: 'Input', fieldName: '{field_name}', label: '{field_label}' }},"
return f''' {{
component: 'Input',
fieldName: '{field_name}',
label: '{field_label}',
}},'''
def generate_grid_form_field(self, field):
field_name = field.name
field_label = getattr(field, 'verbose_name', field_name)
if field_name == 'status':
return "{ component: 'Select', fieldName: 'status', label: '状态', componentProps: { allowClear: true, options: [{ label: '启用', value: 1 }, { label: '禁用', value: 0 }] } },"
return f''' {{
component: 'Select',
fieldName: 'status',
label: '状态',
componentProps: {{
allowClear: true,
options: [
{{ label: '启用', value: 1 }},
{{ label: '禁用', value: 0 }},
],
}},
}},'''
if isinstance(field, models.CharField):
return f"{{ component: 'Input', fieldName: '{field_name}', label: '{field_label}' }},"
return f''' {{
component: 'Input',
fieldName: '{field_name}',
label: '{field_label}',
}},'''
elif isinstance(field, models.IntegerField):
return f"{{ component: 'InputNumber', fieldName: '{field_name}', label: '{field_label}' }},"
return f''' {{
component: 'InputNumber',
fieldName: '{field_name}',
label: '{field_label}',
}},'''
else:
return f"{{ component: 'Input', fieldName: '{field_name}', label: '{field_label}' }},"
return f''' {{
component: 'Input',
fieldName: '{field_name}',
label: '{field_label}',
}},'''
def get_columns_code(self, fields):
columns = []
for field in fields:
if field.name == 'status':
columns.append("{ field: 'status', title: '状态', cellRender: { name: 'CellTag' } },")
columns.append(
""" {
field: 'status',
title: '状态',
cellRender: { name: 'CellTag' },
},"""
)
continue
if isinstance(field, (models.DateField, models.DateTimeField)):
columns.append(f"{{ field: '{field.name}', title: '{getattr(field, 'verbose_name', field.name)}', width: 150, formatter: ({{ cellValue }}) => format_datetime(cellValue) }},")
columns.append(
f""" {{
field: '{field.name}',
title: '{getattr(field, 'verbose_name', field.name)}',
width: 150,
formatter: ({{ cellValue }}) => format_datetime(cellValue),
}},"""
)
continue
columns.append(f"{{ field: '{field.name}', title: '{getattr(field, 'verbose_name', field.name)}' }},")
columns.append(
f""" {{
field: '{field.name}',
title: '{getattr(field, 'verbose_name', field.name)}',
}},"""
)
return columns

View File

@@ -1,6 +1,8 @@
from $app_name.models import $model_name
from utils.serializers import CustomModelSerializer
from utils.custom_model_viewSet import CustomModelViewSet
from django_filters import rest_framework as filters
class ${model_name}Serializer(CustomModelSerializer):
"""
@@ -12,13 +14,20 @@ class ${model_name}Serializer(CustomModelSerializer):
read_only_fields = ['id', 'create_time', 'update_time']
class $model_nameFilter(filters.FilterSet):
class Meta:
model = $model_name
fields = [$filterset_fields]
class ${model_name}ViewSet(CustomModelViewSet):
"""
$verbose_name 视图集
"""
queryset = $model_name.objects.filter(is_deleted=False).order_by('-id')
serializer_class = ${model_name}Serializer
filterset_fields = [$filterset_fields]
filterset_class = [$filterset_fields]
search_fields = ['name'] # 根据实际字段调整
ordering_fields = ['create_time', 'id']
ordering = ['-create_time']

View File

@@ -22,7 +22,7 @@ class DictDataFilter(filters.FilterSet):
class DictDataLabelValueSerializer(serializers.ModelSerializer):
dict_type_value = serializers.CharField(source='dict_type.value')
dict_type = serializers.CharField(source='dict_type.value')
class Meta:
model = DictData
@@ -39,4 +39,4 @@ class DictDataViewSet(CustomModelViewSet):
# 复用filterset_class过滤DictData
queryset = self.get_queryset().filter(status=CommonStatus.ENABLED)
serializer = DictDataLabelValueSerializer(queryset, many=True)
return Response(serializer.data)
return self._build_response(data=serializer.data)

View File

@@ -7,12 +7,17 @@ import { preferences, usePreferences } from '@vben/preferences';
import { App, ConfigProvider, theme } from 'ant-design-vue';
import { antdLocale } from '#/locales';
import { useDictStore } from '#/store/dict';
defineOptions({ name: 'App' });
const { isDark } = usePreferences();
const { tokens } = useAntdDesignTokens();
const dictStore = useDictStore();
dictStore.fetchDictData();
const tokenTheme = computed(() => {
const algorithm = isDark.value
? [theme.darkAlgorithm]

View File

@@ -0,0 +1,23 @@
import { useDictStore } from '#/store/dict';
export function useDictOptions(dictType: string) {
const dictStore = useDictStore();
return dictStore.getOptionsByType(dictType);
}
/**
* 通用字典 value 转 label
* @param dictType 字典类型
* @param value 字典值
* @returns label
*/
export function useDictLabel(dictType: string, value: any): string {
const options = useDictOptions(dictType);
const item = options.find((opt) => opt.value === value);
return item ? item.label : value;
}
export function dictFormatter(dictType: string) {
return ({ cellValue }: { cellValue: any }) =>
useDictLabel(dictType, cellValue);
}

View File

@@ -11,6 +11,7 @@ export namespace AiAIApiKeyApi {
is_deleted: boolean;
name: string;
platform: string;
model_type: string;
api_key: string;
url: string;
status: number;

View File

@@ -0,0 +1,18 @@
// src/store/dict.ts
import { defineStore } from 'pinia';
import { getDictDataSimple } from '#/api/system/dict_data'; // 根据实际路径调整
export const useDictStore = defineStore('dict', {
state: () => ({
dictData: [] as any[],
}),
actions: {
async fetchDictData() {
this.dictData = await getDictDataSimple(); // 根据接口返回结构调整
},
getOptionsByType(type: string) {
return this.dictData.filter((item) => item.dict_type === type);
},
},
});

View File

@@ -28,8 +28,8 @@ export function useSchema(): VbenFormSchema[] {
label: '平台',
componentProps: {
options: PLATFORM_OPTIONS,
style: { minWidth: '180px' },
dropdownStyle: { minWidth: '180px' },
class: 'w-full',
placeholder: '请选择',
},
rules: z.string(),
},

View File

@@ -5,6 +5,7 @@ import type { OnActionClickFn } from '#/adapter/vxe-table';
import type { AiAIModelApi } from '#/models/ai/ai_model';
import { z } from '#/adapter/form';
import { dictFormatter, useDictOptions } from '#/hooks/useDictOptions';
import { $t } from '#/locales';
import { AiAIApiKeyModel } from '#/models/ai/ai_api_key';
import { op } from '#/utils/permission';
@@ -27,15 +28,27 @@ export function useSchema(): VbenFormSchema[] {
},
fieldName: 'key',
label: 'API 秘钥',
rules: z.number(),
},
{
component: 'Input',
component: 'ApiSelect',
fieldName: 'platform',
componentProps: {
options: useDictOptions('ai_platform'),
class: 'w-full',
},
label: '模型平台',
rules: z
.string()
.min(1, $t('ui.formRules.required', ['模型平台']))
.max(100, $t('ui.formRules.maxLength', ['模型平台', 100])),
rules: z.string(),
},
{
component: 'ApiSelect',
fieldName: 'model_type',
componentProps: {
options: useDictOptions('ai_model_type'),
class: 'w-full',
},
label: '模型类型',
rules: z.string(),
},
{
component: 'Input',
@@ -59,6 +72,9 @@ export function useSchema(): VbenFormSchema[] {
component: 'InputNumber',
fieldName: 'sort',
label: '排序',
componentProps: {
class: 'w-full',
},
},
{
component: 'RadioGroup',
@@ -82,12 +98,18 @@ export function useSchema(): VbenFormSchema[] {
{
component: 'InputNumber',
fieldName: 'max_tokens',
label: '回复Token 数',
label: '回复Token数',
componentProps: {
class: 'w-full',
},
},
{
component: 'InputNumber',
fieldName: 'max_contexts',
label: '上下文数量',
componentProps: {
class: 'w-full',
},
},
{
component: 'Input',
@@ -162,6 +184,12 @@ export function useColumns(
{
field: 'platform',
title: '模型平台',
formatter: dictFormatter('ai_platform'),
},
{
field: 'model_type',
title: '模型类型',
formatter: dictFormatter('ai_model_type'),
},
{
field: 'model',