SpringSunYY 5 месяцев назад
Родитель
Сommit
c8f71d3b7b

+ 40 - 0
ruoyi_common/utils/base.py

@@ -21,6 +21,7 @@ from jwt import api_jwt
 from logging import Logger
 
 from ruoyi_common.base.snippet import classproperty
+from pydantic.alias_generators import to_camel
 
 from ..constant import Constants
 
@@ -257,6 +258,45 @@ class DictUtil:
         return new_dict
 
     @classmethod
+    def camelize_keys(cls, dict_obj: dict, deep: bool = True) -> dict:
+        """
+        将字典的所有 key 从下划线命名转换为驼峰命名(front-end 专用)
+
+        Args:
+            dict_obj (dict): 原始字典(通常是实体 / 查询结果的 to_dict)
+            deep (bool): 是否递归处理嵌套字典和列表
+
+        Returns:
+            dict: key 已经转换为驼峰后的新字典
+        """
+        if not isinstance(dict_obj, dict):
+            return dict_obj
+
+        new_dict = {}
+        for k, v in dict_obj.items():
+            # 先转换当前 key
+            new_key = to_camel(k) if isinstance(k, str) else k
+
+            # 再递归处理 value
+            if deep:
+                if isinstance(v, dict):
+                    new_dict[new_key] = cls.camelize_keys(v, deep=True)
+                elif isinstance(v, list):
+                    new_list = []
+                    for item in v:
+                        if isinstance(item, dict):
+                            new_list.append(cls.camelize_keys(item, deep=True))
+                        else:
+                            new_list.append(item)
+                    new_dict[new_key] = new_list
+                else:
+                    new_dict[new_key] = v
+            else:
+                new_dict[new_key] = v
+
+        return new_dict
+
+    @classmethod
     def format_value(cls,dict_obj) -> dict:
         '''
         格式化字典的值

+ 40 - 10
ruoyi_generator/util.py

@@ -30,6 +30,33 @@ def to_underscore(name: str) -> str:
     return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()
 
 
+def to_camel_case(name: str, pascal: bool = True) -> str:
+    """
+    将下划线命名或普通字符串转换为驼峰命名。
+
+    - name = "schedule_info"  -> pascal=True  => "ScheduleInfo"
+    - name = "schedule_info"  -> pascal=False => "scheduleInfo"
+    - name = "ScheduleInfo"   -> pascal=True/False => "ScheduleInfo"/"scheduleInfo"
+    """
+    if name is None:
+        return ""
+    if not isinstance(name, str):
+        name = str(name)
+    if not name:
+        return ""
+
+    # 如果本身已经是驼峰(包含大写且不含下划线),直接按需调整首字母
+    if "_" not in name and any(ch.isupper() for ch in name):
+        base = name[0].upper() + name[1:]
+    else:
+        parts = [p for p in name.split("_") if p]
+        if not parts:
+            return ""
+        base = "".join(p.lower().capitalize() for p in parts)
+
+    return base if pascal else (base[0].lower() + base[1:] if base else base)
+
+
 def capitalize_first(name: str) -> str:
     """
     将字符串首字母大写
@@ -447,10 +474,11 @@ class GenUtils:
                         context = {
                             'table': table,
                             'datetime': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
-                            'underscore': to_underscore,  # 添加自定义过滤器
-                            'capitalize_first': capitalize_first,  # 添加首字母大写函数
-                            'get_import_path': GenUtils.get_import_path,  # 添加导入路径生成函数
-                            'get_tree_column_index': get_tree_column_index,  # 添加树表列索引计算函数
+                            'underscore': to_underscore,  # 下划线命名工具
+                            'capitalize_first': capitalize_first,  # 首字母大写工具
+                            'to_camel_case': to_camel_case,  # 新增:驼峰命名工具
+                            'get_import_path': GenUtils.get_import_path,  # 导入路径生成函数
+                            'get_tree_column_index': get_tree_column_index,  # 树表列索引计算函数
                             'list_cols': list_cols,  # 树表的列表列
                             'query_cols': query_cols,  # 树表的查询列
                             'required_cols': required_cols  # 树表的必填列
@@ -742,9 +770,10 @@ class GenUtils:
                             context = {
                                 'table': table,
                                 'datetime': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
-                                'underscore': to_underscore,  # 添加自定义过滤器
-                                'capitalize_first': capitalize_first,  # 添加首字母大写函数
-                                'get_import_path': GenUtils.get_import_path,  # 添加导入路径生成函数
+                                'underscore': to_underscore,  # 下划线命名工具
+                                'capitalize_first': capitalize_first,  # 首字母大写工具
+                                'to_camel_case': to_camel_case,  # 新增:驼峰命名工具
+                                'get_import_path': GenUtils.get_import_path,  # 导入路径生成函数
                                 'list_cols': list_cols,  # 树表的列表列
                                 'query_cols': query_cols,  # 树表的查询列
                                 'required_cols': required_cols  # 树表的必填列
@@ -1015,9 +1044,10 @@ class GenUtils:
                     context = {
                         'table': table,
                         'datetime': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
-                        'underscore': to_underscore,  # 添加自定义过滤器
-                                'capitalize_first': capitalize_first,  # 添加首字母大写函数
-                        'get_import_path': GenUtils.get_import_path,  # 添加导入路径生成函数
+                        'underscore': to_underscore,  # 下划线命名工具
+                        'capitalize_first': capitalize_first,  # 首字母大写工具
+                        'to_camel_case': to_camel_case,  # 新增:驼峰命名工具
+                        'get_import_path': GenUtils.get_import_path,  # 导入路径生成函数
                         'list_cols': list_cols,  # 树表的列表列
                         'query_cols': query_cols,  # 树表的查询列
                         'required_cols': required_cols  # 树表的必填列

+ 16 - 9
ruoyi_generator/vm/js/api.js.vm

@@ -1,7 +1,13 @@
 import request from '@/utils/request'
 
+{# 使用后端工具函数 to_camel_case 统一处理类名:
+   - apiName  : scheduleInfo(小驼峰)
+   - ApiName  : ScheduleInfo(大驼峰,用于 listScheduleInfo 这种函数名) #}
+{% set apiName = to_camel_case(table.class_name, False) %}
+{% set ApiName = capitalize_first(apiName) %}
+
 // 查询{{ table.function_name }}列表
-export function list{{ table.class_name }}(query) {
+export function list{{ ApiName }}(query) {
   return request({
     url: '/{{ table.module_name }}/{{ table.business_name }}/list',
     method: 'get',
@@ -10,15 +16,15 @@ export function list{{ table.class_name }}(query) {
 }
 
 // 查询{{ table.function_name }}详细
-export function get{{- table.class_name }}({{ underscore(table.pk_column.java_field) if table.pk_column }}) {
+export function get{{ ApiName }}({{ table.pk_column.java_field if table.pk_column }}) {
   return request({
-    url: '/{{ table.module_name }}/{{ table.business_name }}/' + {{- underscore(table.pk_column.java_field) if table.pk_column }},
+    url: '/{{ table.module_name }}/{{ table.business_name }}/' + {{- table.pk_column.java_field if table.pk_column }},
     method: 'get'
   })
 }
 
 // 新增{{ table.function_name }}
-export function add{{ table.class_name }}(data) {
+export function add{{ ApiName }}(data) {
   return request({
     url: '/{{ table.module_name }}/{{ table.business_name }}',
     method: 'post',
@@ -27,24 +33,25 @@ export function add{{ table.class_name }}(data) {
 }
 
 // 修改{{ table.function_name }}
-export function update{{ table.class_name }}(data) {
+export function update{{ ApiName }}(data) {
   return request({
-    url: '/{{ table.module_name }}/{{ table.business_name }}/' + data.{{ underscore(table.pk_column.java_field) if table.pk_column }},
+    // 后端 Flask 控制器使用的是不带主键的 PUT '' 路径,这里保持一致
+    url: '/{{ table.module_name }}/{{ table.business_name }}',
     method: 'put',
     data: data
   })
 }
 
 // 删除{{ table.function_name }}
-export function del{{- table.class_name }}({{ underscore(table.pk_column.java_field) if table.pk_column }}) {
+export function del{{ ApiName }}({{ table.pk_column.java_field if table.pk_column }}) {
   return request({
-    url: '/{{ table.module_name }}/{{ table.business_name }}/' + {{- underscore(table.pk_column.java_field) if table.pk_column }},
+    url: '/{{ table.module_name }}/{{ table.business_name }}/' + {{- table.pk_column.java_field if table.pk_column }},
     method: 'delete'
   })
 }
 
 // 导出{{ table.function_name }}
-export function export{{ table.class_name }}(query) {
+export function export{{ ApiName }}(query) {
   return request({
     url: '/{{ table.module_name }}/{{ table.business_name }}/export',
     method: 'post',

+ 38 - 33
ruoyi_generator/vm/vue/index-tree.vue.vm

@@ -14,17 +14,17 @@
 {%- endif %}
 {%- endif %}
   {%- if column.html_type == 'input' %}
-      <el-form-item label="{{ comment }}" prop="{{ underscore(column.java_field) }}">
+      <el-form-item label="{{ comment }}" prop="{{ column.java_field }}">
         <el-input
-          v-model="queryParams.{{ underscore(column.java_field) }}"
+          v-model="queryParams.{{ column.java_field }}"
           placeholder="请输入{{ comment }}"
           clearable
           @keyup.enter.native="handleQuery"
         />
       </el-form-item>
   {%- elif (column.html_type == 'select' or column.html_type == 'radio') and dict_type != '' %}
-      <el-form-item label="{{ comment }}" prop="{{ underscore(column.java_field) }}">
-        <el-select v-model="queryParams.{{ underscore(column.java_field) }}" placeholder="请选择{{ comment }}" clearable>
+      <el-form-item label="{{ comment }}" prop="{{ column.java_field }}">
+        <el-select v-model="queryParams.{{ column.java_field }}" placeholder="请选择{{ comment }}" clearable>
           <el-option
             v-for="dict in dict.type.{{ dict_type }}"
             :key="dict.value"
@@ -34,15 +34,15 @@
         </el-select>
       </el-form-item>
   {%- elif (column.html_type == 'select' or column.html_type == 'radio') and dict_type == '' %}
-      <el-form-item label="{{ comment }}" prop="{{ underscore(column.java_field) }}">
-        <el-select v-model="queryParams.{{ underscore(column.java_field) }}" placeholder="请选择{{ comment }}" clearable>
+      <el-form-item label="{{ comment }}" prop="{{ column.java_field }}">
+        <el-select v-model="queryParams.{{ column.java_field }}" placeholder="请选择{{ comment }}" clearable>
           <el-option label="请选择字典生成" value="" />
         </el-select>
       </el-form-item>
   {%- elif column.html_type == 'datetime' and column.query_type != 'BETWEEN' %}
-      <el-form-item label="{{ comment }}" prop="{{ underscore(column.java_field) }}">
+      <el-form-item label="{{ comment }}" prop="{{ column.java_field }}">
         <el-date-picker clearable
-          v-model="queryParams.{{ underscore(column.java_field) }}"
+          v-model="queryParams.{{ column.java_field }}"
           type="date"
           value-format="yyyy-MM-dd"
           placeholder="选择{{ comment }}">
@@ -112,24 +112,24 @@
 {%- endif %}
   {%- if column.is_pk == '1' %}
   {%- elif column.is_list and column.list_index is not none and column.html_type == 'datetime' %}
-      <el-table-column label="{{ comment }}" align="center" v-if="columns[{{ column.list_index }}].visible" prop="{{ underscore(java_field) }}" width="180">
+      <el-table-column label="{{ comment }}" align="center" v-if="columns[{{ column.list_index }}].visible" prop="{{ java_field }}" width="180">
         <template slot-scope="scope">
-          <span>{{ "{{ parseTime(scope.row." }}{{ underscore(java_field) }}{{ ", '{y}-{m}-{d}') }}" }}</span>
+          <span>{{ "{{ parseTime(scope.row." }}{{ java_field }}{{ ", '{y}-{m}-{d}') }}" }}</span>
         </template>
       </el-table-column>
   {%- elif column.is_list and column.list_index is not none and column.html_type == 'imageUpload' %}
-      <el-table-column label="{{ comment }}" align="center" v-if="columns[{{ column.list_index }}].visible" prop="{{ underscore(java_field) }}" width="100">
+      <el-table-column label="{{ comment }}" align="center" v-if="columns[{{ column.list_index }}].visible" prop="{{ java_field }}" width="100">
         <template slot-scope="scope">
-          <image-preview :src="scope.row.{{ underscore(java_field) }}" :width="50" :height="50"/>
+          <image-preview :src="scope.row.{{ java_field }}" :width="50" :height="50"/>
         </template>
       </el-table-column>
   {%- elif column.is_list and column.list_index is not none and column.html_type == 'fileUpload' %}
-      <el-table-column label="{{ comment }}" align="center" v-if="columns[{{ column.list_index }}].visible" prop="{{ underscore(java_field) }}" width="100">
+      <el-table-column label="{{ comment }}" align="center" v-if="columns[{{ column.list_index }}].visible" prop="{{ java_field }}" width="100">
         <template slot-scope="scope">
           <div v-if="scope.row.{{ underscore(java_field) }}">
             <el-tooltip placement="top" effect="light">
               <div slot="content">
-                  <div v-for="(file,index) in scope.row.{{ underscore(java_field) }}.split(',')"
+                  <div v-for="(file,index) in scope.row.{{ java_field }}.split(',')"
                        :key="index"
                        style="text-align: left;padding: 5px;">
                     <el-link
@@ -152,20 +152,20 @@
         </template>
       </el-table-column>
   {%- elif column.is_list and column.list_index is not none and column.dict_type and column.dict_type != '' %}
-      <el-table-column label="{{ comment }}" align="center" v-if="columns[{{ column.list_index }}].visible" prop="{{ underscore(java_field) }}">
+      <el-table-column label="{{ comment }}" align="center" v-if="columns[{{ column.list_index }}].visible" prop="{{ java_field }}">
         <template slot-scope="scope">
   {%- if column.html_type == 'checkbox' %}
-          <dict-tag :options="dict.type.{{ column.dict_type }}" :value="scope.row.{{ underscore(java_field) }} ? scope.row.{{ underscore(java_field) }}.split(',') : []"/>
+          <dict-tag :options="dict.type.{{ column.dict_type }}" :value="scope.row.{{ java_field }} ? scope.row.{{ java_field }}.split(',') : []"/>
   {%- else %}
-          <dict-tag :options="dict.type.{{ column.dict_type }}" :value="scope.row.{{ underscore(java_field) }}"/>
+          <dict-tag :options="dict.type.{{ column.dict_type }}" :value="scope.row.{{ java_field }}"/>
   {%- endif %}
         </template>
       </el-table-column>
   {%- elif column.is_list and column.list_index is not none and java_field %}
   {%- if column.list_index == 0 %}
-      <el-table-column label="{{ comment }}" :show-overflow-tooltip="true" v-if="columns[{{ column.list_index }}].visible" prop="{{ underscore(java_field) }}" />
+      <el-table-column label="{{ comment }}" :show-overflow-tooltip="true" v-if="columns[{{ column.list_index }}].visible" prop="{{ java_field }}" />
   {%- else %}
-      <el-table-column label="{{ comment }}" align="center" :show-overflow-tooltip="true" v-if="columns[{{ column.list_index }}].visible" prop="{{ underscore(java_field) }}" />
+      <el-table-column label="{{ comment }}" align="center" :show-overflow-tooltip="true" v-if="columns[{{ column.list_index }}].visible" prop="{{ java_field }}" />
   {%- endif %}
   {%- endif %}
 {%- endfor %}
@@ -315,7 +315,12 @@
 </template>
 
 <script>
-import { list{{ table.class_name }}, get{{ table.class_name }}, del{{ table.class_name }}, add{{ table.class_name }}, update{{ table.class_name }} } from "@/api/{{ table.module_name }}/{{ table.business_name }}";
+{# 使用后端工具函数生成接口前缀:
+   - apiName  : scheduleInfo
+   - ApiName  : ScheduleInfo(listScheduleInfo / getScheduleInfo 等) #}
+{% set apiName = to_camel_case(table.class_name, False) %}
+{% set ApiName = capitalize_first(apiName) %}
+import { list{{ ApiName }}, get{{ ApiName }}, del{{ ApiName }}, add{{ ApiName }}, update{{ ApiName }} } from "@/api/{{ table.module_name }}/{{ table.business_name }}";
 import Treeselect from "@riophae/vue-treeselect";
 import "@riophae/vue-treeselect/dist/vue-treeselect.css";
 {%- set has_file_upload = false %}
@@ -402,7 +407,7 @@ export default {
 {%- endif %}
 {%- endfor %}
 {%- for column in query_cols %}
-        {{ underscore(column.java_field) }}: null{% if not loop.last %},{% endif %}
+        {{ column.java_field }}: null{% if not loop.last %},{% endif %}
 {%- endfor %}
       },
       // 表单参数
@@ -424,7 +429,7 @@ export default {
 {%- set comment = comment.split('(')[0] %}
 {%- endif %}
 {%- endif %}
-        {{ underscore(column.java_field) }}: [
+        {{ column.java_field }}: [
           { required: true, message: "{{ comment }}不能为空", trigger: {% if column.html_type == 'select' or column.html_type == 'radio' %}"change"{% else %}"blur"{% endif %} }
         ]{% if not loop.last %},{% endif %}
 {%- endfor %}
@@ -452,7 +457,7 @@ export default {
       }
 {%- endif %}
 {%- endfor %}
-      list{{ table.class_name }}(this.queryParams).then(response => {
+      list{{ ApiName }}(this.queryParams).then(response => {
         const list = Array.isArray(response && response.data) ? response.data : (Array.isArray(response && response.rows) ? response.rows : []);
         this.{{ table.business_name }}List = this.handleTree(list, "{% if table.tree_code %}{{ table.tree_code }}{% else %}id{% endif %}", "{% if table.tree_parent_code %}{{ table.tree_parent_code }}{% else %}parent_id{% endif %}");
         this.loading = false;
@@ -475,7 +480,7 @@ export default {
     },
     /** 查询{{ table.function_name }}下拉树结构 */
     getTreeselect() {
-      list{{ table.class_name }}().then(response => {
+      list{{ ApiName }}().then(response => {
         this.{{ table.business_name }}Options = [];
         const data = { {% if table.tree_code %}{{ table.tree_code }}{% else %}id{% endif %}: 0, {% if table.tree_name %}{{ table.tree_name }}{% else %}name{% endif %}: '顶级节点', children: [] };
         const list = Array.isArray(response && response.data) ? response.data : (Array.isArray(response && response.rows) ? response.rows : []);
@@ -493,9 +498,9 @@ export default {
       this.form = {
 {%- for column in table.columns %}
   {%- if column.html_type == 'checkbox' %}
-        {{ underscore(column.java_field) }}: []
+        {{ column.java_field }}: []
   {%- else %}
-        {{ underscore(column.java_field) }}: null
+        {{ column.java_field }}: null
   {%- endif %}{% if not loop.last %},{% endif %}
 {%- endfor %}
       };
@@ -521,9 +526,9 @@ export default {
       this.reset();
       this.getTreeselect();
       if (row != null && row.{% if table.tree_code %}{{ table.tree_code }}{% else %}id{% endif %}) {
-        this.form.{% if table.tree_parent_code %}{{ underscore(table.tree_parent_code) }}{% else %}parent_id{% endif %} = row.{% if table.tree_code %}{{ table.tree_code }}{% else %}id{% endif %};
+        this.form.{% if table.tree_parent_code %}{{ table.tree_parent_code }}{% else %}parentId{% endif %} = row.{% if table.tree_code %}{{ table.tree_code }}{% else %}id{% endif %};
       } else {
-        this.form.{% if table.tree_parent_code %}{{ underscore(table.tree_parent_code) }}{% else %}parent_id{% endif %} = 0;
+        this.form.{% if table.tree_parent_code %}{{ table.tree_parent_code }}{% else %}parentId{% endif %} = 0;
       }
       this.open = true;
       this.title = "添加{{ table.function_name }}";
@@ -541,9 +546,9 @@ export default {
       this.reset();
       this.getTreeselect();
       if (row != null) {
-        this.form.{% if table.tree_parent_code %}{{ underscore(table.tree_parent_code) }}{% else %}parent_id{% endif %} = row.{% if table.tree_parent_code %}{{ underscore(table.tree_parent_code) }}{% else %}parent_id{% endif %};
+        this.form.{% if table.tree_parent_code %}{{ table.tree_parent_code }}{% else %}parentId{% endif %} = row.{% if table.tree_parent_code %}{{ table.tree_parent_code }}{% else %}parentId{% endif %};
       }
-      get{{ table.class_name }}(row.{% if table.pk_column %}{{ underscore(table.pk_column.java_field) }}{% else %}id{% endif %}).then(response => {
+      get{{ ApiName }}(row.{% if table.pk_column %}{{ underscore(table.pk_column.java_field) }}{% else %}id{% endif %}).then(response => {
         this.form = response.data;
 {%- for column in table.columns %}
 {%- if column.html_type == 'checkbox' %}
@@ -564,13 +569,13 @@ export default {
 {%- endif %}
 {%- endfor %}
           if (this.form.{% if table.pk_column %}{{ underscore(table.pk_column.java_field) }}{% else %}id{% endif %} != null) {
-            update{{ table.class_name }}(this.form).then(response => {
+            update{{ ApiName }}(this.form).then(response => {
               this.$modal.msgSuccess("修改成功");
               this.open = false;
               this.getList();
             });
           } else {
-            add{{ table.class_name }}(this.form).then(response => {
+            add{{ ApiName }}(this.form).then(response => {
               this.$modal.msgSuccess("新增成功");
               this.open = false;
               this.getList();
@@ -582,7 +587,7 @@ export default {
     /** 删除按钮操作 */
     handleDelete(row) {
       this.$modal.confirm('是否确认删除{{ table.function_name }}编号为"' + row.{% if table.pk_column %}{{ underscore(table.pk_column.java_field) }}{% else %}id{% endif %} + '"的数据项?').then(function() {
-        return del{{ table.class_name }}(row.{% if table.pk_column %}{{ underscore(table.pk_column.java_field) }}{% else %}id{% endif %});
+        return del{{ ApiName }}(row.{% if table.pk_column %}{{ underscore(table.pk_column.java_field) }}{% else %}id{% endif %});
       }).then(() => {
         this.getList();
         this.$modal.msgSuccess("删除成功");

+ 109 - 80
ruoyi_generator/vm/vue/index.vue.vm

@@ -1,34 +1,35 @@
-{% macro getColumnDictType(column) %}
-  {% if column.dict_type != '' %}{{ column.dict_type }}
-  {% else %}null
-  {% endif %}
-{% endmacro %}
-
 <template>
   <div class="app-container">
     <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
 {%- for column in table.columns %}
 {%- if column.is_query %}
-      <el-form-item label="{{ column.column_comment }}" prop="{{ underscore(column.java_field) }}">
-  {%- if column.html_type == 'input' %}
+      <el-form-item label="{{ column.column_comment }}" prop="{{ column.java_field }}">
+{%- if column.html_type == 'input' %}
         <el-input
-          v-model="queryParams.{{ underscore(column.java_field) }}"
+          v-model="queryParams.{{ column.java_field }}"
           placeholder="请输入{{ column.column_comment }}"
           clearable
           @keyup.enter.native="handleQuery"
         />
-  {%- elif column.html_type == 'select' and column.dict_type != '' %}
-        <el-select v-model="queryParams.{{ underscore(column.java_field) }}" placeholder="请选择{{ column.column_comment }}" clearable>
+{%- elif column.html_type == 'select' %}
+  {%- if column.dict_type != '' %}
+        <el-select v-model="queryParams.{{ column.java_field }}" placeholder="请选择{{ column.column_comment }}" clearable>
           <el-option
-            v-for="dict in {{ getColumnDictType(column) }}Options"
+            v-for="dict in dict.type.{{ column.dict_type }}"
             :key="dict.value"
             :label="dict.label"
             :value="dict.value"
           />
         </el-select>
+  {%- else %}
+        <el-select v-model="queryParams.{{ column.java_field }}" placeholder="请选择{{ column.column_comment }}" clearable>
+          <el-option label="请选择字典生成" value="" />
+        </el-select>
+  {%- endif %}
   {%- elif column.html_type == 'datetime' %}
+  {%- set attr_name = capitalize_first(column.java_field) %}
         <el-date-picker
-          v-model="daterange{{ column.java_field }}"
+          v-model="dateRange{{ attr_name }}"
           value-format="yyyy-MM-dd"
           type="daterange"
           range-separator="-"
@@ -106,7 +107,7 @@
 {%- for column in table.columns %}
 {%- if column.is_list %}
   {%- if column.is_pk %}
-      <el-table-column label="{{ column.column_comment }}" align="center" v-if="columns[{{ column.list_index }}].visible" prop="{{ underscore(column.java_field) }}" >
+      <el-table-column label="{{ column.column_comment }}" align="center" v-if="columns[{{ column.list_index }}].visible" prop="{{ column.java_field }}" >
         <template slot-scope="scope">
           <el-button
             size="mini"
@@ -118,21 +119,25 @@
         </template>
       </el-table-column>
   {%- elif column.html_type == 'imageUpload' %}
-      <el-table-column label="{{ column.column_comment }}" align="center" v-if="columns[{{ column.list_index }}].visible" prop="{{ underscore(column.java_field) }}" width="100">
+      <el-table-column label="{{ column.column_comment }}" align="center" v-if="columns[{{ column.list_index }}].visible" prop="{{ column.java_field }}" width="100">
         <template slot-scope="scope">
           <image-preview :src="scope.row.{{ underscore(column.java_field) }}" :width="50" :height="50"/>
         </template>
       </el-table-column>
   {%- elif column.dict_type != '' %}
-      <el-table-column label="{{ column.column_comment }}" align="center" v-if="columns[{{ column.list_index }}].visible" prop="{{ underscore(column.java_field) }}" :formatter="dict_{{ underscore(column.java_field) }}_format" />
+      <el-table-column label="{{ column.column_comment }}" align="center" v-if="columns[{{ column.list_index }}].visible" prop="{{ column.java_field }}">
+        <template slot-scope="scope">
+          <dict-tag :options="dict.type.{{ column.dict_type }}" :value="scope.row.{{ column.java_field }}" />
+        </template>
+      </el-table-column>
   {%- elif column.html_type == 'datetime' %}
-      <el-table-column label="{{ column.column_comment }}" align="center" v-if="columns[{{ column.list_index }}].visible" prop="{{ underscore(column.java_field) }}" width="180">
+      <el-table-column label="{{ column.column_comment }}" align="center" v-if="columns[{{ column.list_index }}].visible" prop="{{ column.java_field }}" width="180">
         <template slot-scope="scope">
-          <span>{{ "{{ scope.row." }}{{ underscore(column.java_field) }}{{ " }}" }}</span>
+          <span>{{ "{{ scope.row." }}{{ column.java_field }}{{ " }}" }}</span>
         </template>
       </el-table-column>
   {%- else %}
-      <el-table-column label="{{ column.column_comment }}" align="center" v-if="columns[{{ column.list_index }}].visible" prop="{{ underscore(column.java_field) }}" />
+      <el-table-column label="{{ column.column_comment }}" align="center" v-if="columns[{{ column.list_index }}].visible" prop="{{ column.java_field }}" />
   {%- endif %}
 {%- endif %}
 {%- endfor %}
@@ -169,47 +174,65 @@
       <el-form ref="form" :model="form" :rules="rules" label-width="80px">
 {%- for column in table.columns %}
 {%- if column.is_insert %}
-        <el-form-item label="{{ column.column_comment }}" prop="{{ underscore(column.java_field) }}">
+        <el-form-item label="{{ column.column_comment }}" prop="{{ column.java_field }}">
   {%- if column.html_type == 'input' %}
-          <el-input v-model="form.{{ underscore(column.java_field) }}" placeholder="请输入{{ column.column_comment }}" />
+          <el-input v-model="form.{{ column.java_field }}" placeholder="请输入{{ column.column_comment }}" />
   {%- elif column.html_type == 'textarea' %}
-          <el-input v-model="form.{{ underscore(column.java_field) }}" type="textarea" placeholder="请输入内容" />
+          <el-input v-model="form.{{ column.java_field }}" type="textarea" placeholder="请输入内容" />
   {%- elif column.html_type == 'select' %}
-          <el-select v-model="form.{{ underscore(column.java_field) }}" placeholder="请选择{{ column.column_comment }}">
+  {%- if column.dict_type != '' %}
+          <el-select v-model="form.{{ column.java_field }}" placeholder="请选择{{ column.column_comment }}">
             <el-option
-              v-for="dict in {{ getColumnDictType(column) }}Options"
+              v-for="dict in dict.type.{{ column.dict_type }}"
               :key="dict.value"
               :label="dict.label"
-              :value="dict.value"
+              :value="{% if column.java_type == 'Integer' or column.java_type == 'Long' %}parseInt(dict.value){% else %}dict.value{% endif %}"
             ></el-option>
           </el-select>
+  {%- else %}
+          <el-select v-model="form.{{ column.java_field }}" placeholder="请选择{{ column.column_comment }}">
+            <el-option label="请选择字典生成" value="" />
+          </el-select>
+  {%- endif %}
   {%- elif column.html_type == 'radio' %}
-          <el-radio-group v-model="form.{{ underscore(column.java_field) }}">
+  {%- if column.dict_type != '' %}
+          <el-radio-group v-model="form.{{ column.java_field }}">
             <el-radio
-              v-for="dict in {{ getColumnDictType(column) }}Options"
+              v-for="dict in dict.type.{{ column.dict_type }}"
               :key="dict.value"
-              :label="dict.value"
+              :label="{% if column.java_type == 'Integer' or column.java_type == 'Long' %}parseInt(dict.value){% else %}dict.value{% endif %}"
             >{{ dict.label }}</el-radio>
           </el-radio-group>
+  {%- else %}
+          <el-radio-group v-model="form.{{ column.java_field }}">
+            <el-radio label="1">请选择字典生成</el-radio>
+          </el-radio-group>
+  {%- endif %}
   {%- elif column.html_type == 'checkbox' %}
-          <el-checkbox-group v-model="form.{{ underscore(column.java_field) }}">
+  {%- if column.dict_type != '' %}
+          <el-checkbox-group v-model="form.{{ column.java_field }}">
             <el-checkbox
-              v-for="dict in {{ getColumnDictType(column) }}Options"
+              v-for="dict in dict.type.{{ column.dict_type }}"
               :key="dict.value"
               :label="dict.value"
             >{{ dict.label }}</el-checkbox>
           </el-checkbox-group>
+  {%- else %}
+          <el-checkbox-group v-model="form.{{ column.java_field }}">
+            <el-checkbox>请选择字典生成</el-checkbox>
+          </el-checkbox-group>
+  {%- endif %}
   {%- elif column.html_type == 'datetime' %}
           <el-date-picker clearable
-            v-model="form.{{ underscore(column.java_field) }}"
+            v-model="form.{{ column.java_field }}"
             type="date"
             value-format="yyyy-MM-dd"
             placeholder="选择{{ column.column_comment }}">
           </el-date-picker>
   {%- elif column.html_type == 'imageUpload' %}
-          <image-upload v-model="form.{{ underscore(column.java_field) }}"/>
+          <image-upload v-model="form.{{ column.java_field }}"/>
   {%- elif column.html_type == 'fileUpload' %}
-          <file-upload v-model="form.{{ underscore(column.java_field) }}"/>
+          <file-upload v-model="form.{{ column.java_field }}"/>
   {%- endif %}
         </el-form-item>
 {%- endif %}
@@ -254,11 +277,27 @@
 </template>
 
 <script>
-import { list{{ table.class_name }}, get{{ table.class_name }}, del{{ table.class_name }}, add{{ table.class_name }}, update{{ table.class_name }}, export{{ table.class_name }}, importTemplate, importData } from "@/api/{{ table.module_name }}/{{ table.business_name }}";
+{# 使用后端工具函数生成接口前缀:
+   - apiName  : scheduleInfo
+   - ApiName  : ScheduleInfo(listScheduleInfo / getScheduleInfo 等) #}
+{% set apiName = to_camel_case(table.class_name, False) %}
+{% set ApiName = capitalize_first(apiName) %}
+import { list{{ ApiName }}, get{{ ApiName }}, del{{ ApiName }}, add{{ ApiName }}, update{{ ApiName }}, export{{ ApiName }}, importTemplate, importData } from "@/api/{{ table.module_name }}/{{ table.business_name }}";
 import { getToken } from "@/utils/auth";
 
 export default {
   name: "{{ table.class_name }}",
+{%- set dicts_list = [] %}
+{%- for column in table.columns %}
+{%- if column.dict_type and column.dict_type != '' %}
+{%- if column.dict_type not in dicts_list %}
+{%- set _ = dicts_list.append(column.dict_type) %}
+{%- endif %}
+{%- endif %}
+{%- endfor %}
+{%- if dicts_list|length > 0 %}
+  dicts: [{%- for dict_type in dicts_list %}'{{ dict_type }}'{%- if not loop.last %}, {% endif %}{%- endfor %}],
+{%- endif %}
   data() {
     return {
       // 遮罩层
@@ -283,12 +322,6 @@ export default {
 {%- endif %}
 {%- endfor %}
       ],
-{%- for column in table.columns %}
-{%- if column.dict_type != '' %}
-      // {{ column.column_comment }}字典
-      {{ getColumnDictType(column) }}Options: [],
-{%- endif %}
-{%- endfor %}
       // 弹出层标题
       title: "",
       // 是否显示弹出层
@@ -296,7 +329,8 @@ export default {
 {%- for column in table.columns %}
 {%- if column.html_type == 'datetime' %}
       // {{ column.column_comment }}时间范围
-      daterange{{ column.java_field }}: [],
+  {%- set attr_name = capitalize_first(column.java_field) %}
+      dateRange{{ attr_name }}: [],
 {%- endif %}
 {%- endfor %}
       // 查询参数
@@ -305,7 +339,7 @@ export default {
         pageSize: 10,
 {%- for column in table.columns %}
 {%- if column.is_query %}
-        {{ underscore(column.java_field) }}: null{% if not loop.last %},{% endif %}
+        {{ column.java_field }}: null{% if not loop.last %},{% endif %}
 {%- endif %}
 {%- endfor %}
       },
@@ -328,28 +362,29 @@ export default {
       },
       // 表单校验
       rules: {
+{%- set required_cols = [] %}
 {%- for column in table.columns %}
-{%- if column.is_required %}
-        {{ underscore(column.java_field) }}: [
-          { required: true, message: "{{ column.column_comment }}不能为空", trigger: "blur" }
-        ]{% if not loop.last %},{% endif %}
+{%- if column.is_required == '1' %}
+{%- set _ = required_cols.append(column) %}
 {%- endif %}
 {%- endfor %}
+{%- for column in required_cols %}
+{%- set comment = column.column_comment %}
+{%- if '(' in comment or '(' in comment %}
+{%- if '(' in comment %}
+{%- set comment = comment.split('(')[0] %}
+{%- elif '(' in comment %}
+{%- set comment = comment.split('(')[0] %}
+{%- endif %}
+{%- endif %}
+        {{ column.java_field }}: [
+          { required: true, message: "{{ comment }}不能为空", trigger: {% if column.html_type == 'select' or column.html_type == 'radio' %}"change"{% else %}"blur"{% endif %} }
+        ]{% if not loop.last %},{% endif %}
+{%- endfor %}
       }
     };
   },
   created() {
-{%- set dictExists = false %}
-{%- for column in table.columns %}
-{%- if column.dict_type != '' %}
-    {%- if not dictExists %}
-    this.getDicts("{{ getColumnDictType(column) }}").then(response => {
-      this.{{ getColumnDictType(column) }}Options = response.data;
-    });
-    {%- set dictExists = true %}
-    {%- endif %}
-{%- endif %}
-{%- endfor %}
     this.getList();
   },
   methods: {
@@ -363,26 +398,19 @@ export default {
       this.queryParams.params = {};
 {%- set has_datetime = true %}
 {%- endif %}
-      if (null != this.daterange{{ column.java_field }} && '' != this.daterange{{ column.java_field }}.toString()) {
-        this.queryParams.params["begin{{ column.java_field }}"] = this.daterange{{ column.java_field }}[0];
-        this.queryParams.params["end{{ column.java_field }}"] = this.daterange{{ column.java_field }}[1];
+  {%- set attr_name = capitalize_first(column.java_field) %}
+      if (null != this.dateRange{{ attr_name }} && '' != this.dateRange{{ attr_name }}.toString()) {
+        this.queryParams.params["begin{{ column.java_field }}"] = this.dateRange{{ attr_name }}[0];
+        this.queryParams.params["end{{ column.java_field }}"] = this.dateRange{{ attr_name }}[1];
       }
 {%- endif %}
 {%- endfor %}
-      list{{ table.class_name }}(this.queryParams).then(response => {
+      list{{ ApiName }}(this.queryParams).then(response => {
         this.{{ table.business_name }}List = response.rows;
         this.total = response.total;
         this.loading = false;
       });
     },
-{%- for column in table.columns %}
-{%- if column.dict_type != '' %}
-    // {{ column.column_comment }}字典翻译
-    dict_{{ underscore(column.java_field) }}_format(row, column) {
-      return this.selectDictLabel(this.{{ getColumnDictType(column) }}Options, row.{{ underscore(column.java_field) }});
-    },
-{%- endif %}
-{%- endfor %}
     // 取消按钮
     cancel() {
       this.open = false;
@@ -392,7 +420,7 @@ export default {
     reset() {
       this.form = {
 {%- for column in table.columns %}
-        {{ underscore(column.java_field) }}: null{% if not loop.last %},{% endif %}
+        {{ column.java_field }}: null{% if not loop.last %},{% endif %}
 {%- endfor %}
       };
       this.resetForm("form");
@@ -406,7 +434,8 @@ export default {
     resetQuery() {
 {%- for column in table.columns %}
 {%- if column.html_type == 'datetime' %}
-      this.daterange{{ column.java_field }} = [];
+  {%- set attr_name = capitalize_first(column.java_field) %}
+      this.dateRange{{ attr_name }} = [];
 {%- endif %}
 {%- endfor %}
       this.resetForm("queryForm");
@@ -414,7 +443,7 @@ export default {
     },
     // 多选框选中数据
     handleSelectionChange(selection) {
-      this.ids = selection.map(item => item.{{ underscore(table.pk_column.java_field) if table.pk_column }})
+      this.ids = selection.map(item => item.{{ table.pk_column.java_field if table.pk_column }})
       this.single = selection.length!==1
       this.multiple = !selection.length
     },
@@ -427,8 +456,8 @@ export default {
     /** 修改按钮操作 */
     handleUpdate(row) {
       this.reset();
-      const {{ underscore(table.pk_column.java_field) if table.pk_column }} = row.{{ underscore(table.pk_column.java_field) if table.pk_column }} || this.ids
-      get{{ table.class_name }}({{ underscore(table.pk_column.java_field) if table.pk_column }}).then(response => {
+      const {{ table.pk_column.java_field if table.pk_column }} = row.{{ table.pk_column.java_field if table.pk_column }} || this.ids
+      get{{ ApiName }}({{ table.pk_column.java_field if table.pk_column }}).then(response => {
         this.form = response.data;
         this.open = true;
         this.title = "修改{{ table.function_name }}";
@@ -438,14 +467,14 @@ export default {
     submitForm() {
       this.$refs["form"].validate(valid => {
         if (valid) {
-          if (this.form.{{ underscore(table.pk_column.java_field) if table.pk_column }} != null) {
-            update{{ table.class_name }}(this.form).then(response => {
+          if (this.form.{{ table.pk_column.java_field if table.pk_column }} != null) {
+            update{{ ApiName }}(this.form).then(response => {
               this.$modal.msgSuccess("修改成功");
               this.open = false;
               this.getList();
             });
           } else {
-            add{{ table.class_name }}(this.form).then(response => {
+            add{{ ApiName }}(this.form).then(response => {
               this.$modal.msgSuccess("新增成功");
               this.open = false;
               this.getList();
@@ -456,9 +485,9 @@ export default {
     },
     /** 删除按钮操作 */
     handleDelete(row) {
-      const {{ table.business_name }}Ids = row.{{ underscore(table.pk_column.java_field) if table.pk_column }} || this.ids;
+      const {{ table.business_name }}Ids = row.{{ table.pk_column.java_field if table.pk_column }} || this.ids;
       this.$modal.confirm('是否确认删除{{ table.function_name }}编号为"' + {{ table.business_name }}Ids + '"的数据项?').then(function() {
-        return del{{ table.class_name }}({{ table.business_name }}Ids);
+        return del{{ ApiName }}({{ table.business_name }}Ids);
       }).then(() => {
         this.getList();
         this.$modal.msgSuccess("删除成功");
@@ -499,4 +528,4 @@ export default {
     }
   }
 };
-</script>
+</script>