mirror of
https://github.com/bufanyun/hotgo.git
synced 2025-11-14 13:13:51 +08:00
发布代码生成、更新20+表单组件,优化数据字典,gf版本更新到2.3.1
This commit is contained in:
509
web/src/views/develop/code/components/BaseInfo.vue
Normal file
509
web/src/views/develop/code/components/BaseInfo.vue
Normal file
@@ -0,0 +1,509 @@
|
||||
<template>
|
||||
<div>
|
||||
<n-card
|
||||
:bordered="true"
|
||||
title="基本设置"
|
||||
class="proCard mt-2"
|
||||
size="small"
|
||||
:segmented="{ content: true }"
|
||||
>
|
||||
<n-form ref="formRef" :model="formValue">
|
||||
<n-row :gutter="24">
|
||||
<n-col :span="6" style="min-width: 200px">
|
||||
<n-form-item label="生成类型" path="title">
|
||||
<n-select
|
||||
placeholder="请选择"
|
||||
:options="selectList.genType"
|
||||
v-model:value="formValue.genType"
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-col>
|
||||
|
||||
<n-col :span="6" style="min-width: 200px">
|
||||
<n-form-item label="实体命名" path="varName">
|
||||
<n-input placeholder="请输入" v-model:value="formValue.varName" />
|
||||
</n-form-item>
|
||||
</n-col>
|
||||
|
||||
<n-col :span="6" style="min-width: 200px">
|
||||
<n-form-item
|
||||
label="数据库"
|
||||
path="dbName"
|
||||
v-show="formValue.genType >= 10 && formValue.genType < 20"
|
||||
>
|
||||
<n-select
|
||||
placeholder="请选择"
|
||||
:options="selectList.db"
|
||||
v-model:value="formValue.dbName"
|
||||
@update:value="handleDbUpdateValue"
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-col>
|
||||
|
||||
<n-col :span="6" style="min-width: 200px">
|
||||
<n-form-item
|
||||
label="数据库表"
|
||||
path="tableName"
|
||||
v-show="formValue.genType >= 10 && formValue.genType < 20"
|
||||
>
|
||||
<n-select
|
||||
filterable
|
||||
tag
|
||||
:loading="tablesLoading"
|
||||
placeholder="请选择"
|
||||
:options="tablesOption"
|
||||
v-model:value="formValue.tableName"
|
||||
@update:value="handleTableUpdateValue"
|
||||
:disabled="formValue.dbName === ''"
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-col>
|
||||
|
||||
<n-col :span="18">
|
||||
<n-form-item
|
||||
label="表格头部按钮组"
|
||||
path="tableName"
|
||||
v-show="formValue.genType >= 10 && formValue.genType < 20"
|
||||
>
|
||||
<n-checkbox-group v-model:value="formValue.options.headOps">
|
||||
<n-space item-style="display: flex;">
|
||||
<n-checkbox value="add" label="新增表单按钮" />
|
||||
<n-checkbox value="batchDel" label="批量删除按钮" />
|
||||
<n-checkbox value="export" label="导出按钮" />
|
||||
</n-space>
|
||||
</n-checkbox-group>
|
||||
</n-form-item>
|
||||
</n-col>
|
||||
|
||||
<n-col :span="24">
|
||||
<n-form-item
|
||||
label="表格列操作"
|
||||
path="columnOps"
|
||||
v-show="formValue.genType >= 10 && formValue.genType < 20"
|
||||
>
|
||||
<n-checkbox-group v-model:value="formValue.options.columnOps">
|
||||
<n-space item-style="display: flex;">
|
||||
<n-checkbox value="edit" label="编辑" />
|
||||
<n-checkbox value="status" label="状态修改" />
|
||||
<n-popover trigger="hover">
|
||||
<template #trigger>
|
||||
<n-icon size="15" class="tips-help-icon" color="#2d8cf0">
|
||||
<QuestionCircleOutlined />
|
||||
</n-icon>
|
||||
</template>
|
||||
<span>主表中存在`status`字段时才会生效</span>
|
||||
</n-popover>
|
||||
<n-checkbox value="del" label="删除" />
|
||||
<n-checkbox value="view" label="详情页" />
|
||||
<n-checkbox value="check" label="开启勾选列" />
|
||||
<n-checkbox value="switch" label="操作开关" />
|
||||
<n-popover trigger="hover">
|
||||
<template #trigger>
|
||||
<n-icon size="15" class="tips-help-icon" color="#2d8cf0">
|
||||
<QuestionCircleOutlined />
|
||||
</n-icon>
|
||||
</template>
|
||||
<span>主表中存在`switch`字段时才会生效</span>
|
||||
</n-popover>
|
||||
</n-space>
|
||||
</n-checkbox-group>
|
||||
</n-form-item>
|
||||
</n-col>
|
||||
|
||||
<n-col :span="24">
|
||||
<n-form-item
|
||||
label="自动化操作"
|
||||
path="autoOps"
|
||||
v-show="formValue.genType >= 10 && formValue.genType < 20"
|
||||
>
|
||||
<n-checkbox-group v-model:value="formValue.options.autoOps">
|
||||
<n-space item-style="display: flex;">
|
||||
<n-checkbox value="genMenuPermissions" label="生成菜单权限" />
|
||||
<n-checkbox value="runDao" label="生成前运行 [gf gen dao]" />
|
||||
<n-popover trigger="hover">
|
||||
<template #trigger>
|
||||
<n-icon size="15" class="tips-help-icon" color="#2d8cf0">
|
||||
<QuestionCircleOutlined />
|
||||
</n-icon>
|
||||
</template>
|
||||
<span>请确保运行环境已安装gf命令</span>
|
||||
</n-popover>
|
||||
<n-checkbox value="runService" label="生成后运行 [gf gen service]" />
|
||||
<n-popover trigger="hover">
|
||||
<template #trigger>
|
||||
<n-icon size="15" class="tips-help-icon" color="#2d8cf0">
|
||||
<QuestionCircleOutlined />
|
||||
</n-icon>
|
||||
</template>
|
||||
<span>请确保运行环境已安装gf命令</span>
|
||||
</n-popover>
|
||||
<n-checkbox value="forcedCover" label="强制覆盖" />
|
||||
<n-popover trigger="hover">
|
||||
<template #trigger>
|
||||
<n-icon size="15" class="tips-help-icon" color="#2d8cf0">
|
||||
<QuestionCircleOutlined />
|
||||
</n-icon>
|
||||
</template>
|
||||
<span>只会强制覆盖需要生成的文件,但不包含SQL文件</span>
|
||||
</n-popover>
|
||||
</n-space>
|
||||
</n-checkbox-group>
|
||||
</n-form-item>
|
||||
</n-col>
|
||||
|
||||
<n-col
|
||||
:span="6"
|
||||
style="min-width: 200px"
|
||||
v-show="formValue.options?.autoOps?.includes('genMenuPermissions')"
|
||||
>
|
||||
<n-form-item label="上级菜单" path="pid">
|
||||
<n-tree-select
|
||||
:options="optionMenuTree"
|
||||
:value="formValue.options.menu.pid"
|
||||
@update:value="handleUpdateMenuPid"
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-col>
|
||||
|
||||
<n-col
|
||||
:span="6"
|
||||
style="min-width: 200px"
|
||||
v-show="formValue.options?.autoOps?.includes('genMenuPermissions')"
|
||||
>
|
||||
<n-form-item label="菜单名称" path="tableComment">
|
||||
<n-input placeholder="请输入" v-model:value="formValue.tableComment" />
|
||||
</n-form-item>
|
||||
</n-col>
|
||||
|
||||
<n-col
|
||||
:span="6"
|
||||
style="min-width: 200px"
|
||||
v-show="formValue.options?.autoOps?.includes('genMenuPermissions')"
|
||||
>
|
||||
<n-form-item label="菜单图标" path="menuIcon">
|
||||
<IconSelector style="width: 100%" v-model:value="formValue.options.menu.icon" />
|
||||
</n-form-item>
|
||||
</n-col>
|
||||
|
||||
<n-col
|
||||
:span="6"
|
||||
style="min-width: 200px"
|
||||
v-show="formValue.options?.autoOps?.includes('genMenuPermissions')"
|
||||
>
|
||||
<n-form-item label="菜单排序" path="menuIcon">
|
||||
<n-input-number
|
||||
style="width: 100%"
|
||||
placeholder="请输入"
|
||||
v-model:value="formValue.options.menu.sort"
|
||||
clearable
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-col>
|
||||
</n-row>
|
||||
</n-form>
|
||||
</n-card>
|
||||
|
||||
<n-card
|
||||
:bordered="true"
|
||||
title="关联表设置"
|
||||
class="proCard mt-2"
|
||||
size="small"
|
||||
:segmented="{ content: true }"
|
||||
v-show="formValue.genType >= 10 && formValue.genType < 20"
|
||||
>
|
||||
<template #header-extra>
|
||||
<n-space>
|
||||
<n-button type="warning" @click="addJoin" :disabled="formValue.options?.join?.length >= 3"
|
||||
>新增关联表</n-button
|
||||
>
|
||||
</n-space>
|
||||
</template>
|
||||
|
||||
<n-form ref="formRef" :model="formValue">
|
||||
<n-alert :show-icon="false">关联表数量建议在三个以下</n-alert>
|
||||
|
||||
<n-row :gutter="6" v-for="(join, index) in formValue.options.join" :key="index">
|
||||
<n-col :span="6" style="min-width: 200px">
|
||||
<n-form-item label="关联表" path="join.linkTable">
|
||||
<n-select
|
||||
filterable
|
||||
tag
|
||||
:loading="tablesLoading"
|
||||
placeholder="请选择"
|
||||
:options="linkTablesOption"
|
||||
v-model:value="join.linkTable"
|
||||
@update:value="handleLinkTableUpdateValue(join)"
|
||||
:disabled="formValue.dbName === ''"
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-col>
|
||||
|
||||
<n-col :span="3" style="min-width: 100px">
|
||||
<n-form-item
|
||||
label="别名"
|
||||
path="join.alias"
|
||||
v-show="formValue.genType >= 10 && formValue.genType < 20"
|
||||
>
|
||||
<n-input
|
||||
placeholder="请输入"
|
||||
v-model:value="join.alias"
|
||||
@update:value="updateJoinAlias"
|
||||
/>
|
||||
|
||||
<template #feedback> {{ joinAliasFeedback }}</template>
|
||||
</n-form-item>
|
||||
</n-col>
|
||||
|
||||
<n-col :span="3" style="min-width: 100px">
|
||||
<n-form-item label="关联方式" path="join.linkMode">
|
||||
<n-select
|
||||
placeholder="请选择"
|
||||
:options="selectList.linkMode"
|
||||
v-model:value="join.linkMode"
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-col>
|
||||
<n-col :span="5" style="min-width: 180px">
|
||||
<n-form-item label="关联字段" path="join.field">
|
||||
<n-select
|
||||
filterable
|
||||
tag
|
||||
:loading="linkColumnsLoading"
|
||||
placeholder="请选择"
|
||||
:options="linkColumnsOption[join.uuid]"
|
||||
v-model:value="join.field"
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-col>
|
||||
|
||||
<n-col :span="5" style="min-width: 180px">
|
||||
<n-form-item label="主表关联字段" path="join.masterField">
|
||||
<n-select
|
||||
filterable
|
||||
tag
|
||||
:loading="columnsLoading"
|
||||
placeholder="请选择"
|
||||
:options="columnsOption"
|
||||
v-model:value="join.masterField"
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-col>
|
||||
|
||||
<n-col :span="2" style="min-width: 50px">
|
||||
<n-space>
|
||||
<n-form-item label="操作" path="title">
|
||||
<n-button @click="delJoin(join, index)" size="small" strong secondary type="error"
|
||||
>移除</n-button
|
||||
>
|
||||
</n-form-item>
|
||||
</n-space>
|
||||
</n-col>
|
||||
</n-row>
|
||||
</n-form>
|
||||
</n-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref, computed, watch } from 'vue';
|
||||
import { FormInst } from 'naive-ui';
|
||||
import { newState, selectListObj } from './model';
|
||||
import { TableSelect, ColumnSelect } from '@/api/develop/code';
|
||||
import { getRandomString } from '@/utils/charset';
|
||||
import IconSelector from '@/components/IconSelector/index.vue';
|
||||
import { QuestionCircleOutlined } from '@vicons/antd';
|
||||
import { getMenuList } from '@/api/system/menu';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { isLetterBegin } from '@/utils/is';
|
||||
|
||||
const formRef = ref<FormInst | null>(null);
|
||||
const tablesLoading = ref(false);
|
||||
const columnsLoading = ref(false);
|
||||
const linkColumnsLoading = ref(false);
|
||||
const tablesOption = ref<any>([]); // 数据库表选项
|
||||
const columnsOption = ref<any>([]); // 主表字段选项
|
||||
const linkTablesOption = ref<any>([]); // 关联表选项
|
||||
const linkColumnsOption = ref<any>([]); // 关联表字段选项
|
||||
|
||||
const optionMenuTree = ref([
|
||||
{
|
||||
id: 0,
|
||||
key: 0,
|
||||
label: '根目录',
|
||||
pid: 0,
|
||||
title: '根目录',
|
||||
type: 1,
|
||||
},
|
||||
]);
|
||||
|
||||
const emit = defineEmits(['update:value']);
|
||||
|
||||
interface Props {
|
||||
value?: any;
|
||||
selectList: any;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
value: newState(null),
|
||||
selectList: selectListObj,
|
||||
});
|
||||
|
||||
watch(props, async (newVal, oldVal) => {
|
||||
if (newVal.value.dbName != oldVal.value.dbName) {
|
||||
await instLoad();
|
||||
}
|
||||
});
|
||||
|
||||
const formValue = computed({
|
||||
get() {
|
||||
return props.value;
|
||||
},
|
||||
set(value) {
|
||||
emit('update:value', value);
|
||||
},
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
setTimeout(async function () {
|
||||
await instLoad();
|
||||
// 切换tab时会导致选项被清空,这里重新进行加载
|
||||
await loadLinkColumnsOption();
|
||||
await loadMenuTreeOption();
|
||||
}, 500);
|
||||
});
|
||||
|
||||
const loadMenuTreeOption = async () => {
|
||||
const options = await getMenuList();
|
||||
optionMenuTree.value = [
|
||||
{
|
||||
id: 0,
|
||||
key: 0,
|
||||
label: '根目录',
|
||||
pid: 0,
|
||||
title: '根目录',
|
||||
type: 1,
|
||||
},
|
||||
];
|
||||
optionMenuTree.value = optionMenuTree.value.concat(options.list);
|
||||
};
|
||||
|
||||
const loadSelect = async () => {
|
||||
columnsOption.value = await loadColumnSelect(formValue.value.tableName);
|
||||
};
|
||||
|
||||
async function instLoad() {
|
||||
columnsLoading.value = true;
|
||||
tablesLoading.value = true;
|
||||
await loadSelect();
|
||||
await loadTableSelect(formValue.value.dbName);
|
||||
tablesLoading.value = false;
|
||||
columnsLoading.value = false;
|
||||
}
|
||||
|
||||
async function loadLinkColumnsOption() {
|
||||
if (formValue.value.options.join === undefined) {
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < formValue.value.options.join.length; i++) {
|
||||
linkColumnsLoading.value = true;
|
||||
linkColumnsOption.value[formValue.value.options.join[i].uuid] = await loadColumnSelect(
|
||||
formValue.value.options.join[i].linkTable
|
||||
);
|
||||
linkColumnsLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 处理选项更新
|
||||
async function handleDbUpdateValue(value, _option) {
|
||||
tablesLoading.value = true;
|
||||
await loadTableSelect(value);
|
||||
tablesLoading.value = false;
|
||||
}
|
||||
|
||||
async function loadTableSelect(value) {
|
||||
const options = await TableSelect({ name: value });
|
||||
tablesOption.value = cloneDeep(options);
|
||||
linkTablesOption.value = cloneDeep(options);
|
||||
}
|
||||
|
||||
async function loadColumnSelect(value) {
|
||||
return await ColumnSelect({ name: formValue.value.dbName, table: value });
|
||||
}
|
||||
|
||||
function handleTableUpdateValue(value, option) {
|
||||
formValue.value.varName = option?.defVarName as string;
|
||||
formValue.value.daoName = option?.daoName as string;
|
||||
formValue.value.tableComment = option?.defTableComment as string;
|
||||
}
|
||||
|
||||
function addJoin() {
|
||||
if (formValue.value.options.join === undefined) {
|
||||
formValue.value.options.join = [];
|
||||
}
|
||||
let uuid = getRandomString(16, true);
|
||||
formValue.value.options.join.push({
|
||||
uuid: uuid,
|
||||
linkTable: '',
|
||||
alias: '',
|
||||
linkMode: 1,
|
||||
field: '',
|
||||
masterField: '',
|
||||
daoName: '',
|
||||
columns: [],
|
||||
});
|
||||
linkColumnsOption.value[uuid] = [];
|
||||
}
|
||||
|
||||
function delJoin(join, index) {
|
||||
formValue.value.options.join.splice(index, 1);
|
||||
delete linkColumnsOption.value[join.uuid];
|
||||
let i = linkTablesOption.value.findIndex((res) => res.value === join.linkTable);
|
||||
if (i > -1) {
|
||||
linkTablesOption.value[i].disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function handleLinkTableUpdateValue(join) {
|
||||
let i = linkTablesOption.value.findIndex((res) => res.value === join.linkTable);
|
||||
if (i > -1) {
|
||||
join.alias = linkTablesOption.value[i].defAlias;
|
||||
join.daoName = linkTablesOption.value[i].daoName;
|
||||
linkTablesOption.value[i].disabled = true;
|
||||
}
|
||||
|
||||
linkColumnsLoading.value = true;
|
||||
linkColumnsOption.value[join.uuid] = await loadColumnSelect(join.linkTable);
|
||||
// 清空更新前的字段
|
||||
join.field = '';
|
||||
linkColumnsLoading.value = false;
|
||||
}
|
||||
|
||||
const joinAliasFeedback = ref('');
|
||||
function updateJoinAlias(value: string) {
|
||||
if (value.length < 3) {
|
||||
joinAliasFeedback.value = '别名不能小于3位';
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isLetterBegin(value)) {
|
||||
joinAliasFeedback.value = '别名必须以字母开头';
|
||||
return;
|
||||
}
|
||||
joinAliasFeedback.value = '';
|
||||
}
|
||||
|
||||
function handleUpdateMenuPid(value: string | number | Array<string | number> | null) {
|
||||
formValue.value.options.menu.pid = value;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
::v-deep(.default_text_value) {
|
||||
color: var(--n-tab-text-color-active);
|
||||
}
|
||||
::v-deep(.tips-help-icon) {
|
||||
margin-left: -16px;
|
||||
margin-top: 5px;
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
357
web/src/views/develop/code/components/EditMasterCell.vue
Normal file
357
web/src/views/develop/code/components/EditMasterCell.vue
Normal file
@@ -0,0 +1,357 @@
|
||||
<template>
|
||||
<n-spin :show="show" description="加载中...">
|
||||
<n-card :bordered="false" class="proCard">
|
||||
<BasicTable
|
||||
:single-line="false"
|
||||
size="small"
|
||||
:striped="true"
|
||||
:resizable="true"
|
||||
:columns="columns"
|
||||
:dataSource="dataSource"
|
||||
:openChecked="false"
|
||||
:showTopRight="false"
|
||||
:row-key="(row) => row.id"
|
||||
ref="actionRef"
|
||||
:canResize="true"
|
||||
:resizeHeightOffset="-20000"
|
||||
:pagination="false"
|
||||
:scroll-x="1090"
|
||||
:scrollbar-props="{ trigger: 'none' }"
|
||||
/>
|
||||
</n-card>
|
||||
</n-spin>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, h, onMounted, ref } from 'vue';
|
||||
import { BasicTable } from '@/components/Table';
|
||||
import { genInfoObj, selectListObj } from '@/views/develop/code/components/model';
|
||||
import { ColumnList } from '@/api/develop/code';
|
||||
import { NButton, NCheckbox, NInput, NSelect, NTooltip, NTreeSelect } from 'naive-ui';
|
||||
import { HelpCircleOutline } from '@vicons/ionicons5';
|
||||
import { renderIcon } from '@/utils';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
|
||||
const renderTooltip = (trigger, content) => {
|
||||
return h(NTooltip, null, {
|
||||
trigger: () => trigger,
|
||||
default: () => content,
|
||||
});
|
||||
};
|
||||
|
||||
const emit = defineEmits(['update:value']);
|
||||
|
||||
interface Props {
|
||||
value?: any;
|
||||
selectList: any;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
value: genInfoObj,
|
||||
selectList: selectListObj,
|
||||
});
|
||||
|
||||
const formValue = computed({
|
||||
get() {
|
||||
return props.value;
|
||||
},
|
||||
set(value) {
|
||||
emit('update:value', value);
|
||||
},
|
||||
});
|
||||
|
||||
const actionRef = ref();
|
||||
const columns = ref<any>([]);
|
||||
const show = ref(false);
|
||||
const dataSource = ref(formValue.value.masterColumns);
|
||||
onMounted(async () => {
|
||||
show.value = true;
|
||||
if (formValue.value.masterColumns.length === 0) {
|
||||
formValue.value.masterColumns = await ColumnList({
|
||||
name: formValue.value.dbName,
|
||||
table: formValue.value.tableName,
|
||||
});
|
||||
dataSource.value = formValue.value.masterColumns;
|
||||
}
|
||||
|
||||
columns.value = [
|
||||
{
|
||||
title: '位置',
|
||||
key: 'id',
|
||||
width: 50,
|
||||
},
|
||||
{
|
||||
title(_column) {
|
||||
return renderTooltip(
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
ghost: true,
|
||||
strong: true,
|
||||
size: 'small',
|
||||
text: true,
|
||||
iconPlacement: 'right',
|
||||
},
|
||||
{ default: () => '字段', icon: renderIcon(HelpCircleOutline) }
|
||||
),
|
||||
'Go类型和属性定义取决于你在/hack/config.yaml中的配置参数'
|
||||
);
|
||||
},
|
||||
key: 'field',
|
||||
align: 'center',
|
||||
width: 800,
|
||||
children: [
|
||||
{
|
||||
title: '字段列名',
|
||||
key: 'name',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: '物理类型',
|
||||
key: 'sqlType',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: 'Go属性',
|
||||
key: 'goName',
|
||||
width: 130,
|
||||
},
|
||||
{
|
||||
title: 'Go类型',
|
||||
key: 'goType',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: 'Ts属性',
|
||||
key: 'tsName',
|
||||
width: 130,
|
||||
},
|
||||
{
|
||||
title: 'Ts类型',
|
||||
key: 'tsType',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '字段描述',
|
||||
key: 'dc',
|
||||
width: 150,
|
||||
render(row) {
|
||||
return h(NInput, {
|
||||
value: row.dc,
|
||||
onUpdateValue: function (e) {
|
||||
row.dc = e;
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
width: 800,
|
||||
title(_column) {
|
||||
return renderTooltip(
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
ghost: true,
|
||||
strong: true,
|
||||
size: 'small',
|
||||
text: true,
|
||||
iconPlacement: 'right',
|
||||
},
|
||||
{ default: () => '新增/编辑表单', icon: renderIcon(HelpCircleOutline) }
|
||||
),
|
||||
'勾选编辑以后会在新增、编辑表单中显示该字段;当同时勾选列表查询时,会优先使用配置的表单组件'
|
||||
);
|
||||
},
|
||||
key: 'edit',
|
||||
align: 'center',
|
||||
children: [
|
||||
{
|
||||
align: 'center',
|
||||
title: '编辑',
|
||||
key: 'isEdit',
|
||||
width: 50,
|
||||
render(row) {
|
||||
return h(NCheckbox, {
|
||||
defaultChecked: row.isEdit,
|
||||
disabled: row.name === 'id',
|
||||
onUpdateChecked: function (e) {
|
||||
row.isEdit = e;
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '必填',
|
||||
key: 'required',
|
||||
width: 50,
|
||||
align: 'center',
|
||||
render(row) {
|
||||
return h(NCheckbox, {
|
||||
defaultChecked: row.required,
|
||||
disabled: row.name === 'id',
|
||||
onUpdateChecked: function (e) {
|
||||
row.required = e;
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '唯一',
|
||||
key: 'unique',
|
||||
width: 50,
|
||||
align: 'center',
|
||||
render(row) {
|
||||
return h(NCheckbox, {
|
||||
defaultChecked: row.unique,
|
||||
disabled: row.name === 'id',
|
||||
onUpdateChecked: function (e) {
|
||||
row.unique = e;
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '表单组件',
|
||||
key: 'formMode',
|
||||
width: 200,
|
||||
render(row) {
|
||||
return h(NSelect, {
|
||||
value: row.formMode,
|
||||
options: getFormModeOptions(row.tsType),
|
||||
// render: function (row) {
|
||||
// return props.selectList?.formMode ?? [];
|
||||
// },
|
||||
// onFocus: function (e) {
|
||||
// console.log('表单组件 onFocus row:', e);
|
||||
// },
|
||||
onUpdateValue: function (e) {
|
||||
row.formMode = e;
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '表单验证',
|
||||
key: 'formRole',
|
||||
width: 200,
|
||||
render(row) {
|
||||
return h(NSelect, {
|
||||
value: row.formRole,
|
||||
disabled: row.name === 'id',
|
||||
options: props.selectList?.formRole ?? [],
|
||||
onUpdateValue: function (e) {
|
||||
row.formRole = e;
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '字典类型',
|
||||
key: 'dictType',
|
||||
width: 300,
|
||||
render(row) {
|
||||
return h(NTreeSelect, {
|
||||
value: row.dictType,
|
||||
disabled: row.name === 'id',
|
||||
options: props.selectList?.dictMode ?? [],
|
||||
onUpdateValue: function (e) {
|
||||
row.dictType = e;
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
width: 800,
|
||||
title: '列表',
|
||||
key: 'list',
|
||||
align: 'center',
|
||||
children: [
|
||||
{
|
||||
title: '列表',
|
||||
key: 'isList',
|
||||
width: 50,
|
||||
align: 'center',
|
||||
render(row) {
|
||||
return h(NCheckbox, {
|
||||
defaultChecked: row.isList,
|
||||
onUpdateChecked: function (e) {
|
||||
row.isList = e;
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '导出',
|
||||
key: 'isExport',
|
||||
width: 50,
|
||||
align: 'center',
|
||||
render(row) {
|
||||
return h(NCheckbox, {
|
||||
defaultChecked: row.isExport,
|
||||
onUpdateChecked: function (e) {
|
||||
row.isExport = e;
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '查询',
|
||||
key: 'isQuery',
|
||||
width: 50,
|
||||
align: 'center',
|
||||
render(row) {
|
||||
return h(NCheckbox, {
|
||||
defaultChecked: row.isQuery,
|
||||
onUpdateChecked: function (e) {
|
||||
row.isQuery = e;
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '查询条件',
|
||||
key: 'queryWhere',
|
||||
width: 300,
|
||||
render(row) {
|
||||
return h(NSelect, {
|
||||
value: row.queryWhere,
|
||||
disabled: row.name === 'id',
|
||||
options: props.selectList?.whereMode ?? [],
|
||||
onUpdateValue: function (e) {
|
||||
row.queryWhere = e;
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
show.value = false;
|
||||
});
|
||||
|
||||
function getFormModeOptions(type: string) {
|
||||
const options = cloneDeep(props.selectList?.formMode ?? []);
|
||||
if (options.length === 0) {
|
||||
return [];
|
||||
}
|
||||
switch (type) {
|
||||
case 'number':
|
||||
for (let i = 0; i < options.length; i++) {
|
||||
const allows = ['InputNumber', 'Radio', 'Select', 'Switch', 'Rate'];
|
||||
if (!allows.includes(options[i].value)) {
|
||||
options[i].disabled = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
return options;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
243
web/src/views/develop/code/components/EditSlaveCell.vue
Normal file
243
web/src/views/develop/code/components/EditSlaveCell.vue
Normal file
@@ -0,0 +1,243 @@
|
||||
<template>
|
||||
<n-spin :show="show" description="加载中...">
|
||||
<n-card :bordered="false" class="proCard">
|
||||
<BasicTable
|
||||
:single-line="false"
|
||||
size="small"
|
||||
:striped="true"
|
||||
:resizable="true"
|
||||
:columns="columns"
|
||||
:dataSource="dataSource"
|
||||
:openChecked="false"
|
||||
:showTopRight="false"
|
||||
:row-key="(row) => row.id"
|
||||
ref="actionRef"
|
||||
:canResize="true"
|
||||
:resizeHeightOffset="-20000"
|
||||
:pagination="false"
|
||||
:scroll-x="1090"
|
||||
:scrollbar-props="{ trigger: 'none' }"
|
||||
/>
|
||||
</n-card>
|
||||
</n-spin>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { Component, computed, h, onMounted, ref } from 'vue';
|
||||
import { BasicTable } from '@/components/Table';
|
||||
import { genInfoObj, selectListObj } from '@/views/develop/code/components/model';
|
||||
import { ColumnList } from '@/api/develop/code';
|
||||
import { NButton, NCheckbox, NIcon, NInput, NSelect, NTooltip } from 'naive-ui';
|
||||
import { HelpCircleOutline } from '@vicons/ionicons5';
|
||||
|
||||
const renderTooltip = (trigger, content) => {
|
||||
return h(NTooltip, null, {
|
||||
trigger: () => trigger,
|
||||
default: () => content,
|
||||
});
|
||||
};
|
||||
function renderIcon(icon: Component) {
|
||||
return () => h(NIcon, null, { default: () => h(icon) });
|
||||
}
|
||||
|
||||
const emit = defineEmits(['update:value']);
|
||||
|
||||
interface Props {
|
||||
value?: any;
|
||||
selectList: any;
|
||||
uuid: string;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
value: genInfoObj,
|
||||
selectList: selectListObj,
|
||||
uuid: '',
|
||||
});
|
||||
|
||||
const columns = ref<any>([]);
|
||||
|
||||
const formValue = computed({
|
||||
get() {
|
||||
return props.value;
|
||||
},
|
||||
set(value) {
|
||||
emit('update:value', value);
|
||||
},
|
||||
});
|
||||
|
||||
function getIndex() {
|
||||
if (formValue.value.options.join.length === 0) {
|
||||
return -1;
|
||||
}
|
||||
for (let i = 0; i < formValue.value.options.join.length; i++) {
|
||||
if (formValue.value.options.join[i].uuid === props.uuid) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
const show = ref(false);
|
||||
const dataSource = ref([]);
|
||||
onMounted(async () => {
|
||||
show.value = true;
|
||||
setTimeout(async () => {
|
||||
const index = getIndex();
|
||||
if (formValue.value.options.join[index].columns.length === 0) {
|
||||
formValue.value.options.join[index].columns = await ColumnList({
|
||||
name: formValue.value.dbName,
|
||||
table: formValue.value.options.join[index].linkTable,
|
||||
isLink: 1,
|
||||
alias: formValue.value.options.join[index].alias,
|
||||
});
|
||||
}
|
||||
|
||||
dataSource.value = formValue.value.options.join[index].columns;
|
||||
|
||||
columns.value = [
|
||||
{
|
||||
title: '位置',
|
||||
key: 'id',
|
||||
width: 50,
|
||||
},
|
||||
{
|
||||
title(_column) {
|
||||
return renderTooltip(
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
ghost: true,
|
||||
strong: true,
|
||||
size: 'small',
|
||||
text: true,
|
||||
iconPlacement: 'right',
|
||||
},
|
||||
{ default: () => '字段', icon: renderIcon(HelpCircleOutline) }
|
||||
),
|
||||
'Go类型和属性定义取决于你在/hack/config.yaml中的配置参数'
|
||||
);
|
||||
},
|
||||
key: 'field',
|
||||
align: 'center',
|
||||
width: 800,
|
||||
children: [
|
||||
{
|
||||
title: '字段列名',
|
||||
key: 'name',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: '物理类型',
|
||||
key: 'sqlType',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: 'Go属性',
|
||||
key: 'goName',
|
||||
width: 260,
|
||||
},
|
||||
{
|
||||
title: 'Go类型',
|
||||
key: 'goType',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: 'Ts属性',
|
||||
key: 'tsName',
|
||||
width: 260,
|
||||
},
|
||||
{
|
||||
title: 'Ts类型',
|
||||
key: 'tsType',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '字段描述',
|
||||
key: 'dc',
|
||||
width: 150,
|
||||
render(row) {
|
||||
return h(NInput, {
|
||||
value: row.dc,
|
||||
onUpdateValue: function (e) {
|
||||
row.dc = e;
|
||||
// await saveProductCustom(row.id, 'frontShow', e);
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
width: 800,
|
||||
title: '列表',
|
||||
key: 'list',
|
||||
align: 'center',
|
||||
children: [
|
||||
{
|
||||
title: '列表',
|
||||
key: 'isList',
|
||||
width: 50,
|
||||
align: 'center',
|
||||
render(row) {
|
||||
return h(NCheckbox, {
|
||||
defaultChecked: row.isList,
|
||||
onUpdateChecked: function (e) {
|
||||
row.isList = e;
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '导出',
|
||||
key: 'isExport',
|
||||
width: 50,
|
||||
align: 'center',
|
||||
render(row) {
|
||||
return h(NCheckbox, {
|
||||
defaultChecked: row.isExport,
|
||||
onUpdateChecked: function (e) {
|
||||
row.isExport = e;
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '查询',
|
||||
key: 'isQuery',
|
||||
width: 50,
|
||||
align: 'center',
|
||||
render(row) {
|
||||
return h(NCheckbox, {
|
||||
defaultChecked: row.isQuery,
|
||||
onUpdateChecked: function (e) {
|
||||
row.isQuery = e;
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '查询条件',
|
||||
key: 'queryWhere',
|
||||
width: 300,
|
||||
render(row) {
|
||||
return h(NSelect, {
|
||||
value: row.queryWhere,
|
||||
disabled: row.name === 'id',
|
||||
options: props.selectList?.whereMode ?? [],
|
||||
onUpdateValue: function (e) {
|
||||
row.queryWhere = e;
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
show.value = false;
|
||||
}, 50);
|
||||
});
|
||||
|
||||
const actionRef = ref();
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
84
web/src/views/develop/code/components/PreviewTab.vue
Normal file
84
web/src/views/develop/code/components/PreviewTab.vue
Normal file
@@ -0,0 +1,84 @@
|
||||
<template>
|
||||
<div>
|
||||
<n-tabs type="line" animated>
|
||||
<n-tab-pane v-for="(view, index) in views" :key="index" :name="view.name" :tab="view.name">
|
||||
<n-tag :type="view.tag.type" class="tag-margin">
|
||||
{{ view.tag.label }}
|
||||
<template #icon>
|
||||
<n-icon :component="view.tag.icon" />
|
||||
</template>
|
||||
{{ view.path }}
|
||||
</n-tag>
|
||||
<n-scrollbar class="code-scrollbar" trigger="none">
|
||||
<n-code :code="view.content" />
|
||||
</n-scrollbar>
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import {
|
||||
CheckmarkCircle,
|
||||
CheckmarkDoneCircle,
|
||||
CloseCircleOutline,
|
||||
HelpCircleOutline,
|
||||
RemoveCircleOutline,
|
||||
} from '@vicons/ionicons5';
|
||||
|
||||
interface Props {
|
||||
previewModel: any;
|
||||
showModal: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
previewModel: cloneDeep({ views: {} }),
|
||||
showModal: false,
|
||||
});
|
||||
|
||||
const views = computed(() => {
|
||||
let tmpViews: any = [];
|
||||
let i = 0;
|
||||
for (const [k, v] of Object.entries(props.previewModel.views)) {
|
||||
let item = v as any;
|
||||
item.name = k;
|
||||
switch (item.meth) {
|
||||
case 1:
|
||||
item.tag = { type: 'success', label: '创建文件', icon: CheckmarkCircle };
|
||||
break;
|
||||
case 2:
|
||||
item.tag = { type: 'warning', label: '覆盖文件', icon: CheckmarkDoneCircle };
|
||||
break;
|
||||
case 3:
|
||||
item.tag = { type: 'info', label: '已存在跳过', icon: CloseCircleOutline };
|
||||
break;
|
||||
case 4:
|
||||
item.tag = { type: 'error', label: '不生成', icon: RemoveCircleOutline };
|
||||
break;
|
||||
default:
|
||||
item.tag = { type: 'error', label: '未知状态', icon: HelpCircleOutline };
|
||||
}
|
||||
tmpViews[i] = item;
|
||||
i++;
|
||||
}
|
||||
return tmpViews;
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
::v-deep(.alert-margin) {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
::v-deep(.tag-margin) {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
::v-deep(.code-scrollbar) {
|
||||
height: calc(100vh - 300px);
|
||||
background: #282b2e;
|
||||
color: #e0e2e4;
|
||||
padding: 10px;
|
||||
}
|
||||
</style>
|
||||
62
web/src/views/develop/code/components/model.ts
Normal file
62
web/src/views/develop/code/components/model.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
|
||||
export const genFileObj = {
|
||||
meth: 1,
|
||||
content: '',
|
||||
path: '',
|
||||
required: true,
|
||||
};
|
||||
|
||||
export interface joinAttr {
|
||||
uuid: string;
|
||||
linkTable: string;
|
||||
alias: string;
|
||||
linkMode: number;
|
||||
field: string;
|
||||
masterField: string;
|
||||
columns: any;
|
||||
}
|
||||
|
||||
export const genInfoObj = {
|
||||
id: 0,
|
||||
genType: 10,
|
||||
varName: '',
|
||||
options: {
|
||||
headOps: ['add', 'batchDel', 'export'],
|
||||
columnOps: ['edit', 'del', 'view', 'status', 'switch', 'check'],
|
||||
autoOps: ['genMenuPermissions', 'runDao', 'runService'],
|
||||
join: [],
|
||||
menu: {
|
||||
pid: 0,
|
||||
icon: 'MenuOutlined',
|
||||
sort: 0,
|
||||
},
|
||||
},
|
||||
dbName: '',
|
||||
tableName: '',
|
||||
tableComment: '',
|
||||
daoName: '',
|
||||
masterColumns: [],
|
||||
status: 2,
|
||||
createdAt: '',
|
||||
updatedAt: '',
|
||||
};
|
||||
|
||||
export const selectListObj = {
|
||||
db: [],
|
||||
genType: [],
|
||||
status: [],
|
||||
tables: [],
|
||||
formMode: [],
|
||||
formRole: [],
|
||||
dictMode: [],
|
||||
whereMode: [],
|
||||
buildMeth: [],
|
||||
};
|
||||
|
||||
export function newState(state) {
|
||||
if (state !== null) {
|
||||
return cloneDeep(state);
|
||||
}
|
||||
return cloneDeep(genInfoObj);
|
||||
}
|
||||
264
web/src/views/develop/code/deploy.vue
Normal file
264
web/src/views/develop/code/deploy.vue
Normal file
@@ -0,0 +1,264 @@
|
||||
<template>
|
||||
<div>
|
||||
<n-spin :show="show" description="正在生成配置信息...">
|
||||
<n-card>
|
||||
<n-tabs
|
||||
type="card"
|
||||
class="card-tabs"
|
||||
:default-value="value"
|
||||
animated
|
||||
tab-style="min-width: 80px;"
|
||||
style="margin: 0 -4px"
|
||||
pane-style="padding-left: 4px; padding-right: 4px; box-sizing: border-box;"
|
||||
:on-update:value="updateTabs"
|
||||
ref="tabsRef"
|
||||
@close="handleClose"
|
||||
@add="handleAdd"
|
||||
>
|
||||
<n-tab-pane v-for="panel in panels" :key="panel" :name="panel">
|
||||
<template v-if="panel === '基本信息'">
|
||||
<BaseInfo v-model:value="genInfo" :selectList="selectList" />
|
||||
</template>
|
||||
|
||||
<template v-if="panel === '主表字段'">
|
||||
<EditMasterCell v-model:value="genInfo" :selectList="selectList" />
|
||||
</template>
|
||||
</n-tab-pane>
|
||||
|
||||
<n-tab-pane
|
||||
v-for="panel in slavePanels"
|
||||
:key="panel"
|
||||
:name="panel"
|
||||
v-show="slavePanels.length > 0 && slavePanels !== []"
|
||||
>
|
||||
<EditSlaveCell
|
||||
v-model:value="genInfo"
|
||||
:uuid="slaveMap[panel]"
|
||||
:selectList="selectList"
|
||||
/>
|
||||
</n-tab-pane>
|
||||
|
||||
<template #suffix>
|
||||
<n-space>
|
||||
<n-button type="primary" @click="preview">预览代码</n-button>
|
||||
<n-button type="success" :loading="formBtnLoading" @click="submitBuild"
|
||||
>提交生成</n-button
|
||||
>
|
||||
<n-button type="info" dashed :loading="formBtnLoading" @click="submitSave"
|
||||
>仅保存配置</n-button
|
||||
>
|
||||
</n-space>
|
||||
</template>
|
||||
</n-tabs>
|
||||
|
||||
<n-modal
|
||||
v-model:show="showModal"
|
||||
:block-scroll="false"
|
||||
:mask-closable="false"
|
||||
:show-icon="false"
|
||||
preset="card"
|
||||
title="预览代码"
|
||||
style="width: 95%"
|
||||
>
|
||||
<PreviewTab :previewModel="previewModel" />
|
||||
<template #action>
|
||||
<n-space justify="end">
|
||||
<n-button @click="() => (showModal = false)">关闭</n-button>
|
||||
<n-button type="info" :loading="formBtnLoading" @click="submitBuild"
|
||||
>提交生成</n-button
|
||||
>
|
||||
</n-space>
|
||||
</template>
|
||||
</n-modal>
|
||||
</n-card>
|
||||
</n-spin>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.card-tabs .n-tabs-nav--bar-type {
|
||||
padding-left: 4px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref, watch } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useDialog, useMessage } from 'naive-ui';
|
||||
import BaseInfo from './components/BaseInfo.vue';
|
||||
import EditMasterCell from './components/EditMasterCell.vue';
|
||||
import EditSlaveCell from './components/EditSlaveCell.vue';
|
||||
import { Selects, View, Preview, Build, Edit } from '@/api/develop/code';
|
||||
import { selectListObj, newState } from '@/views/develop/code/components/model';
|
||||
import PreviewTab from '@/views/develop/code/components/PreviewTab.vue';
|
||||
import { isJsonString } from '@/utils/is';
|
||||
|
||||
interface Props {
|
||||
genId?: number;
|
||||
}
|
||||
const props = withDefaults(defineProps<Props>(), { genId: 0 });
|
||||
const router = useRouter();
|
||||
const genId = Number(router.currentRoute.value.params.id ?? props.genId);
|
||||
const show = ref(false);
|
||||
const message = useMessage();
|
||||
const selectList = ref<any>(selectListObj);
|
||||
const genInfo = ref(newState(null));
|
||||
const tabsRef = ref();
|
||||
const value = ref('基本信息');
|
||||
const panels = ref(['基本信息', '主表字段']);
|
||||
const slaveMap = ref<any>([]);
|
||||
const slavePanels = ref<any>([]);
|
||||
const showModal = ref(false);
|
||||
const formBtnLoading = ref(false);
|
||||
const previewModel = ref<any>();
|
||||
const dialog = useDialog();
|
||||
|
||||
onMounted(async () => {
|
||||
if (genId < 1 && props.genId < 1) {
|
||||
message.error('生成ID不正确,请检查!');
|
||||
return;
|
||||
}
|
||||
await getGenInfo();
|
||||
await loadSelect();
|
||||
});
|
||||
|
||||
async function getGenInfo() {
|
||||
let tmp = await View({ id: genId });
|
||||
if (isJsonString(tmp.options)) {
|
||||
tmp.options = JSON.parse(tmp.options);
|
||||
}
|
||||
|
||||
if (tmp.masterColumns === undefined || tmp.masterColumns.length === 0) {
|
||||
tmp.masterColumns = [];
|
||||
}
|
||||
if (isJsonString(tmp.masterColumns)) {
|
||||
tmp.masterColumns = JSON.parse(tmp.masterColumns);
|
||||
}
|
||||
|
||||
genInfo.value = tmp;
|
||||
}
|
||||
|
||||
watch(
|
||||
genInfo,
|
||||
(newVal, _oldVal) => {
|
||||
if (newVal.genType >= 10 && newVal.genType < 20) {
|
||||
handleAdd('主表字段');
|
||||
} else {
|
||||
handleClose('主表字段');
|
||||
}
|
||||
|
||||
if (newVal.options.join !== undefined) {
|
||||
slavePanels.value = [];
|
||||
for (let i = 0; i <= newVal.options.join.length; i++) {
|
||||
if (newVal.options.join[i]?.alias !== undefined && newVal.options.join[i]?.alias !== '') {
|
||||
handleSlaveAdd(
|
||||
'关联表[ ' + newVal.options.join[i]?.alias + ' ]',
|
||||
newVal.options.join[i]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
deep: true, // 是否深度监听
|
||||
}
|
||||
);
|
||||
|
||||
function updateTabs(value: string | number) {
|
||||
console.log('value:' + value);
|
||||
}
|
||||
|
||||
function handleAdd(name: string) {
|
||||
const nameIndex = panels.value.findIndex((panelName) => panelName === name);
|
||||
if (!~nameIndex) {
|
||||
panels.value.push(name);
|
||||
}
|
||||
}
|
||||
|
||||
function handleSlaveAdd(name: string, join) {
|
||||
const nameIndex = slavePanels.value.findIndex((panelName) => panelName === name);
|
||||
if (!~nameIndex) {
|
||||
slavePanels.value.push(name);
|
||||
slaveMap.value[name] = join.uuid;
|
||||
}
|
||||
}
|
||||
|
||||
function _handleSlaveClose(name: string) {
|
||||
const nameIndex = panels.value.findIndex((panelName) => panelName === name);
|
||||
if (!~nameIndex) return;
|
||||
panels.value.splice(nameIndex, 1);
|
||||
if (name === value.value) {
|
||||
value.value = panels.value[Math.min(nameIndex, panels.value.length - 1)];
|
||||
}
|
||||
}
|
||||
|
||||
function handleClose(name: string) {
|
||||
const nameIndex = panels.value.findIndex((panelName) => panelName === name);
|
||||
if (!~nameIndex) return;
|
||||
panels.value.splice(nameIndex, 1);
|
||||
if (name === value.value) {
|
||||
value.value = panels.value[Math.min(nameIndex, panels.value.length - 1)];
|
||||
}
|
||||
}
|
||||
|
||||
const loadSelect = async () => {
|
||||
selectList.value = await Selects({});
|
||||
};
|
||||
|
||||
async function preview() {
|
||||
previewModel.value = await Preview(genInfo.value);
|
||||
showModal.value = true;
|
||||
}
|
||||
|
||||
function submitBuild() {
|
||||
dialog.warning({
|
||||
title: '警告',
|
||||
content: '你确定要提交生成吗?',
|
||||
positiveText: '确定',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: () => {
|
||||
Build(genInfo.value).then((_res) => {
|
||||
setTimeout(function () {
|
||||
location.reload();
|
||||
}, 1500);
|
||||
message.success('生成提交成功,即将刷新页面..');
|
||||
});
|
||||
},
|
||||
onNegativeClick: () => {
|
||||
// message.error('取消');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function submitSave() {
|
||||
dialog.warning({
|
||||
title: '警告',
|
||||
content: '你确定要保存生成配置吗?',
|
||||
positiveText: '确定',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: () => {
|
||||
Edit(genInfo.value).then((_res) => {
|
||||
message.success('操作成功');
|
||||
});
|
||||
},
|
||||
onNegativeClick: () => {
|
||||
// message.error('取消');
|
||||
},
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
::v-deep(.alert-margin) {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
::v-deep(.tag-margin) {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
::v-deep(.code-scrollbar) {
|
||||
height: calc(100vh - 300px);
|
||||
background: #282b2e;
|
||||
color: #e0e2e4;
|
||||
padding: 10px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,39 +1,452 @@
|
||||
<template>
|
||||
<div class="flex flex-col justify-center page-container">
|
||||
<div class="text-center">
|
||||
<h1 class="text-base">代码生成,敬请期待</h1>
|
||||
<n-button type="info" @click="goHome">回到首页</n-button>
|
||||
</div>
|
||||
</div>
|
||||
<n-card :bordered="false" class="proCard">
|
||||
<n-card :bordered="false" title="代码生成"> 你可以在这里查看到平台所有的短信发送记录。 </n-card>
|
||||
<BasicForm @register="register" @submit="handleSubmit" @reset="handleReset" ref="searchFormRef">
|
||||
<template #statusSlot="{ model, field }">
|
||||
<n-input v-model:value="model[field]" />
|
||||
</template>
|
||||
</BasicForm>
|
||||
|
||||
<BasicTable
|
||||
:columns="columns"
|
||||
:request="loadDataTable"
|
||||
:row-key="(row) => row.id"
|
||||
ref="actionRef"
|
||||
:actionColumn="actionColumn"
|
||||
@update:checked-row-keys="onCheckedRow"
|
||||
:scroll-x="1090"
|
||||
>
|
||||
<template #tableTitle>
|
||||
<n-button type="primary" @click="addTable">
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<PlusOutlined />
|
||||
</n-icon>
|
||||
</template>
|
||||
生成
|
||||
</n-button>
|
||||
|
||||
<n-button type="error" @click="batchDelete" :disabled="batchDeleteDisabled">
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<DeleteOutlined />
|
||||
</n-icon>
|
||||
</template>
|
||||
批量删除
|
||||
</n-button>
|
||||
</template>
|
||||
</BasicTable>
|
||||
|
||||
<n-modal
|
||||
v-model:show="showModal"
|
||||
:show-icon="false"
|
||||
preset="dialog"
|
||||
title="生成"
|
||||
:style="{
|
||||
width: dialogWidth,
|
||||
}"
|
||||
>
|
||||
<!-- <n-alert :show-icon="false" type="info">-->
|
||||
<!-- 注意:!-->
|
||||
<!-- </n-alert>-->
|
||||
<n-form
|
||||
:model="formParams"
|
||||
:rules="rules"
|
||||
ref="formRef"
|
||||
label-placement="left"
|
||||
:label-width="80"
|
||||
class="py-4"
|
||||
>
|
||||
<n-form-item label="生成类型" path="genType">
|
||||
<n-select
|
||||
placeholder="请选择"
|
||||
:options="selectList.genType"
|
||||
v-model:value="formParams.genType"
|
||||
/>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item
|
||||
label="数据库"
|
||||
path="dbName"
|
||||
v-if="formParams.genType >= 10 && formParams.genType < 20"
|
||||
>
|
||||
<n-select
|
||||
placeholder="请选择"
|
||||
:options="selectList.db"
|
||||
v-model:value="formParams.dbName"
|
||||
@update:value="handleDbUpdateValue"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item
|
||||
label="数据库表"
|
||||
path="tableName"
|
||||
v-if="formParams.genType >= 10 && formParams.genType < 20"
|
||||
>
|
||||
<n-select
|
||||
filterable
|
||||
tag
|
||||
:loading="tablesLoading"
|
||||
placeholder="请选择"
|
||||
:options="selectList.tables"
|
||||
v-model:value="formParams.tableName"
|
||||
@update:value="handleTableUpdateValue"
|
||||
:disabled="formParams.dbName === ''"
|
||||
/>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item
|
||||
label="菜单名称"
|
||||
path="tableComment"
|
||||
v-show="formParams.genType >= 10 && formParams.genType < 20"
|
||||
>
|
||||
<n-input placeholder="请输入" v-model:value="formParams.tableComment" />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="实体命名" path="varName">
|
||||
<n-input placeholder="请输入" v-model:value="formParams.varName" />
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
|
||||
<template #action>
|
||||
<n-space>
|
||||
<n-button @click="() => (showModal = false)">取消</n-button>
|
||||
<n-button type="info" :loading="formBtnLoading" @click="confirmForm">生成配置</n-button>
|
||||
</n-space>
|
||||
</template>
|
||||
</n-modal>
|
||||
</n-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { h, onBeforeMount, reactive, ref } from 'vue';
|
||||
import { NTag, TreeSelectOption, useDialog, useMessage } from 'naive-ui';
|
||||
import { BasicTable, TableAction } from '@/components/Table';
|
||||
import { BasicForm, FormSchema, useForm } from '@/components/Form/index';
|
||||
import { List, Delete, Edit, Selects, TableSelect } from '@/api/develop/code';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { PlusOutlined, DeleteOutlined } from '@vicons/antd';
|
||||
import { newState } from '@/views/develop/code/components/model';
|
||||
import { getOptionLabel } from '@/utils/hotgo';
|
||||
|
||||
const selectList = ref({
|
||||
db: [],
|
||||
genType: [],
|
||||
status: [],
|
||||
tables: [],
|
||||
formMode: [],
|
||||
formRole: [],
|
||||
dictMode: [],
|
||||
whereMode: [],
|
||||
});
|
||||
const columns = [
|
||||
{
|
||||
title: '生成ID',
|
||||
key: 'id',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '生成类型',
|
||||
key: 'genType',
|
||||
render(row) {
|
||||
return h(
|
||||
NTag,
|
||||
{
|
||||
style: {
|
||||
marginRight: '6px',
|
||||
},
|
||||
type: 'info',
|
||||
bordered: false,
|
||||
},
|
||||
{
|
||||
default: () => getOptionLabel(selectList.value.genType, row.genType),
|
||||
}
|
||||
);
|
||||
},
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
title: '实体命名',
|
||||
key: 'varName',
|
||||
render(row) {
|
||||
return row.varName;
|
||||
},
|
||||
width: 180,
|
||||
},
|
||||
{
|
||||
title: '数据库',
|
||||
key: 'dbName',
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
title: '数据表',
|
||||
key: 'tableName',
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
title: '菜单名称',
|
||||
key: 'tableComment',
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
title: '生成状态',
|
||||
key: 'status',
|
||||
render(row) {
|
||||
return h(
|
||||
NTag,
|
||||
{
|
||||
style: {
|
||||
marginRight: '6px',
|
||||
},
|
||||
type: row.status == 1 ? 'success' : 'warning',
|
||||
bordered: false,
|
||||
},
|
||||
{
|
||||
default: () => getOptionLabel(selectList.value.status, row.status),
|
||||
}
|
||||
);
|
||||
},
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
key: 'createdAt',
|
||||
width: 180,
|
||||
},
|
||||
{
|
||||
title: '更新时间',
|
||||
key: 'updatedAt',
|
||||
width: 180,
|
||||
},
|
||||
];
|
||||
|
||||
const dialog = useDialog();
|
||||
const batchDeleteDisabled = ref(true);
|
||||
const checkedIds = ref([]);
|
||||
const searchFormRef = ref<any>();
|
||||
|
||||
const schemas = ref<FormSchema[]>([
|
||||
{
|
||||
field: 'genType',
|
||||
component: 'NSelect',
|
||||
label: '生成类型',
|
||||
componentProps: {
|
||||
placeholder: '请选择生成类型',
|
||||
options: [],
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'varName',
|
||||
component: 'NInput',
|
||||
label: '实体命名',
|
||||
componentProps: {
|
||||
placeholder: '请输入实体命名',
|
||||
onInput: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
rules: [{ trigger: ['blur'] }],
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
component: 'NSelect',
|
||||
label: '生成状态',
|
||||
componentProps: {
|
||||
placeholder: '请选择状态码',
|
||||
options: [],
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
const router = useRouter();
|
||||
const showModal = ref(false);
|
||||
const formBtnLoading = ref(false);
|
||||
const formRef: any = ref(null);
|
||||
const message = useMessage();
|
||||
const actionRef = ref();
|
||||
const formParams = ref<any>();
|
||||
|
||||
function goHome() {
|
||||
router.push('/');
|
||||
const rules = {
|
||||
varName: {
|
||||
required: true,
|
||||
trigger: ['blur', 'input'],
|
||||
message: '实体命名不能为空,首字母大写',
|
||||
},
|
||||
};
|
||||
|
||||
const actionColumn = reactive({
|
||||
width: 220,
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
// fixed: 'right',
|
||||
render(record) {
|
||||
return h(TableAction as any, {
|
||||
style: 'button',
|
||||
actions: [
|
||||
{
|
||||
label: '生成配置',
|
||||
onClick: handleEdit.bind(null, record),
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
onClick: handleDelete.bind(null, record),
|
||||
},
|
||||
],
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const [register, {}] = useForm({
|
||||
gridProps: { cols: '1 s:1 m:2 l:3 xl:4 2xl:4' },
|
||||
labelWidth: 80,
|
||||
schemas,
|
||||
});
|
||||
|
||||
function onCheckedRow(rowKeys) {
|
||||
batchDeleteDisabled.value = rowKeys.length <= 0;
|
||||
checkedIds.value = rowKeys;
|
||||
}
|
||||
|
||||
function handleDelete(record: Recordable) {
|
||||
dialog.warning({
|
||||
title: '警告',
|
||||
content: '你确定要删除?',
|
||||
positiveText: '确定',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: () => {
|
||||
Delete(record).then((_res) => {
|
||||
console.log('_res:' + JSON.stringify(_res));
|
||||
message.success('操作成功');
|
||||
reloadTable();
|
||||
});
|
||||
},
|
||||
onNegativeClick: () => {
|
||||
// message.error('取消');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function batchDelete() {
|
||||
dialog.warning({
|
||||
title: '警告',
|
||||
content: '你确定要删除?',
|
||||
positiveText: '确定',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: () => {
|
||||
Delete({ id: checkedIds.value }).then((_res) => {
|
||||
message.success('操作成功');
|
||||
reloadTable();
|
||||
});
|
||||
},
|
||||
onNegativeClick: () => {
|
||||
// message.error('取消');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const loadDataTable = async (res) => {
|
||||
mapWidth();
|
||||
return await List({ ...res, ...searchFormRef.value?.formModel });
|
||||
};
|
||||
|
||||
function reloadTable() {
|
||||
actionRef.value.reload();
|
||||
}
|
||||
|
||||
function handleEdit(record: Recordable) {
|
||||
router.push({ name: 'develop_code_deploy', params: { id: record.id } });
|
||||
}
|
||||
|
||||
function handleSubmit(_values: Recordable) {
|
||||
reloadTable();
|
||||
}
|
||||
|
||||
function handleReset(_values: Recordable) {
|
||||
reloadTable();
|
||||
}
|
||||
|
||||
function addTable() {
|
||||
showModal.value = true;
|
||||
formParams.value = newState(null);
|
||||
}
|
||||
|
||||
function confirmForm(e) {
|
||||
e.preventDefault();
|
||||
formBtnLoading.value = true;
|
||||
formRef.value.validate((errors) => {
|
||||
if (!errors) {
|
||||
console.log('formParams:' + JSON.stringify(formParams.value));
|
||||
Edit(formParams.value).then((res) => {
|
||||
message.success('生成成功,正在前往配置');
|
||||
setTimeout(() => {
|
||||
showModal.value = false;
|
||||
router.push({ name: 'develop_code_deploy', params: { id: res.id } });
|
||||
});
|
||||
});
|
||||
} else {
|
||||
message.error('请填写完整信息');
|
||||
}
|
||||
formBtnLoading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
const dialogWidth = ref('50%');
|
||||
function mapWidth() {
|
||||
let val = document.body.clientWidth;
|
||||
const def = 840; // 默认宽度
|
||||
if (val < def) {
|
||||
dialogWidth.value = '100%';
|
||||
} else {
|
||||
dialogWidth.value = def + 'px';
|
||||
}
|
||||
|
||||
return dialogWidth.value;
|
||||
}
|
||||
|
||||
onBeforeMount(async () => {
|
||||
await loadSelect();
|
||||
});
|
||||
|
||||
const loadSelect = async () => {
|
||||
selectList.value = await Selects({});
|
||||
|
||||
for (const item of schemas.value) {
|
||||
switch (item.field) {
|
||||
case 'status':
|
||||
item.componentProps.options = selectList.value.status;
|
||||
break;
|
||||
case 'genType':
|
||||
item.componentProps.options = selectList.value.genType;
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const tablesLoading = ref(false);
|
||||
// 处理选项更新
|
||||
async function handleDbUpdateValue(
|
||||
value: string | number | Array<string | number> | null,
|
||||
_option: TreeSelectOption | null | Array<TreeSelectOption | null>
|
||||
) {
|
||||
tablesLoading.value = true;
|
||||
await loadTableSelect(value);
|
||||
tablesLoading.value = false;
|
||||
}
|
||||
|
||||
async function loadTableSelect(value) {
|
||||
selectList.value.tables = await TableSelect({ name: value });
|
||||
}
|
||||
|
||||
function handleTableUpdateValue(value, option) {
|
||||
formParams.value.varName = option?.defVarName as string;
|
||||
formParams.value.daoName = option?.daoName as string;
|
||||
formParams.value.tableComment = option?.defTableComment as string;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.page-container {
|
||||
width: 100%;
|
||||
border-radius: 4px;
|
||||
padding: 50px 0;
|
||||
height: 60vh;
|
||||
|
||||
.text-center {
|
||||
h1 {
|
||||
color: #666;
|
||||
padding: 20px 0;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
width: 350px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style lang="less" scoped></style>
|
||||
|
||||
Reference in New Issue
Block a user