RuralDatabase/reference/3d-map/renderApiServer/renderVue/renderListView copy.js

679 lines
23 KiB
JavaScript
Raw Permalink Normal View History

2024-06-30 21:39:37 +08:00
//思路1、根据renderController生成的目录将每个目录下的getpage文件读取出来查找<any,和>(`中间的字符串获取到接口返回类型的名称,
//2、将获取到的类型名称从interface目录下读取出来然后查找data:和下一个;之间的字符串用replace方法将空格去掉获取到data的类型名称
//2.1、继续在getpage文件中查找 `符号和`符号间的数据例如`/api/admin/api/get-page`用replace去掉/get-page然后再将/替换成冒号:(用来设置权限)
// 3、将获取到的data的类型名称从interface目录下读取出来然后查找list:和下一个;之间的字符串,用replace方法将[]和空格去掉获取到list的类型名称
// 4、将获取到的list的类型名称从interface目录下读取出来查找大括号{}中的数据将这个字符串放到之前我写好的内容中待改善可以生成到table中每一个字段的column
const content = `
<!--
此代码由renderVue工具自动生成
作者mask
-->
<template>
<div class="my-layout">
<el-card class="mt8" shadow="never" :body-style="{ paddingBottom: '0' }">
<el-form :inline="true" @submit.stop.prevent>
<div class="search-box">
<div class="search-simple-box">
<el-form-item>
<my-select-input v-model="state.basic" :filters="state.filters" />
</el-form-item>
<el-form-item>
<el-link type="primary" @click.prevent="openAdvancedSearch">高级搜索</el-link>
</el-form-item>
</div>
<div class="func-btn-box">
<el-form-item>
<el-checkbox class="mr10" v-model="listView.state.pageInput.isDelete" label="已删除" size="large"
@change="onSearch" />
<el-button type="primary" :loading="listView.state.listLoading" size="default" @click="onSearch"> 查询
</el-button>
<el-button type="warning" size="default" @click="cleanSearchParams"> 重置
</el-button>
<el-button v-auth="'<%= permissionPrefix %>:add'" type="primary" size="default" @click="onAdd"> 新增 </el-button>
<!-- 判断条件需要放到单独的标签中否则切换时会导致渲染问题 -->
<template v-if="!listView.state.pageInput.isDelete">
<el-button :loading="listView.state.batchSoftDeleteLoading" size="default"
v-auth="'<%= permissionPrefix %>:soft-delete'" type="danger" :disabled="!selected.length"
@click="onSelectSoftDelete">
批量删除
</el-button>
</template>
<template v-if="listView.state.pageInput.isDelete">
<el-button :loading="listView.state.batchDeleteLoading" size="default" v-auth="'<%= permissionPrefix %>:delete'"
type="danger" :disabled="!selected.length" @click="onSelectDelete"> 批量彻底删除
</el-button>
<el-button :loading="listView.state.batchRecoveryLoading" size="default" v-auth="'<%= permissionPrefix %>:delete'"
type="success" :disabled="!selected.length" @click="onSelectRefresh">
批量恢复
</el-button>
</template>
<el-button :loading="listView.state.downloadLoading" size="default" v-auth="'<%= permissionPrefix %>:add'"
type="success" @click="onDownload">
下载导入模板 </el-button>
<!-- bind解决this指向的问题 -->
<el-upload v-auth="'<%= permissionPrefix %>:add'" class="upload-import" :action="importObject.importUrl"
:headers="{ 'Authorization': listView.token }"
:on-success="importObject.onImportSuccess.bind(importObject)"
:on-error="importObject.onImportError.bind(importObject)"
:on-progress="importObject.onImportProgress.bind(importObject)" :auto-upload="true"
:show-file-list="false">
<el-button :loading="importObject.importLoading" size="default" type="warning" class="ml10">
导入模板
</el-button>
</el-upload>
</el-form-item>
</div>
</div>
</el-form>
</el-card>
<el-card class="my-fill mt8" shadow="never">
<el-table ref="table" :data="listView.state.list" style="width: 100%" v-loading="listView.state.listLoading"
row-key="id" default-expand-all @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" />
<%- tableColumn %>
<el-table-column label="操作" width="180" fixed="right" header-align="center" align="center">
<template #default="{ row, $index }">
<el-button v-auth="'<%= permissionPrefix %>:update'" size="small" text type="primary" @click.stop="onEdit(row)"
v-if="!row.isDeleted">编辑</el-button>
<el-button v-auth="'<%= permissionPrefix %>:soft-delete'" size="small" text type="danger"
@click.stop="onSoftDelete(row)" v-if="!row.isDeleted">删除</el-button>
<el-button v-auth="'<%= permissionPrefix %>:get'" size="small" text type="warning" @click.stop="onOpenDetail(row)"
v-if="!row.isDeleted">查看</el-button>
<el-button v-auth="'<%= permissionPrefix %>:delete'" size="small" text type="danger" @click.stop="onDelete(row)"
v-if="row.isDeleted">彻底删除</el-button>
<el-button v-auth="'<%= permissionPrefix %>:delete'" size="small" text type="success" @click.stop="onRefresh(row)"
v-if="row.isDeleted">恢复</el-button>
</template>
</el-table-column>
</el-table>
<div class="my-flex my-flex-end" style="margin-top: 20px">
<el-pagination v-model:currentPage="listView.state.pageInput.currentPage"
v-model:page-size="listView.state.pageInput.pageSize" :total="listView.state.total"
:page-sizes="[10, 20, 50, 100]" small background @size-change="onSizeChange" @current-change="onCurrentChange"
layout="total, sizes, prev, pager, next, jumper" />
</div>
</el-card>
<!-- 根据新增接口中是否有字典类型生成sexOptions -->
<FormDialog ref="FormDialogRef" @refresh="onQuery">
</FormDialog>
<DetailDialog ref="detailDialogRef" />
<AdvancedSearch ref="AdvancedSearchRef" v-model="listView.state.isShowAdvancedSearch"
:modelFilter="state.advancedSearchParams" v-model:model-controlinput="state.controlinput" :filters="state.filters"
@get-model-controlinput="getAdvancedSearchControlinput" @getData="getAdvancedSearchData">
</AdvancedSearch>
</div>
</template>
<script lang="ts" setup name="<%= permissionPrefix.replace(':','-') %>">
import { ref, reactive, onMounted, getCurrentInstance, onBeforeMount, defineAsyncComponent, onActivated, onDeactivated } from 'vue'
import { formatterTime } from '/@/utils/format'
import { DemoListOutput, DictGetListDto } from '/@/api/interface'
import { getDictLabel } from '/@/utils/dictUtils';
import { useDictListInfo } from '/@/stores/dictListInfo';
import { storeToRefs } from 'pinia';
import {
demoGetPage,
demoBatchDelete,
demoDelete,
demoBatchSoftDelete,
demoBatchRecovery,
demoDownloadTemplate,
demoSoftDelete,
demoRecovery,
} from '/@/api/controller'
import { useListView } from '/@/hooks/useListView'
const storesUseUserInfo = useDictListInfo()
const { dictListInfo } = storeToRefs(storesUseUserInfo)
// 引入组件
const AdvancedSearch = defineAsyncComponent(() => import('/@/components/AdvancedSearch/index.vue'))
const FormDialog = defineAsyncComponent(() => import('./components/form-dialog.vue'))
const DetailDialog = defineAsyncComponent(() => import('./components/detail-dialog.vue'))
const MySelectInput = defineAsyncComponent(() => import('/@/components/my-select-input/index.vue'))
const { proxy } = getCurrentInstance() as any
const FormDialogRef = ref()
const detailDialogRef = ref()
const listView = useListView()
const state = reactive({
//基础搜索
basic: {} as DynamicFilterInfo,
//AdvancedSearch组件中的ControlScreenInput组件 作用:回显
controlinput: [] as Array<DynamicFilterInfo>,
//高级搜索参数
advancedSearchParams: {},
//高级搜索
filters: [
<% filters.forEach(function(itemKey,itemIndex) { %>{
field: '<%= itemKey.field %>',
operator: '<%= itemKey.operator %>',
description: '<%= itemKey.description %>',
componentName: '<%= itemKey.componentName %>',
defaultSelect: <%= !itemIndex %>,
},<% }) %>
] as Array<DynamicFilterInfo>,
})
//处理获取列表的请求参数
function initListParams() {
//state.controlinput 为 ControlScreenInput组件过滤的参数
//state.filter 为 其余筛选组件过滤的参数
let params = [state.basic, ...Object.values(state.advancedSearchParams)]
console.log(state.advancedSearchParams);
let filters = params.map((item: any) => {
if ((typeof item.value === 'object' && item.value.length) || (typeof item.value == 'string' && item.value) || typeof item.value == 'number' || typeof item.value == 'boolean') {
return item
} else {
return {}
}
}).filter(obj => Object.keys(obj).length !== 0)
listView.state.pageInput.dynamicFilter = {
...listView.state.pageInput.dynamicFilter,
//用来筛选表中没有储存的数据
filters: filters
}
}
function onQuery() {
//注意这里的demoGetPage的规则是 接口的http请求方式 + 接口的完整路径) (驼峰命名,首字母小写)
initListParams()
listView.onQuery(demoGetPage)
}
//搜索
function onSearch() {
listView.initPageInput()
onQuery()
}
const selected: any = ref([])
//表格多选
const handleSelectionChange = (selection: any) => {
selected.value = selection
}
//将选中的多选内容装换成只含id的数组
function initSelectParams(): number[] | [] {
return selected.value.map((item: any) => {
return item.id
})
}
//批量删除
function onSelectSoftDelete() {
listView.onMultipleSoftDelete(demoBatchSoftDelete, initSelectParams(), onSearch)
}
//批量彻底删除
function onSelectDelete() {
listView.onMultipleDelete(demoBatchDelete, initSelectParams(), onSearch)
}
//批量恢复
function onSelectRefresh() {
listView.onMultipleRecovery(demoBatchRecovery, initSelectParams(), onSearch)
}
//恢复 注意修改row.name
const onRefresh = async (row: DemoListOutput) => {
proxy.$modal
.confirmDelete(\`确定要恢复当前数据?\`)
.then(async () => {
if (row.id) {
await demoRecovery({ id: row.id })
onQuery()
} else {
proxy.$modal.msgError('id为空')
}
})
.catch(() => { })
}
//下载模板
function onDownload() {
listView.onDownload(demoDownloadTemplate)
}
//软删除 注意修改row.name
const onSoftDelete = (row: DemoListOutput) => {
proxy.$modal
.confirmDelete(\`确定要删除当前数据?\`)
.then(async () => {
try {
if (row.id == undefined) return proxy.$modal.msgError('id不能为空')
let res = await demoSoftDelete({ id: row.id })
if (res.success) {
onQuery()
proxy.$modal.msgSuccess('删除成功')
} else {
proxy.$modal.msgError(res.msg)
}
} catch (error) {
}
})
}
//彻底删除 注意修改row.name
const onDelete = (row: DemoListOutput) => {
proxy.$modal
.confirmDelete(\`确定要删除当前数据?\`)
.then(async () => {
if (row.id) {
try {
let res = await demoDelete({ id: row.id })
if (res.success) {
onQuery()
proxy.$modal.msgSuccess('删除成功')
} else {
proxy.$modal.msgError(res.msg)
}
} catch (error) {
proxy.$modal.msgError(error)
}
} else {
proxy.$modal.msgError('id为空')
}
})
}
const importObject = new listView.ImportFile(onSearch, '/api/demo/demo/import')
const onAdd = () => {
console.log(FormDialogRef.value);
FormDialogRef.value.open()
}
const onEdit = (row: any) => {
FormDialogRef.value.open(row)
}
// 打开详情
const onOpenDetail = (row: any) => {
detailDialogRef.value.open(row)
}
const onSizeChange = (val: number) => {
listView.state.pageInput.pageSize = val
onQuery()
}
const onCurrentChange = (val: number) => {
listView.state.pageInput.currentPage = val
onQuery()
}
/**
* 打开高级搜索
*/
function openAdvancedSearch() {
listView.state.isShowAdvancedSearch = true
}
/**
* 重置搜索
*/
const AdvancedSearchRef = ref()
function cleanSearchParams() {
state.basic.value = ''
AdvancedSearchRef.value.exportReset()
}
/**
* 获取高级搜索中的数据
*/
function getAdvancedSearchData(pararm: any) {
state.advancedSearchParams = JSON.parse(JSON.stringify(pararm)) // 深拷贝防止修改state中的数据
}
function getAdvancedSearchControlinput(controlinput: DynamicFilterInfo[]) {
state.controlinput = JSON.parse(JSON.stringify(controlinput)) // 深拷贝防止修改state中的数据
}
onActivated(() => {
// listView.onQuery(demoGetPage(state.pageInput))
// !!!!!!!!!!!注意这里EmplateTest需要根据列表名称进行改动不然有可能会出问题
// 表单组件中的EmplateTest也需要更改
// (页面缓存时使用)
// eventBus.off('refreshEmplateTest')
// eventBus.on('refreshEmplateTest', () => {
// onQuery()
// })
})
onDeactivated(() => {
//这里销毁 (页面缓存时使用)
// eventBus.off('refreshEmplateTest')
})
/**
* 下一步封装计划
*
* 1. 列表中操作栏的按钮需要封装成一个按钮组件才能拥有自己单独的loading
* 2. 封装表单新增/修改方法
*
* **/
onMounted(() => {
onQuery()
})
</script>
<style scoped lang="scss"></style>
`;
let ejs = require('ejs');
const fs = require('fs');
const path = require('path');
let { renderFile } = require('../utils/index');
const options = {
printWidth: 999,
tabWidth: 2,
proseWrap: 'never',
trailingComma: 'none',
semi: true,
singleQuote: true,
jsxSingleQuote: true,
vueIndentScriptAndStyle: false,
parser: 'vue',
};
// 获取renderController生成的目录
const directoryPath = path.join(__dirname, '../../src/api/controller');
// 读取每个目录下的getpage文件
fs.readdir(directoryPath, (err, files) => {
if (err) {
console.log('Unable to scan directory: ' + err);
} else {
// 遍历每个目录
files.forEach((file) => {
const filePath = path.join(directoryPath, file);
let arr = [];
let dirName = '';
//获取目录名称
fs.stat(filePath, (err, stats) => {
if (err) return;
if (stats.isDirectory()) {
arr = filePath.split('\\');
dirName = arr[arr.length - 1];
// 读取目录下getpage文件内容
let getPageFilePath = filePath + '\\' + dirName + 'GetPage.ts';
fs.readFile(getPageFilePath, 'utf8', (err, data) => {
if (err) {
console.log('Unable to read file: ' + err);
} else {
// 查找<any,和>中间的字符串获取接口返回类型
const regex = /<any, (.*?)>/;
const returnTypeArr = regex.exec(data);
if (!returnTypeArr) return;
const returnType = returnTypeArr[1].replace(' ', '');
const interfaceUrlRegex = /`([^}]+)`/;
const interfaceUrlArr = interfaceUrlRegex.exec(data);
if (!interfaceUrlArr) return;
//权限前缀
const permissionPrefix = interfaceUrlArr[1].replace('/get-page', '').replaceAll('/', ':').slice(1);
// 从interface目录下读取接口返回类型的文件
const interfaceFilePath = path.join(__dirname, '../../src/api/interface/apiTypes', returnType + '.ts');
// 读取接口文件内容
fs.readFile(interfaceFilePath, 'utf8', (err, interfaceData) => {
if (err) {
console.log('Unable to read interface file: ' + err);
} else {
// 查找data:和;之间的字符串获取data类型
const dataRegExp = /data:\s(.*?);/;
const dataNameArr = dataRegExp.exec(interfaceData);
if (!dataNameArr) return;
const dataName = dataNameArr[1].replace(/\s/g, '');
// 从interface目录下读取data的文件
const dataFilePath = path.join(__dirname, '../../src/api/interface/apiTypes', dataName + '.ts');
// 读取data文件内容
fs.readFile(dataFilePath, 'utf8', (err, dataInterfaceData) => {
if (err) {
console.log('Unable to read data interface file: ' + err);
} else {
// 查找list:和;之间的字符串获取list类型
const listRegExp = /list:\s(.*?);/;
const listTypeArr = listRegExp.exec(dataInterfaceData);
if (!listTypeArr) return;
const listType = listTypeArr[1].replace(/\s/g, '').replace('[]', '');
// 从interface目录下读取list的文件
const listFilePath = path.join(__dirname, '../../src/api/interface/apiTypes', listType + '.ts');
// 读取list文件内容
fs.readFile(listFilePath, 'utf8', (err, listInterfaceData) => {
if (err) {
console.log('Unable to read list interface file: ' + err);
} else {
// 查找大括号{}中的内容
const fieldsRegExp = /{([^}]+)}/;
const fieldsArr = fieldsRegExp.exec(listInterfaceData);
if (!fieldsArr) return;
const fields = fieldsArr[1];
// console.log(fields);
// 生成table中每一个字段的column
// 待实现
let tableStr = renderTable(fields, permissionPrefix);
renderFile('../../src/views/renderVue/' + dirName + '/index.vue', tableStr, options);
}
});
}
});
}
});
}
});
} else {
return;
}
});
});
}
});
/**
* 生成表格的列
* @param {*} barr { label: string, prop: string, type: string }[]
*/
function renderElTableColumn(barr) {
/**
* 生成的el-table-column数据
*/
let tableColumn = '';
// 遍历过滤后的数组
for (let i = 0; i < barr.length; i++) {
// 如果对象标签中包含字典
if (barr[i].label.indexOf('字典') != -1) {
// 调用renderDictColumn函数生成el-table-column数据
tableColumn += renderDictColumn(barr[i]);
// 如果对象标签中包含是否
} else if (barr[i].label.indexOf('是否') != -1) {
// 调用renderBooleanColumn函数生成el-table-column数据
tableColumn += renderBooleanColumn(barr[i]);
// 如果对象标签中包含日期
} else if (barr[i].label.indexOf('日期') != -1) {
// 调用renderDateColumn函数生成el-table-column数据
tableColumn += renderDateColumn(barr[i]);
// 如果对象标签中不包含字典、是否、日期
} else {
// 调用renderDateColumn函数生成el-table-column数据
tableColumn += `${`<el-table-column label="${barr[i].label}" prop="${barr[i].prop}" show-overflow-tooltip />\n`}`;
}
}
return tableColumn;
}
/**
* 生成表格的高级搜索参数
* @param {*} barr { label: string, prop: string, type: string }[]
*/
function renderFilters(barr) {
let filters = [];
for (let i = 0; i < barr.length; i++) {
// 如果对象标签中包含字典
if (barr[i].type == 'string') {
filters.push({
field: barr[i].prop,
operator: 'Contains',
description: barr[i].label,
componentName: 'el-input',
defaultSelect: !i,
});
}
}
return filters;
}
function renderTable(fields, permissionPrefix) {
/**
* 生成的el-table-column数据
*/
let tableColumn = '';
let filters = [];
// 过滤掉不需要生成的字段
let filter = ['创建者', '创建时间', '修改者', '修改时间', 'Id', '附件'];
// 注释和字段的字符串数组
let arr = findStringsBeforeNewLines(fields.replace(/\t/g, ''));
// 声明一个空数组
let objArr = [];
// label注释prop属性名type类型
let obj = { label: '', prop: '', type: '' };
// 声明一个布尔值
let isFlag = true;
// 遍历数组
for (let i = 0; i < arr.length; i++) {
// 如果数组中的元素不为空且布尔值为true
if (!!arr[i] && isFlag) {
// 获取属性标签
let label = getPropertyLabel(arr[i]);
// 如果标签不为空
if (!!label) {
// 将标签赋值给对象
obj.label = label;
// 获取属性prop和type
let b = getPropertyProp(arr[i + 1]);
// 将prop赋值给对象
obj.prop = b[0];
// 将type赋值给对象
obj.type = b[1];
// 将对象添加到数组中
objArr.push(obj);
// 将布尔值设置为false
obj = { label: '', prop: '', type: '' };
// 将布尔值设置为true
isFlag = false;
}
// 如果数组中的元素不为空且布尔值为false
} else {
// 将布尔值设置为true
isFlag = true;
// 跳出本次循环
continue;
}
}
// 过滤数组
let barr = objArr.filter((item) => {
// 声明一个布尔值
let isFlag = true;
// 遍历过滤字段
filter.map((filterItem) => {
// 如果过滤字段在对象标签中则将布尔值设置为false
if (item.label.indexOf(filterItem) !== -1) {
return (isFlag = false);
}
});
// 如果布尔值为false则返回false
if (!isFlag) {
return false;
// 如果布尔值为true则返回true
} else {
return true;
}
});
tableColumn = renderElTableColumn(barr);
filters = renderFilters(barr);
console.log(permissionPrefix);
let templateStr = ejs.render(content, { barr, permissionPrefix, tableColumn, filters: filters });
return templateStr;
}
/*
* 参数str字符串
* 返回匹配的子字符串数组
*/
function findStringsBeforeNewLines(str) {
// 使用正则表达式匹配换行符之前的字符串
let matches = str.match(/[^\n]+/g);
// 返回匹配的子字符串数组
return matches;
}
//生成字典行
function renderDictColumn(item) {
return `${`<el-table-column prop="${item.prop}" label="${item.label}" min-width="120" show-overflow-tooltip>
<template #default="{ row }">
{{ getDictLabel(dictListInfo.${item.prop},
row.${item.prop}) }}
</template>
</el-table-column>`}\n`;
}
//生成 是否 字段的行
function renderBooleanColumn(item) {
return `${`<el-table-column label="${item.label}" prop="${item.prop}" >
<template #default="{ row }">
{{ row.${item.prop} ? '是' : '否' }}
</template>
</el-table-column>`}\n`;
}
//生成 日期 字段的行
function renderDateColumn(item) {
return `${`<el-table-column prop="${item.prop}" label="${item.label}" :formatter="formatterTime" min-width="120" />\n`}`;
}
function getPropertyProp(property) {
let str = property.replace(/;/g, '').replace(/\?/g, '').replace(/\s/g, '');
// 根据:分割字符串为数组
let arr = str.split(/:|\?/);
return arr;
}
// 功能根据传入的comment参数获取对应的属性标签
// 正则表达式:/\/\*\* (.+?) \*\//
// 返回值match[1]
function getPropertyLabel(comment) {
// 定义正则表达式用于匹配comment中的属性标签
let regex = /\/\*\* (.+?) \*\//;
// 使用正则表达式匹配comment中的属性标签
let match = comment.match(regex);
// 返回匹配到的属性标签,如果没有匹配到,则返回空字符串
return match ? match[1] : '';
}