mirror of
				https://github.com/continew-org/continew-admin.git
				synced 2025-11-04 10:57:10 +08:00 
			
		
		
		
	refactor: 适配 3.0 前端代码生成模板,代码预览及生成
This commit is contained in:
		@@ -16,6 +16,7 @@
 | 
			
		||||
 | 
			
		||||
package top.charles7c.continew.admin.generator.config.properties;
 | 
			
		||||
 | 
			
		||||
import cn.hutool.core.io.file.FileNameUtil;
 | 
			
		||||
import cn.hutool.core.map.MapUtil;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import org.springframework.boot.context.properties.ConfigurationProperties;
 | 
			
		||||
@@ -71,5 +72,15 @@ public class GeneratorProperties {
 | 
			
		||||
         * 排除字段
 | 
			
		||||
         */
 | 
			
		||||
        private String[] excludeFields;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * 扩展名
 | 
			
		||||
         */
 | 
			
		||||
        private String extension = FileNameUtil.EXT_JAVA;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * 是否为后端模板
 | 
			
		||||
         */
 | 
			
		||||
        private boolean backend = true;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,6 @@ import cn.hutool.core.collection.CollUtil;
 | 
			
		||||
import cn.hutool.core.convert.Convert;
 | 
			
		||||
import cn.hutool.core.date.DateUtil;
 | 
			
		||||
import cn.hutool.core.io.FileUtil;
 | 
			
		||||
import cn.hutool.core.io.file.FileNameUtil;
 | 
			
		||||
import cn.hutool.core.util.ClassUtil;
 | 
			
		||||
import cn.hutool.core.util.StrUtil;
 | 
			
		||||
import cn.hutool.core.util.ZipUtil;
 | 
			
		||||
@@ -227,7 +226,7 @@ public class GeneratorServiceImpl implements GeneratorService {
 | 
			
		||||
            .lastIndexOfIgnoreCase(packageName, StringConstants.DOT) + 1);
 | 
			
		||||
        genConfigMap.put("apiModuleName", apiModuleName);
 | 
			
		||||
        genConfigMap.put("apiName", StrUtil.lowerFirst(genConfig.getClassNamePrefix()));
 | 
			
		||||
        // 渲染后端代码
 | 
			
		||||
        // 渲染代码
 | 
			
		||||
        String classNamePrefix = genConfig.getClassNamePrefix();
 | 
			
		||||
        Map<String, TemplateConfig> templateConfigMap = generatorProperties.getTemplateConfigs();
 | 
			
		||||
        for (Map.Entry<String, TemplateConfig> templateConfigEntry : templateConfigMap.entrySet()) {
 | 
			
		||||
@@ -235,27 +234,22 @@ public class GeneratorServiceImpl implements GeneratorService {
 | 
			
		||||
            String className = classNamePrefix + StrUtil.nullToEmpty(templateConfigEntry.getKey());
 | 
			
		||||
            genConfigMap.put("className", className);
 | 
			
		||||
            TemplateConfig templateConfig = templateConfigEntry.getValue();
 | 
			
		||||
            String content = TemplateUtils.render(templateConfig.getTemplatePath(), genConfigMap);
 | 
			
		||||
            boolean isBackend = templateConfig.isBackend();
 | 
			
		||||
            String extension = templateConfig.getExtension();
 | 
			
		||||
            GeneratePreviewResp generatePreview = new GeneratePreviewResp();
 | 
			
		||||
            generatePreview.setFileName(className + FileNameUtil.EXT_JAVA);
 | 
			
		||||
            generatePreview.setContent(content);
 | 
			
		||||
            generatePreview.setBackend(true);
 | 
			
		||||
            generatePreview.setBackend(isBackend);
 | 
			
		||||
            generatePreviewList.add(generatePreview);
 | 
			
		||||
            if (isBackend) {
 | 
			
		||||
                generatePreview.setFileName(className + extension);
 | 
			
		||||
                generatePreview.setContent(TemplateUtils.render(templateConfig.getTemplatePath(), genConfigMap));
 | 
			
		||||
            } else {
 | 
			
		||||
                generatePreview.setFileName(".vue".equals(extension) && "index".equals(templateConfigEntry.getKey())
 | 
			
		||||
                    ? "index.vue"
 | 
			
		||||
                    : this.getFrontendFileName(classNamePrefix, className, extension));
 | 
			
		||||
                genConfigMap.put("fieldConfigs", fieldConfigList);
 | 
			
		||||
                generatePreview.setContent(TemplateUtils.render(templateConfig.getTemplatePath(), genConfigMap));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        // 渲染前端代码
 | 
			
		||||
        // api 代码
 | 
			
		||||
        genConfigMap.put("fieldConfigs", fieldConfigList);
 | 
			
		||||
        String apiContent = TemplateUtils.render("api.ftl", genConfigMap);
 | 
			
		||||
        GeneratePreviewResp apiGeneratePreview = new GeneratePreviewResp();
 | 
			
		||||
        apiGeneratePreview.setFileName(classNamePrefix.toLowerCase() + ".ts");
 | 
			
		||||
        apiGeneratePreview.setContent(apiContent);
 | 
			
		||||
        generatePreviewList.add(apiGeneratePreview);
 | 
			
		||||
        // view 代码
 | 
			
		||||
        String viewContent = TemplateUtils.render("index.ftl", genConfigMap);
 | 
			
		||||
        GeneratePreviewResp viewGeneratePreview = new GeneratePreviewResp();
 | 
			
		||||
        viewGeneratePreview.setFileName("index.vue");
 | 
			
		||||
        viewGeneratePreview.setContent(viewContent);
 | 
			
		||||
        generatePreviewList.add(viewGeneratePreview);
 | 
			
		||||
        return generatePreviewList;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -265,45 +259,12 @@ public class GeneratorServiceImpl implements GeneratorService {
 | 
			
		||||
            String tempDir = SystemUtil.getUserInfo().getTempDir();
 | 
			
		||||
            // 删除旧代码
 | 
			
		||||
            FileUtil.del(tempDir + projectProperties.getAppName());
 | 
			
		||||
 | 
			
		||||
            tableNames.forEach(tableName -> {
 | 
			
		||||
                // 初始化配置及数据
 | 
			
		||||
                List<GeneratePreviewResp> generatePreviewList = this.preview(tableName);
 | 
			
		||||
                GenConfigDO genConfig = genConfigMapper.selectById(tableName);
 | 
			
		||||
                // 生成后端代码
 | 
			
		||||
                Map<Boolean, List<GeneratePreviewResp>> generatePreviewListMap = generatePreviewList.stream()
 | 
			
		||||
                    .collect(Collectors.groupingBy(GeneratePreviewResp::isBackend));
 | 
			
		||||
                this.generateBackendCode(generatePreviewListMap.get(true), genConfig);
 | 
			
		||||
                // 生成前端代码
 | 
			
		||||
                List<GeneratePreviewResp> frontendGeneratePreviewList = generatePreviewListMap.get(false);
 | 
			
		||||
                String packageName = genConfig.getPackageName();
 | 
			
		||||
                String moduleName = StrUtil.subSuf(packageName, StrUtil
 | 
			
		||||
                    .lastIndexOfIgnoreCase(packageName, StringConstants.DOT) + 1);
 | 
			
		||||
 | 
			
		||||
                // 例如:continew-admin-ui/src
 | 
			
		||||
                String frontendBasicPackagePath = tempDir + String.join(File.separator, projectProperties
 | 
			
		||||
                    .getAppName(), projectProperties.getAppName() + "-ui", "src");
 | 
			
		||||
                // 1、生成 api 代码
 | 
			
		||||
                GeneratePreviewResp apiGeneratePreview = frontendGeneratePreviewList.get(0);
 | 
			
		||||
                // 例如:continew-admin-ui/src/src/api/system
 | 
			
		||||
                String apiPath = String.join(File.separator, frontendBasicPackagePath, "api", moduleName);
 | 
			
		||||
                // 例如:continew-admin-ui/src/api/system/user.ts
 | 
			
		||||
                File apiFile = new File(apiPath, apiGeneratePreview.getFileName());
 | 
			
		||||
                if (!apiFile.exists() || Boolean.TRUE.equals(genConfig.getIsOverride())) {
 | 
			
		||||
                    FileUtil.writeUtf8String(apiGeneratePreview.getContent(), apiFile);
 | 
			
		||||
                }
 | 
			
		||||
                // 2、生成 view 代码
 | 
			
		||||
                GeneratePreviewResp viewGeneratePreview = frontendGeneratePreviewList.get(1);
 | 
			
		||||
                // 例如:continew-admin-ui/src/views/system
 | 
			
		||||
                String vuePath = String.join(File.separator, frontendBasicPackagePath, "views", moduleName, StrUtil
 | 
			
		||||
                    .lowerFirst(genConfig.getClassNamePrefix()));
 | 
			
		||||
                // 例如:continew-admin-ui/src/views/system/user/index.vue
 | 
			
		||||
                File vueFile = new File(vuePath, viewGeneratePreview.getFileName());
 | 
			
		||||
                if (!vueFile.exists() || Boolean.TRUE.equals(genConfig.getIsOverride())) {
 | 
			
		||||
                    FileUtil.writeUtf8String(viewGeneratePreview.getContent(), vueFile);
 | 
			
		||||
                }
 | 
			
		||||
                // 生成代码
 | 
			
		||||
                this.generateCode(generatePreviewList, genConfigMapper.selectById(tableName));
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            // 打包下载
 | 
			
		||||
            File tempDirFile = new File(tempDir, projectProperties.getAppName());
 | 
			
		||||
            String zipFilePath = tempDirFile.getPath() + jodd.io.ZipUtil.ZIP_EXT;
 | 
			
		||||
@@ -316,27 +277,46 @@ public class GeneratorServiceImpl implements GeneratorService {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 生成后端代码
 | 
			
		||||
     * 生成代码
 | 
			
		||||
     *
 | 
			
		||||
     * @param generatePreviewList 生成预览列表
 | 
			
		||||
     * @param genConfig           生成配置
 | 
			
		||||
     */
 | 
			
		||||
    private void generateBackendCode(List<GeneratePreviewResp> generatePreviewList, GenConfigDO genConfig) {
 | 
			
		||||
    private void generateCode(List<GeneratePreviewResp> generatePreviewList, GenConfigDO genConfig) {
 | 
			
		||||
        // 获取前后端基础路径
 | 
			
		||||
        String backendBasicPackagePath = this.buildBackendBasicPackagePath(genConfig);
 | 
			
		||||
        String frontendBasicPackagePath = SystemUtil.getUserInfo().getTempDir() + String
 | 
			
		||||
            .join(File.separator, projectProperties.getAppName(), projectProperties.getAppName() + "-ui");
 | 
			
		||||
        String packageName = genConfig.getPackageName();
 | 
			
		||||
        String moduleName = StrUtil.subSuf(packageName, StrUtil
 | 
			
		||||
            .lastIndexOfIgnoreCase(packageName, StringConstants.DOT) + 1);
 | 
			
		||||
        // 生成代码
 | 
			
		||||
        Map<String, TemplateConfig> templateConfigMap = generatorProperties.getTemplateConfigs();
 | 
			
		||||
        for (GeneratePreviewResp generatePreview : generatePreviewList) {
 | 
			
		||||
            // 获取对应模板配置
 | 
			
		||||
            TemplateConfig templateConfig = templateConfigMap.get(generatePreview.getFileName()
 | 
			
		||||
                .replace(genConfig.getClassNamePrefix(), StringConstants.EMPTY)
 | 
			
		||||
                .replace(FileNameUtil.EXT_JAVA, StringConstants.EMPTY));
 | 
			
		||||
            // 例如:continew-admin/continew-admin-system/src/main/java/top/charles7c/continew/admin/system/service/impl
 | 
			
		||||
            String packagePath = String.join(File.separator, backendBasicPackagePath, templateConfig.getPackageName()
 | 
			
		||||
                .replace(StringConstants.DOT, File.separator));
 | 
			
		||||
            // 例如:continew-admin/continew-admin-system/src/main/java/top/charles7c/continew/admin/system/service/impl/XxxServiceImpl.java
 | 
			
		||||
            File classFile = new File(packagePath, generatePreview.getFileName());
 | 
			
		||||
            TemplateConfig templateConfig = templateConfigMap.getOrDefault(StrUtil.subBefore(generatePreview
 | 
			
		||||
                .getFileName(), StringConstants.DOT, true)
 | 
			
		||||
                .replace(genConfig.getClassNamePrefix(), StringConstants.EMPTY), templateConfigMap.get("api"));
 | 
			
		||||
            String packagePath;
 | 
			
		||||
            if (generatePreview.isBackend()) {
 | 
			
		||||
                // 例如:continew-admin/continew-system/src/main/java/top/continew/admin/system/service/impl
 | 
			
		||||
                packagePath = String.join(File.separator, backendBasicPackagePath, templateConfig.getPackageName()
 | 
			
		||||
                    .replace(StringConstants.DOT, File.separator));
 | 
			
		||||
            } else {
 | 
			
		||||
                // 例如:continew-admin/continew-admin-ui/src/views/system
 | 
			
		||||
                packagePath = String.join(File.separator, frontendBasicPackagePath, templateConfig.getPackageName()
 | 
			
		||||
                    .replace(StringConstants.SLASH, File.separator), moduleName);
 | 
			
		||||
                // 例如:continew-admin/continew-admin-ui/src/views/system/user
 | 
			
		||||
                packagePath = ".vue".equals(templateConfig.getExtension())
 | 
			
		||||
                    ? packagePath + File.separator + StrUtil.lowerFirst(genConfig.getClassNamePrefix())
 | 
			
		||||
                    : packagePath;
 | 
			
		||||
            }
 | 
			
		||||
            // 后端:continew-admin/continew-system/src/main/java/top/continew/admin/system/service/impl/XxxServiceImpl.java
 | 
			
		||||
            // 前端:continew-admin/continew-admin-ui/src/views/system/user/index.vue
 | 
			
		||||
            File file = new File(packagePath, generatePreview.getFileName());
 | 
			
		||||
            // 如果已经存在,且不允许覆盖,则跳过
 | 
			
		||||
            if (!classFile.exists() || Boolean.TRUE.equals(genConfig.getIsOverride())) {
 | 
			
		||||
                FileUtil.writeUtf8String(generatePreview.getContent(), classFile);
 | 
			
		||||
            if (!file.exists() || Boolean.TRUE.equals(genConfig.getIsOverride())) {
 | 
			
		||||
                FileUtil.writeUtf8String(generatePreview.getContent(), file);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -348,13 +328,25 @@ public class GeneratorServiceImpl implements GeneratorService {
 | 
			
		||||
     * @return 后端包路径
 | 
			
		||||
     */
 | 
			
		||||
    private String buildBackendBasicPackagePath(GenConfigDO genConfig) {
 | 
			
		||||
        // 例如:continew-admin/continew-admin-system/src/main/java/top/charles7c/continew/admin/system
 | 
			
		||||
        // 例如:continew-admin/continew-system/src/main/java/top/continew/admin/system
 | 
			
		||||
        return SystemUtil.getUserInfo().getTempDir() + String.join(File.separator, projectProperties
 | 
			
		||||
            .getAppName(), projectProperties.getAppName(), genConfig.getModuleName(), "src", "main", "java", genConfig
 | 
			
		||||
                .getPackageName()
 | 
			
		||||
                .replace(StringConstants.DOT, File.separator));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取前端文件名
 | 
			
		||||
     *
 | 
			
		||||
     * @param classNamePrefix 类名前缀
 | 
			
		||||
     * @param className       类名
 | 
			
		||||
     * @param extension       扩展名
 | 
			
		||||
     * @return 前端文件名
 | 
			
		||||
     */
 | 
			
		||||
    private String getFrontendFileName(String classNamePrefix, String className, String extension) {
 | 
			
		||||
        return (".ts".equals(extension) ? StrUtil.lowerFirst(classNamePrefix) : className) + extension;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 预处理生成配置
 | 
			
		||||
     *
 | 
			
		||||
 
 | 
			
		||||
@@ -1,57 +0,0 @@
 | 
			
		||||
import axios from 'axios';
 | 
			
		||||
import qs from 'query-string';
 | 
			
		||||
 | 
			
		||||
const BASE_URL = '/${apiModuleName}/${apiName}';
 | 
			
		||||
 | 
			
		||||
export interface DataRecord {
 | 
			
		||||
<#if fieldConfigs??>
 | 
			
		||||
<#list fieldConfigs as fieldConfig>
 | 
			
		||||
  ${fieldConfig.fieldName}?: string;
 | 
			
		||||
</#list>
 | 
			
		||||
  createUserString?: string;
 | 
			
		||||
  updateUserString?: string;
 | 
			
		||||
</#if>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ListParam {
 | 
			
		||||
<#if fieldConfigs??>
 | 
			
		||||
<#list fieldConfigs as fieldConfig>
 | 
			
		||||
  <#if fieldConfig.showInQuery>
 | 
			
		||||
  ${fieldConfig.fieldName}?: string;
 | 
			
		||||
  </#if>
 | 
			
		||||
</#list>
 | 
			
		||||
</#if>
 | 
			
		||||
  page?: number;
 | 
			
		||||
  size?: number;
 | 
			
		||||
  sort?: Array<string>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ListRes {
 | 
			
		||||
  list: DataRecord[];
 | 
			
		||||
  total: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function list(params: ListParam) {
 | 
			
		||||
  return axios.get<ListRes>(`${'$'}{BASE_URL}`, {
 | 
			
		||||
    params,
 | 
			
		||||
    paramsSerializer: (obj) => {
 | 
			
		||||
      return qs.stringify(obj);
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function get(id: string) {
 | 
			
		||||
  return axios.get<DataRecord>(`${'$'}{BASE_URL}/${'$'}{id}`);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function add(req: DataRecord) {
 | 
			
		||||
  return axios.post(BASE_URL, req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function update(req: DataRecord, id: string) {
 | 
			
		||||
  return axios.put(`${'$'}{BASE_URL}/${'$'}{id}`, req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function del(ids: string | Array<string>) {
 | 
			
		||||
  return axios.delete(`${'$'}{BASE_URL}/${'$'}{ids}`);
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,103 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <a-modal
 | 
			
		||||
    v-model:visible="visible"
 | 
			
		||||
    :title="title"
 | 
			
		||||
    :mask-closable="false"
 | 
			
		||||
    :esc-to-close="false"
 | 
			
		||||
    :modal-style="{ maxWidth: '520px' }"
 | 
			
		||||
    width="90%"
 | 
			
		||||
    @before-ok="save"
 | 
			
		||||
    @close="reset"
 | 
			
		||||
  >
 | 
			
		||||
    <GiForm ref="formRef" v-model="form" :options="options" :columns="columns" />
 | 
			
		||||
  </a-modal>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { get${classNamePrefix}, add${classNamePrefix}, update${classNamePrefix} } from '@/apis'
 | 
			
		||||
import { Message } from '@arco-design/web-vue'
 | 
			
		||||
import { GiForm, type Columns } from '@/components/GiForm'
 | 
			
		||||
import { useForm } from '@/hooks'
 | 
			
		||||
 | 
			
		||||
const dataId = ref('')
 | 
			
		||||
const isUpdate = computed(() => !!dataId.value)
 | 
			
		||||
const title = computed(() => (isUpdate.value ? '修改${businessName}' : '新增${businessName}'))
 | 
			
		||||
const formRef = ref<InstanceType<typeof GiForm>>()
 | 
			
		||||
 | 
			
		||||
const options: Options = {
 | 
			
		||||
  form: {},
 | 
			
		||||
  col: { xs: 24, sm: 24, md: 24, lg: 24, xl: 24, xxl: 24 },
 | 
			
		||||
  btns: { hide: true }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const columns: Columns = [
 | 
			
		||||
<#list fieldConfigs as fieldConfig>
 | 
			
		||||
  <#if fieldConfig.showInForm>
 | 
			
		||||
  {
 | 
			
		||||
    label: '${fieldConfig.comment}',
 | 
			
		||||
    field: '${fieldConfig.fieldName}',
 | 
			
		||||
    <#if fieldConfig.formType = 'TEXT'>
 | 
			
		||||
    type: 'input',
 | 
			
		||||
    <#elseif fieldConfig.formType = 'TEXT_AREA'>
 | 
			
		||||
    type: 'textarea',
 | 
			
		||||
    </#if>
 | 
			
		||||
    <#if fieldConfig.isRequired>
 | 
			
		||||
    rules: [{ required: true, message: '请输入${fieldConfig.comment}' }]
 | 
			
		||||
    </#if>
 | 
			
		||||
  },
 | 
			
		||||
  </#if>
 | 
			
		||||
</#list>
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
const { form, resetForm } = useForm<({
 | 
			
		||||
    // todo 待补充
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
// 重置
 | 
			
		||||
const reset = () => {
 | 
			
		||||
  formRef.value?.formRef?.resetFields()
 | 
			
		||||
  resetForm()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const visible = ref(false)
 | 
			
		||||
// 新增
 | 
			
		||||
const onAdd = () => {
 | 
			
		||||
  reset()
 | 
			
		||||
  dataId.value = ''
 | 
			
		||||
  visible.value = true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 修改
 | 
			
		||||
const onUpdate = async (id: string) => {
 | 
			
		||||
  reset()
 | 
			
		||||
  dataId.value = id
 | 
			
		||||
  const res = await get${classNamePrefix}(id)
 | 
			
		||||
  Object.assign(form, res.data)
 | 
			
		||||
  visible.value = true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 保存
 | 
			
		||||
const save = async () => {
 | 
			
		||||
  try {
 | 
			
		||||
    const isInvalid = await formRef.value?.formRef?.validate()
 | 
			
		||||
    if (isInvalid) return false
 | 
			
		||||
    if (isUpdate.value) {
 | 
			
		||||
      await update${classNamePrefix}(form, dataId.value)
 | 
			
		||||
      Message.success('修改成功')
 | 
			
		||||
    } else {
 | 
			
		||||
      await add${classNamePrefix}(form)
 | 
			
		||||
      Message.success('新增成功')
 | 
			
		||||
    }
 | 
			
		||||
    emit('save-success')
 | 
			
		||||
    return true
 | 
			
		||||
  } catch (error) {
 | 
			
		||||
    return false
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const emit = defineEmits<{
 | 
			
		||||
  (e: 'save-success'): void
 | 
			
		||||
}>()
 | 
			
		||||
 | 
			
		||||
defineExpose({ onAdd, onUpdate })
 | 
			
		||||
</script>
 | 
			
		||||
@@ -0,0 +1,41 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <a-drawer v-model:visible="visible" title="${businessName}详情" :width="width >= 580 ? 580 : '100%'" :footer="false">
 | 
			
		||||
    <a-descriptions :column="2" size="large" class="general-description">
 | 
			
		||||
      <#list fieldConfigs as fieldConfig>
 | 
			
		||||
      <a-descriptions-item label="${fieldConfig.comment}">{{ dataDetail?.${fieldConfig.fieldName} }}</a-descriptions-item>
 | 
			
		||||
      <#if fieldConfig.fieldName = 'createUser'>
 | 
			
		||||
      <a-descriptions-item label="创建人">{{ dataDetail?.createUserString }}</a-descriptions-item>
 | 
			
		||||
      <#elseif fieldConfig.fieldName = 'updateUser'>
 | 
			
		||||
      <a-descriptions-item label="修改人">{{ dataDetail?.updateUserString }}</a-descriptions-item>
 | 
			
		||||
      </#if>
 | 
			
		||||
      </#list>
 | 
			
		||||
    </a-descriptions>
 | 
			
		||||
  </a-drawer>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { get${classNamePrefix}, type ${classNamePrefix}DetailResp } from '@/apis'
 | 
			
		||||
import { useWindowSize } from '@vueuse/core'
 | 
			
		||||
 | 
			
		||||
const { width } = useWindowSize()
 | 
			
		||||
 | 
			
		||||
const visible = ref(false)
 | 
			
		||||
const dataId = ref('')
 | 
			
		||||
const dataDetail = ref<${classNamePrefix}DetailResp>()
 | 
			
		||||
// 查询详情
 | 
			
		||||
const getDataDetail = async () => {
 | 
			
		||||
  const res = await get${classNamePrefix}(dataId.value)
 | 
			
		||||
  dataDetail.value = res.data
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 打开详情
 | 
			
		||||
const onDetail = async (id: string) => {
 | 
			
		||||
  dataId.value = id
 | 
			
		||||
  await getDataDetail()
 | 
			
		||||
  visible.value = true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
defineExpose({ onDetail })
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped></style>
 | 
			
		||||
@@ -0,0 +1,58 @@
 | 
			
		||||
import http from '@/utils/http'
 | 
			
		||||
 | 
			
		||||
const BASE_URL = '/${apiModuleName}/${apiName}'
 | 
			
		||||
 | 
			
		||||
export interface ${classNamePrefix}Resp {
 | 
			
		||||
<#if fieldConfigs??>
 | 
			
		||||
<#list fieldConfigs as fieldConfig>
 | 
			
		||||
  <#if fieldConfig.showInList>
 | 
			
		||||
  ${fieldConfig.fieldName}: string
 | 
			
		||||
  </#if>
 | 
			
		||||
</#list>
 | 
			
		||||
  createUserString: string
 | 
			
		||||
  updateUserString: string
 | 
			
		||||
</#if>
 | 
			
		||||
}
 | 
			
		||||
export interface ${classNamePrefix}DetailResp {
 | 
			
		||||
<#if fieldConfigs??>
 | 
			
		||||
<#list fieldConfigs as fieldConfig>
 | 
			
		||||
  ${fieldConfig.fieldName}: string
 | 
			
		||||
</#list>
 | 
			
		||||
  createUserString: string
 | 
			
		||||
  updateUserString: string
 | 
			
		||||
</#if>
 | 
			
		||||
}
 | 
			
		||||
export interface ${classNamePrefix}Query extends PageQuery {
 | 
			
		||||
<#if fieldConfigs??>
 | 
			
		||||
<#list fieldConfigs as fieldConfig>
 | 
			
		||||
  <#if fieldConfig.showInQuery>
 | 
			
		||||
  ${fieldConfig.fieldName}: string
 | 
			
		||||
  </#if>
 | 
			
		||||
</#list>
 | 
			
		||||
</#if>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** @desc 查询${businessName}列表 */
 | 
			
		||||
export function list${classNamePrefix}(query: ${classNamePrefix}Query) {
 | 
			
		||||
  return http.get<PageRes<${classNamePrefix}Resp[]>>(`${'$'}{BASE_URL}`, query)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** @desc 查询${businessName}详情 */
 | 
			
		||||
export function get${classNamePrefix}(id: string) {
 | 
			
		||||
  return http.get<${classNamePrefix}DetailResp>(`${'$'}{BASE_URL}/${'$'}{id}`)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** @desc 新增${businessName} */
 | 
			
		||||
export function add${classNamePrefix}(data: any) {
 | 
			
		||||
  return http.post(`${'$'}{BASE_URL}`, data)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** @desc 修改${businessName} */
 | 
			
		||||
export function update${classNamePrefix}(data: any, id: string) {
 | 
			
		||||
  return http.put(`${'$'}{BASE_URL}/${'$'}{id}`, data)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** @desc 删除${businessName} */
 | 
			
		||||
export function delete${classNamePrefix}(id: string) {
 | 
			
		||||
  return http.del(`${'$'}{BASE_URL}/${'$'}{id}`)
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,148 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="gi_page">
 | 
			
		||||
    <a-card title="${businessName}管理" class="general-card">
 | 
			
		||||
      <GiTable
 | 
			
		||||
        ref="tableRef"
 | 
			
		||||
        row-key="id"
 | 
			
		||||
        :data="dataList"
 | 
			
		||||
        :columns="columns"
 | 
			
		||||
        :loading="loading"
 | 
			
		||||
        :scroll="{ x: '100%', y: '100%', minWidth: 1000 }"
 | 
			
		||||
        :pagination="pagination"
 | 
			
		||||
        :disabledColumnKeys="['name']"
 | 
			
		||||
        @refresh="search"
 | 
			
		||||
      >
 | 
			
		||||
        <template #custom-left>
 | 
			
		||||
          <#list fieldConfigs as fieldConfig>
 | 
			
		||||
          <#if fieldConfig.showInQuery>
 | 
			
		||||
          <a-input v-model="queryForm.${fieldConfig.fieldName}" placeholder="请输入${fieldConfig.comment}" allow-clear @change="search">
 | 
			
		||||
            <template #prefix><icon-search /></template>
 | 
			
		||||
          </a-input>
 | 
			
		||||
          </#if>
 | 
			
		||||
          </#list>
 | 
			
		||||
          <a-button @click="reset">重置</a-button>
 | 
			
		||||
        </template>
 | 
			
		||||
        <template #custom-right>
 | 
			
		||||
          <a-button v-permission="['${apiModuleName}:${apiName}:add']" type="primary" @click="onAdd">
 | 
			
		||||
            <template #icon><icon-plus /></template>
 | 
			
		||||
            <span>新增</span>
 | 
			
		||||
          </a-button>
 | 
			
		||||
          <a-tooltip content="导出">
 | 
			
		||||
            <a-button v-permission="['${apiModuleName}:${apiName}:export']" @click="onExport">
 | 
			
		||||
              <template #icon>
 | 
			
		||||
                <icon-download />
 | 
			
		||||
              </template>
 | 
			
		||||
            </a-button>
 | 
			
		||||
          </a-tooltip>
 | 
			
		||||
        </template>
 | 
			
		||||
        <template #action="{ record }">
 | 
			
		||||
          <a-space>
 | 
			
		||||
            <a-link v-permission="['${apiModuleName}:${apiName}:update']" @click="onUpdate(record)">修改</a-link>
 | 
			
		||||
            <a-link
 | 
			
		||||
              v-permission="['${apiModuleName}:${apiName}:delete']"
 | 
			
		||||
              status="danger"
 | 
			
		||||
              :disabled="record.disabled"
 | 
			
		||||
              @click="onDelete(record)"
 | 
			
		||||
            >
 | 
			
		||||
              删除
 | 
			
		||||
            </a-link>
 | 
			
		||||
          </a-space>
 | 
			
		||||
        </template>
 | 
			
		||||
      </GiTable>
 | 
			
		||||
    </a-card>
 | 
			
		||||
 | 
			
		||||
    <${classNamePrefix}AddModal ref="${classNamePrefix}AddModalRef" @save-success="search" />
 | 
			
		||||
    <${classNamePrefix}DetailDrawer ref="${classNamePrefix}DetailDrawerRef" />
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { list${classNamePrefix}, delete${classNamePrefix}, export${classNamePrefix}, type ${classNamePrefix}Resp, type ${classNamePrefix}Query } from '@/apis'
 | 
			
		||||
import ${classNamePrefix}AddModal from './${classNamePrefix}AddModal.vue'
 | 
			
		||||
import ${classNamePrefix}DetailDrawer from './${classNamePrefix}DetailDrawer.vue'
 | 
			
		||||
import { Message } from '@arco-design/web-vue'
 | 
			
		||||
import type { TreeInstance } from '@arco-design/web-vue'
 | 
			
		||||
import type { TableInstanceColumns } from '@/components/GiTable/type'
 | 
			
		||||
import { useTable, useDownload } from '@/hooks'
 | 
			
		||||
import { isMobile } from '@/utils'
 | 
			
		||||
import has from '@/utils/has'
 | 
			
		||||
 | 
			
		||||
defineOptions({ name: '${classNamePrefix}' })
 | 
			
		||||
 | 
			
		||||
const columns: TableInstanceColumns[] = [
 | 
			
		||||
<#if fieldConfigs??>
 | 
			
		||||
  <#list fieldConfigs as fieldConfig>
 | 
			
		||||
  <#if fieldConfig.showInList>
 | 
			
		||||
  { title: '${fieldConfig.comment}', dataIndex: '${fieldConfig.fieldName}' },
 | 
			
		||||
  </#if>
 | 
			
		||||
  </#list>
 | 
			
		||||
</#if>
 | 
			
		||||
  {
 | 
			
		||||
    title: '操作',
 | 
			
		||||
    slotName: 'action',
 | 
			
		||||
    width: 130,
 | 
			
		||||
    align: 'center',
 | 
			
		||||
    fixed: !isMobile() ? 'right' : undefined,
 | 
			
		||||
    show: has.hasPermOr(['${apiModuleName}:${apiName}:update', '${apiModuleName}:${apiName}:delete'])
 | 
			
		||||
  }
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
const queryForm: ${classNamePrefix}Query = reactive({
 | 
			
		||||
<#list fieldConfigs as fieldConfig>
 | 
			
		||||
<#if fieldConfig.showInQuery>
 | 
			
		||||
  ${fieldConfig.fieldName}: undefined,
 | 
			
		||||
</#if>
 | 
			
		||||
</#list>
 | 
			
		||||
  sort: ['createTime,desc']
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
const {
 | 
			
		||||
  tableData: dataList,
 | 
			
		||||
  loading,
 | 
			
		||||
  pagination,
 | 
			
		||||
  search,
 | 
			
		||||
  handleDelete
 | 
			
		||||
} = useTable((p) => list${classNamePrefix}({ ...queryForm, page: p.page, size: p.size }), { immediate: true })
 | 
			
		||||
 | 
			
		||||
// 重置
 | 
			
		||||
const reset = () => {
 | 
			
		||||
<#list fieldConfigs as fieldConfig>
 | 
			
		||||
<#if fieldConfig.showInQuery>
 | 
			
		||||
  queryForm.${fieldConfig.fieldName} = undefined
 | 
			
		||||
</#if>
 | 
			
		||||
</#list>
 | 
			
		||||
  search()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 删除
 | 
			
		||||
const onDelete = (item: ${classNamePrefix}Resp) => {
 | 
			
		||||
  return handleDelete(() => delete${classNamePrefix}(item.id), {
 | 
			
		||||
    content: `是否确定删除该条数据?`,
 | 
			
		||||
    showModal: true
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 导出
 | 
			
		||||
const onExport = () => {
 | 
			
		||||
  useDownload(() => export${classNamePrefix}(queryForm))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const ${classNamePrefix}AddModalRef = ref<InstanceType<typeof ${classNamePrefix}AddModal>>()
 | 
			
		||||
// 新增
 | 
			
		||||
const onAdd = () => {
 | 
			
		||||
  ${classNamePrefix}AddModalRef.value?.onAdd()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 修改
 | 
			
		||||
const onUpdate = (item: ${classNamePrefix}Resp) => {
 | 
			
		||||
  ${classNamePrefix}AddModalRef.value?.onUpdate(item.id)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const ${classNamePrefix}DetailDrawerRef = ref<InstanceType<typeof ${classNamePrefix}DetailDrawer>>()
 | 
			
		||||
// 详情
 | 
			
		||||
const onDetail = (item: ${classNamePrefix}Resp) => {
 | 
			
		||||
  ${classNamePrefix}DetailDrawerRef.value?.onDetail(item.id)
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped></style>
 | 
			
		||||
@@ -1,511 +0,0 @@
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
  import {
 | 
			
		||||
    DataRecord,
 | 
			
		||||
    ListParam,
 | 
			
		||||
    list,
 | 
			
		||||
    get,
 | 
			
		||||
    add,
 | 
			
		||||
    update,
 | 
			
		||||
    del,
 | 
			
		||||
  } from '@/api/${apiModuleName}/${apiName}';
 | 
			
		||||
  import checkPermission from '@/utils/permission';
 | 
			
		||||
 | 
			
		||||
  const { proxy } = getCurrentInstance() as any;
 | 
			
		||||
  // const { dis_enable_status_enum } = proxy.useDict('dis_enable_status_enum');
 | 
			
		||||
 | 
			
		||||
  const queryFormRef = ref();
 | 
			
		||||
  const formRef = ref();
 | 
			
		||||
  const dataList = ref<DataRecord[]>([]);
 | 
			
		||||
  const dataDetail = ref<DataRecord>({
 | 
			
		||||
    // TODO 待补充详情字段默认值
 | 
			
		||||
  });
 | 
			
		||||
  const total = ref(0);
 | 
			
		||||
  const ids = ref<Array<string>>([]);
 | 
			
		||||
  const title = ref('');
 | 
			
		||||
  const single = ref(true);
 | 
			
		||||
  const multiple = ref(true);
 | 
			
		||||
  const showQuery = ref(true);
 | 
			
		||||
  const loading = ref(false);
 | 
			
		||||
  const detailLoading = ref(false);
 | 
			
		||||
  const exportLoading = ref(false);
 | 
			
		||||
  const visible = ref(false);
 | 
			
		||||
  const detailVisible = ref(false);
 | 
			
		||||
 | 
			
		||||
  const data = reactive({
 | 
			
		||||
    // 查询参数
 | 
			
		||||
    queryParams: {
 | 
			
		||||
      <#list fieldConfigs as fieldConfig>
 | 
			
		||||
      <#if fieldConfig.showInQuery>
 | 
			
		||||
      ${fieldConfig.fieldName}: undefined,
 | 
			
		||||
      </#if>
 | 
			
		||||
      </#list>
 | 
			
		||||
      page: 1,
 | 
			
		||||
      size: 10,
 | 
			
		||||
      sort: ['createTime,desc'],
 | 
			
		||||
    },
 | 
			
		||||
    // 表单数据
 | 
			
		||||
    form: {} as DataRecord,
 | 
			
		||||
    // 表单验证规则
 | 
			
		||||
    rules: {
 | 
			
		||||
      <#list fieldConfigs as fieldConfig>
 | 
			
		||||
      <#if fieldConfig.showInForm && fieldConfig.isRequired>
 | 
			
		||||
      ${fieldConfig.fieldName}: [{ required: true, message: '${fieldConfig.comment}不能为空' }],
 | 
			
		||||
      </#if>
 | 
			
		||||
      </#list>
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
  const { queryParams, form, rules } = toRefs(data);
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * 查询列表
 | 
			
		||||
   *
 | 
			
		||||
   * @param params 查询参数
 | 
			
		||||
   */
 | 
			
		||||
  const getList = (params: ListParam = { ...queryParams.value }) => {
 | 
			
		||||
    loading.value = true;
 | 
			
		||||
    list(params)
 | 
			
		||||
      .then((res) => {
 | 
			
		||||
        dataList.value = res.data.list;
 | 
			
		||||
        total.value = res.data.total;
 | 
			
		||||
      })
 | 
			
		||||
      .finally(() => {
 | 
			
		||||
        loading.value = false;
 | 
			
		||||
      });
 | 
			
		||||
  };
 | 
			
		||||
  getList();
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * 打开新增对话框
 | 
			
		||||
   */
 | 
			
		||||
  const toAdd = () => {
 | 
			
		||||
    reset();
 | 
			
		||||
    title.value = '新增${businessName}';
 | 
			
		||||
    visible.value = true;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * 打开修改对话框
 | 
			
		||||
   *
 | 
			
		||||
   * @param id ID
 | 
			
		||||
   */
 | 
			
		||||
  const toUpdate = (id: string) => {
 | 
			
		||||
    reset();
 | 
			
		||||
    get(id).then((res) => {
 | 
			
		||||
      form.value = res.data;
 | 
			
		||||
      title.value = '修改${businessName}';
 | 
			
		||||
      visible.value = true;
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * 重置表单
 | 
			
		||||
   */
 | 
			
		||||
  const reset = () => {
 | 
			
		||||
    form.value = {
 | 
			
		||||
      // TODO 待补充需要重置的字段默认值,详情请参考其他模块 index.vue
 | 
			
		||||
    };
 | 
			
		||||
    formRef.value.resetFields();
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * 取消
 | 
			
		||||
   */
 | 
			
		||||
  const handleCancel = () => {
 | 
			
		||||
    visible.value = false;
 | 
			
		||||
    formRef.value.resetFields();
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * 确定
 | 
			
		||||
   */
 | 
			
		||||
  const handleOk = () => {
 | 
			
		||||
    formRef.value.validate((valid: any) => {
 | 
			
		||||
      if (!valid) {
 | 
			
		||||
        if (form.value.id !== undefined) {
 | 
			
		||||
          update(form.value, form.value.id).then((res) => {
 | 
			
		||||
            handleCancel();
 | 
			
		||||
            getList();
 | 
			
		||||
            proxy.$message.success(res.msg);
 | 
			
		||||
          });
 | 
			
		||||
        } else {
 | 
			
		||||
          add(form.value).then((res) => {
 | 
			
		||||
            handleCancel();
 | 
			
		||||
            getList();
 | 
			
		||||
            proxy.$message.success(res.msg);
 | 
			
		||||
          });
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * 查看详情
 | 
			
		||||
   *
 | 
			
		||||
   * @param id ID
 | 
			
		||||
   */
 | 
			
		||||
  const toDetail = async (id: string) => {
 | 
			
		||||
    if (detailLoading.value) return;
 | 
			
		||||
    detailLoading.value = true;
 | 
			
		||||
    detailVisible.value = true;
 | 
			
		||||
    get(id)
 | 
			
		||||
      .then((res) => {
 | 
			
		||||
        dataDetail.value = res.data;
 | 
			
		||||
      })
 | 
			
		||||
      .finally(() => {
 | 
			
		||||
        detailLoading.value = false;
 | 
			
		||||
      });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * 关闭详情
 | 
			
		||||
   */
 | 
			
		||||
  const handleDetailCancel = () => {
 | 
			
		||||
    detailVisible.value = false;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * 批量删除
 | 
			
		||||
   */
 | 
			
		||||
  const handleBatchDelete = () => {
 | 
			
		||||
    if (ids.value.length === 0) {
 | 
			
		||||
      proxy.$message.info('请选择要删除的数据');
 | 
			
		||||
    } else {
 | 
			
		||||
      proxy.$modal.warning({
 | 
			
		||||
        title: '警告',
 | 
			
		||||
        titleAlign: 'start',
 | 
			
		||||
        content: `是否确定删除所选的${r'${ids.value.length}'}条数据?`,
 | 
			
		||||
        hideCancel: false,
 | 
			
		||||
        onOk: () => {
 | 
			
		||||
          handleDelete(ids.value);
 | 
			
		||||
        },
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * 删除
 | 
			
		||||
   *
 | 
			
		||||
   * @param ids ID 列表
 | 
			
		||||
   */
 | 
			
		||||
  const handleDelete = (ids: Array<string>) => {
 | 
			
		||||
    del(ids).then((res) => {
 | 
			
		||||
      proxy.$message.success(res.msg);
 | 
			
		||||
      getList();
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * 已选择的数据行发生改变时触发
 | 
			
		||||
   *
 | 
			
		||||
   * @param rowKeys ID 列表
 | 
			
		||||
   */
 | 
			
		||||
  const handleSelectionChange = (rowKeys: Array<any>) => {
 | 
			
		||||
    ids.value = rowKeys;
 | 
			
		||||
    single.value = rowKeys.length !== 1;
 | 
			
		||||
    multiple.value = !rowKeys.length;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * 导出
 | 
			
		||||
   */
 | 
			
		||||
  const handleExport = () => {
 | 
			
		||||
    if (exportLoading.value) return;
 | 
			
		||||
    exportLoading.value = true;
 | 
			
		||||
    proxy
 | 
			
		||||
      .download('/${apiModuleName}/${apiName}/export', { ...queryParams.value }, '${businessName}数据')
 | 
			
		||||
      .finally(() => {
 | 
			
		||||
        exportLoading.value = false;
 | 
			
		||||
      });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * 查询
 | 
			
		||||
   */
 | 
			
		||||
  const handleQuery = () => {
 | 
			
		||||
    getList();
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * 重置
 | 
			
		||||
   */
 | 
			
		||||
  const resetQuery = () => {
 | 
			
		||||
    queryFormRef.value.resetFields();
 | 
			
		||||
    handleQuery();
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * 切换页码
 | 
			
		||||
   *
 | 
			
		||||
   * @param current 页码
 | 
			
		||||
   */
 | 
			
		||||
  const handlePageChange = (current: number) => {
 | 
			
		||||
    queryParams.value.page = current;
 | 
			
		||||
    getList();
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * 切换每页条数
 | 
			
		||||
   *
 | 
			
		||||
   * @param pageSize 每页条数
 | 
			
		||||
   */
 | 
			
		||||
  const handlePageSizeChange = (pageSize: number) => {
 | 
			
		||||
    queryParams.value.size = pageSize;
 | 
			
		||||
    getList();
 | 
			
		||||
  };
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
  export default {
 | 
			
		||||
    name: '${classNamePrefix}',
 | 
			
		||||
  };
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="app-container">
 | 
			
		||||
    <Breadcrumb :items="['menu.${apiModuleName}', 'menu.${apiModuleName}.${apiName}.list']" />
 | 
			
		||||
    <a-card class="general-card" :title="$t('menu.${apiModuleName}.${apiName}.list')">
 | 
			
		||||
      <!-- 头部区域 -->
 | 
			
		||||
      <div class="header">
 | 
			
		||||
        <!-- 搜索栏 -->
 | 
			
		||||
        <div v-if="showQuery" class="header-query">
 | 
			
		||||
          <a-form ref="queryFormRef" :model="queryParams" layout="inline">
 | 
			
		||||
            <#list fieldConfigs as fieldConfig>
 | 
			
		||||
            <#if fieldConfig.showInQuery>
 | 
			
		||||
            <a-form-item field="${fieldConfig.fieldName}" hide-label>
 | 
			
		||||
              <a-input
 | 
			
		||||
                v-model="queryParams.${fieldConfig.fieldName}"
 | 
			
		||||
                placeholder="输入${fieldConfig.comment}搜索"
 | 
			
		||||
                allow-clear
 | 
			
		||||
                style="width: 150px"
 | 
			
		||||
                @press-enter="handleQuery"
 | 
			
		||||
              />
 | 
			
		||||
            </a-form-item>
 | 
			
		||||
            </#if>
 | 
			
		||||
            </#list>
 | 
			
		||||
            <a-form-item hide-label>
 | 
			
		||||
              <a-space>
 | 
			
		||||
                <a-button type="primary" @click="handleQuery">
 | 
			
		||||
                  <template #icon><icon-search /></template>查询
 | 
			
		||||
                </a-button>
 | 
			
		||||
                <a-button @click="resetQuery">
 | 
			
		||||
                  <template #icon><icon-refresh /></template>重置
 | 
			
		||||
                </a-button>
 | 
			
		||||
              </a-space>
 | 
			
		||||
            </a-form-item>
 | 
			
		||||
          </a-form>
 | 
			
		||||
        </div>
 | 
			
		||||
        <!-- 操作栏 -->
 | 
			
		||||
        <div class="header-operation">
 | 
			
		||||
          <a-row>
 | 
			
		||||
            <a-col :span="12">
 | 
			
		||||
              <a-space>
 | 
			
		||||
                <a-button
 | 
			
		||||
                  v-permission="['${apiModuleName}:${apiName}:add']"
 | 
			
		||||
                  type="primary"
 | 
			
		||||
                  @click="toAdd"
 | 
			
		||||
                >
 | 
			
		||||
                  <template #icon><icon-plus /></template>新增
 | 
			
		||||
                </a-button>
 | 
			
		||||
                <a-button
 | 
			
		||||
                  v-permission="['${apiModuleName}:${apiName}:update']"
 | 
			
		||||
                  type="primary"
 | 
			
		||||
                  status="success"
 | 
			
		||||
                  :disabled="single"
 | 
			
		||||
                  :title="single ? '请选择一条要修改的数据' : ''"
 | 
			
		||||
                  @click="toUpdate(ids[0])"
 | 
			
		||||
                >
 | 
			
		||||
                  <template #icon><icon-edit /></template>修改
 | 
			
		||||
                </a-button>
 | 
			
		||||
                <a-button
 | 
			
		||||
                  v-permission="['${apiModuleName}:${apiName}:delete']"
 | 
			
		||||
                  type="primary"
 | 
			
		||||
                  status="danger"
 | 
			
		||||
                  :disabled="multiple"
 | 
			
		||||
                  :title="multiple ? '请选择要删除的数据' : ''"
 | 
			
		||||
                  @click="handleBatchDelete"
 | 
			
		||||
                >
 | 
			
		||||
                  <template #icon><icon-delete /></template>删除
 | 
			
		||||
                </a-button>
 | 
			
		||||
                <a-button
 | 
			
		||||
                  v-permission="['${apiModuleName}:${apiName}:export']"
 | 
			
		||||
                  :loading="exportLoading"
 | 
			
		||||
                  type="primary"
 | 
			
		||||
                  status="warning"
 | 
			
		||||
                  @click="handleExport"
 | 
			
		||||
                >
 | 
			
		||||
                  <template #icon><icon-download /></template>导出
 | 
			
		||||
                </a-button>
 | 
			
		||||
              </a-space>
 | 
			
		||||
            </a-col>
 | 
			
		||||
            <a-col :span="12">
 | 
			
		||||
              <right-toolbar
 | 
			
		||||
                v-model:show-query="showQuery"
 | 
			
		||||
                @refresh="getList"
 | 
			
		||||
              />
 | 
			
		||||
            </a-col>
 | 
			
		||||
          </a-row>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <!-- 列表区域 -->
 | 
			
		||||
      <a-table
 | 
			
		||||
        row-key="id"
 | 
			
		||||
        :data="dataList"
 | 
			
		||||
        :loading="loading"
 | 
			
		||||
        :row-selection="{
 | 
			
		||||
          type: 'checkbox',
 | 
			
		||||
          showCheckedAll: true,
 | 
			
		||||
          onlyCurrent: false,
 | 
			
		||||
        }"
 | 
			
		||||
        :pagination="{
 | 
			
		||||
          showTotal: true,
 | 
			
		||||
          showPageSize: true,
 | 
			
		||||
          total: total,
 | 
			
		||||
          current: queryParams.page,
 | 
			
		||||
        }"
 | 
			
		||||
        :bordered="false"
 | 
			
		||||
        column-resizable
 | 
			
		||||
        stripe
 | 
			
		||||
        size="large"
 | 
			
		||||
        @page-change="handlePageChange"
 | 
			
		||||
        @page-size-change="handlePageSizeChange"
 | 
			
		||||
        @selection-change="handleSelectionChange"
 | 
			
		||||
      >
 | 
			
		||||
        <template #columns>
 | 
			
		||||
          <#list fieldConfigs as fieldConfig>
 | 
			
		||||
          <#if fieldConfig_index = 0>
 | 
			
		||||
          <a-table-column title="${fieldConfig.comment}" data-index="${fieldConfig.fieldName}">
 | 
			
		||||
            <template #cell="{ record }">
 | 
			
		||||
              <a-link @click="toDetail(record.id)">{{ record.${fieldConfig.fieldName} }}</a-link>
 | 
			
		||||
            </template>
 | 
			
		||||
          </a-table-column>
 | 
			
		||||
          <#else>
 | 
			
		||||
          <#if fieldConfig.showInList>
 | 
			
		||||
          <a-table-column title="${fieldConfig.comment}" data-index="${fieldConfig.fieldName}" />
 | 
			
		||||
          </#if>
 | 
			
		||||
          </#if>
 | 
			
		||||
          </#list>
 | 
			
		||||
          <a-table-column
 | 
			
		||||
            v-if="checkPermission(['${apiModuleName}:${apiName}:update', '${apiModuleName}:${apiName}:delete'])"
 | 
			
		||||
            title="操作"
 | 
			
		||||
            align="center"
 | 
			
		||||
          >
 | 
			
		||||
            <template #cell="{ record }">
 | 
			
		||||
              <a-button
 | 
			
		||||
                v-permission="['${apiModuleName}:${apiName}:update']"
 | 
			
		||||
                type="text"
 | 
			
		||||
                size="small"
 | 
			
		||||
                title="修改"
 | 
			
		||||
                @click="toUpdate(record.id)"
 | 
			
		||||
              >
 | 
			
		||||
                <template #icon><icon-edit /></template>修改
 | 
			
		||||
              </a-button>
 | 
			
		||||
              <a-popconfirm
 | 
			
		||||
                content="是否确定删除该数据?"
 | 
			
		||||
                type="warning"
 | 
			
		||||
                @ok="handleDelete([record.id])"
 | 
			
		||||
              >
 | 
			
		||||
                <a-button
 | 
			
		||||
                  v-permission="['${apiModuleName}:${apiName}:delete']"
 | 
			
		||||
                  type="text"
 | 
			
		||||
                  size="small"
 | 
			
		||||
                  title="删除"
 | 
			
		||||
                  :disabled="record.disabled"
 | 
			
		||||
                >
 | 
			
		||||
                  <template #icon><icon-delete /></template>删除
 | 
			
		||||
                </a-button>
 | 
			
		||||
              </a-popconfirm>
 | 
			
		||||
            </template>
 | 
			
		||||
          </a-table-column>
 | 
			
		||||
        </template>
 | 
			
		||||
      </a-table>
 | 
			
		||||
 | 
			
		||||
      <!-- 表单区域 -->
 | 
			
		||||
      <a-modal
 | 
			
		||||
        :title="title"
 | 
			
		||||
        :visible="visible"
 | 
			
		||||
        :mask-closable="false"
 | 
			
		||||
        :esc-to-close="false"
 | 
			
		||||
        unmount-on-close
 | 
			
		||||
        render-to-body
 | 
			
		||||
        @ok="handleOk"
 | 
			
		||||
        @cancel="handleCancel"
 | 
			
		||||
      >
 | 
			
		||||
        <a-form ref="formRef" :model="form" :rules="rules" size="large">
 | 
			
		||||
          <#list fieldConfigs as fieldConfig>
 | 
			
		||||
          <#if fieldConfig.showInForm>
 | 
			
		||||
          <a-form-item label="${fieldConfig.comment}" field="${fieldConfig.fieldName}">
 | 
			
		||||
            <#if fieldConfig.formType = 'TEXT'>
 | 
			
		||||
            <a-input v-model="form.${fieldConfig.fieldName}" placeholder="请输入${fieldConfig.comment}" />
 | 
			
		||||
            <#elseif fieldConfig.formType = 'TEXT_AREA'>
 | 
			
		||||
            <a-textarea
 | 
			
		||||
              v-model="form.${fieldConfig.fieldName}"
 | 
			
		||||
              :max-length="200"
 | 
			
		||||
              placeholder="请输入${fieldConfig.comment}"
 | 
			
		||||
              :auto-size="{
 | 
			
		||||
                minRows: 3,
 | 
			
		||||
              }"
 | 
			
		||||
              show-word-limit
 | 
			
		||||
            />
 | 
			
		||||
            <#elseif fieldConfig.formType = 'SELECT'>
 | 
			
		||||
              <#--<a-select
 | 
			
		||||
                v-model="form.${fieldConfig.fieldName}"
 | 
			
		||||
                :options="${apiName}Options"
 | 
			
		||||
                placeholder="请选择${fieldConfig.comment}"
 | 
			
		||||
                :loading="${apiName}Loading"
 | 
			
		||||
                multiple
 | 
			
		||||
                allow-clear
 | 
			
		||||
                :allow-search="{ retainInputValue: true }"
 | 
			
		||||
                style="width: 416px"
 | 
			
		||||
              />-->
 | 
			
		||||
            <#elseif fieldConfig.formType = 'RADIO'>
 | 
			
		||||
            <#--<a-radio-group v-model="form.${fieldConfig.fieldName}" type="button">
 | 
			
		||||
            </a-radio-group>-->
 | 
			
		||||
            <#elseif fieldConfig.formType = 'DATE'>
 | 
			
		||||
            <a-date-picker v-model="form.${fieldConfig.fieldName}" placeholder="请选择${fieldConfig.comment}"/>
 | 
			
		||||
            <#elseif fieldConfig.formType = 'DATE_TIME'>
 | 
			
		||||
            <a-date-picker
 | 
			
		||||
              v-model="form.${fieldConfig.fieldName}"
 | 
			
		||||
              placeholder="请选择${fieldConfig.comment}"
 | 
			
		||||
              show-time
 | 
			
		||||
              format="YYYY-MM-DD HH:mm:ss"
 | 
			
		||||
            />
 | 
			
		||||
            </#if>
 | 
			
		||||
          </a-form-item>
 | 
			
		||||
          </#if>
 | 
			
		||||
          </#list>
 | 
			
		||||
        </a-form>
 | 
			
		||||
      </a-modal>
 | 
			
		||||
 | 
			
		||||
      <!-- 详情区域 -->
 | 
			
		||||
      <a-drawer
 | 
			
		||||
        title="${businessName}详情"
 | 
			
		||||
        :visible="detailVisible"
 | 
			
		||||
        :width="580"
 | 
			
		||||
        :footer="false"
 | 
			
		||||
        unmount-on-close
 | 
			
		||||
        render-to-body
 | 
			
		||||
        @cancel="handleDetailCancel"
 | 
			
		||||
      >
 | 
			
		||||
        <a-descriptions :column="2" bordered size="large">
 | 
			
		||||
          <#list fieldConfigs as fieldConfig>
 | 
			
		||||
          <a-descriptions-item label="${fieldConfig.comment}">
 | 
			
		||||
            <a-skeleton v-if="detailLoading" :animation="true">
 | 
			
		||||
              <a-skeleton-line :rows="1" />
 | 
			
		||||
            </a-skeleton>
 | 
			
		||||
            <#if fieldConfig.fieldName = 'createUser'>
 | 
			
		||||
            <span v-else>{{ dataDetail.createUserString }}</span>
 | 
			
		||||
            <#elseif fieldConfig.fieldName = 'updateUser'>
 | 
			
		||||
            <span v-else>{{ dataDetail.updateUserString }}</span>
 | 
			
		||||
            <#else>
 | 
			
		||||
            <span v-else>{{ dataDetail.${fieldConfig.fieldName} }}</span>
 | 
			
		||||
            </#if>
 | 
			
		||||
          </a-descriptions-item>
 | 
			
		||||
          </#list>
 | 
			
		||||
        </a-descriptions>
 | 
			
		||||
      </a-drawer>
 | 
			
		||||
    </a-card>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<style scoped lang="less"></style>
 | 
			
		||||
@@ -43,6 +43,7 @@
 | 
			
		||||
            <artifactId>continew-admin-generator</artifactId>
 | 
			
		||||
        </dependency>
 | 
			
		||||
 | 
			
		||||
        <!-- 系统管理模块(存放系统管理模块相关功能,例如:部门管理、角色管理、用户管理等) -->
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>top.charles7c.continew</groupId>
 | 
			
		||||
            <artifactId>continew-admin-system</artifactId>
 | 
			
		||||
 
 | 
			
		||||
@@ -42,7 +42,7 @@ generator:
 | 
			
		||||
  templateConfigs:
 | 
			
		||||
    DO:
 | 
			
		||||
      # 模板路径
 | 
			
		||||
      templatePath: Entity.ftl
 | 
			
		||||
      templatePath: backend/Entity.ftl
 | 
			
		||||
      # 包名称
 | 
			
		||||
      packageName: model.entity
 | 
			
		||||
      # 排除字段
 | 
			
		||||
@@ -53,20 +53,20 @@ generator:
 | 
			
		||||
        - updateUser
 | 
			
		||||
        - updateTime
 | 
			
		||||
    Query:
 | 
			
		||||
      templatePath: Query.ftl
 | 
			
		||||
      templatePath: backend/Query.ftl
 | 
			
		||||
      packageName: model.query
 | 
			
		||||
    Req:
 | 
			
		||||
      templatePath: Req.ftl
 | 
			
		||||
      templatePath: backend/Req.ftl
 | 
			
		||||
      packageName: model.req
 | 
			
		||||
    Resp:
 | 
			
		||||
      templatePath: Resp.ftl
 | 
			
		||||
      templatePath: backend/Resp.ftl
 | 
			
		||||
      packageName: model.resp
 | 
			
		||||
      excludeFields:
 | 
			
		||||
        - id
 | 
			
		||||
        - createUser
 | 
			
		||||
        - createTime
 | 
			
		||||
    DetailResp:
 | 
			
		||||
      templatePath: DetailResp.ftl
 | 
			
		||||
      templatePath: backend/DetailResp.ftl
 | 
			
		||||
      packageName: model.resp
 | 
			
		||||
      excludeFields:
 | 
			
		||||
        - id
 | 
			
		||||
@@ -75,14 +75,34 @@ generator:
 | 
			
		||||
        - updateUser
 | 
			
		||||
        - updateTime
 | 
			
		||||
    Mapper:
 | 
			
		||||
      templatePath: Mapper.ftl
 | 
			
		||||
      templatePath: backend/Mapper.ftl
 | 
			
		||||
      packageName: mapper
 | 
			
		||||
    Service:
 | 
			
		||||
      templatePath: Service.ftl
 | 
			
		||||
      templatePath: backend/Service.ftl
 | 
			
		||||
      packageName: service
 | 
			
		||||
    ServiceImpl:
 | 
			
		||||
      templatePath: ServiceImpl.ftl
 | 
			
		||||
      templatePath: backend/ServiceImpl.ftl
 | 
			
		||||
      packageName: service.impl
 | 
			
		||||
    Controller:
 | 
			
		||||
      templatePath: Controller.ftl
 | 
			
		||||
      packageName: controller
 | 
			
		||||
      templatePath: backend/Controller.ftl
 | 
			
		||||
      packageName: controller
 | 
			
		||||
    api:
 | 
			
		||||
      templatePath: frontend/api.ftl
 | 
			
		||||
      packageName: src/apis
 | 
			
		||||
      extension: .ts
 | 
			
		||||
      backend: false
 | 
			
		||||
    index:
 | 
			
		||||
      templatePath: frontend/index.ftl
 | 
			
		||||
      packageName: src/views
 | 
			
		||||
      extension: .vue
 | 
			
		||||
      backend: false
 | 
			
		||||
    AddModal:
 | 
			
		||||
      templatePath: frontend/AddModal.ftl
 | 
			
		||||
      packageName: src/views
 | 
			
		||||
      extension: .vue
 | 
			
		||||
      backend: false
 | 
			
		||||
    DetailDrawer:
 | 
			
		||||
      templatePath: frontend/DetailDrawer.ftl
 | 
			
		||||
      packageName: src/views
 | 
			
		||||
      extension: .vue
 | 
			
		||||
      backend: false
 | 
			
		||||
		Reference in New Issue
	
	Block a user