mirror of
https://gitee.com/zhijiantianya/ruoyi-vue-pro.git
synced 2026-10-06 07:22:59 +08:00
fix(infra): 修复前端代码生成模板的组件绑定与主子表行为
修复 Vben5 字典控件、日期及树选择属性、导入文件移除和 API 导入路径。 修复主子表数据获取、临时编号、必填校验和主表切换后的旧数据残留。 补齐工具方法与组件导入,修正 Ele 工具栏标签及 Vue3 单选多选绑定。 同步 Vue2 组件标签与 prop 声明、旧 Vben 日期范围格式。 更新生成快照,增加跨前端组件契约测试。 验证:85 条 CodegenEngine 测试、138 份 Vue 模板编译通过; Codex、Qwen、DeepSeek 三路评审通过。
This commit is contained in:
+7
-3
@@ -244,9 +244,13 @@
|
||||
Editor,
|
||||
#end
|
||||
},
|
||||
props:[
|
||||
'${subJoinColumn.javaField}'
|
||||
],// ${subJoinColumn.columnComment}(主表的关联字段)
|
||||
props: {
|
||||
// ${subJoinColumn.columnComment}(主表的关联字段)
|
||||
${subJoinColumn.javaField}: {
|
||||
type: #if ($subJoinColumn.javaType == "String")String#else Number#end,
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
|
||||
+8
-4
@@ -78,7 +78,7 @@
|
||||
</el-table>
|
||||
#if ($table.templateType == 11)
|
||||
<!-- 分页组件 -->
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
|
||||
<Pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"/>
|
||||
<!-- 对话框(添加 / 修改) -->
|
||||
<${subSimpleClassName}Form ref="formRef" @success="getList" />
|
||||
@@ -98,9 +98,13 @@
|
||||
${subSimpleClassName}Form
|
||||
},
|
||||
#end
|
||||
props:[
|
||||
'${subJoinColumn.javaField}'
|
||||
],// ${subJoinColumn.columnComment}(主表的关联字段)
|
||||
props: {
|
||||
// ${subJoinColumn.columnComment}(主表的关联字段)
|
||||
${subJoinColumn.javaField}: {
|
||||
type: #if ($subJoinColumn.javaType == "String")String#else Number#end,
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
|
||||
@@ -86,7 +86,7 @@
|
||||
</el-button>
|
||||
</el-col>
|
||||
#end
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
<RightToolbar :showSearch.sync="showSearch" @queryTable="getList"></RightToolbar>
|
||||
</el-row>
|
||||
#if ($importEnable)
|
||||
<input ref="importFileRef" type="file" style="display: none" accept=".xls,.xlsx" @change="handleImportFileChange" />
|
||||
@@ -186,7 +186,7 @@
|
||||
## 特殊:树表专属逻辑(树不需要分页)
|
||||
#if ( $table.templateType != 2 )
|
||||
<!-- 分页组件 -->
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
|
||||
<Pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"/>
|
||||
#end
|
||||
<!-- 对话框(添加 / 修改) -->
|
||||
|
||||
+1
-1
@@ -79,7 +79,7 @@
|
||||
<el-radio
|
||||
v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
|
||||
+2
-2
@@ -111,7 +111,7 @@
|
||||
<el-radio
|
||||
v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
@@ -233,7 +233,7 @@
|
||||
<el-radio
|
||||
v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
<el-radio
|
||||
v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
|
||||
@@ -78,6 +78,12 @@ export const searchFormSchema: FormSchema[] = [
|
||||
},
|
||||
#elseif($column.htmlType == "datetime")
|
||||
component: 'RangePicker',
|
||||
componentProps: {
|
||||
showTime: true,
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
allowClear: true,
|
||||
},
|
||||
#end
|
||||
colProps: { span: 8 },
|
||||
},
|
||||
|
||||
+4
-1
@@ -112,7 +112,10 @@ const [Modal, modalApi] = useVbenModal({
|
||||
#set ($index = $foreach.count - 1)
|
||||
#set ($subClassNameVar = $subClassNameVars.get($index))
|
||||
#if ($subTable.subJoinMany) ## 一对多
|
||||
## TODO 列表值校验?
|
||||
if (!${subClassNameVar}FormRef.value?.validate()) {
|
||||
subTabsName.value = '${subClassNameVar}';
|
||||
return;
|
||||
}
|
||||
#else
|
||||
try {
|
||||
await ${subClassNameVar}FormRef.value?.validate()
|
||||
|
||||
+11
-1
@@ -48,6 +48,11 @@ function beforeUpload(file: FileType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** 移除文件 */
|
||||
function handleRemove() {
|
||||
fileRef.value = null;
|
||||
}
|
||||
|
||||
/** 下载导入模板 */
|
||||
async function handleDownload() {
|
||||
const data = await import${simpleClassName}Template();
|
||||
@@ -58,7 +63,12 @@ async function handleDownload() {
|
||||
<template>
|
||||
<Modal title="导入${table.classComment}" class="w-1/3">
|
||||
<div class="mx-4">
|
||||
<Upload :max-count="1" accept=".xls,.xlsx" :before-upload="beforeUpload">
|
||||
<Upload
|
||||
:max-count="1"
|
||||
accept=".xls,.xlsx"
|
||||
:before-upload="beforeUpload"
|
||||
:on-remove="handleRemove"
|
||||
>
|
||||
<Button type="primary"> 选择 Excel 文件 </Button>
|
||||
</Upload>
|
||||
</div>
|
||||
|
||||
+12
-7
@@ -65,12 +65,7 @@ const queryParams = reactive({
|
||||
#end
|
||||
#foreach ($column in $columns)
|
||||
#if ($column.listOperation)
|
||||
#if ($column.listOperationCondition != 'BETWEEN')
|
||||
$column.javaField: undefined,
|
||||
#end
|
||||
#if ($column.htmlType == "datetime" || $column.listOperationCondition == "BETWEEN")
|
||||
$column.javaField: undefined,
|
||||
#end
|
||||
$column.javaField: undefined,
|
||||
#end
|
||||
#end
|
||||
})
|
||||
@@ -197,7 +192,17 @@ function handleRowCheckboxChange({
|
||||
async function handleExport() {
|
||||
try {
|
||||
exportLoading.value = true;
|
||||
const data = await export${simpleClassName}(queryParams);
|
||||
const params = cloneDeep(queryParams) as any;
|
||||
#foreach ($column in $columns)
|
||||
#if ($column.listOperation)
|
||||
#if ($column.htmlType == "datetime" || $column.listOperationCondition == "BETWEEN")
|
||||
if (params.${column.javaField} && Array.isArray(params.${column.javaField})) {
|
||||
params.${column.javaField} = (params.${column.javaField} as string[]).join(',');
|
||||
}
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
const data = await export${simpleClassName}(params);
|
||||
downloadFileFromBlobPart({ fileName: '${table.classComment}.xls', source: data });
|
||||
}finally {
|
||||
exportLoading.value = false;
|
||||
|
||||
+55
-14
@@ -46,25 +46,54 @@ async function handleDelete(row: ${simpleClassName}Api.${subSimpleClassName}) {
|
||||
await tableRef.value?.remove(row);
|
||||
}
|
||||
|
||||
/** 提供获取表格数据的方法供父组件调用 */
|
||||
defineExpose({
|
||||
getData: (): ${simpleClassName}Api.${subSimpleClassName}[] => {
|
||||
const data = list.value as ${simpleClassName}Api.${subSimpleClassName}[];
|
||||
const removeRecords = tableRef.value?.getRemoveRecords() as ${simpleClassName}Api.${subSimpleClassName}[];
|
||||
const insertRecords = tableRef.value?.getInsertRecords() as ${simpleClassName}Api.${subSimpleClassName}[];
|
||||
return [
|
||||
...data.filter(
|
||||
(row) => !removeRecords.some((removed) => removed.id === row.id),
|
||||
),
|
||||
...insertRecords.map((row: any) => ({ ...row, id: undefined })),
|
||||
];
|
||||
},
|
||||
});
|
||||
/** 获取当前子表数据,新增行不提交表格生成的临时编号 */
|
||||
function getData(): ${simpleClassName}Api.${subSimpleClassName}[] {
|
||||
const table = tableRef.value;
|
||||
if (!table) {
|
||||
return [];
|
||||
}
|
||||
const { fullData } = table.getTableData();
|
||||
const inserted = new Set(table.getInsertRecords());
|
||||
return fullData.map((row) =>
|
||||
inserted.has(row) ? { ...row, id: undefined } : { ...row },
|
||||
);
|
||||
}
|
||||
|
||||
#set ($hasRequiredColumn = false)
|
||||
#foreach ($column in $subColumns)
|
||||
#if (($column.createOperation || $column.updateOperation) && !$column.nullable && !$column.primaryKey && $column.id != $subJoinColumn.id)
|
||||
#set ($hasRequiredColumn = true)
|
||||
#end
|
||||
#end
|
||||
/** 校验子表必填字段 */
|
||||
function validate(): boolean {
|
||||
#if ($hasRequiredColumn)
|
||||
for (const [index, row] of getData().entries()) {
|
||||
#foreach ($column in $subColumns)
|
||||
#if (($column.createOperation || $column.updateOperation) && !$column.nullable && !$column.primaryKey && $column.id != $subJoinColumn.id)
|
||||
if (
|
||||
row.${column.javaField} === null ||
|
||||
row.${column.javaField} === undefined ||
|
||||
(typeof row.${column.javaField} === 'string' && String(row.${column.javaField}).trim() === '') ||
|
||||
(Array.isArray(row.${column.javaField}) && row.${column.javaField}.length === 0)
|
||||
) {
|
||||
message.error(`第 #[[${index + 1}]]# 行:${column.columnComment}不能为空`);
|
||||
return false;
|
||||
}
|
||||
#end
|
||||
#end
|
||||
}
|
||||
#end
|
||||
return true;
|
||||
}
|
||||
|
||||
defineExpose({ getData, validate });
|
||||
|
||||
/** 监听主表的关联字段的变化,加载对应的子表数据 */
|
||||
watch(
|
||||
() => props.${subJoinColumn.javaField},
|
||||
async (val) => {
|
||||
list.value = [];
|
||||
if (!val) {
|
||||
return;
|
||||
}
|
||||
@@ -103,6 +132,17 @@ defineExpose({
|
||||
watch(
|
||||
() => props.${subJoinColumn.javaField},
|
||||
async (val) => {
|
||||
formData.value = {
|
||||
#foreach ($column in $subColumns)
|
||||
#if ($column.createOperation || $column.updateOperation)
|
||||
#if ($column.htmlType == "checkbox")
|
||||
$column.javaField: [],
|
||||
#else
|
||||
$column.javaField: undefined,
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
};
|
||||
if (!val) {
|
||||
return;
|
||||
}
|
||||
@@ -120,6 +160,7 @@ watch(
|
||||
#foreach($column in $subColumns)
|
||||
#if ($column.createOperation || $column.updateOperation)
|
||||
#set ($comment = $column.columnComment)
|
||||
#set ($dictType = $column.dictType)
|
||||
#set ($javaField = $column.javaField)
|
||||
#set ($javaType = $column.javaType)
|
||||
#if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short")
|
||||
|
||||
+6
-5
@@ -36,7 +36,7 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
id: 0,
|
||||
${treeNameColumn.javaField}: '顶级${table.classComment}',
|
||||
});
|
||||
return handleTree(data);
|
||||
return handleTree(data, 'id', '${treeParentColumn.javaField}');
|
||||
},
|
||||
labelField: '${treeNameColumn.javaField}',
|
||||
valueField: 'id',
|
||||
@@ -89,7 +89,7 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
placeholder: '请选择${comment}',
|
||||
},
|
||||
#elseif($column.htmlType == "checkbox")## 多选框
|
||||
component: 'Checkbox',
|
||||
component: 'CheckboxGroup',
|
||||
componentProps: {
|
||||
#if ("" != $dictType)## 有数据字典
|
||||
options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
|
||||
@@ -306,7 +306,7 @@ export function use${subSimpleClassName}FormSchema(): VbenFormSchema[] {
|
||||
placeholder: '请选择${comment}',
|
||||
},
|
||||
#elseif($column.htmlType == "checkbox")## 多选框
|
||||
component: 'Checkbox',
|
||||
component: 'CheckboxGroup',
|
||||
componentProps: {
|
||||
#if ("" != $dictType)## 有数据字典
|
||||
options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
|
||||
@@ -446,6 +446,7 @@ export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions<${api
|
||||
#if ($column.createOperation || $column.updateOperation)
|
||||
#if (!$column.primaryKey && $column.listOperationResult && $column.id != $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写
|
||||
#set ($dictType = $column.dictType)
|
||||
#set ($javaType = $column.javaType)
|
||||
#set ($javaField = $column.javaField)
|
||||
#set ($comment = $column.columnComment)
|
||||
#if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short")
|
||||
@@ -540,7 +541,7 @@ export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions<${api
|
||||
placeholder: '请选择${comment}',
|
||||
},
|
||||
#elseif($column.htmlType == "checkbox")## 多选框
|
||||
component: 'Checkbox',
|
||||
component: 'CheckboxGroup',
|
||||
componentProps: {
|
||||
#if ("" != $dictType)## 有数据字典
|
||||
options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
|
||||
@@ -590,7 +591,7 @@ export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions<${api
|
||||
#end
|
||||
#if ($table.templateType == 12) ## 内嵌情况
|
||||
/** 列表的字段 */
|
||||
export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions<${simpleClassName}Api.${subSimpleClassName}>['columns'] {
|
||||
export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions<${apiName}.${subSimpleClassName}>['columns'] {
|
||||
return [
|
||||
#foreach($column in $subColumns)
|
||||
#if ($column.listOperationResult)
|
||||
|
||||
+1
-5
@@ -6,7 +6,7 @@ import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message#if ($table.templateType == 11), Tabs#end } from 'ant-design-vue';
|
||||
import { message#if ($table.templateType == 10 || $table.templateType == 12), Tabs#end } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { create${simpleClassName}, get${simpleClassName}, update${simpleClassName} } from '#/api/${table.moduleName}/${table.businessName}';
|
||||
@@ -69,15 +69,11 @@ const [Modal, modalApi] = useVbenModal({
|
||||
#foreach ($subTable in $subTables)
|
||||
#set ($index = $foreach.count - 1)
|
||||
#set ($subClassNameVar = $subClassNameVars.get($index))
|
||||
#if ($subTable.subJoinMany) ## 一对多
|
||||
## TODO 列表值校验?
|
||||
#else
|
||||
const ${subClassNameVar}Valid = await ${subClassNameVar}FormRef.value?.validate();
|
||||
if (!${subClassNameVar}Valid) {
|
||||
subTabsName.value = '${subClassNameVar}';
|
||||
return;
|
||||
}
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
|
||||
+6
@@ -51,6 +51,11 @@ function beforeUpload(file: FileType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** 移除文件 */
|
||||
function handleRemove() {
|
||||
formApi.setFieldValue('file', undefined);
|
||||
}
|
||||
|
||||
/** 下载模板 */
|
||||
async function handleDownload() {
|
||||
const data = await import${simpleClassName}Template();
|
||||
@@ -67,6 +72,7 @@ async function handleDownload() {
|
||||
:max-count="1"
|
||||
accept=".xls,.xlsx"
|
||||
:before-upload="beforeUpload"
|
||||
:on-remove="handleRemove"
|
||||
>
|
||||
<Button type="primary"> 选择 Excel 文件 </Button>
|
||||
</Upload>
|
||||
|
||||
+43
-14
@@ -14,7 +14,7 @@
|
||||
|
||||
#if ($subTable.subJoinMany) ## 一对多
|
||||
import { Plus } from "@vben/icons";
|
||||
import { Button, Tabs, Checkbox, Input, Textarea, Select,RadioGroup,CheckboxGroup, DatePicker } from 'ant-design-vue';
|
||||
import { Button, Checkbox, CheckboxGroup, DatePicker, Input, message, RadioGroup, Select, Tabs, Textarea } from 'ant-design-vue';
|
||||
import { ImageUpload, FileUpload } from "#/components/upload";
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { use${subSimpleClassName}GridEditColumns } from '../data';
|
||||
@@ -59,26 +59,54 @@ const handleDelete = async (row: ${apiName}.${subSimpleClassName}) => {
|
||||
await gridApi.grid.remove(row);
|
||||
}
|
||||
|
||||
/** 提供获取表格数据的方法供父组件调用 */
|
||||
defineExpose({
|
||||
getData: (): ${apiName}.${subSimpleClassName}[] => {
|
||||
const data = gridApi.grid.getData() as ${apiName}.${subSimpleClassName}[];
|
||||
const removeRecords = gridApi.grid.getRemoveRecords() as ${apiName}.${subSimpleClassName}[];
|
||||
const insertRecords = gridApi.grid.getInsertRecords() as ${apiName}.${subSimpleClassName}[];
|
||||
return data
|
||||
.filter((row) => !removeRecords.some((removed) => removed.id === row.id))
|
||||
.concat(insertRecords.map((row: any) => ({ ...row, id: undefined })));
|
||||
},
|
||||
});
|
||||
/** 获取当前子表数据,新增行不提交表格生成的临时编号 */
|
||||
function getData(): ${apiName}.${subSimpleClassName}[] {
|
||||
const { fullData } = gridApi.grid.getTableData();
|
||||
const inserted = new Set(gridApi.grid.getInsertRecords());
|
||||
return fullData.map((row) =>
|
||||
inserted.has(row) ? { ...row, id: undefined } : { ...row },
|
||||
);
|
||||
}
|
||||
|
||||
#set ($hasRequiredColumn = false)
|
||||
#foreach ($column in $subColumns)
|
||||
#if (($column.createOperation || $column.updateOperation) && !$column.nullable && !$column.primaryKey && $column.id != $subJoinColumn.id)
|
||||
#set ($hasRequiredColumn = true)
|
||||
#end
|
||||
#end
|
||||
/** 校验子表必填字段 */
|
||||
function validate(): boolean {
|
||||
#if ($hasRequiredColumn)
|
||||
for (const [index, row] of getData().entries()) {
|
||||
#foreach ($column in $subColumns)
|
||||
#if (($column.createOperation || $column.updateOperation) && !$column.nullable && !$column.primaryKey && $column.id != $subJoinColumn.id)
|
||||
if (
|
||||
row.${column.javaField} === null ||
|
||||
row.${column.javaField} === undefined ||
|
||||
(typeof row.${column.javaField} === 'string' && String(row.${column.javaField}).trim() === '') ||
|
||||
(Array.isArray(row.${column.javaField}) && row.${column.javaField}.length === 0)
|
||||
) {
|
||||
message.error(`第 #[[${index + 1}]]# 行:${column.columnComment}不能为空`);
|
||||
return false;
|
||||
}
|
||||
#end
|
||||
#end
|
||||
}
|
||||
#end
|
||||
return true;
|
||||
}
|
||||
|
||||
defineExpose({ getData, validate });
|
||||
|
||||
/** 监听主表的关联字段的变化,加载对应的子表数据 */
|
||||
watch(
|
||||
() => props.${subJoinColumn.javaField},
|
||||
async (val) => {
|
||||
await nextTick();
|
||||
if (!val) {
|
||||
await gridApi.grid.loadData([]);
|
||||
return;
|
||||
}
|
||||
await nextTick();
|
||||
await gridApi.grid.loadData(await get${subSimpleClassName}ListBy${SubJoinColumnName}(props.${subJoinColumn.javaField}!));
|
||||
},
|
||||
{ immediate: true },
|
||||
@@ -110,10 +138,11 @@ defineExpose({
|
||||
watch(
|
||||
() => props.${subJoinColumn.javaField},
|
||||
async (val) => {
|
||||
await nextTick();
|
||||
await formApi.resetForm();
|
||||
if (!val) {
|
||||
return;
|
||||
}
|
||||
await nextTick();
|
||||
await formApi.setValues(await get${subSimpleClassName}By${SubJoinColumnName}(props.${subJoinColumn.javaField}!));
|
||||
},
|
||||
{ immediate: true },
|
||||
|
||||
+4
-1
@@ -118,7 +118,10 @@ const [Modal, modalApi] = useVbenModal({
|
||||
#set ($index = $foreach.count - 1)
|
||||
#set ($subClassNameVar = $subClassNameVars.get($index))
|
||||
#if ($subTable.subJoinMany) ## 一对多
|
||||
## TODO 列表值校验?
|
||||
if (!${subClassNameVar}FormRef.value?.validate()) {
|
||||
subTabsName.value = '${subClassNameVar}';
|
||||
return;
|
||||
}
|
||||
#else
|
||||
try {
|
||||
await ${subClassNameVar}FormRef.value?.validate()
|
||||
|
||||
+7
-1
@@ -46,6 +46,11 @@ function beforeUpload(file: File) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** 移除文件 */
|
||||
function handleRemove() {
|
||||
fileRef.value = null;
|
||||
}
|
||||
|
||||
/** 下载导入模板 */
|
||||
async function handleDownload() {
|
||||
const data = await import${simpleClassName}Template();
|
||||
@@ -56,7 +61,8 @@ async function handleDownload() {
|
||||
<template>
|
||||
<Modal title="导入${table.classComment}" class="w-1/3">
|
||||
<div class="mx-4">
|
||||
<Upload :max-count="1" accept=".xls,.xlsx" :before-upload="beforeUpload">
|
||||
<Upload :max-count="1" accept=".xls,.xlsx" :before-upload="beforeUpload"
|
||||
:on-remove="handleRemove">
|
||||
<Button type="primary"> 选择 Excel 文件 </Button>
|
||||
</Upload>
|
||||
</div>
|
||||
|
||||
+12
-7
@@ -77,12 +77,7 @@ const queryParams = reactive({
|
||||
#end
|
||||
#foreach ($column in $columns)
|
||||
#if ($column.listOperation)
|
||||
#if ($column.listOperationCondition != 'BETWEEN')
|
||||
$column.javaField: undefined,
|
||||
#end
|
||||
#if ($column.htmlType == "datetime" || $column.listOperationCondition == "BETWEEN")
|
||||
$column.javaField: undefined,
|
||||
#end
|
||||
$column.javaField: undefined,
|
||||
#end
|
||||
#end
|
||||
})
|
||||
@@ -209,7 +204,17 @@ function handleRowCheckboxChange({
|
||||
async function handleExport() {
|
||||
try {
|
||||
exportLoading.value = true;
|
||||
const data = await export${simpleClassName}(queryParams);
|
||||
const params = cloneDeep(queryParams) as any;
|
||||
#foreach ($column in $columns)
|
||||
#if ($column.listOperation)
|
||||
#if ($column.htmlType == "datetime" || $column.listOperationCondition == "BETWEEN")
|
||||
if (params.${column.javaField} && Array.isArray(params.${column.javaField})) {
|
||||
params.${column.javaField} = (params.${column.javaField} as string[]).join(',');
|
||||
}
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
const data = await export${simpleClassName}(params);
|
||||
downloadFileFromBlobPart({ fileName: '${table.classComment}.xls', source: data });
|
||||
}finally {
|
||||
exportLoading.value = false;
|
||||
|
||||
+56
-15
@@ -18,7 +18,7 @@ import { computed, ref, h, onMounted, watch, nextTick } from 'vue';
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
import { getDictOptions } from '@vben/hooks';
|
||||
|
||||
import { Button, Checkbox, CheckboxGroup, DatePicker#if (!$subTable.subJoinMany), Form, FormItem#end, Input, Radio, RadioGroup, Select#if ($selectOptionEnable), SelectOption#end, TextArea } from 'antdv-next';
|
||||
import { Button, Checkbox, CheckboxGroup, DatePicker#if (!$subTable.subJoinMany), Form, FormItem#end, Input#if ($subTable.subJoinMany), message#end, Radio, RadioGroup, Select#if ($selectOptionEnable), SelectOption#end, TextArea } from 'antdv-next';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
#if ($subTable.subJoinMany) ## 一对多
|
||||
@@ -52,25 +52,54 @@ async function handleDelete(row: ${simpleClassName}Api.${subSimpleClassName}) {
|
||||
await tableRef.value?.remove(row);
|
||||
}
|
||||
|
||||
/** 提供获取表格数据的方法供父组件调用 */
|
||||
defineExpose({
|
||||
getData: (): ${simpleClassName}Api.${subSimpleClassName}[] => {
|
||||
const data = list.value as ${simpleClassName}Api.${subSimpleClassName}[];
|
||||
const removeRecords = tableRef.value?.getRemoveRecords() as ${simpleClassName}Api.${subSimpleClassName}[];
|
||||
const insertRecords = tableRef.value?.getInsertRecords() as ${simpleClassName}Api.${subSimpleClassName}[];
|
||||
return [
|
||||
...data.filter(
|
||||
(row) => !removeRecords.some((removed) => removed.id === row.id),
|
||||
),
|
||||
...insertRecords.map((row: any) => ({ ...row, id: undefined })),
|
||||
];
|
||||
},
|
||||
});
|
||||
/** 获取当前子表数据,新增行不提交表格生成的临时编号 */
|
||||
function getData(): ${simpleClassName}Api.${subSimpleClassName}[] {
|
||||
const table = tableRef.value;
|
||||
if (!table) {
|
||||
return [];
|
||||
}
|
||||
const { fullData } = table.getTableData();
|
||||
const inserted = new Set(table.getInsertRecords());
|
||||
return fullData.map((row) =>
|
||||
inserted.has(row) ? { ...row, id: undefined } : { ...row },
|
||||
);
|
||||
}
|
||||
|
||||
#set ($hasRequiredColumn = false)
|
||||
#foreach ($column in $subColumns)
|
||||
#if (($column.createOperation || $column.updateOperation) && !$column.nullable && !$column.primaryKey && $column.id != $subJoinColumn.id)
|
||||
#set ($hasRequiredColumn = true)
|
||||
#end
|
||||
#end
|
||||
/** 校验子表必填字段 */
|
||||
function validate(): boolean {
|
||||
#if ($hasRequiredColumn)
|
||||
for (const [index, row] of getData().entries()) {
|
||||
#foreach ($column in $subColumns)
|
||||
#if (($column.createOperation || $column.updateOperation) && !$column.nullable && !$column.primaryKey && $column.id != $subJoinColumn.id)
|
||||
if (
|
||||
row.${column.javaField} === null ||
|
||||
row.${column.javaField} === undefined ||
|
||||
(typeof row.${column.javaField} === 'string' && String(row.${column.javaField}).trim() === '') ||
|
||||
(Array.isArray(row.${column.javaField}) && row.${column.javaField}.length === 0)
|
||||
) {
|
||||
message.error(`第 #[[${index + 1}]]# 行:${column.columnComment}不能为空`);
|
||||
return false;
|
||||
}
|
||||
#end
|
||||
#end
|
||||
}
|
||||
#end
|
||||
return true;
|
||||
}
|
||||
|
||||
defineExpose({ getData, validate });
|
||||
|
||||
/** 监听主表的关联字段的变化,加载对应的子表数据 */
|
||||
watch(
|
||||
() => props.${subJoinColumn.javaField},
|
||||
async (val) => {
|
||||
list.value = [];
|
||||
if (!val) {
|
||||
return;
|
||||
}
|
||||
@@ -109,6 +138,17 @@ defineExpose({
|
||||
watch(
|
||||
() => props.${subJoinColumn.javaField},
|
||||
async (val) => {
|
||||
formData.value = {
|
||||
#foreach ($column in $subColumns)
|
||||
#if ($column.createOperation || $column.updateOperation)
|
||||
#if ($column.htmlType == "checkbox")
|
||||
$column.javaField: [],
|
||||
#else
|
||||
$column.javaField: undefined,
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
};
|
||||
if (!val) {
|
||||
return;
|
||||
}
|
||||
@@ -126,6 +166,7 @@ watch(
|
||||
#foreach($column in $subColumns)
|
||||
#if ($column.createOperation || $column.updateOperation)
|
||||
#set ($comment = $column.columnComment)
|
||||
#set ($dictType = $column.dictType)
|
||||
#set ($javaField = $column.javaField)
|
||||
#set ($javaType = $column.javaType)
|
||||
#if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short")
|
||||
|
||||
+6
-5
@@ -36,7 +36,7 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
id: 0,
|
||||
${treeNameColumn.javaField}: '顶级${table.classComment}',
|
||||
});
|
||||
return handleTree(data);
|
||||
return handleTree(data, 'id', '${treeParentColumn.javaField}');
|
||||
},
|
||||
labelField: '${treeNameColumn.javaField}',
|
||||
valueField: 'id',
|
||||
@@ -89,7 +89,7 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
placeholder: '请选择${comment}',
|
||||
},
|
||||
#elseif($column.htmlType == "checkbox")## 多选框
|
||||
component: 'Checkbox',
|
||||
component: 'CheckboxGroup',
|
||||
componentProps: {
|
||||
#if ("" != $dictType)## 有数据字典
|
||||
options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
|
||||
@@ -305,7 +305,7 @@ export function use${subSimpleClassName}FormSchema(): VbenFormSchema[] {
|
||||
placeholder: '请选择${comment}',
|
||||
},
|
||||
#elseif($column.htmlType == "checkbox")## 多选框
|
||||
component: 'Checkbox',
|
||||
component: 'CheckboxGroup',
|
||||
componentProps: {
|
||||
#if ("" != $dictType)## 有数据字典
|
||||
options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
|
||||
@@ -444,6 +444,7 @@ export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions<${api
|
||||
#if ($column.createOperation || $column.updateOperation)
|
||||
#if (!$column.primaryKey && $column.listOperationResult && $column.id != $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写
|
||||
#set ($dictType = $column.dictType)
|
||||
#set ($javaType = $column.javaType)
|
||||
#set ($javaField = $column.javaField)
|
||||
#set ($comment = $column.columnComment)
|
||||
#if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short")
|
||||
@@ -538,7 +539,7 @@ export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions<${api
|
||||
placeholder: '请选择${comment}',
|
||||
},
|
||||
#elseif($column.htmlType == "checkbox")## 多选框
|
||||
component: 'Checkbox',
|
||||
component: 'CheckboxGroup',
|
||||
componentProps: {
|
||||
#if ("" != $dictType)## 有数据字典
|
||||
options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
|
||||
@@ -587,7 +588,7 @@ export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions<${api
|
||||
#end
|
||||
#if ($table.templateType == 12) ## 内嵌情况
|
||||
/** 列表的字段 */
|
||||
export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions<${simpleClassName}Api.${subSimpleClassName}>['columns'] {
|
||||
export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions<${apiName}.${subSimpleClassName}>['columns'] {
|
||||
return [
|
||||
#foreach($column in $subColumns)
|
||||
#if ($column.listOperationResult)
|
||||
|
||||
-4
@@ -69,15 +69,11 @@ const [Modal, modalApi] = useVbenModal({
|
||||
#foreach ($subTable in $subTables)
|
||||
#set ($index = $foreach.count - 1)
|
||||
#set ($subClassNameVar = $subClassNameVars.get($index))
|
||||
#if ($subTable.subJoinMany) ## 一对多
|
||||
## TODO 列表值校验?
|
||||
#else
|
||||
const ${subClassNameVar}Valid = await ${subClassNameVar}FormRef.value?.validate();
|
||||
if (!${subClassNameVar}Valid) {
|
||||
subTabsName.value = '${subClassNameVar}';
|
||||
return;
|
||||
}
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
|
||||
+6
@@ -49,6 +49,11 @@ function beforeUpload(file: File) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** 移除文件 */
|
||||
function handleRemove() {
|
||||
formApi.setFieldValue('file', undefined);
|
||||
}
|
||||
|
||||
/** 下载模板 */
|
||||
async function handleDownload() {
|
||||
const data = await import${simpleClassName}Template();
|
||||
@@ -65,6 +70,7 @@ async function handleDownload() {
|
||||
:max-count="1"
|
||||
accept=".xls,.xlsx"
|
||||
:before-upload="beforeUpload"
|
||||
:on-remove="handleRemove"
|
||||
>
|
||||
<Button type="primary"> 选择 Excel 文件 </Button>
|
||||
</Upload>
|
||||
|
||||
+43
-13
@@ -26,6 +26,7 @@ import {
|
||||
CheckboxGroup,
|
||||
DatePicker,
|
||||
Input,
|
||||
message,
|
||||
RadioGroup,
|
||||
Select,
|
||||
#if ($selectOptionEnable)
|
||||
@@ -77,26 +78,54 @@ const handleDelete = async (row: ${apiName}.${subSimpleClassName}) => {
|
||||
await gridApi.grid.remove(row);
|
||||
}
|
||||
|
||||
/** 提供获取表格数据的方法供父组件调用 */
|
||||
defineExpose({
|
||||
getData: (): ${apiName}.${subSimpleClassName}[] => {
|
||||
const data = gridApi.grid.getData() as ${apiName}.${subSimpleClassName}[];
|
||||
const removeRecords = gridApi.grid.getRemoveRecords() as ${apiName}.${subSimpleClassName}[];
|
||||
const insertRecords = gridApi.grid.getInsertRecords() as ${apiName}.${subSimpleClassName}[];
|
||||
return data
|
||||
.filter((row) => !removeRecords.some((removed) => removed.id === row.id))
|
||||
.concat(insertRecords.map((row: any) => ({ ...row, id: undefined })));
|
||||
},
|
||||
});
|
||||
/** 获取当前子表数据,新增行不提交表格生成的临时编号 */
|
||||
function getData(): ${apiName}.${subSimpleClassName}[] {
|
||||
const { fullData } = gridApi.grid.getTableData();
|
||||
const inserted = new Set(gridApi.grid.getInsertRecords());
|
||||
return fullData.map((row) =>
|
||||
inserted.has(row) ? { ...row, id: undefined } : { ...row },
|
||||
);
|
||||
}
|
||||
|
||||
#set ($hasRequiredColumn = false)
|
||||
#foreach ($column in $subColumns)
|
||||
#if (($column.createOperation || $column.updateOperation) && !$column.nullable && !$column.primaryKey && $column.id != $subJoinColumn.id)
|
||||
#set ($hasRequiredColumn = true)
|
||||
#end
|
||||
#end
|
||||
/** 校验子表必填字段 */
|
||||
function validate(): boolean {
|
||||
#if ($hasRequiredColumn)
|
||||
for (const [index, row] of getData().entries()) {
|
||||
#foreach ($column in $subColumns)
|
||||
#if (($column.createOperation || $column.updateOperation) && !$column.nullable && !$column.primaryKey && $column.id != $subJoinColumn.id)
|
||||
if (
|
||||
row.${column.javaField} === null ||
|
||||
row.${column.javaField} === undefined ||
|
||||
(typeof row.${column.javaField} === 'string' && String(row.${column.javaField}).trim() === '') ||
|
||||
(Array.isArray(row.${column.javaField}) && row.${column.javaField}.length === 0)
|
||||
) {
|
||||
message.error(`第 #[[${index + 1}]]# 行:${column.columnComment}不能为空`);
|
||||
return false;
|
||||
}
|
||||
#end
|
||||
#end
|
||||
}
|
||||
#end
|
||||
return true;
|
||||
}
|
||||
|
||||
defineExpose({ getData, validate });
|
||||
|
||||
/** 监听主表的关联字段的变化,加载对应的子表数据 */
|
||||
watch(
|
||||
() => props.${subJoinColumn.javaField},
|
||||
async (val) => {
|
||||
await nextTick();
|
||||
if (!val) {
|
||||
await gridApi.grid.loadData([]);
|
||||
return;
|
||||
}
|
||||
await nextTick();
|
||||
await gridApi.grid.loadData(await get${subSimpleClassName}ListBy${SubJoinColumnName}(props.${subJoinColumn.javaField}!));
|
||||
},
|
||||
{ immediate: true },
|
||||
@@ -128,10 +157,11 @@ defineExpose({
|
||||
watch(
|
||||
() => props.${subJoinColumn.javaField},
|
||||
async (val) => {
|
||||
await nextTick();
|
||||
await formApi.resetForm();
|
||||
if (!val) {
|
||||
return;
|
||||
}
|
||||
await nextTick();
|
||||
await formApi.setValues(await get${subSimpleClassName}By${SubJoinColumnName}(props.${subJoinColumn.javaField}!));
|
||||
},
|
||||
{ immediate: true },
|
||||
|
||||
+53
-50
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';
|
||||
import type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${table.businessName}';
|
||||
import type { FormRules } from 'element-plus';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
@@ -9,7 +9,7 @@ import { Tinymce as RichTextarea } from '#/components/tinymce';
|
||||
import { ImageUpload, FileUpload } from "#/components/upload";
|
||||
import { ElMessage, ElTabs, ElTabPane, ElForm, ElFormItem, ElInput, ElSelect, ElOption, ElRadioGroup, ElRadio, ElCheckboxGroup, ElCheckbox, ElDatePicker, ElTreeSelect } from 'element-plus';
|
||||
#if($table.templateType == 2)## 树表需要导入这些
|
||||
import { get${simpleClassName}List } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';
|
||||
import { get${simpleClassName}List } from '#/api/${table.moduleName}/${table.businessName}';
|
||||
import { handleTree } from '@vben/utils'
|
||||
#end
|
||||
## 特殊:主子表专属逻辑
|
||||
@@ -23,7 +23,7 @@ import { handleTree } from '@vben/utils'
|
||||
|
||||
import { computed, ref, reactive } from 'vue';
|
||||
import { $t } from '#/locales';
|
||||
import { get${simpleClassName}, create${simpleClassName}, update${simpleClassName} } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';
|
||||
import { get${simpleClassName}, create${simpleClassName}, update${simpleClassName} } from '#/api/${table.moduleName}/${table.businessName}';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
|
||||
@@ -111,7 +111,10 @@ const [Modal, modalApi] = useVbenModal({
|
||||
#set ($index = $foreach.count - 1)
|
||||
#set ($subClassNameVar = $subClassNameVars.get($index))
|
||||
#if ($subTable.subJoinMany) ## 一对多
|
||||
## TODO 列表值校验?
|
||||
if (!${subClassNameVar}FormRef.value?.validate()) {
|
||||
subTabsName.value = '${subClassNameVar}';
|
||||
return;
|
||||
}
|
||||
#else
|
||||
try {
|
||||
await ${subClassNameVar}FormRef.value?.validate()
|
||||
@@ -180,7 +183,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
|
||||
<template>
|
||||
<Modal :title="getTitle">
|
||||
<el-form
|
||||
<ElForm
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="rules"
|
||||
@@ -201,8 +204,8 @@ const [Modal, modalApi] = useVbenModal({
|
||||
#set ($dictMethod = "boolean")
|
||||
#end
|
||||
#if ( $table.templateType == 2 && $column.id == $treeParentColumn.id )
|
||||
<el-form-item label="${comment}" prop="${javaField}">
|
||||
<el-tree-select
|
||||
<ElFormItem label="${comment}" prop="${javaField}">
|
||||
<ElTreeSelect
|
||||
v-model="formData.${javaField}"
|
||||
:data="${classNameVar}Tree"
|
||||
#if ($treeNameColumn.javaField == "name")
|
||||
@@ -222,100 +225,100 @@ const [Modal, modalApi] = useVbenModal({
|
||||
default-expand-all
|
||||
placeholder="请选择${comment}"
|
||||
/>
|
||||
</el-form-item>
|
||||
</ElFormItem>
|
||||
#elseif ($column.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里
|
||||
<el-form-item label="${comment}" prop="${javaField}">
|
||||
<el-input v-model="formData.${javaField}" placeholder="请输入${comment}" />
|
||||
</el-form-item>
|
||||
<ElFormItem label="${comment}" prop="${javaField}">
|
||||
<ElInput v-model="formData.${javaField}" placeholder="请输入${comment}" />
|
||||
</ElFormItem>
|
||||
#elseif($column.htmlType == "imageUpload")## 图片上传
|
||||
<el-form-item label="${comment}" prop="${javaField}">
|
||||
<ElFormItem label="${comment}" prop="${javaField}">
|
||||
<ImageUpload v-model="formData.${javaField}" />
|
||||
</el-form-item>
|
||||
</ElFormItem>
|
||||
#elseif($column.htmlType == "fileUpload")## 文件上传
|
||||
<el-form-item label="${comment}" prop="${javaField}">
|
||||
<ElFormItem label="${comment}" prop="${javaField}">
|
||||
<FileUpload v-model="formData.${javaField}" />
|
||||
</el-form-item>
|
||||
</ElFormItem>
|
||||
#elseif($column.htmlType == "editor")## 文本编辑器
|
||||
<el-form-item label="${comment}" prop="${javaField}">
|
||||
<ElFormItem label="${comment}" prop="${javaField}">
|
||||
<RichTextarea v-model="formData.${javaField}" height="500px" />
|
||||
</el-form-item>
|
||||
</ElFormItem>
|
||||
#elseif($column.htmlType == "select")## 下拉框
|
||||
<el-form-item label="${comment}" prop="${javaField}">
|
||||
<el-select v-model="formData.${javaField}" placeholder="请选择${comment}">
|
||||
<ElFormItem label="${comment}" prop="${javaField}">
|
||||
<ElSelect v-model="formData.${javaField}" placeholder="请选择${comment}">
|
||||
#if ("" != $dictType)## 有数据字典
|
||||
<el-option
|
||||
<ElOption
|
||||
v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
:label="dict.label"
|
||||
/>
|
||||
#else##没数据字典
|
||||
<el-option label="请选择字典生成" value="" />
|
||||
<ElOption label="请选择字典生成" value="" />
|
||||
#end
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
#elseif($column.htmlType == "checkbox")## 多选框
|
||||
<el-form-item label="${comment}" prop="${javaField}">
|
||||
<el-checkbox-group v-model="formData.${javaField}">
|
||||
<ElFormItem label="${comment}" prop="${javaField}">
|
||||
<ElCheckboxGroup v-model="formData.${javaField}">
|
||||
#if ("" != $dictType)## 有数据字典
|
||||
<el-checkbox
|
||||
<ElCheckbox
|
||||
v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-checkbox>
|
||||
</ElCheckbox>
|
||||
#else##没数据字典
|
||||
<el-checkbox label="请选择字典生成" />
|
||||
<ElCheckbox label="请选择字典生成" />
|
||||
#end
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
</ElCheckboxGroup>
|
||||
</ElFormItem>
|
||||
#elseif($column.htmlType == "radio")## 单选框
|
||||
<el-form-item label="${comment}" prop="${javaField}">
|
||||
<el-radio-group v-model="formData.${javaField}">
|
||||
<ElFormItem label="${comment}" prop="${javaField}">
|
||||
<ElRadioGroup v-model="formData.${javaField}">
|
||||
#if ("" != $dictType)## 有数据字典
|
||||
<el-radio
|
||||
<ElRadio
|
||||
v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
</ElRadio>
|
||||
#else##没数据字典
|
||||
<el-radio :label="1">请选择字典生成</el-radio>
|
||||
<ElRadio :label="1">请选择字典生成</ElRadio>
|
||||
#end
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</ElRadioGroup>
|
||||
</ElFormItem>
|
||||
#elseif($column.htmlType == "datetime")## 时间框
|
||||
<el-form-item label="${comment}" prop="${javaField}">
|
||||
<el-date-picker
|
||||
<ElFormItem label="${comment}" prop="${javaField}">
|
||||
<ElDatePicker
|
||||
v-model="formData.${javaField}"
|
||||
value-format="x"
|
||||
placeholder="选择${comment}"
|
||||
/>
|
||||
</el-form-item>
|
||||
</ElFormItem>
|
||||
#elseif($column.htmlType == "textarea")## 文本框
|
||||
<el-form-item label="${comment}" prop="${javaField}">
|
||||
<el-input v-model="formData.${javaField}" type="textarea" placeholder="请输入${comment}" />
|
||||
</el-form-item>
|
||||
<ElFormItem label="${comment}" prop="${javaField}">
|
||||
<ElInput v-model="formData.${javaField}" type="textarea" placeholder="请输入${comment}" />
|
||||
</ElFormItem>
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
</el-form>
|
||||
</ElForm>
|
||||
## 特殊:主子表专属逻辑
|
||||
#if ( $table.templateType == 10 || $table.templateType == 12 )
|
||||
<!-- 子表的表单 -->
|
||||
<el-tabs v-model="subTabsName">
|
||||
<ElTabs v-model="subTabsName">
|
||||
#foreach ($subTable in $subTables)
|
||||
#set ($index = $foreach.count - 1)
|
||||
#set ($subClassNameVar = $subClassNameVars.get($index))
|
||||
#set ($subSimpleClassName = $subSimpleClassNames.get($index))
|
||||
#set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))
|
||||
<el-tab-pane name="$subClassNameVar" label="${subTable.classComment}">
|
||||
<ElTabPane name="$subClassNameVar" label="${subTable.classComment}">
|
||||
<${subSimpleClassName}Form ref="${subClassNameVar}FormRef" :${subJoinColumn_strikeCase}="formData?.id" />
|
||||
</el-tab-pane>
|
||||
</ElTabPane>
|
||||
#end
|
||||
</el-tabs>
|
||||
</ElTabs>
|
||||
#end
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
+12
@@ -47,6 +47,16 @@ function handleChange(file: any) {
|
||||
}
|
||||
}
|
||||
|
||||
/** 移除文件 */
|
||||
function handleRemove() {
|
||||
fileRef.value = null;
|
||||
}
|
||||
|
||||
/** 超出文件数量 */
|
||||
function handleExceed() {
|
||||
ElMessage.warning('只能选择一个文件,请先移除已选文件');
|
||||
}
|
||||
|
||||
/** 下载导入模板 */
|
||||
async function handleDownload() {
|
||||
const data = await import${simpleClassName}Template();
|
||||
@@ -61,6 +71,8 @@ async function handleDownload() {
|
||||
:limit="1"
|
||||
accept=".xls,.xlsx"
|
||||
:on-change="handleChange"
|
||||
:on-remove="handleRemove"
|
||||
:on-exceed="handleExceed"
|
||||
:auto-upload="false"
|
||||
>
|
||||
<ElButton type="primary"> 选择 Excel 文件 </ElButton>
|
||||
|
||||
+77
-72
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';
|
||||
import type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${table.businessName}';
|
||||
|
||||
import { ref, h, reactive, onMounted, nextTick } from 'vue';
|
||||
|
||||
@@ -26,10 +26,10 @@ import { VxeColumn, VxeTable } from '#/adapter/vxe-table';
|
||||
import { $t } from '#/locales';
|
||||
#if (${table.templateType} == 2)## 树表接口
|
||||
import { handleTree,isEmpty } from '@vben/utils'
|
||||
import { get${simpleClassName}List, delete${simpleClassName}, export${simpleClassName} } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';
|
||||
import { get${simpleClassName}List, delete${simpleClassName}, export${simpleClassName} } from '#/api/${table.moduleName}/${table.businessName}';
|
||||
#else## 标准表接口
|
||||
import { isEmpty } from '@vben/utils';
|
||||
import { get${simpleClassName}Page, delete${simpleClassName},#if ($deleteBatchEnable) delete${simpleClassName}List,#end export${simpleClassName} } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';
|
||||
import { get${simpleClassName}Page, delete${simpleClassName},#if ($deleteBatchEnable) delete${simpleClassName}List,#end export${simpleClassName} } from '#/api/${table.moduleName}/${table.businessName}';
|
||||
#end
|
||||
#if ($importEnable)
|
||||
import ${simpleClassName}Import from './modules/import-form.vue';
|
||||
@@ -65,12 +65,7 @@ const queryParams = reactive({
|
||||
#end
|
||||
#foreach ($column in $columns)
|
||||
#if ($column.listOperation)
|
||||
#if ($column.listOperationCondition != 'BETWEEN')
|
||||
$column.javaField: undefined,
|
||||
#end
|
||||
#if ($column.htmlType == "datetime" || $column.listOperationCondition == "BETWEEN")
|
||||
$column.javaField: undefined,
|
||||
#end
|
||||
$column.javaField: undefined,
|
||||
#end
|
||||
#end
|
||||
})
|
||||
@@ -195,7 +190,17 @@ function handleRowCheckboxChange({
|
||||
async function handleExport() {
|
||||
try {
|
||||
exportLoading.value = true;
|
||||
const data = await export${simpleClassName}(queryParams);
|
||||
const params = cloneDeep(queryParams) as any;
|
||||
#foreach ($column in $columns)
|
||||
#if ($column.listOperation)
|
||||
#if ($column.htmlType == "datetime" || $column.listOperationCondition == "BETWEEN")
|
||||
if (params.${column.javaField} && Array.isArray(params.${column.javaField})) {
|
||||
params.${column.javaField} = (params.${column.javaField} as string[]).join(',');
|
||||
}
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
const data = await export${simpleClassName}(params);
|
||||
downloadFileFromBlobPart({ fileName: '${table.classComment}.xls', source: data });
|
||||
}finally {
|
||||
exportLoading.value = false;
|
||||
@@ -228,7 +233,7 @@ onMounted(() => {
|
||||
|
||||
<ContentWrap v-if="!hiddenSearchBar">
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form
|
||||
<ElForm
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
inline
|
||||
@@ -247,49 +252,49 @@ onMounted(() => {
|
||||
#set ($dictMethod = "boolean")
|
||||
#end
|
||||
#if ($column.htmlType == "input")
|
||||
<el-form-item label="${comment}">
|
||||
<el-input
|
||||
<ElFormItem label="${comment}">
|
||||
<ElInput
|
||||
v-model="queryParams.${javaField}"
|
||||
placeholder="请输入${comment}"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-[240px]"
|
||||
/>
|
||||
</el-form-item>
|
||||
</ElFormItem>
|
||||
#elseif ($column.htmlType == "select" || $column.htmlType == "radio" || $column.htmlType == "checkbox")
|
||||
<el-form-item label="${comment}">
|
||||
<el-select
|
||||
<ElFormItem label="${comment}">
|
||||
<ElSelect
|
||||
v-model="queryParams.${javaField}"
|
||||
placeholder="请选择${comment}"
|
||||
clearable
|
||||
class="!w-[240px]"
|
||||
>
|
||||
#if ("" != $dictType)## 设置了 dictType 数据字典的情况
|
||||
<el-option
|
||||
<ElOption
|
||||
v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
:label="dict.label"
|
||||
/>
|
||||
#else## 未设置 dictType 数据字典的情况
|
||||
<el-option label="请选择字典生成" value="" />
|
||||
<ElOption label="请选择字典生成" value="" />
|
||||
#end
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
#elseif($column.htmlType == "datetime")
|
||||
#if ($column.listOperationCondition != "BETWEEN")## 非范围
|
||||
<el-form-item label="${comment}">
|
||||
<el-date-picker
|
||||
<ElFormItem label="${comment}">
|
||||
<ElDatePicker
|
||||
v-model="queryParams.${javaField}"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="选择${comment}"
|
||||
clearable
|
||||
class="!w-[240px]"
|
||||
/>
|
||||
</el-form-item>
|
||||
</ElFormItem>
|
||||
#else## 范围
|
||||
<el-form-item label="${comment}">
|
||||
<el-date-picker
|
||||
<ElFormItem label="${comment}">
|
||||
<ElDatePicker
|
||||
v-model="queryParams.${javaField}"
|
||||
type="daterange"
|
||||
value-format="YYYY-MM-DD"
|
||||
@@ -298,33 +303,33 @@ onMounted(() => {
|
||||
end-placeholder="结束日期"
|
||||
class="!w-[240px]"
|
||||
/>
|
||||
</el-form-item>
|
||||
</ElFormItem>
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
<el-form-item>
|
||||
<el-button class="ml-2" @click="resetQuery"> 重置 </el-button>
|
||||
<el-button class="ml-2" @click="handleQuery" type="primary">
|
||||
<ElFormItem>
|
||||
<ElButton class="ml-2" @click="resetQuery"> 重置 </ElButton>
|
||||
<ElButton class="ml-2" @click="handleQuery" type="primary">
|
||||
搜索
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ElButton>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap title="${table.classComment}">
|
||||
<template #extra>
|
||||
<TableToolbar
|
||||
<VbenVxeTableToolbar
|
||||
ref="tableToolbarRef"
|
||||
v-model:hidden-search="hiddenSearchBar"
|
||||
>
|
||||
#if (${table.templateType} == 2)
|
||||
<el-button @click="handleExpand" class="mr-2">
|
||||
<ElButton @click="handleExpand" class="mr-2">
|
||||
{{ isExpanded ? '收缩' : '展开' }}
|
||||
</el-button>
|
||||
</ElButton>
|
||||
#end
|
||||
<el-button
|
||||
<ElButton
|
||||
class="ml-2"
|
||||
:icon="h(Plus)"
|
||||
type="primary"
|
||||
@@ -332,18 +337,18 @@ onMounted(() => {
|
||||
v-access:code="['${permissionPrefix}:create']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.create', ['${table.classComment}']) }}
|
||||
</el-button>
|
||||
</ElButton>
|
||||
#if ($importEnable)
|
||||
<el-button
|
||||
<ElButton
|
||||
class="ml-2"
|
||||
type="primary"
|
||||
@click="handleImport"
|
||||
v-access:code="['${permissionPrefix}:import']"
|
||||
>
|
||||
导入
|
||||
</el-button>
|
||||
</ElButton>
|
||||
#end
|
||||
<el-button
|
||||
<ElButton
|
||||
:icon="h(Download)"
|
||||
type="primary"
|
||||
class="ml-2"
|
||||
@@ -352,9 +357,9 @@ onMounted(() => {
|
||||
v-access:code="['${permissionPrefix}:export']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.export') }}
|
||||
</el-button>
|
||||
</ElButton>
|
||||
#if ($table.templateType != 2 && $deleteBatchEnable)
|
||||
<el-button
|
||||
<ElButton
|
||||
:icon="h(Trash2)"
|
||||
type="danger"
|
||||
class="ml-2"
|
||||
@@ -363,11 +368,11 @@ onMounted(() => {
|
||||
v-access:code="['${table.moduleName}:${simpleClassName_strikeCase}:delete']"
|
||||
>
|
||||
批量删除
|
||||
</el-button>
|
||||
</ElButton>
|
||||
#end
|
||||
</TableToolbar>
|
||||
</VbenVxeTableToolbar>
|
||||
</template>
|
||||
<vxe-table
|
||||
<VxeTable
|
||||
ref="tableRef"
|
||||
:data="list"
|
||||
#if ( $table.templateType == 2 )
|
||||
@@ -395,27 +400,27 @@ onMounted(() => {
|
||||
#end
|
||||
>
|
||||
#if ($table.templateType != 2 && $deleteBatchEnable)
|
||||
<vxe-column type="checkbox" width="40"></vxe-column>
|
||||
<VxeColumn type="checkbox" width="40"></VxeColumn>
|
||||
#end
|
||||
## 特殊:主子表专属逻辑
|
||||
#if ( $table.templateType == 12 && $subTables && $subTables.size() > 0 )
|
||||
<!-- 子表的列表 -->
|
||||
<vxe-column type="expand" width="60">
|
||||
<VxeColumn type="expand" width="60">
|
||||
<template #content="{ row }">
|
||||
<!-- 子表的表单 -->
|
||||
<el-tabs v-model="subTabsName" class="mx-8">
|
||||
<ElTabs v-model="subTabsName" class="mx-8">
|
||||
#foreach ($subTable in $subTables)
|
||||
#set ($index = $foreach.count - 1)
|
||||
#set ($subClassNameVar = $subClassNameVars.get($index))
|
||||
#set ($subSimpleClassName = $subSimpleClassNames.get($index))
|
||||
#set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))
|
||||
<el-tab-pane name="$subClassNameVar" label="${subTable.classComment}">
|
||||
<ElTabPane name="$subClassNameVar" label="${subTable.classComment}">
|
||||
<${subSimpleClassName}List :${subJoinColumn_strikeCase}="row?.id" />
|
||||
</el-tab-pane>
|
||||
</ElTabPane>
|
||||
#end
|
||||
</el-tabs>
|
||||
</ElTabs>
|
||||
</template>
|
||||
</vxe-column>
|
||||
</VxeColumn>
|
||||
#end
|
||||
#foreach($column in $columns)
|
||||
#if ($column.listOperationResult)
|
||||
@@ -424,28 +429,28 @@ onMounted(() => {
|
||||
#set ($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
|
||||
#set ($comment=$column.columnComment)
|
||||
#if ($column.javaType == "LocalDateTime")## 时间类型
|
||||
<vxe-column field="${javaField}" title="${comment}" align="center">
|
||||
<VxeColumn field="${javaField}" title="${comment}" align="center">
|
||||
<template #default="{row}">
|
||||
{{formatDateTime(row.${javaField})}}
|
||||
</template>
|
||||
</vxe-column>
|
||||
</VxeColumn>
|
||||
#elseif($column.dictType && "" != $column.dictType)## 数据字典
|
||||
<vxe-column field="${javaField}" title="${comment}" align="center">
|
||||
<VxeColumn field="${javaField}" title="${comment}" align="center">
|
||||
<template #default="{row}">
|
||||
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="row.${javaField}" />
|
||||
</template>
|
||||
</vxe-column>
|
||||
</VxeColumn>
|
||||
#elseif ($table.templateType == 2 && $javaField == $treeNameColumn.javaField)
|
||||
<vxe-column field="${javaField}" title="${comment}" align="center" tree-node/>
|
||||
<VxeColumn field="${javaField}" title="${comment}" align="center" tree-node/>
|
||||
#else
|
||||
<vxe-column field="${javaField}" title="${comment}" align="center" />
|
||||
<VxeColumn field="${javaField}" title="${comment}" align="center" />
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
<vxe-column field="operation" title="操作" align="center">
|
||||
<VxeColumn field="operation" title="操作" align="center">
|
||||
<template #default="{row}">
|
||||
#if ( $table.templateType == 2 )
|
||||
<el-button
|
||||
<ElButton
|
||||
size="small"
|
||||
type="primary"
|
||||
link
|
||||
@@ -453,9 +458,9 @@ onMounted(() => {
|
||||
v-access:code="['${permissionPrefix}:create']"
|
||||
>
|
||||
新增下级
|
||||
</el-button>
|
||||
</ElButton>
|
||||
#end
|
||||
<el-button
|
||||
<ElButton
|
||||
size="small"
|
||||
type="primary"
|
||||
link
|
||||
@@ -463,8 +468,8 @@ onMounted(() => {
|
||||
v-access:code="['${permissionPrefix}:update']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.edit') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
</ElButton>
|
||||
<ElButton
|
||||
size="small"
|
||||
type="danger"
|
||||
link
|
||||
@@ -476,14 +481,14 @@ onMounted(() => {
|
||||
v-access:code="['${permissionPrefix}:delete']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.delete') }}
|
||||
</el-button>
|
||||
</ElButton>
|
||||
</template>
|
||||
</vxe-column>
|
||||
</vxe-table>
|
||||
</VxeColumn>
|
||||
</VxeTable>
|
||||
#if ( $table.templateType != 2 )
|
||||
<!-- 分页 -->
|
||||
<div class="mt-2 flex justify-end">
|
||||
<el-pagination
|
||||
<ElPagination
|
||||
:total="total"
|
||||
v-model:current-page="queryParams.pageNo"
|
||||
v-model:page-size="queryParams.pageSize"
|
||||
@@ -498,17 +503,17 @@ onMounted(() => {
|
||||
#if ($table.templateType == 11) ## erp情况
|
||||
<ContentWrap>
|
||||
<!-- 子表的表单 -->
|
||||
<el-tabs v-model="subTabsName">
|
||||
<ElTabs v-model="subTabsName">
|
||||
#foreach ($subTable in $subTables)
|
||||
#set ($index = $foreach.count - 1)
|
||||
#set ($subClassNameVar = $subClassNameVars.get($index))
|
||||
#set ($subSimpleClassName = $subSimpleClassNames.get($index))
|
||||
#set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))
|
||||
<el-tab-pane name="$subClassNameVar" label="${subTable.classComment}">
|
||||
<ElTabPane name="$subClassNameVar" label="${subTable.classComment}">
|
||||
<${subSimpleClassName}List :${subJoinColumn_strikeCase}="select${simpleClassName}?.id" />
|
||||
</el-tab-pane>
|
||||
</ElTabPane>
|
||||
#end
|
||||
</el-tabs>
|
||||
</ElTabs>
|
||||
</ContentWrap>
|
||||
#end
|
||||
</Page>
|
||||
|
||||
+41
-41
@@ -3,7 +3,7 @@
|
||||
#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段
|
||||
#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex))
|
||||
<script lang="ts" setup>
|
||||
import type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';
|
||||
import type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${table.businessName}';
|
||||
import type { FormRules } from 'element-plus';
|
||||
|
||||
import { computed, ref, reactive } from 'vue';
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { get${subSimpleClassName}, create${subSimpleClassName}, update${subSimpleClassName} } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';
|
||||
import { get${subSimpleClassName}, create${subSimpleClassName}, update${subSimpleClassName} } from '#/api/${table.moduleName}/${table.businessName}';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
const getTitle = computed(() => {
|
||||
@@ -107,7 +107,7 @@
|
||||
|
||||
<template>
|
||||
<Modal :title="getTitle">
|
||||
<el-form
|
||||
<ElForm
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="rules"
|
||||
@@ -128,83 +128,83 @@
|
||||
#set ($dictMethod = "boolean")
|
||||
#end
|
||||
#if ($column.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里
|
||||
<el-form-item label="${comment}" prop="${javaField}">
|
||||
<el-input v-model="formData.${javaField}" placeholder="请输入${comment}" />
|
||||
</el-form-item>
|
||||
<ElFormItem label="${comment}" prop="${javaField}">
|
||||
<ElInput v-model="formData.${javaField}" placeholder="请输入${comment}" />
|
||||
</ElFormItem>
|
||||
#elseif($column.htmlType == "imageUpload")## 图片上传
|
||||
<el-form-item label="${comment}" prop="${javaField}">
|
||||
<ElFormItem label="${comment}" prop="${javaField}">
|
||||
<ImageUpload v-model="formData.${javaField}" />
|
||||
</el-form-item>
|
||||
</ElFormItem>
|
||||
#elseif($column.htmlType == "fileUpload")## 文件上传
|
||||
<el-form-item label="${comment}" prop="${javaField}">
|
||||
<ElFormItem label="${comment}" prop="${javaField}">
|
||||
<FileUpload v-model="formData.${javaField}" />
|
||||
</el-form-item>
|
||||
</ElFormItem>
|
||||
#elseif($column.htmlType == "editor")## 文本编辑器
|
||||
<el-form-item label="${comment}" prop="${javaField}">
|
||||
<ElFormItem label="${comment}" prop="${javaField}">
|
||||
<RichTextarea v-model="formData.${javaField}" height="500px" />
|
||||
</el-form-item>
|
||||
</ElFormItem>
|
||||
#elseif($column.htmlType == "select")## 下拉框
|
||||
<el-form-item label="${comment}" prop="${javaField}">
|
||||
<el-select v-model="formData.${javaField}" placeholder="请选择${comment}">
|
||||
<ElFormItem label="${comment}" prop="${javaField}">
|
||||
<ElSelect v-model="formData.${javaField}" placeholder="请选择${comment}">
|
||||
#if ("" != $dictType)## 有数据字典
|
||||
<el-option
|
||||
<ElOption
|
||||
v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
:label="dict.label"
|
||||
/>
|
||||
#else##没数据字典
|
||||
<el-option label="请选择字典生成" value="" />
|
||||
<ElOption label="请选择字典生成" value="" />
|
||||
#end
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
#elseif($column.htmlType == "checkbox")## 多选框
|
||||
<el-form-item label="${comment}" prop="${javaField}">
|
||||
<el-checkbox-group v-model="formData.${javaField}">
|
||||
<ElFormItem label="${comment}" prop="${javaField}">
|
||||
<ElCheckboxGroup v-model="formData.${javaField}">
|
||||
#if ("" != $dictType)## 有数据字典
|
||||
<el-checkbox
|
||||
<ElCheckbox
|
||||
v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-checkbox>
|
||||
</ElCheckbox>
|
||||
#else##没数据字典
|
||||
<el-checkbox label="请选择字典生成" />
|
||||
<ElCheckbox label="请选择字典生成" />
|
||||
#end
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
</ElCheckboxGroup>
|
||||
</ElFormItem>
|
||||
#elseif($column.htmlType == "radio")## 单选框
|
||||
<el-form-item label="${comment}" prop="${javaField}">
|
||||
<el-radio-group v-model="formData.${javaField}">
|
||||
<ElFormItem label="${comment}" prop="${javaField}">
|
||||
<ElRadioGroup v-model="formData.${javaField}">
|
||||
#if ("" != $dictType)## 有数据字典
|
||||
<el-radio
|
||||
<ElRadio
|
||||
v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
</ElRadio>
|
||||
#else##没数据字典
|
||||
<el-radio :label="1">请选择字典生成</el-radio>
|
||||
<ElRadio :label="1">请选择字典生成</ElRadio>
|
||||
#end
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</ElRadioGroup>
|
||||
</ElFormItem>
|
||||
#elseif($column.htmlType == "datetime")## 时间框
|
||||
<el-form-item label="${comment}" prop="${javaField}">
|
||||
<el-date-picker
|
||||
<ElFormItem label="${comment}" prop="${javaField}">
|
||||
<ElDatePicker
|
||||
v-model="formData.${javaField}"
|
||||
value-format="x"
|
||||
placeholder="选择${comment}"
|
||||
/>
|
||||
</el-form-item>
|
||||
</ElFormItem>
|
||||
#elseif($column.htmlType == "textarea")## 文本框
|
||||
<el-form-item label="${comment}" prop="${javaField}">
|
||||
<el-input v-model="formData.${javaField}" type="textarea" placeholder="请输入${comment}" />
|
||||
</el-form-item>
|
||||
<ElFormItem label="${comment}" prop="${javaField}">
|
||||
<ElInput v-model="formData.${javaField}" type="textarea" placeholder="请输入${comment}" />
|
||||
</ElFormItem>
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
</el-form>
|
||||
</ElForm>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
+181
-104
@@ -5,26 +5,61 @@
|
||||
#set ($subClassNameVar = $subClassNameVars.get($subIndex))
|
||||
#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写
|
||||
<script lang="ts" setup>
|
||||
import type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';
|
||||
#if ($subTable.subJoinMany)
|
||||
import type { VxeTableInstance } from '#/adapter/vxe-table';
|
||||
#else
|
||||
import type { FormRules } from 'element-plus';
|
||||
#end
|
||||
import type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${table.businessName}';
|
||||
|
||||
import { computed, ref, reactive, h, onMounted,watch,nextTick } from 'vue';
|
||||
#if ($subTable.subJoinMany)
|
||||
import { ref, watch } from 'vue';
|
||||
#else
|
||||
import { nextTick, reactive, ref, watch } from 'vue';
|
||||
#end
|
||||
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
import { getDictOptions } from '@vben/hooks';
|
||||
#if ($subTable.subJoinMany)
|
||||
import { Plus } from '@vben/icons';
|
||||
#end
|
||||
|
||||
import { ElMessage, ElTabs, ElTabPane, ElForm, ElFormItem, ElInput, ElButton, ElSelect, ElOption, ElRadioGroup, ElRadio, ElCheckboxGroup, ElCheckbox, ElDatePicker } from 'element-plus';
|
||||
import { $t } from '#/locales';
|
||||
#set ($hasCheckbox = false)
|
||||
#foreach ($column in $subColumns)
|
||||
#if (($column.createOperation || $column.updateOperation) && $column.id != $subJoinColumn.id && $column.htmlType == "checkbox")
|
||||
#set ($hasCheckbox = true)
|
||||
#end
|
||||
#end
|
||||
import {
|
||||
#if ($subTable.subJoinMany)
|
||||
ElButton,
|
||||
#else
|
||||
ElForm,
|
||||
ElFormItem,
|
||||
#end
|
||||
#if ($hasCheckbox)
|
||||
ElCheckbox,
|
||||
ElCheckboxGroup,
|
||||
#end
|
||||
ElDatePicker,
|
||||
ElInput,
|
||||
#if ($subTable.subJoinMany)
|
||||
ElMessage,
|
||||
#end
|
||||
ElOption,
|
||||
ElRadio,
|
||||
ElRadioGroup,
|
||||
ElSelect,
|
||||
} from 'element-plus';
|
||||
|
||||
#if ($subTable.subJoinMany) ## 一对多
|
||||
import type { VxeTableInstance } from '#/adapter/vxe-table';
|
||||
import { Plus } from "@vben/icons";
|
||||
import { VxeColumn, VxeTable } from '#/adapter/vxe-table';
|
||||
import { get${subSimpleClassName}ListBy${SubJoinColumnName} } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';
|
||||
import { get${subSimpleClassName}ListBy${SubJoinColumnName} } from '#/api/${table.moduleName}/${table.businessName}';
|
||||
#else
|
||||
import type { FormRules } from 'element-plus';
|
||||
import { Tinymce as RichTextarea } from '#/components/tinymce';
|
||||
import { get${subSimpleClassName}By${SubJoinColumnName} } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';
|
||||
import { get${subSimpleClassName}By${SubJoinColumnName} } from '#/api/${table.moduleName}/${table.businessName}';
|
||||
#end
|
||||
import { $t } from '#/locales';
|
||||
|
||||
const props = defineProps<{
|
||||
${subJoinColumn.javaField}?: number // ${subJoinColumn.columnComment}(主表的关联字段)
|
||||
@@ -43,24 +78,54 @@ async function onDelete(row: ${simpleClassName}Api.${subSimpleClassName}) {
|
||||
await tableRef.value?.remove(row);
|
||||
}
|
||||
|
||||
/** 提供获取表格数据的方法供父组件调用 */
|
||||
defineExpose({
|
||||
getData: (): ${simpleClassName}Api.${subSimpleClassName}[] => {
|
||||
const data = list.value as ${simpleClassName}Api.${subSimpleClassName}[];
|
||||
const removeRecords = tableRef.value?.getRemoveRecords() as ${simpleClassName}Api.${subSimpleClassName}[];
|
||||
const insertRecords = tableRef.value?.getInsertRecords() as ${simpleClassName}Api.${subSimpleClassName}[];
|
||||
return [
|
||||
...data.filter(
|
||||
(row) => !removeRecords.some((removed) => removed.id === row.id),
|
||||
),
|
||||
...insertRecords.map((row: any) => ({ ...row, id: undefined })),
|
||||
},
|
||||
});
|
||||
/** 获取当前子表数据,新增行不提交表格生成的临时编号 */
|
||||
function getData(): ${simpleClassName}Api.${subSimpleClassName}[] {
|
||||
const table = tableRef.value;
|
||||
if (!table) {
|
||||
return [];
|
||||
}
|
||||
const { fullData } = table.getTableData();
|
||||
const inserted = new Set(table.getInsertRecords());
|
||||
return fullData.map((row) =>
|
||||
inserted.has(row) ? { ...row, id: undefined } : { ...row },
|
||||
);
|
||||
}
|
||||
|
||||
#set ($hasRequiredColumn = false)
|
||||
#foreach ($column in $subColumns)
|
||||
#if (($column.createOperation || $column.updateOperation) && !$column.nullable && !$column.primaryKey && $column.id != $subJoinColumn.id)
|
||||
#set ($hasRequiredColumn = true)
|
||||
#end
|
||||
#end
|
||||
/** 校验子表必填字段 */
|
||||
function validate(): boolean {
|
||||
#if ($hasRequiredColumn)
|
||||
for (const [index, row] of getData().entries()) {
|
||||
#foreach ($column in $subColumns)
|
||||
#if (($column.createOperation || $column.updateOperation) && !$column.nullable && !$column.primaryKey && $column.id != $subJoinColumn.id)
|
||||
if (
|
||||
row.${column.javaField} === null ||
|
||||
row.${column.javaField} === undefined ||
|
||||
(typeof row.${column.javaField} === 'string' && String(row.${column.javaField}).trim() === '') ||
|
||||
(Array.isArray(row.${column.javaField}) && row.${column.javaField}.length === 0)
|
||||
) {
|
||||
ElMessage.error(`第 #[[${index + 1}]]# 行:${column.columnComment}不能为空`);
|
||||
return false;
|
||||
}
|
||||
#end
|
||||
#end
|
||||
}
|
||||
#end
|
||||
return true;
|
||||
}
|
||||
|
||||
defineExpose({ getData, validate });
|
||||
|
||||
/** 监听主表的关联字段的变化,加载对应的子表数据 */
|
||||
watch(
|
||||
() => props.${subJoinColumn.javaField},
|
||||
async (val) => {
|
||||
list.value = [];
|
||||
if (!val) {
|
||||
return;
|
||||
}
|
||||
@@ -99,6 +164,17 @@ defineExpose({
|
||||
watch(
|
||||
() => props.${subJoinColumn.javaField},
|
||||
async (val) => {
|
||||
formData.value = {
|
||||
#foreach ($column in $subColumns)
|
||||
#if ($column.createOperation || $column.updateOperation)
|
||||
#if ($column.htmlType == "checkbox")
|
||||
$column.javaField: [],
|
||||
#else
|
||||
$column.javaField: undefined,
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
};
|
||||
if (!val) {
|
||||
return;
|
||||
}
|
||||
@@ -112,10 +188,11 @@ watch(
|
||||
|
||||
<template>
|
||||
#if ($subTable.subJoinMany) ## 一对多
|
||||
<vxe-table ref="tableRef" :data="list" show-overflow class="mx-4">
|
||||
<VxeTable ref="tableRef" :data="list" show-overflow class="mx-4">
|
||||
#foreach($column in $subColumns)
|
||||
#if ($column.createOperation || $column.updateOperation)
|
||||
#set ($comment = $column.columnComment)
|
||||
#set ($dictType = $column.dictType)
|
||||
#set ($javaField = $column.javaField)
|
||||
#set ($javaType = $column.javaType)
|
||||
#if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short")
|
||||
@@ -127,99 +204,99 @@ watch(
|
||||
#end
|
||||
#if ( $column.id == $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写
|
||||
#elseif ($column.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里
|
||||
<vxe-column field="${javaField}" title="${comment}" align="center">
|
||||
<VxeColumn field="${javaField}" title="${comment}" align="center">
|
||||
<template #default="{row}">
|
||||
<el-input v-model="row.${javaField}" />
|
||||
<ElInput v-model="row.${javaField}" />
|
||||
</template>
|
||||
</vxe-column>
|
||||
</VxeColumn>
|
||||
#elseif($column.htmlType == "imageUpload")## 图片上传
|
||||
<vxe-column field="${javaField}" title="${comment}" align="center">
|
||||
<VxeColumn field="${javaField}" title="${comment}" align="center">
|
||||
<template #default="{row}">
|
||||
<ImageUpload v-model="row.${javaField}" />
|
||||
</template>
|
||||
</vxe-column>
|
||||
</VxeColumn>
|
||||
#elseif($column.htmlType == "fileUpload")## 文件上传
|
||||
<vxe-column field="${javaField}" title="${comment}" align="center">
|
||||
<VxeColumn field="${javaField}" title="${comment}" align="center">
|
||||
<template #default="{row}">
|
||||
<FileUpload v-model="row.${javaField}" />
|
||||
</template>
|
||||
</vxe-column>
|
||||
</VxeColumn>
|
||||
#elseif($column.htmlType == "select")## 下拉框
|
||||
<vxe-column field="${javaField}" title="${comment}" align="center">
|
||||
<VxeColumn field="${javaField}" title="${comment}" align="center">
|
||||
<template #default="{row}">
|
||||
<el-select v-model="row.${javaField}" placeholder="请选择${comment}">
|
||||
<ElSelect v-model="row.${javaField}" placeholder="请选择${comment}">
|
||||
#if ("" != $dictType)## 有数据字典
|
||||
<el-option
|
||||
<ElOption
|
||||
v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
:label="dict.label"
|
||||
/>
|
||||
#else##没数据字典
|
||||
<el-option label="请选择字典生成" value="" />
|
||||
<ElOption label="请选择字典生成" value="" />
|
||||
#end
|
||||
</el-select>
|
||||
</ElSelect>
|
||||
</template>
|
||||
</vxe-column>
|
||||
</VxeColumn>
|
||||
#elseif($column.htmlType == "checkbox")## 多选框
|
||||
<vxe-column field="${javaField}" title="${comment}" align="center">
|
||||
<VxeColumn field="${javaField}" title="${comment}" align="center">
|
||||
<template #default="{row}">
|
||||
<el-checkbox-group v-model="row.${javaField}">
|
||||
<ElCheckboxGroup v-model="row.${javaField}">
|
||||
#if ("" != $dictType)## 有数据字典
|
||||
<el-checkbox
|
||||
<ElCheckbox
|
||||
v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-checkbox>
|
||||
</ElCheckbox>
|
||||
#else##没数据字典
|
||||
<el-checkbox label="请选择字典生成" />
|
||||
<ElCheckbox label="请选择字典生成" />
|
||||
#end
|
||||
</el-checkbox-group>
|
||||
</ElCheckboxGroup>
|
||||
</template>
|
||||
</vxe-column>
|
||||
</VxeColumn>
|
||||
#elseif($column.htmlType == "radio")## 单选框
|
||||
<vxe-column field="${javaField}" title="${comment}" align="center">
|
||||
<VxeColumn field="${javaField}" title="${comment}" align="center">
|
||||
<template #default="{row}">
|
||||
<el-radio-group v-model="row.${javaField}">
|
||||
<ElRadioGroup v-model="row.${javaField}">
|
||||
#if ("" != $dictType)## 有数据字典
|
||||
<el-radio
|
||||
<ElRadio
|
||||
v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
</ElRadio>
|
||||
#else##没数据字典
|
||||
<el-radio :label="1">请选择字典生成</el-radio>
|
||||
<ElRadio :label="1">请选择字典生成</ElRadio>
|
||||
#end
|
||||
</el-radio-group>
|
||||
</ElRadioGroup>
|
||||
</template>
|
||||
</vxe-column>
|
||||
</VxeColumn>
|
||||
#elseif($column.htmlType == "datetime")## 时间框
|
||||
<vxe-column field="${javaField}" title="${comment}" align="center">
|
||||
<VxeColumn field="${javaField}" title="${comment}" align="center">
|
||||
<template #default="{row}">
|
||||
<el-date-picker
|
||||
<ElDatePicker
|
||||
v-model="row.${javaField}"
|
||||
type="datetime"
|
||||
value-format="x"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
/>
|
||||
</template>
|
||||
</vxe-column>
|
||||
</VxeColumn>
|
||||
#elseif($column.htmlType == "textarea" || $column.htmlType == "editor")## 文本框
|
||||
<vxe-column field="${javaField}" title="${comment}" align="center">
|
||||
<VxeColumn field="${javaField}" title="${comment}" align="center">
|
||||
<template #default="{row}">
|
||||
<el-input v-model="row.${javaField}" type="textarea" />
|
||||
<ElInput v-model="row.${javaField}" type="textarea" />
|
||||
</template>
|
||||
</vxe-column>
|
||||
</VxeColumn>
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
<vxe-column field="operation" title="操作" align="center">
|
||||
<VxeColumn field="operation" title="操作" align="center">
|
||||
<template #default="{row}">
|
||||
<el-button
|
||||
<ElButton
|
||||
size="small"
|
||||
type="danger"
|
||||
link
|
||||
@@ -227,17 +304,17 @@ watch(
|
||||
v-access:code="['${permissionPrefix}:delete']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.delete') }}
|
||||
</el-button>
|
||||
</ElButton>
|
||||
</template>
|
||||
</vxe-column>
|
||||
</vxe-table>
|
||||
</VxeColumn>
|
||||
</VxeTable>
|
||||
<div class="flex justify-center mt-4">
|
||||
<el-button :icon="h(Plus)" type="primary" plain @click="handleAdd" v-access:code="['${permissionPrefix}:create']">
|
||||
<ElButton :icon="Plus" type="primary" plain @click="handleAdd" v-access:code="['${permissionPrefix}:create']">
|
||||
{{ $t('ui.actionTitle.create', ['${subTable.classComment}']) }}
|
||||
</el-button>
|
||||
</ElButton>
|
||||
</div>
|
||||
#else
|
||||
<el-form
|
||||
<ElForm
|
||||
ref="formRef"
|
||||
class="mx-4"
|
||||
:model="formData"
|
||||
@@ -259,83 +336,83 @@ watch(
|
||||
#set ($dictMethod = "boolean")
|
||||
#end
|
||||
#if ($column.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里
|
||||
<el-form-item label="${comment}" prop="${javaField}">
|
||||
<el-input v-model="formData.${javaField}" placeholder="请输入${comment}" />
|
||||
</el-form-item>
|
||||
<ElFormItem label="${comment}" prop="${javaField}">
|
||||
<ElInput v-model="formData.${javaField}" placeholder="请输入${comment}" />
|
||||
</ElFormItem>
|
||||
#elseif($column.htmlType == "imageUpload")## 图片上传
|
||||
<el-form-item label="${comment}" prop="${javaField}">
|
||||
<ElFormItem label="${comment}" prop="${javaField}">
|
||||
<ImageUpload v-model="formData.${javaField}" />
|
||||
</el-form-item>
|
||||
</ElFormItem>
|
||||
#elseif($column.htmlType == "fileUpload")## 文件上传
|
||||
<el-form-item label="${comment}" prop="${javaField}">
|
||||
<ElFormItem label="${comment}" prop="${javaField}">
|
||||
<FileUpload v-model="formData.${javaField}" />
|
||||
</el-form-item>
|
||||
</ElFormItem>
|
||||
#elseif($column.htmlType == "editor")## 文本编辑器
|
||||
<el-form-item label="${comment}" prop="${javaField}">
|
||||
<ElFormItem label="${comment}" prop="${javaField}">
|
||||
<RichTextarea v-model="formData.${javaField}" height="500px" />
|
||||
</el-form-item>
|
||||
</ElFormItem>
|
||||
#elseif($column.htmlType == "select")## 下拉框
|
||||
<el-form-item label="${comment}" prop="${javaField}">
|
||||
<el-select v-model="formData.${javaField}" placeholder="请选择${comment}">
|
||||
<ElFormItem label="${comment}" prop="${javaField}">
|
||||
<ElSelect v-model="formData.${javaField}" placeholder="请选择${comment}">
|
||||
#if ("" != $dictType)## 有数据字典
|
||||
<el-option
|
||||
<ElOption
|
||||
v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
:label="dict.label"
|
||||
/>
|
||||
#else##没数据字典
|
||||
<el-option label="请选择字典生成" value="" />
|
||||
<ElOption label="请选择字典生成" value="" />
|
||||
#end
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
#elseif($column.htmlType == "checkbox")## 多选框
|
||||
<el-form-item label="${comment}" prop="${javaField}">
|
||||
<el-checkbox-group v-model="formData.${javaField}">
|
||||
<ElFormItem label="${comment}" prop="${javaField}">
|
||||
<ElCheckboxGroup v-model="formData.${javaField}">
|
||||
#if ("" != $dictType)## 有数据字典
|
||||
<el-checkbox
|
||||
<ElCheckbox
|
||||
v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-checkbox>
|
||||
</ElCheckbox>
|
||||
#else##没数据字典
|
||||
<el-checkbox label="请选择字典生成" />
|
||||
<ElCheckbox label="请选择字典生成" />
|
||||
#end
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
</ElCheckboxGroup>
|
||||
</ElFormItem>
|
||||
#elseif($column.htmlType == "radio")## 单选框
|
||||
<el-form-item label="${comment}" prop="${javaField}">
|
||||
<el-radio-group v-model="formData.${javaField}">
|
||||
<ElFormItem label="${comment}" prop="${javaField}">
|
||||
<ElRadioGroup v-model="formData.${javaField}">
|
||||
#if ("" != $dictType)## 有数据字典
|
||||
<el-radio
|
||||
<ElRadio
|
||||
v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
</ElRadio>
|
||||
#else##没数据字典
|
||||
<el-radio :label="1">请选择字典生成</el-radio>
|
||||
<ElRadio :label="1">请选择字典生成</ElRadio>
|
||||
#end
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</ElRadioGroup>
|
||||
</ElFormItem>
|
||||
#elseif($column.htmlType == "datetime")## 时间框
|
||||
<el-form-item label="${comment}" prop="${javaField}">
|
||||
<el-date-picker
|
||||
<ElFormItem label="${comment}" prop="${javaField}">
|
||||
<ElDatePicker
|
||||
v-model="formData.${javaField}"
|
||||
value-format="x"
|
||||
placeholder="选择${comment}"
|
||||
/>
|
||||
</el-form-item>
|
||||
</ElFormItem>
|
||||
#elseif($column.htmlType == "textarea")## 文本框
|
||||
<el-form-item label="${comment}" prop="${javaField}">
|
||||
<el-input v-model="formData.${javaField}" type="textarea" placeholder="请输入${comment}" />
|
||||
</el-form-item>
|
||||
<ElFormItem label="${comment}" prop="${javaField}">
|
||||
<ElInput v-model="formData.${javaField}" type="textarea" placeholder="请输入${comment}" />
|
||||
</ElFormItem>
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
</el-form>
|
||||
</ElForm>
|
||||
#end
|
||||
</template>
|
||||
|
||||
+56
-55
@@ -6,13 +6,14 @@
|
||||
#set ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($subIndex))
|
||||
#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写
|
||||
<script lang="ts" setup>
|
||||
import type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';
|
||||
import type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${table.businessName}';
|
||||
import type { VxeTableInstance } from '#/adapter/vxe-table';
|
||||
|
||||
import { reactive,ref, h, nextTick,watch,onMounted } from 'vue';
|
||||
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
import { getDictOptions } from '@vben/hooks';
|
||||
import { formatDateTime } from '@vben/utils';
|
||||
|
||||
import { DictTag } from '#/components/dict-tag';
|
||||
import { getRangePickerDefaultProps } from '#/utils';
|
||||
@@ -31,13 +32,13 @@
|
||||
#end
|
||||
|
||||
#if ($table.templateType == 11) ## erp
|
||||
import { delete${subSimpleClassName},#if ($deleteBatchEnable) delete${subSimpleClassName}List,#end get${subSimpleClassName}Page } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';
|
||||
import { isEmpty } from '@vben/utils';
|
||||
import { delete${subSimpleClassName},#if ($deleteBatchEnable) delete${subSimpleClassName}List,#end get${subSimpleClassName}Page } from '#/api/${table.moduleName}/${table.businessName}';
|
||||
import { cloneDeep, isEmpty } from '@vben/utils';
|
||||
#else
|
||||
#if ($subTable.subJoinMany) ## 一对多
|
||||
import { get${subSimpleClassName}ListBy${SubJoinColumnName} } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';
|
||||
import { get${subSimpleClassName}ListBy${SubJoinColumnName} } from '#/api/${table.moduleName}/${table.businessName}';
|
||||
#else
|
||||
import { get${subSimpleClassName}By${SubJoinColumnName} } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';
|
||||
import { get${subSimpleClassName}By${SubJoinColumnName} } from '#/api/${table.moduleName}/${table.businessName}';
|
||||
#end
|
||||
#end
|
||||
|
||||
@@ -203,7 +204,7 @@ onMounted(() => {
|
||||
<div class="h-[600px]">
|
||||
<ContentWrap v-if="!hiddenSearchBar">
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form
|
||||
<ElForm
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
inline
|
||||
@@ -222,49 +223,49 @@ onMounted(() => {
|
||||
#set ($dictMethod = "boolean")
|
||||
#end
|
||||
#if ($column.htmlType == "input")
|
||||
<el-form-item label="${comment}">
|
||||
<el-input
|
||||
<ElFormItem label="${comment}">
|
||||
<ElInput
|
||||
v-model="queryParams.${javaField}"
|
||||
placeholder="请输入${comment}"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-[240px]"
|
||||
/>
|
||||
</el-form-item>
|
||||
</ElFormItem>
|
||||
#elseif ($column.htmlType == "select" || $column.htmlType == "radio" || $column.htmlType == "checkbox")
|
||||
<el-form-item label="${comment}">
|
||||
<el-select
|
||||
<ElFormItem label="${comment}">
|
||||
<ElSelect
|
||||
v-model="queryParams.${javaField}"
|
||||
placeholder="请选择${comment}"
|
||||
clearable
|
||||
class="!w-[240px]"
|
||||
>
|
||||
#if ("" != $dictType)## 设置了 dictType 数据字典的情况
|
||||
<el-option
|
||||
<ElOption
|
||||
v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
:label="dict.label"
|
||||
/>
|
||||
#else## 未设置 dictType 数据字典的情况
|
||||
<el-option label="请选择字典生成" value="" />
|
||||
<ElOption label="请选择字典生成" value="" />
|
||||
#end
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
#elseif($column.htmlType == "datetime")
|
||||
#if ($column.listOperationCondition != "BETWEEN")## 非范围
|
||||
<el-form-item label="${comment}">
|
||||
<el-date-picker
|
||||
<ElFormItem label="${comment}">
|
||||
<ElDatePicker
|
||||
v-model="queryParams.${javaField}"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="选择${comment}"
|
||||
clearable
|
||||
class="!w-[240px]"
|
||||
/>
|
||||
</el-form-item>
|
||||
</ElFormItem>
|
||||
#else## 范围
|
||||
<el-form-item label="${comment}">
|
||||
<el-date-picker
|
||||
<ElFormItem label="${comment}">
|
||||
<ElDatePicker
|
||||
v-model="queryParams.${javaField}"
|
||||
type="daterange"
|
||||
value-format="YYYY-MM-DD"
|
||||
@@ -273,18 +274,18 @@ onMounted(() => {
|
||||
end-placeholder="结束日期"
|
||||
class="!w-[240px]"
|
||||
/>
|
||||
</el-form-item>
|
||||
</ElFormItem>
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
<el-form-item>
|
||||
<el-button class="ml-2" @click="resetQuery"> 重置 </el-button>
|
||||
<el-button class="ml-2" @click="handleQuery" type="primary">
|
||||
<ElFormItem>
|
||||
<ElButton class="ml-2" @click="resetQuery"> 重置 </ElButton>
|
||||
<ElButton class="ml-2" @click="handleQuery" type="primary">
|
||||
搜索
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ElButton>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
@@ -294,7 +295,7 @@ onMounted(() => {
|
||||
ref="tableToolbarRef"
|
||||
v-model:hidden-search="hiddenSearchBar"
|
||||
>
|
||||
<el-button
|
||||
<ElButton
|
||||
class="ml-2"
|
||||
:icon="h(Plus)"
|
||||
type="primary"
|
||||
@@ -302,9 +303,9 @@ onMounted(() => {
|
||||
v-access:code="['${permissionPrefix}:create']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.create', ['${table.classComment}']) }}
|
||||
</el-button>
|
||||
</ElButton>
|
||||
#if ($deleteBatchEnable)
|
||||
<el-button
|
||||
<ElButton
|
||||
:icon="h(Trash2)"
|
||||
type="danger"
|
||||
class="ml-2"
|
||||
@@ -313,11 +314,11 @@ onMounted(() => {
|
||||
v-access:code="['${table.moduleName}:${simpleClassName_strikeCase}:delete']"
|
||||
>
|
||||
批量删除
|
||||
</el-button>
|
||||
</ElButton>
|
||||
#end
|
||||
</TableToolbar>
|
||||
</VbenVxeTableToolbar>
|
||||
</template>
|
||||
<vxe-table
|
||||
<VxeTable
|
||||
ref="tableRef"
|
||||
:data="list"
|
||||
show-overflow
|
||||
@@ -328,7 +329,7 @@ onMounted(() => {
|
||||
#end
|
||||
>
|
||||
#if ($deleteBatchEnable)
|
||||
<vxe-column type="checkbox" width="40"></vxe-column>
|
||||
<VxeColumn type="checkbox" width="40"></VxeColumn>
|
||||
#end
|
||||
#foreach($column in $subColumns)
|
||||
#if ($column.listOperationResult)
|
||||
@@ -336,27 +337,27 @@ onMounted(() => {
|
||||
#set ($javaField = $column.javaField)
|
||||
#set ($comment=$column.columnComment)
|
||||
#if ($column.javaType == "LocalDateTime")## 时间类型
|
||||
<vxe-column field="${javaField}" title="${comment}" align="center">
|
||||
<VxeColumn field="${javaField}" title="${comment}" align="center">
|
||||
<template #default="{row}">
|
||||
{{formatDateTime(row.${javaField})}}
|
||||
</template>
|
||||
</vxe-column>
|
||||
</VxeColumn>
|
||||
#elseif($column.dictType && "" != $column.dictType)## 数据字典
|
||||
<vxe-column field="${javaField}" title="${comment}" align="center">
|
||||
<VxeColumn field="${javaField}" title="${comment}" align="center">
|
||||
<template #default="{row}">
|
||||
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="row.${javaField}" />
|
||||
</template>
|
||||
</vxe-column>
|
||||
</VxeColumn>
|
||||
#elseif ($table.templateType == 2 && $javaField == $treeNameColumn.javaField)
|
||||
<vxe-column field="${javaField}" title="${comment}" align="center" tree-node/>
|
||||
<VxeColumn field="${javaField}" title="${comment}" align="center" tree-node/>
|
||||
#else
|
||||
<vxe-column field="${javaField}" title="${comment}" align="center" />
|
||||
<VxeColumn field="${javaField}" title="${comment}" align="center" />
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
<vxe-column field="operation" title="操作" align="center">
|
||||
<VxeColumn field="operation" title="操作" align="center">
|
||||
<template #default="{row}">
|
||||
<el-button
|
||||
<ElButton
|
||||
size="small"
|
||||
type="primary"
|
||||
link
|
||||
@@ -364,8 +365,8 @@ onMounted(() => {
|
||||
v-access:code="['${permissionPrefix}:update']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.edit') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
</ElButton>
|
||||
<ElButton
|
||||
size="small"
|
||||
type="danger"
|
||||
link
|
||||
@@ -374,13 +375,13 @@ onMounted(() => {
|
||||
v-access:code="['${permissionPrefix}:delete']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.delete') }}
|
||||
</el-button>
|
||||
</ElButton>
|
||||
</template>
|
||||
</vxe-column>
|
||||
</vxe-table>
|
||||
</VxeColumn>
|
||||
</VxeTable>
|
||||
<!-- 分页 -->
|
||||
<div class="mt-2 flex justify-end">
|
||||
<el-pagination
|
||||
<ElPagination
|
||||
:total="total"
|
||||
v-model:current-page="queryParams.pageNo"
|
||||
v-model:page-size="queryParams.pageSize"
|
||||
@@ -394,7 +395,7 @@ onMounted(() => {
|
||||
</div>
|
||||
#else
|
||||
<ContentWrap title="${subTable.classComment}列表">
|
||||
<vxe-table
|
||||
<VxeTable
|
||||
:data="list"
|
||||
show-overflow
|
||||
:loading="loading"
|
||||
@@ -405,23 +406,23 @@ onMounted(() => {
|
||||
#set ($javaField = $column.javaField)
|
||||
#set ($comment=$column.columnComment)
|
||||
#if ($column.javaType == "LocalDateTime")## 时间类型
|
||||
<vxe-column field="${javaField}" title="${comment}" align="center">
|
||||
<VxeColumn field="${javaField}" title="${comment}" align="center">
|
||||
<template #default="{row}">
|
||||
{{formatDateTime(row.${javaField})}}
|
||||
</template>
|
||||
</vxe-column>
|
||||
</VxeColumn>
|
||||
#elseif($column.dictType && "" != $column.dictType)## 数据字典
|
||||
<vxe-column field="${javaField}" title="${comment}" align="center">
|
||||
<VxeColumn field="${javaField}" title="${comment}" align="center">
|
||||
<template #default="{row}">
|
||||
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="row.${javaField}" />
|
||||
</template>
|
||||
</vxe-column>
|
||||
</VxeColumn>
|
||||
#else
|
||||
<vxe-column field="${javaField}" title="${comment}" align="center" />
|
||||
<VxeColumn field="${javaField}" title="${comment}" align="center" />
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
</vxe-table>
|
||||
</VxeTable>
|
||||
</ContentWrap>
|
||||
#end
|
||||
</template>
|
||||
|
||||
+14
-15
@@ -36,13 +36,14 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
id: 0,
|
||||
${treeNameColumn.javaField}: '顶级${table.classComment}',
|
||||
});
|
||||
return handleTree(data);
|
||||
return handleTree(data, 'id', '${treeParentColumn.javaField}');
|
||||
},
|
||||
labelField: '${treeNameColumn.javaField}',
|
||||
valueField: 'id',
|
||||
childrenField: 'children',
|
||||
placeholder: '请选择上级${table.classComment}',
|
||||
treeDefaultExpandAll: true,
|
||||
defaultExpandAll: true,
|
||||
checkStrictly: true,
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
@@ -89,7 +90,7 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
placeholder: '请选择${comment}',
|
||||
},
|
||||
#elseif($column.htmlType == "checkbox")## 多选框
|
||||
component: 'Checkbox',
|
||||
component: 'CheckboxGroup',
|
||||
componentProps: {
|
||||
#if ("" != $dictType)## 有数据字典
|
||||
options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
|
||||
@@ -105,13 +106,12 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
#else##没数据字典
|
||||
options: [],
|
||||
#end
|
||||
buttonStyle: 'solid',
|
||||
optionType: 'button',
|
||||
isButton: true,
|
||||
},
|
||||
#elseif($column.htmlType == "datetime")## 时间框
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
showTime: true,
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'x',
|
||||
class: '!w-full',
|
||||
@@ -308,7 +308,7 @@ export function use${subSimpleClassName}FormSchema(): VbenFormSchema[] {
|
||||
placeholder: '请选择${comment}',
|
||||
},
|
||||
#elseif($column.htmlType == "checkbox")## 多选框
|
||||
component: 'Checkbox',
|
||||
component: 'CheckboxGroup',
|
||||
componentProps: {
|
||||
#if ("" != $dictType)## 有数据字典
|
||||
options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
|
||||
@@ -324,13 +324,12 @@ export function use${subSimpleClassName}FormSchema(): VbenFormSchema[] {
|
||||
#else##没数据字典
|
||||
options: [],
|
||||
#end
|
||||
buttonStyle: 'solid',
|
||||
optionType: 'button',
|
||||
isButton: true,
|
||||
},
|
||||
#elseif($column.htmlType == "datetime")## 时间框
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
showTime: true,
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'x',
|
||||
class: '!w-full',
|
||||
@@ -450,6 +449,7 @@ export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions<${api
|
||||
#if ($column.createOperation || $column.updateOperation)
|
||||
#if (!$column.primaryKey && $column.listOperationResult && $column.id != $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写
|
||||
#set ($dictType = $column.dictType)
|
||||
#set ($javaType = $column.javaType)
|
||||
#set ($javaField = $column.javaField)
|
||||
#set ($comment = $column.columnComment)
|
||||
#if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short")
|
||||
@@ -544,7 +544,7 @@ export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions<${api
|
||||
placeholder: '请选择${comment}',
|
||||
},
|
||||
#elseif($column.htmlType == "checkbox")## 多选框
|
||||
component: 'Checkbox',
|
||||
component: 'CheckboxGroup',
|
||||
componentProps: {
|
||||
#if ("" != $dictType)## 有数据字典
|
||||
options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
|
||||
@@ -560,13 +560,12 @@ export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions<${api
|
||||
#else##没数据字典
|
||||
options: [],
|
||||
#end
|
||||
buttonStyle: 'solid',
|
||||
optionType: 'button',
|
||||
isButton: true,
|
||||
},
|
||||
#elseif($column.htmlType == "datetime")## 时间框
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
showTime: true,
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'x',
|
||||
class: '!w-full',
|
||||
@@ -596,7 +595,7 @@ export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions<${api
|
||||
#end
|
||||
#if ($table.templateType == 12) ## 内嵌情况
|
||||
/** 列表的字段 */
|
||||
export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions<${simpleClassName}Api.${subSimpleClassName}>['columns'] {
|
||||
export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions<${apiName}.${subSimpleClassName}>['columns'] {
|
||||
return [
|
||||
#foreach($column in $subColumns)
|
||||
#if ($column.listOperationResult)
|
||||
|
||||
+1
-5
@@ -6,7 +6,7 @@ import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { ElMessage#if ($table.templateType == 11), ElTabs, ElTabPane#end } from 'element-plus';
|
||||
import { ElMessage#if ($table.templateType == 10 || $table.templateType == 12), ElTabPane, ElTabs#end } from 'element-plus';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { create${simpleClassName}, get${simpleClassName}, update${simpleClassName} } from '#/api/${table.moduleName}/${table.businessName}';
|
||||
@@ -69,15 +69,11 @@ const [Modal, modalApi] = useVbenModal({
|
||||
#foreach ($subTable in $subTables)
|
||||
#set ($index = $foreach.count - 1)
|
||||
#set ($subClassNameVar = $subClassNameVars.get($index))
|
||||
#if ($subTable.subJoinMany) ## 一对多
|
||||
## TODO 列表值校验?
|
||||
#else
|
||||
const ${subClassNameVar}Valid = await ${subClassNameVar}FormRef.value?.validate();
|
||||
if (!${subClassNameVar}Valid) {
|
||||
subTabsName.value = '${subClassNameVar}';
|
||||
return;
|
||||
}
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
|
||||
+12
@@ -50,6 +50,16 @@ function handleChange(file: any) {
|
||||
}
|
||||
}
|
||||
|
||||
/** 移除文件 */
|
||||
function handleRemove() {
|
||||
formApi.setFieldValue('file', undefined);
|
||||
}
|
||||
|
||||
/** 超出文件数量 */
|
||||
function handleExceed() {
|
||||
ElMessage.warning('只能选择一个文件,请先移除已选文件');
|
||||
}
|
||||
|
||||
/** 下载模板 */
|
||||
async function handleDownload() {
|
||||
const data = await import${simpleClassName}Template();
|
||||
@@ -66,6 +76,8 @@ async function handleDownload() {
|
||||
:limit="1"
|
||||
accept=".xls,.xlsx"
|
||||
:on-change="handleChange"
|
||||
:on-remove="handleRemove"
|
||||
:on-exceed="handleExceed"
|
||||
:auto-upload="false"
|
||||
>
|
||||
<ElButton type="primary"> 选择 Excel 文件 </ElButton>
|
||||
|
||||
+75
-47
@@ -14,10 +14,9 @@
|
||||
|
||||
#if ($subTable.subJoinMany) ## 一对多
|
||||
import { Plus } from "@vben/icons";
|
||||
import { ElButton, ElTabs, ElTabPane, ElCheckbox, ElInput, ElSelect, ElOption, ElRadioGroup, ElCheckboxGroup, ElDatePicker } from 'element-plus';
|
||||
import { ElButton, ElCheckbox, ElCheckboxGroup, ElDatePicker, ElInput, ElMessage, ElOption, ElRadio, ElRadioGroup, ElSelect, ElTabPane, ElTabs } from 'element-plus';
|
||||
import { ImageUpload, FileUpload } from "#/components/upload";
|
||||
import type { OnActionClickParams } from '#/adapter/vxe-table';
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { use${subSimpleClassName}GridEditColumns } from '../data';
|
||||
import { get${subSimpleClassName}ListBy${SubJoinColumnName} } from '#/api/${table.moduleName}/${table.businessName}';
|
||||
#else
|
||||
@@ -31,22 +30,9 @@ const props = defineProps<{
|
||||
}>()
|
||||
|
||||
#if ($subTable.subJoinMany) ## 一对多
|
||||
/** 表格操作按钮的回调函数 */
|
||||
function onActionClick({
|
||||
code,
|
||||
row,
|
||||
}: OnActionClickParams<${apiName}.${subSimpleClassName}>) {
|
||||
switch (code) {
|
||||
case 'delete': {
|
||||
onDelete(row);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
gridOptions: {
|
||||
columns: use${subSimpleClassName}GridEditColumns(onActionClick),
|
||||
columns: use${subSimpleClassName}GridEditColumns(),
|
||||
border: true,
|
||||
showOverflow: true,
|
||||
autoResize: true,
|
||||
@@ -65,7 +51,7 @@ gridOptions: {
|
||||
|
||||
/** 添加${subTable.classComment} */
|
||||
const onAdd = async () => {
|
||||
await gridApi.grid.insertAt({} as ${simpleClassName}Api.${subSimpleClassName}, -1);
|
||||
await gridApi.grid.insertAt({} as ${apiName}.${subSimpleClassName}, -1);
|
||||
}
|
||||
|
||||
/** 删除${subTable.classComment} */
|
||||
@@ -73,26 +59,54 @@ const onDelete = async (row: ${apiName}.${subSimpleClassName}) => {
|
||||
await gridApi.grid.remove(row);
|
||||
}
|
||||
|
||||
/** 提供获取表格数据的方法供父组件调用 */
|
||||
defineExpose({
|
||||
getData: (): ${apiName}.${subSimpleClassName}[] => {
|
||||
const data = gridApi.grid.getData() as ${apiName}.${subSimpleClassName}[];
|
||||
const removeRecords = gridApi.grid.getRemoveRecords() as ${apiName}.${subSimpleClassName}[];
|
||||
const insertRecords = gridApi.grid.getInsertRecords() as ${apiName}.${subSimpleClassName}[];
|
||||
return data
|
||||
.filter((row) => !removeRecords.some((removed) => removed.id === row.id))
|
||||
.concat(insertRecords.map((row: any) => ({ ...row, id: undefined })));
|
||||
},
|
||||
});
|
||||
/** 获取当前子表数据,新增行不提交表格生成的临时编号 */
|
||||
function getData(): ${apiName}.${subSimpleClassName}[] {
|
||||
const { fullData } = gridApi.grid.getTableData();
|
||||
const inserted = new Set(gridApi.grid.getInsertRecords());
|
||||
return fullData.map((row) =>
|
||||
inserted.has(row) ? { ...row, id: undefined } : { ...row },
|
||||
);
|
||||
}
|
||||
|
||||
#set ($hasRequiredColumn = false)
|
||||
#foreach ($column in $subColumns)
|
||||
#if (($column.createOperation || $column.updateOperation) && !$column.nullable && !$column.primaryKey && $column.id != $subJoinColumn.id)
|
||||
#set ($hasRequiredColumn = true)
|
||||
#end
|
||||
#end
|
||||
/** 校验子表必填字段 */
|
||||
function validate(): boolean {
|
||||
#if ($hasRequiredColumn)
|
||||
for (const [index, row] of getData().entries()) {
|
||||
#foreach ($column in $subColumns)
|
||||
#if (($column.createOperation || $column.updateOperation) && !$column.nullable && !$column.primaryKey && $column.id != $subJoinColumn.id)
|
||||
if (
|
||||
row.${column.javaField} === null ||
|
||||
row.${column.javaField} === undefined ||
|
||||
(typeof row.${column.javaField} === 'string' && String(row.${column.javaField}).trim() === '') ||
|
||||
(Array.isArray(row.${column.javaField}) && row.${column.javaField}.length === 0)
|
||||
) {
|
||||
ElMessage.error(`第 #[[${index + 1}]]# 行:${column.columnComment}不能为空`);
|
||||
return false;
|
||||
}
|
||||
#end
|
||||
#end
|
||||
}
|
||||
#end
|
||||
return true;
|
||||
}
|
||||
|
||||
defineExpose({ getData, validate });
|
||||
|
||||
/** 监听主表的关联字段的变化,加载对应的子表数据 */
|
||||
watch(
|
||||
() => props.${subJoinColumn.javaField},
|
||||
async (val) => {
|
||||
await nextTick();
|
||||
if (!val) {
|
||||
await gridApi.grid.loadData([]);
|
||||
return;
|
||||
}
|
||||
await nextTick();
|
||||
await gridApi.grid.loadData(await get${subSimpleClassName}ListBy${SubJoinColumnName}(props.${subJoinColumn.javaField}!));
|
||||
},
|
||||
{ immediate: true },
|
||||
@@ -124,10 +138,11 @@ defineExpose({
|
||||
watch(
|
||||
() => props.${subJoinColumn.javaField},
|
||||
async (val) => {
|
||||
await nextTick();
|
||||
await formApi.resetForm();
|
||||
if (!val) {
|
||||
return;
|
||||
}
|
||||
await nextTick();
|
||||
await formApi.setValues(await get${subSimpleClassName}By${SubJoinColumnName}(props.${subJoinColumn.javaField}!));
|
||||
},
|
||||
{ immediate: true },
|
||||
@@ -144,7 +159,7 @@ watch(
|
||||
#if ( $column.id == $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写
|
||||
#elseif ($column.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里
|
||||
<template #${javaField}="{ row }">
|
||||
<el-input v-model="row.${javaField}" />
|
||||
<ElInput v-model="row.${javaField}" />
|
||||
</template>
|
||||
#elseif($column.htmlType == "imageUpload")## 图片上传
|
||||
<template #${javaField}="{ row }">
|
||||
@@ -156,29 +171,29 @@ watch(
|
||||
</template>
|
||||
#elseif($column.htmlType == "select")## 下拉框
|
||||
<template #${javaField}="{ row, column }">
|
||||
<el-select v-model="row.${javaField}" class="w-full">
|
||||
<el-option v-for="option in column.params.options" :key="option.value" :value="option.value" :label="option.label" />
|
||||
</el-select>
|
||||
<ElSelect v-model="row.${javaField}" class="w-full">
|
||||
<ElOption v-for="option in column.params.options" :key="option.value" :value="option.value" :label="option.label" />
|
||||
</ElSelect>
|
||||
</template>
|
||||
#elseif($column.htmlType == "checkbox")## 多选框
|
||||
<template #${javaField}="{ row, column }">
|
||||
<el-checkbox-group v-model="row.${javaField}">
|
||||
<el-checkbox v-for="option in column.params.options" :key="option.value" :label="option.value">
|
||||
<ElCheckboxGroup v-model="row.${javaField}">
|
||||
<ElCheckbox v-for="option in column.params.options" :key="option.value" :value="option.value">
|
||||
{{ option.label }}
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</ElCheckbox>
|
||||
</ElCheckboxGroup>
|
||||
</template>
|
||||
#elseif($column.htmlType == "radio")## 单选框
|
||||
<template #${javaField}="{ row, column }">
|
||||
<el-radio-group v-model="row.${javaField}">
|
||||
<el-radio v-for="option in column.params.options" :key="option.value" :label="option.value">
|
||||
<ElRadioGroup v-model="row.${javaField}">
|
||||
<ElRadio v-for="option in column.params.options" :key="option.value" :value="option.value">
|
||||
{{ option.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</ElRadio>
|
||||
</ElRadioGroup>
|
||||
</template>
|
||||
#elseif($column.htmlType == "datetime")## 时间框
|
||||
<template #${javaField}="{ row }">
|
||||
<el-date-picker
|
||||
<ElDatePicker
|
||||
v-model="row.${javaField}"
|
||||
type="datetime"
|
||||
value-format="x"
|
||||
@@ -187,16 +202,29 @@ watch(
|
||||
</template>
|
||||
#elseif($column.htmlType == "textarea" || $column.htmlType == "editor")## 文本框
|
||||
<template #${javaField}="{ row }">
|
||||
<el-input v-model="row.${javaField}" type="textarea" />
|
||||
<ElInput v-model="row.${javaField}" type="textarea" />
|
||||
</template>
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: $t('common.delete'),
|
||||
type: 'danger', link: true,
|
||||
icon: ACTION_ICON.DELETE,
|
||||
auth: ['${permissionPrefix}:delete'],
|
||||
onClick: onDelete.bind(null, row),
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
<div class="flex justify-center -mt-4">
|
||||
<el-button :icon="Plus" type="primary" plain @click="onAdd" v-access:code="['${subTable.moduleName}:${simpleClassName_strikeCase}:create']">
|
||||
<ElButton :icon="Plus" type="primary" plain @click="onAdd" v-access:code="['${subTable.moduleName}:${simpleClassName_strikeCase}:create']">
|
||||
{{ $t('ui.actionTitle.create', ['${subTable.classComment}']) }}
|
||||
</el-button>
|
||||
</ElButton>
|
||||
</div>
|
||||
#else
|
||||
<Form class="mx-4" />
|
||||
|
||||
+234
@@ -0,0 +1,234 @@
|
||||
package cn.iocoder.yudao.module.infra.service.codegen.inner;
|
||||
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
|
||||
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenFrontTypeEnum;
|
||||
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum;
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.EnumSource;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* {@link CodegenEngine} 的前端组件契约回归测试
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class CodegenEngineFrontendContractTest extends CodegenEngineAbstractTest {
|
||||
|
||||
@ParameterizedTest
|
||||
@EnumSource(value = CodegenFrontTypeEnum.class, names = {
|
||||
"VUE3_VBEN5_ANTD_SCHEMA", "VUE3_VBEN5_ANTDV_NEXT_SCHEMA", "VUE3_VBEN5_EP_SCHEMA"})
|
||||
public void testExecute_dictionaryControls(CodegenFrontTypeEnum frontType) {
|
||||
// 准备参数:同一字典的多选、单选和数字必填字段
|
||||
CodegenTableDO table = getTable("student").setFrontType(frontType.getType())
|
||||
.setTemplateType(CodegenTemplateTypeEnum.ONE.getType());
|
||||
List<CodegenColumnDO> columns = getColumnList("student");
|
||||
columns.add(new CodegenColumnDO().setJavaField("tags").setJavaType("String")
|
||||
.setColumnComment("标签").setDictType("system_user_sex").setHtmlType("checkbox")
|
||||
.setCreateOperation(true).setUpdateOperation(true).setNullable(false).setPrimaryKey(false));
|
||||
columns.add(new CodegenColumnDO().setJavaField("status").setJavaType("Integer")
|
||||
.setColumnComment("状态").setDictType("system_user_sex").setHtmlType("radio")
|
||||
.setCreateOperation(true).setUpdateOperation(true).setNullable(false).setPrimaryKey(false));
|
||||
columns.add(new CodegenColumnDO().setJavaField("amount").setJavaType("Integer")
|
||||
.setColumnComment("金额").setHtmlType("inputNumber").setDictType("")
|
||||
.setCreateOperation(true).setUpdateOperation(true).setNullable(false).setPrimaryKey(false));
|
||||
|
||||
// 调用
|
||||
Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns, null, null);
|
||||
// 调试用:导出真实生成结果,供前端组件验证
|
||||
String outputPath = System.getProperty("codegen.contractOutput");
|
||||
if (outputPath != null) {
|
||||
writeResult(result, outputPath + "/" + frontType.name());
|
||||
}
|
||||
// 断言:组选项不能变成 boolean Checkbox,必填数字不能被 Zod 推导为 null
|
||||
String data = findFile(result, "/data.ts");
|
||||
assertTrue(data.contains("component: 'CheckboxGroup'"));
|
||||
assertFalse(data.contains("component: 'Checkbox',"));
|
||||
assertTrue(data.contains("rules: 'required'"));
|
||||
assertFalse(data.contains("z.number("));
|
||||
if (frontType == CodegenFrontTypeEnum.VUE3_VBEN5_EP_SCHEMA) {
|
||||
assertTrue(data.contains("isButton: true"));
|
||||
assertFalse(data.contains("buttonStyle:"));
|
||||
assertFalse(data.contains("optionType:"));
|
||||
assertFalse(data.contains("allowClear:"));
|
||||
} else {
|
||||
assertTrue(data.contains("optionType: 'button'"));
|
||||
assertFalse(data.contains("clearable:"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecute_eleTreeParentSelectable() {
|
||||
// 准备参数
|
||||
CodegenTableDO table = getTable("category").setFrontType(CodegenFrontTypeEnum.VUE3_VBEN5_EP_SCHEMA.getType())
|
||||
.setTemplateType(CodegenTemplateTypeEnum.TREE.getType());
|
||||
// 调用
|
||||
Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, getColumnList("category"), null, null);
|
||||
// 断言:父节点可作为上级,使用 Element Plus 属性
|
||||
String data = findFile(result, "/data.ts");
|
||||
assertTrue(data.contains("defaultExpandAll: true"));
|
||||
assertTrue(data.contains("checkStrictly: true"));
|
||||
assertFalse(data.contains("treeDefaultExpandAll"));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@EnumSource(value = CodegenFrontTypeEnum.class, names = {
|
||||
"VUE3_VBEN5_ANTD_SCHEMA", "VUE3_VBEN5_ANTD_GENERAL",
|
||||
"VUE3_VBEN5_ANTDV_NEXT_SCHEMA", "VUE3_VBEN5_ANTDV_NEXT_GENERAL",
|
||||
"VUE3_VBEN5_EP_SCHEMA", "VUE3_VBEN5_EP_GENERAL"})
|
||||
public void testExecute_importRemovalClearsPayload(CodegenFrontTypeEnum frontType) {
|
||||
// 准备参数
|
||||
codegenProperties.setImportEnable(true);
|
||||
codegenEngine.initGlobalBindingMap();
|
||||
CodegenTableDO table = getTable("student").setFrontType(frontType.getType())
|
||||
.setTemplateType(CodegenTemplateTypeEnum.ONE.getType());
|
||||
// 调用
|
||||
Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, getColumnList("student"), null, null);
|
||||
// 断言:移除可视文件时也清空提交来源
|
||||
String form = findFile(result, "/import-form.vue");
|
||||
assertTrue(form.contains(":on-remove=\"handleRemove\""));
|
||||
assertTrue(form.contains("formApi.setFieldValue('file', undefined)") || form.contains("fileRef.value = null"));
|
||||
if (frontType == CodegenFrontTypeEnum.VUE3_VBEN5_EP_SCHEMA
|
||||
|| frontType == CodegenFrontTypeEnum.VUE3_VBEN5_EP_GENERAL) {
|
||||
assertTrue(form.contains(":on-exceed=\"handleExceed\""));
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@EnumSource(value = CodegenFrontTypeEnum.class, names = {
|
||||
"VUE3_VBEN5_ANTD_SCHEMA", "VUE3_VBEN5_ANTD_GENERAL",
|
||||
"VUE3_VBEN5_ANTDV_NEXT_SCHEMA", "VUE3_VBEN5_ANTDV_NEXT_GENERAL",
|
||||
"VUE3_VBEN5_EP_SCHEMA", "VUE3_VBEN5_EP_GENERAL"})
|
||||
public void testExecute_masterChildEditing(CodegenFrontTypeEnum frontType) {
|
||||
// 准备参数:一对多联系人和一对一班主任
|
||||
CodegenTableDO table = getTable("student").setFrontType(frontType.getType())
|
||||
.setTemplateType(CodegenTemplateTypeEnum.MASTER_NORMAL.getType());
|
||||
CodegenTableDO contact = getTable("contact").setFrontType(frontType.getType())
|
||||
.setTemplateType(CodegenTemplateTypeEnum.SUB.getType()).setSubJoinColumnId(100L).setSubJoinMany(true);
|
||||
CodegenTableDO teacher = getTable("teacher").setFrontType(frontType.getType())
|
||||
.setTemplateType(CodegenTemplateTypeEnum.SUB.getType()).setSubJoinColumnId(200L).setSubJoinMany(false);
|
||||
// 调用
|
||||
Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, getColumnList("student"),
|
||||
List.of(contact, teacher), List.of(getColumnList("contact"), getColumnList("teacher")));
|
||||
// 断言:新增和删除以当前表格为准,不重复追加,新增临时编号不提交
|
||||
String form = findFile(result, "/student-contact-form.vue");
|
||||
assertTrue(form.contains("getTableData()"));
|
||||
assertTrue(form.contains("inserted.has(row) ? { ...row, id: undefined } : { ...row }"));
|
||||
assertFalse(form.contains("getRemoveRecords()"));
|
||||
assertFalse(form.contains(".concat(insertRecords"));
|
||||
assertFalse(form.contains("$dictType"));
|
||||
assertTrue(form.contains("getStudentContactListByStudentId"));
|
||||
if (frontType.name().endsWith("SCHEMA")) {
|
||||
assertTrue(form.contains("<template #actions="));
|
||||
}
|
||||
result.forEach((path, content) -> {
|
||||
if (path.endsWith(".vue") || path.endsWith("data.ts")) {
|
||||
assertFalse(content.contains("#/api/infra/student"), path + ":API 导入必须匹配生成目录");
|
||||
assertFalse(content.contains("VxeTableGridOptions<StudentApi.StudentContact>") && content.contains("import type { InfraStudentApi }"),
|
||||
path + ":子表命名空间应与导入一致");
|
||||
}
|
||||
});
|
||||
if (frontType == CodegenFrontTypeEnum.VUE3_VBEN5_ANTDV_NEXT_GENERAL) {
|
||||
assertTrue(form.matches("(?s).*import\\s*\\{[^}]*\\bmessage\\b[^}]*}\\s*from 'antdv-next'.*"),
|
||||
"子表校验提示必须导入实际的消息组件");
|
||||
}
|
||||
if (frontType == CodegenFrontTypeEnum.VUE3_VBEN5_EP_GENERAL) {
|
||||
assertTrue(form.contains(":icon=\"Plus\""));
|
||||
assertFalse(form.contains("h(Plus)"), "图标使用导入的组件,避免未绑定的 h");
|
||||
}
|
||||
assertTrue(form.contains("function validate(): boolean"));
|
||||
assertTrue(form.contains("=== undefined"));
|
||||
assertTrue(form.contains("loadData([])") || form.contains("list.value = []"));
|
||||
String parentForm = findFile(result, "/modules/form.vue");
|
||||
assertTrue(parentForm.contains("studentContactFormRef.value?.validate()"));
|
||||
assertFalse(parentForm.contains("TODO 列表值校验"));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@EnumSource(value = CodegenTemplateTypeEnum.class, names = { "MASTER_ERP", "MASTER_INNER" })
|
||||
public void testExecute_eleChildListImports(CodegenTemplateTypeEnum templateType) {
|
||||
// 准备参数
|
||||
CodegenFrontTypeEnum frontType = CodegenFrontTypeEnum.VUE3_VBEN5_EP_GENERAL;
|
||||
CodegenTableDO table = getTable("student").setFrontType(frontType.getType())
|
||||
.setTemplateType(templateType.getType());
|
||||
CodegenTableDO contact = getTable("contact").setFrontType(frontType.getType())
|
||||
.setTemplateType(CodegenTemplateTypeEnum.SUB.getType()).setSubJoinColumnId(100L).setSubJoinMany(true);
|
||||
// 调用
|
||||
Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, getColumnList("student"),
|
||||
List.of(contact), List.of(getColumnList("contact")));
|
||||
// 断言:ERP 子表查询实际调用的工具必须导入
|
||||
String index = findFile(result, "/index.vue");
|
||||
assertTrue(index.contains("<VbenVxeTableToolbar"));
|
||||
assertTrue(index.contains("</VbenVxeTableToolbar>"));
|
||||
assertFalse(index.contains("<TableToolbar"));
|
||||
String list = findFile(result, "/student-contact-list.vue");
|
||||
if (templateType == CodegenTemplateTypeEnum.MASTER_ERP) {
|
||||
assertTrue(list.contains("<VbenVxeTableToolbar"));
|
||||
assertTrue(list.contains("</VbenVxeTableToolbar>"));
|
||||
assertFalse(list.contains("</TableToolbar>"));
|
||||
assertTrue(list.contains("cloneDeep(queryParams)"));
|
||||
assertTrue(list.matches("(?s).*import\\s*\\{[^}]*\\bcloneDeep\\b[^}]*}\\s*from '@vben/utils'.*"));
|
||||
}
|
||||
assertTrue(list.contains("formatDateTime(row."));
|
||||
assertTrue(list.matches("(?s).*import\\s*\\{[^}]*\\bformatDateTime\\b[^}]*}\\s*from '@vben/utils'.*"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecute_vue3ImportUploadResponse() {
|
||||
// 准备参数
|
||||
codegenProperties.setImportEnable(true);
|
||||
codegenEngine.initGlobalBindingMap();
|
||||
CodegenTableDO table = getTable("student").setFrontType(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType())
|
||||
.setTemplateType(CodegenTemplateTypeEnum.ONE.getType());
|
||||
// 调用
|
||||
Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, getColumnList("student"), null, null);
|
||||
// 断言
|
||||
String form = findFile(result, "/StudentImportForm.vue");
|
||||
assertTrue(form.contains("const res = await StudentApi.importStudent(formData)"));
|
||||
assertTrue(form.contains("const data = res.data"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testExecute_vben2DateRangeFormat() {
|
||||
// 准备参数
|
||||
CodegenTableDO table = getTable("student").setFrontType(CodegenFrontTypeEnum.VUE3_VBEN2_ANTD_SCHEMA.getType())
|
||||
.setTemplateType(CodegenTemplateTypeEnum.ONE.getType());
|
||||
// 调用
|
||||
Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, getColumnList("student"), null, null);
|
||||
// 断言:查询区间提交后端需要的时间字符串
|
||||
String data = findFile(result, ".data.ts");
|
||||
assertTrue(data.contains("valueFormat: 'YYYY-MM-DD HH:mm:ss'"));
|
||||
assertTrue(data.contains("allowClear: true"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecute_uniappPaginationContract() {
|
||||
// 准备参数
|
||||
CodegenTableDO table = getTable("student").setFrontType(CodegenFrontTypeEnum.VUE3_ADMIN_UNIAPP_WOT.getType())
|
||||
.setTemplateType(CodegenTemplateTypeEnum.ONE.getType());
|
||||
// 调用
|
||||
Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, getColumnList("student"), null, null);
|
||||
// 断言:移动端已有分页/失败恢复契约保持有效,避免机械套用 PC 组件写法
|
||||
String api = findFile(result, "/index.ts");
|
||||
assertTrue(api.contains("params: PageParam"));
|
||||
assertFalse(api.contains("PageReqVO"));
|
||||
String list = result.entrySet().stream()
|
||||
.filter(entry -> entry.getKey().endsWith("/index.vue") && entry.getValue().contains("<z-paging"))
|
||||
.findFirst().orElseThrow().getValue();
|
||||
assertTrue(list.contains("completeByTotal(data.list, data.total)"));
|
||||
assertTrue(list.contains("complete(false)"));
|
||||
assertTrue(list.contains("uni.$off("));
|
||||
}
|
||||
|
||||
private static String findFile(Map<String, String> result, String suffix) {
|
||||
return result.entrySet().stream().filter(entry -> entry.getKey().endsWith(suffix))
|
||||
.findFirst().orElseThrow(() -> new AssertionError("缺少生成文件:" + suffix)).getValue();
|
||||
}
|
||||
}
|
||||
+13
-7
@@ -35,12 +35,11 @@ const total = ref(0) // 列表的总页数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
name: undefined,
|
||||
birthday: undefined,
|
||||
birthday: undefined,
|
||||
sex: undefined,
|
||||
enabled: undefined,
|
||||
createTime: undefined,
|
||||
name: undefined,
|
||||
birthday: undefined,
|
||||
sex: undefined,
|
||||
enabled: undefined,
|
||||
createTime: undefined,
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
@@ -136,7 +135,14 @@ function handleRowCheckboxChange({
|
||||
async function handleExport() {
|
||||
try {
|
||||
exportLoading.value = true;
|
||||
const data = await exportStudent(queryParams);
|
||||
const params = cloneDeep(queryParams) as any;
|
||||
if (params.birthday && Array.isArray(params.birthday)) {
|
||||
params.birthday = (params.birthday as string[]).join(',');
|
||||
}
|
||||
if (params.createTime && Array.isArray(params.createTime)) {
|
||||
params.createTime = (params.createTime as string[]).join(',');
|
||||
}
|
||||
const data = await exportStudent(params);
|
||||
downloadFileFromBlobPart({ fileName: '学生.xls', source: data });
|
||||
}finally {
|
||||
exportLoading.value = false;
|
||||
|
||||
+5
-1
@@ -72,7 +72,11 @@ const [Modal, modalApi] = useVbenModal({
|
||||
async onConfirm() {
|
||||
await formRef.value?.validate();
|
||||
// 校验子表单
|
||||
try {
|
||||
if (!studentContactFormRef.value?.validate()) {
|
||||
subTabsName.value = 'studentContact';
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await studentTeacherFormRef.value?.validate()
|
||||
} catch (e) {
|
||||
subTabsName.value = 'studentTeacher'
|
||||
|
||||
+13
-7
@@ -31,12 +31,11 @@ const total = ref(0) // 列表的总页数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
name: undefined,
|
||||
birthday: undefined,
|
||||
birthday: undefined,
|
||||
sex: undefined,
|
||||
enabled: undefined,
|
||||
createTime: undefined,
|
||||
name: undefined,
|
||||
birthday: undefined,
|
||||
sex: undefined,
|
||||
enabled: undefined,
|
||||
createTime: undefined,
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
@@ -132,7 +131,14 @@ function handleRowCheckboxChange({
|
||||
async function handleExport() {
|
||||
try {
|
||||
exportLoading.value = true;
|
||||
const data = await exportStudent(queryParams);
|
||||
const params = cloneDeep(queryParams) as any;
|
||||
if (params.birthday && Array.isArray(params.birthday)) {
|
||||
params.birthday = (params.birthday as string[]).join(',');
|
||||
}
|
||||
if (params.createTime && Array.isArray(params.createTime)) {
|
||||
params.createTime = (params.createTime as string[]).join(',');
|
||||
}
|
||||
const data = await exportStudent(params);
|
||||
downloadFileFromBlobPart({ fileName: '学生.xls', source: data });
|
||||
}finally {
|
||||
exportLoading.value = false;
|
||||
|
||||
+87
-16
@@ -32,25 +32,96 @@ async function handleDelete(row: StudentApi.StudentContact) {
|
||||
await tableRef.value?.remove(row);
|
||||
}
|
||||
|
||||
/** 提供获取表格数据的方法供父组件调用 */
|
||||
defineExpose({
|
||||
getData: (): StudentApi.StudentContact[] => {
|
||||
const data = list.value as StudentApi.StudentContact[];
|
||||
const removeRecords = tableRef.value?.getRemoveRecords() as StudentApi.StudentContact[];
|
||||
const insertRecords = tableRef.value?.getInsertRecords() as StudentApi.StudentContact[];
|
||||
return [
|
||||
...data.filter(
|
||||
(row) => !removeRecords.some((removed) => removed.id === row.id),
|
||||
),
|
||||
...insertRecords.map((row: any) => ({ ...row, id: undefined })),
|
||||
];
|
||||
},
|
||||
});
|
||||
/** 获取当前子表数据,新增行不提交表格生成的临时编号 */
|
||||
function getData(): StudentApi.StudentContact[] {
|
||||
const table = tableRef.value;
|
||||
if (!table) {
|
||||
return [];
|
||||
}
|
||||
const { fullData } = table.getTableData();
|
||||
const inserted = new Set(table.getInsertRecords());
|
||||
return fullData.map((row) =>
|
||||
inserted.has(row) ? { ...row, id: undefined } : { ...row },
|
||||
);
|
||||
}
|
||||
|
||||
/** 校验子表必填字段 */
|
||||
function validate(): boolean {
|
||||
for (const [index, row] of getData().entries()) {
|
||||
if (
|
||||
row.name === null ||
|
||||
row.name === undefined ||
|
||||
(typeof row.name === 'string' && String(row.name).trim() === '') ||
|
||||
(Array.isArray(row.name) && row.name.length === 0)
|
||||
) {
|
||||
message.error(`第 ${index + 1} 行:名字不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.description === null ||
|
||||
row.description === undefined ||
|
||||
(typeof row.description === 'string' && String(row.description).trim() === '') ||
|
||||
(Array.isArray(row.description) && row.description.length === 0)
|
||||
) {
|
||||
message.error(`第 ${index + 1} 行:简介不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.birthday === null ||
|
||||
row.birthday === undefined ||
|
||||
(typeof row.birthday === 'string' && String(row.birthday).trim() === '') ||
|
||||
(Array.isArray(row.birthday) && row.birthday.length === 0)
|
||||
) {
|
||||
message.error(`第 ${index + 1} 行:出生日期不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.sex === null ||
|
||||
row.sex === undefined ||
|
||||
(typeof row.sex === 'string' && String(row.sex).trim() === '') ||
|
||||
(Array.isArray(row.sex) && row.sex.length === 0)
|
||||
) {
|
||||
message.error(`第 ${index + 1} 行:性别不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.enabled === null ||
|
||||
row.enabled === undefined ||
|
||||
(typeof row.enabled === 'string' && String(row.enabled).trim() === '') ||
|
||||
(Array.isArray(row.enabled) && row.enabled.length === 0)
|
||||
) {
|
||||
message.error(`第 ${index + 1} 行:是否有效不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.avatar === null ||
|
||||
row.avatar === undefined ||
|
||||
(typeof row.avatar === 'string' && String(row.avatar).trim() === '') ||
|
||||
(Array.isArray(row.avatar) && row.avatar.length === 0)
|
||||
) {
|
||||
message.error(`第 ${index + 1} 行:头像不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.memo === null ||
|
||||
row.memo === undefined ||
|
||||
(typeof row.memo === 'string' && String(row.memo).trim() === '') ||
|
||||
(Array.isArray(row.memo) && row.memo.length === 0)
|
||||
) {
|
||||
message.error(`第 ${index + 1} 行:备注不能为空`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
defineExpose({ getData, validate });
|
||||
|
||||
/** 监听主表的关联字段的变化,加载对应的子表数据 */
|
||||
watch(
|
||||
() => props.studentId,
|
||||
async (val) => {
|
||||
list.value = [];
|
||||
if (!val) {
|
||||
return;
|
||||
}
|
||||
@@ -86,7 +157,7 @@ watch(
|
||||
<template #default="{ row }">
|
||||
<Select v-model:value="row.sex" placeholder="请选择性别">
|
||||
<Select.Option
|
||||
v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), 'number')"
|
||||
v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
@@ -99,7 +170,7 @@ watch(
|
||||
<template #default="{ row }">
|
||||
<RadioGroup v-model:value="row.enabled">
|
||||
<Radio
|
||||
v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), 'boolean')"
|
||||
v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
|
||||
+12
@@ -51,6 +51,18 @@ defineExpose({
|
||||
watch(
|
||||
() => props.studentId,
|
||||
async (val) => {
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
studentId: undefined,
|
||||
name: undefined,
|
||||
description: undefined,
|
||||
birthday: undefined,
|
||||
sex: undefined,
|
||||
enabled: undefined,
|
||||
avatar: undefined,
|
||||
video: undefined,
|
||||
memo: undefined,
|
||||
};
|
||||
if (!val) {
|
||||
return;
|
||||
}
|
||||
|
||||
+5
-1
@@ -72,7 +72,11 @@ const [Modal, modalApi] = useVbenModal({
|
||||
async onConfirm() {
|
||||
await formRef.value?.validate();
|
||||
// 校验子表单
|
||||
try {
|
||||
if (!studentContactFormRef.value?.validate()) {
|
||||
subTabsName.value = 'studentContact';
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await studentTeacherFormRef.value?.validate()
|
||||
} catch (e) {
|
||||
subTabsName.value = 'studentTeacher'
|
||||
|
||||
+13
-7
@@ -27,12 +27,11 @@ const total = ref(0) // 列表的总页数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
name: undefined,
|
||||
birthday: undefined,
|
||||
birthday: undefined,
|
||||
sex: undefined,
|
||||
enabled: undefined,
|
||||
createTime: undefined,
|
||||
name: undefined,
|
||||
birthday: undefined,
|
||||
sex: undefined,
|
||||
enabled: undefined,
|
||||
createTime: undefined,
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
@@ -128,7 +127,14 @@ function handleRowCheckboxChange({
|
||||
async function handleExport() {
|
||||
try {
|
||||
exportLoading.value = true;
|
||||
const data = await exportStudent(queryParams);
|
||||
const params = cloneDeep(queryParams) as any;
|
||||
if (params.birthday && Array.isArray(params.birthday)) {
|
||||
params.birthday = (params.birthday as string[]).join(',');
|
||||
}
|
||||
if (params.createTime && Array.isArray(params.createTime)) {
|
||||
params.createTime = (params.createTime as string[]).join(',');
|
||||
}
|
||||
const data = await exportStudent(params);
|
||||
downloadFileFromBlobPart({ fileName: '学生.xls', source: data });
|
||||
}finally {
|
||||
exportLoading.value = false;
|
||||
|
||||
+87
-16
@@ -32,25 +32,96 @@ async function handleDelete(row: StudentApi.StudentContact) {
|
||||
await tableRef.value?.remove(row);
|
||||
}
|
||||
|
||||
/** 提供获取表格数据的方法供父组件调用 */
|
||||
defineExpose({
|
||||
getData: (): StudentApi.StudentContact[] => {
|
||||
const data = list.value as StudentApi.StudentContact[];
|
||||
const removeRecords = tableRef.value?.getRemoveRecords() as StudentApi.StudentContact[];
|
||||
const insertRecords = tableRef.value?.getInsertRecords() as StudentApi.StudentContact[];
|
||||
return [
|
||||
...data.filter(
|
||||
(row) => !removeRecords.some((removed) => removed.id === row.id),
|
||||
),
|
||||
...insertRecords.map((row: any) => ({ ...row, id: undefined })),
|
||||
];
|
||||
},
|
||||
});
|
||||
/** 获取当前子表数据,新增行不提交表格生成的临时编号 */
|
||||
function getData(): StudentApi.StudentContact[] {
|
||||
const table = tableRef.value;
|
||||
if (!table) {
|
||||
return [];
|
||||
}
|
||||
const { fullData } = table.getTableData();
|
||||
const inserted = new Set(table.getInsertRecords());
|
||||
return fullData.map((row) =>
|
||||
inserted.has(row) ? { ...row, id: undefined } : { ...row },
|
||||
);
|
||||
}
|
||||
|
||||
/** 校验子表必填字段 */
|
||||
function validate(): boolean {
|
||||
for (const [index, row] of getData().entries()) {
|
||||
if (
|
||||
row.name === null ||
|
||||
row.name === undefined ||
|
||||
(typeof row.name === 'string' && String(row.name).trim() === '') ||
|
||||
(Array.isArray(row.name) && row.name.length === 0)
|
||||
) {
|
||||
message.error(`第 ${index + 1} 行:名字不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.description === null ||
|
||||
row.description === undefined ||
|
||||
(typeof row.description === 'string' && String(row.description).trim() === '') ||
|
||||
(Array.isArray(row.description) && row.description.length === 0)
|
||||
) {
|
||||
message.error(`第 ${index + 1} 行:简介不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.birthday === null ||
|
||||
row.birthday === undefined ||
|
||||
(typeof row.birthday === 'string' && String(row.birthday).trim() === '') ||
|
||||
(Array.isArray(row.birthday) && row.birthday.length === 0)
|
||||
) {
|
||||
message.error(`第 ${index + 1} 行:出生日期不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.sex === null ||
|
||||
row.sex === undefined ||
|
||||
(typeof row.sex === 'string' && String(row.sex).trim() === '') ||
|
||||
(Array.isArray(row.sex) && row.sex.length === 0)
|
||||
) {
|
||||
message.error(`第 ${index + 1} 行:性别不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.enabled === null ||
|
||||
row.enabled === undefined ||
|
||||
(typeof row.enabled === 'string' && String(row.enabled).trim() === '') ||
|
||||
(Array.isArray(row.enabled) && row.enabled.length === 0)
|
||||
) {
|
||||
message.error(`第 ${index + 1} 行:是否有效不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.avatar === null ||
|
||||
row.avatar === undefined ||
|
||||
(typeof row.avatar === 'string' && String(row.avatar).trim() === '') ||
|
||||
(Array.isArray(row.avatar) && row.avatar.length === 0)
|
||||
) {
|
||||
message.error(`第 ${index + 1} 行:头像不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.memo === null ||
|
||||
row.memo === undefined ||
|
||||
(typeof row.memo === 'string' && String(row.memo).trim() === '') ||
|
||||
(Array.isArray(row.memo) && row.memo.length === 0)
|
||||
) {
|
||||
message.error(`第 ${index + 1} 行:备注不能为空`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
defineExpose({ getData, validate });
|
||||
|
||||
/** 监听主表的关联字段的变化,加载对应的子表数据 */
|
||||
watch(
|
||||
() => props.studentId,
|
||||
async (val) => {
|
||||
list.value = [];
|
||||
if (!val) {
|
||||
return;
|
||||
}
|
||||
@@ -86,7 +157,7 @@ watch(
|
||||
<template #default="{ row }">
|
||||
<Select v-model:value="row.sex" placeholder="请选择性别">
|
||||
<Select.Option
|
||||
v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), 'number')"
|
||||
v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
@@ -99,7 +170,7 @@ watch(
|
||||
<template #default="{ row }">
|
||||
<RadioGroup v-model:value="row.enabled">
|
||||
<Radio
|
||||
v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), 'boolean')"
|
||||
v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
|
||||
+12
@@ -51,6 +51,18 @@ defineExpose({
|
||||
watch(
|
||||
() => props.studentId,
|
||||
async (val) => {
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
studentId: undefined,
|
||||
name: undefined,
|
||||
description: undefined,
|
||||
birthday: undefined,
|
||||
sex: undefined,
|
||||
enabled: undefined,
|
||||
avatar: undefined,
|
||||
video: undefined,
|
||||
memo: undefined,
|
||||
};
|
||||
if (!val) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -27,12 +27,11 @@ const total = ref(0) // 列表的总页数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
name: undefined,
|
||||
birthday: undefined,
|
||||
birthday: undefined,
|
||||
sex: undefined,
|
||||
enabled: undefined,
|
||||
createTime: undefined,
|
||||
name: undefined,
|
||||
birthday: undefined,
|
||||
sex: undefined,
|
||||
enabled: undefined,
|
||||
createTime: undefined,
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
@@ -128,7 +127,14 @@ function handleRowCheckboxChange({
|
||||
async function handleExport() {
|
||||
try {
|
||||
exportLoading.value = true;
|
||||
const data = await exportStudent(queryParams);
|
||||
const params = cloneDeep(queryParams) as any;
|
||||
if (params.birthday && Array.isArray(params.birthday)) {
|
||||
params.birthday = (params.birthday as string[]).join(',');
|
||||
}
|
||||
if (params.createTime && Array.isArray(params.createTime)) {
|
||||
params.createTime = (params.createTime as string[]).join(',');
|
||||
}
|
||||
const data = await exportStudent(params);
|
||||
downloadFileFromBlobPart({ fileName: '学生.xls', source: data });
|
||||
}finally {
|
||||
exportLoading.value = false;
|
||||
|
||||
+11
-1
@@ -48,6 +48,11 @@ function beforeUpload(file: FileType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** 移除文件 */
|
||||
function handleRemove() {
|
||||
fileRef.value = null;
|
||||
}
|
||||
|
||||
/** 下载导入模板 */
|
||||
async function handleDownload() {
|
||||
const data = await importStudentTemplate();
|
||||
@@ -58,7 +63,12 @@ async function handleDownload() {
|
||||
<template>
|
||||
<Modal title="导入学生" class="w-1/3">
|
||||
<div class="mx-4">
|
||||
<Upload :max-count="1" accept=".xls,.xlsx" :before-upload="beforeUpload">
|
||||
<Upload
|
||||
:max-count="1"
|
||||
accept=".xls,.xlsx"
|
||||
:before-upload="beforeUpload"
|
||||
:on-remove="handleRemove"
|
||||
>
|
||||
<Button type="primary"> 选择 Excel 文件 </Button>
|
||||
</Upload>
|
||||
</div>
|
||||
|
||||
+13
-7
@@ -28,12 +28,11 @@ const total = ref(0) // 列表的总页数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
name: undefined,
|
||||
birthday: undefined,
|
||||
birthday: undefined,
|
||||
sex: undefined,
|
||||
enabled: undefined,
|
||||
createTime: undefined,
|
||||
name: undefined,
|
||||
birthday: undefined,
|
||||
sex: undefined,
|
||||
enabled: undefined,
|
||||
createTime: undefined,
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
@@ -138,7 +137,14 @@ function handleRowCheckboxChange({
|
||||
async function handleExport() {
|
||||
try {
|
||||
exportLoading.value = true;
|
||||
const data = await exportStudent(queryParams);
|
||||
const params = cloneDeep(queryParams) as any;
|
||||
if (params.birthday && Array.isArray(params.birthday)) {
|
||||
params.birthday = (params.birthday as string[]).join(',');
|
||||
}
|
||||
if (params.createTime && Array.isArray(params.createTime)) {
|
||||
params.createTime = (params.createTime as string[]).join(',');
|
||||
}
|
||||
const data = await exportStudent(params);
|
||||
downloadFileFromBlobPart({ fileName: '学生.xls', source: data });
|
||||
}finally {
|
||||
exportLoading.value = false;
|
||||
|
||||
@@ -24,7 +24,7 @@ const loading = ref(true) // 列表的加载中
|
||||
const list = ref<any[]>([]) // 树列表的数据
|
||||
|
||||
const queryParams = reactive({
|
||||
name: undefined,
|
||||
name: undefined,
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
@@ -91,7 +91,8 @@ async function handleDelete(row: CategoryApi.Category) {
|
||||
async function handleExport() {
|
||||
try {
|
||||
exportLoading.value = true;
|
||||
const data = await exportCategory(queryParams);
|
||||
const params = cloneDeep(queryParams) as any;
|
||||
const data = await exportCategory(params);
|
||||
downloadFileFromBlobPart({ fileName: '分类.xls', source: data });
|
||||
}finally {
|
||||
exportLoading.value = false;
|
||||
|
||||
@@ -5,7 +5,7 @@ import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message, Tabs } from 'ant-design-vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { createStudent, getStudent, updateStudent } from '#/api/infra/demo';
|
||||
|
||||
+3
-3
@@ -269,7 +269,7 @@ export function useGridColumns(): VxeTableGridOptions<InfraStudentApi.Student>['
|
||||
minWidth: 120,
|
||||
slots: { default: 'sex' },
|
||||
params: {
|
||||
options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'boolean'),
|
||||
options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number'),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -309,7 +309,7 @@ export function useGridColumns(): VxeTableGridOptions<InfraStudentApi.Student>['
|
||||
}
|
||||
|
||||
/** 列表的字段 */
|
||||
export function useStudentContactGridColumns(): VxeTableGridOptions<StudentApi.StudentContact>['columns'] {
|
||||
export function useStudentContactGridColumns(): VxeTableGridOptions<InfraStudentApi.StudentContact>['columns'] {
|
||||
return [
|
||||
{
|
||||
field: 'id',
|
||||
@@ -490,7 +490,7 @@ export function useGridColumns(): VxeTableGridOptions<InfraStudentApi.Student>['
|
||||
}
|
||||
|
||||
/** 列表的字段 */
|
||||
export function useStudentTeacherGridColumns(): VxeTableGridOptions<StudentApi.StudentTeacher>['columns'] {
|
||||
export function useStudentTeacherGridColumns(): VxeTableGridOptions<InfraStudentApi.StudentTeacher>['columns'] {
|
||||
return [
|
||||
{
|
||||
field: 'id',
|
||||
|
||||
+7
-2
@@ -5,7 +5,7 @@ import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
import { message, Tabs } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { createStudent, getStudent, updateStudent } from '#/api/infra/demo';
|
||||
@@ -48,7 +48,12 @@ const [Modal, modalApi] = useVbenModal({
|
||||
return;
|
||||
}
|
||||
// 校验子表单
|
||||
const studentTeacherValid = await studentTeacherFormRef.value?.validate();
|
||||
const studentContactValid = await studentContactFormRef.value?.validate();
|
||||
if (!studentContactValid) {
|
||||
subTabsName.value = 'studentContact';
|
||||
return;
|
||||
}
|
||||
const studentTeacherValid = await studentTeacherFormRef.value?.validate();
|
||||
if (!studentTeacherValid) {
|
||||
subTabsName.value = 'studentTeacher';
|
||||
return;
|
||||
|
||||
+83
-13
@@ -6,7 +6,7 @@
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { Plus } from "@vben/icons";
|
||||
import { Button, Tabs, Checkbox, Input, Textarea, Select,RadioGroup,CheckboxGroup, DatePicker } from 'ant-design-vue';
|
||||
import { Button, Checkbox, CheckboxGroup, DatePicker, Input, message, RadioGroup, Select, Tabs, Textarea } from 'ant-design-vue';
|
||||
import { ImageUpload, FileUpload } from "#/components/upload";
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { useStudentContactGridEditColumns } from '../data';
|
||||
@@ -45,26 +45,96 @@ const handleDelete = async (row: InfraStudentApi.StudentContact) => {
|
||||
await gridApi.grid.remove(row);
|
||||
}
|
||||
|
||||
/** 提供获取表格数据的方法供父组件调用 */
|
||||
defineExpose({
|
||||
getData: (): InfraStudentApi.StudentContact[] => {
|
||||
const data = gridApi.grid.getData() as InfraStudentApi.StudentContact[];
|
||||
const removeRecords = gridApi.grid.getRemoveRecords() as InfraStudentApi.StudentContact[];
|
||||
const insertRecords = gridApi.grid.getInsertRecords() as InfraStudentApi.StudentContact[];
|
||||
return data
|
||||
.filter((row) => !removeRecords.some((removed) => removed.id === row.id))
|
||||
.concat(insertRecords.map((row: any) => ({ ...row, id: undefined })));
|
||||
},
|
||||
});
|
||||
/** 获取当前子表数据,新增行不提交表格生成的临时编号 */
|
||||
function getData(): InfraStudentApi.StudentContact[] {
|
||||
const { fullData } = gridApi.grid.getTableData();
|
||||
const inserted = new Set(gridApi.grid.getInsertRecords());
|
||||
return fullData.map((row) =>
|
||||
inserted.has(row) ? { ...row, id: undefined } : { ...row },
|
||||
);
|
||||
}
|
||||
|
||||
/** 校验子表必填字段 */
|
||||
function validate(): boolean {
|
||||
for (const [index, row] of getData().entries()) {
|
||||
if (
|
||||
row.name === null ||
|
||||
row.name === undefined ||
|
||||
(typeof row.name === 'string' && String(row.name).trim() === '') ||
|
||||
(Array.isArray(row.name) && row.name.length === 0)
|
||||
) {
|
||||
message.error(`第 ${index + 1} 行:名字不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.description === null ||
|
||||
row.description === undefined ||
|
||||
(typeof row.description === 'string' && String(row.description).trim() === '') ||
|
||||
(Array.isArray(row.description) && row.description.length === 0)
|
||||
) {
|
||||
message.error(`第 ${index + 1} 行:简介不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.birthday === null ||
|
||||
row.birthday === undefined ||
|
||||
(typeof row.birthday === 'string' && String(row.birthday).trim() === '') ||
|
||||
(Array.isArray(row.birthday) && row.birthday.length === 0)
|
||||
) {
|
||||
message.error(`第 ${index + 1} 行:出生日期不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.sex === null ||
|
||||
row.sex === undefined ||
|
||||
(typeof row.sex === 'string' && String(row.sex).trim() === '') ||
|
||||
(Array.isArray(row.sex) && row.sex.length === 0)
|
||||
) {
|
||||
message.error(`第 ${index + 1} 行:性别不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.enabled === null ||
|
||||
row.enabled === undefined ||
|
||||
(typeof row.enabled === 'string' && String(row.enabled).trim() === '') ||
|
||||
(Array.isArray(row.enabled) && row.enabled.length === 0)
|
||||
) {
|
||||
message.error(`第 ${index + 1} 行:是否有效不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.avatar === null ||
|
||||
row.avatar === undefined ||
|
||||
(typeof row.avatar === 'string' && String(row.avatar).trim() === '') ||
|
||||
(Array.isArray(row.avatar) && row.avatar.length === 0)
|
||||
) {
|
||||
message.error(`第 ${index + 1} 行:头像不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.memo === null ||
|
||||
row.memo === undefined ||
|
||||
(typeof row.memo === 'string' && String(row.memo).trim() === '') ||
|
||||
(Array.isArray(row.memo) && row.memo.length === 0)
|
||||
) {
|
||||
message.error(`第 ${index + 1} 行:备注不能为空`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
defineExpose({ getData, validate });
|
||||
|
||||
/** 监听主表的关联字段的变化,加载对应的子表数据 */
|
||||
watch(
|
||||
() => props.studentId,
|
||||
async (val) => {
|
||||
await nextTick();
|
||||
if (!val) {
|
||||
await gridApi.grid.loadData([]);
|
||||
return;
|
||||
}
|
||||
await nextTick();
|
||||
await gridApi.grid.loadData(await getStudentContactListByStudentId(props.studentId!));
|
||||
},
|
||||
{ immediate: true },
|
||||
|
||||
+2
-1
@@ -39,10 +39,11 @@ defineExpose({
|
||||
watch(
|
||||
() => props.studentId,
|
||||
async (val) => {
|
||||
await nextTick();
|
||||
await formApi.resetForm();
|
||||
if (!val) {
|
||||
return;
|
||||
}
|
||||
await nextTick();
|
||||
await formApi.setValues(await getStudentTeacherByStudentId(props.studentId!));
|
||||
},
|
||||
{ immediate: true },
|
||||
|
||||
+1
-1
@@ -268,7 +268,7 @@ export function useGridColumns(): VxeTableGridOptions<InfraStudentApi.Student>['
|
||||
minWidth: 120,
|
||||
slots: { default: 'sex' },
|
||||
params: {
|
||||
options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'boolean'),
|
||||
options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number'),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
+7
-2
@@ -5,7 +5,7 @@ import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
import { message, Tabs } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { createStudent, getStudent, updateStudent } from '#/api/infra/demo';
|
||||
@@ -48,7 +48,12 @@ const [Modal, modalApi] = useVbenModal({
|
||||
return;
|
||||
}
|
||||
// 校验子表单
|
||||
const studentTeacherValid = await studentTeacherFormRef.value?.validate();
|
||||
const studentContactValid = await studentContactFormRef.value?.validate();
|
||||
if (!studentContactValid) {
|
||||
subTabsName.value = 'studentContact';
|
||||
return;
|
||||
}
|
||||
const studentTeacherValid = await studentTeacherFormRef.value?.validate();
|
||||
if (!studentTeacherValid) {
|
||||
subTabsName.value = 'studentTeacher';
|
||||
return;
|
||||
|
||||
+83
-13
@@ -6,7 +6,7 @@
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { Plus } from "@vben/icons";
|
||||
import { Button, Tabs, Checkbox, Input, Textarea, Select,RadioGroup,CheckboxGroup, DatePicker } from 'ant-design-vue';
|
||||
import { Button, Checkbox, CheckboxGroup, DatePicker, Input, message, RadioGroup, Select, Tabs, Textarea } from 'ant-design-vue';
|
||||
import { ImageUpload, FileUpload } from "#/components/upload";
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { useStudentContactGridEditColumns } from '../data';
|
||||
@@ -45,26 +45,96 @@ const handleDelete = async (row: InfraStudentApi.StudentContact) => {
|
||||
await gridApi.grid.remove(row);
|
||||
}
|
||||
|
||||
/** 提供获取表格数据的方法供父组件调用 */
|
||||
defineExpose({
|
||||
getData: (): InfraStudentApi.StudentContact[] => {
|
||||
const data = gridApi.grid.getData() as InfraStudentApi.StudentContact[];
|
||||
const removeRecords = gridApi.grid.getRemoveRecords() as InfraStudentApi.StudentContact[];
|
||||
const insertRecords = gridApi.grid.getInsertRecords() as InfraStudentApi.StudentContact[];
|
||||
return data
|
||||
.filter((row) => !removeRecords.some((removed) => removed.id === row.id))
|
||||
.concat(insertRecords.map((row: any) => ({ ...row, id: undefined })));
|
||||
},
|
||||
});
|
||||
/** 获取当前子表数据,新增行不提交表格生成的临时编号 */
|
||||
function getData(): InfraStudentApi.StudentContact[] {
|
||||
const { fullData } = gridApi.grid.getTableData();
|
||||
const inserted = new Set(gridApi.grid.getInsertRecords());
|
||||
return fullData.map((row) =>
|
||||
inserted.has(row) ? { ...row, id: undefined } : { ...row },
|
||||
);
|
||||
}
|
||||
|
||||
/** 校验子表必填字段 */
|
||||
function validate(): boolean {
|
||||
for (const [index, row] of getData().entries()) {
|
||||
if (
|
||||
row.name === null ||
|
||||
row.name === undefined ||
|
||||
(typeof row.name === 'string' && String(row.name).trim() === '') ||
|
||||
(Array.isArray(row.name) && row.name.length === 0)
|
||||
) {
|
||||
message.error(`第 ${index + 1} 行:名字不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.description === null ||
|
||||
row.description === undefined ||
|
||||
(typeof row.description === 'string' && String(row.description).trim() === '') ||
|
||||
(Array.isArray(row.description) && row.description.length === 0)
|
||||
) {
|
||||
message.error(`第 ${index + 1} 行:简介不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.birthday === null ||
|
||||
row.birthday === undefined ||
|
||||
(typeof row.birthday === 'string' && String(row.birthday).trim() === '') ||
|
||||
(Array.isArray(row.birthday) && row.birthday.length === 0)
|
||||
) {
|
||||
message.error(`第 ${index + 1} 行:出生日期不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.sex === null ||
|
||||
row.sex === undefined ||
|
||||
(typeof row.sex === 'string' && String(row.sex).trim() === '') ||
|
||||
(Array.isArray(row.sex) && row.sex.length === 0)
|
||||
) {
|
||||
message.error(`第 ${index + 1} 行:性别不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.enabled === null ||
|
||||
row.enabled === undefined ||
|
||||
(typeof row.enabled === 'string' && String(row.enabled).trim() === '') ||
|
||||
(Array.isArray(row.enabled) && row.enabled.length === 0)
|
||||
) {
|
||||
message.error(`第 ${index + 1} 行:是否有效不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.avatar === null ||
|
||||
row.avatar === undefined ||
|
||||
(typeof row.avatar === 'string' && String(row.avatar).trim() === '') ||
|
||||
(Array.isArray(row.avatar) && row.avatar.length === 0)
|
||||
) {
|
||||
message.error(`第 ${index + 1} 行:头像不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.memo === null ||
|
||||
row.memo === undefined ||
|
||||
(typeof row.memo === 'string' && String(row.memo).trim() === '') ||
|
||||
(Array.isArray(row.memo) && row.memo.length === 0)
|
||||
) {
|
||||
message.error(`第 ${index + 1} 行:备注不能为空`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
defineExpose({ getData, validate });
|
||||
|
||||
/** 监听主表的关联字段的变化,加载对应的子表数据 */
|
||||
watch(
|
||||
() => props.studentId,
|
||||
async (val) => {
|
||||
await nextTick();
|
||||
if (!val) {
|
||||
await gridApi.grid.loadData([]);
|
||||
return;
|
||||
}
|
||||
await nextTick();
|
||||
await gridApi.grid.loadData(await getStudentContactListByStudentId(props.studentId!));
|
||||
},
|
||||
{ immediate: true },
|
||||
|
||||
+2
-1
@@ -39,10 +39,11 @@ defineExpose({
|
||||
watch(
|
||||
() => props.studentId,
|
||||
async (val) => {
|
||||
await nextTick();
|
||||
await formApi.resetForm();
|
||||
if (!val) {
|
||||
return;
|
||||
}
|
||||
await nextTick();
|
||||
await formApi.setValues(await getStudentTeacherByStudentId(props.studentId!));
|
||||
},
|
||||
{ immediate: true },
|
||||
|
||||
+6
@@ -51,6 +51,11 @@ function beforeUpload(file: FileType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** 移除文件 */
|
||||
function handleRemove() {
|
||||
formApi.setFieldValue('file', undefined);
|
||||
}
|
||||
|
||||
/** 下载模板 */
|
||||
async function handleDownload() {
|
||||
const data = await importStudentTemplate();
|
||||
@@ -67,6 +72,7 @@ async function handleDownload() {
|
||||
:max-count="1"
|
||||
accept=".xls,.xlsx"
|
||||
:before-upload="beforeUpload"
|
||||
:on-remove="handleRemove"
|
||||
>
|
||||
<Button type="primary"> 选择 Excel 文件 </Button>
|
||||
</Upload>
|
||||
|
||||
@@ -32,7 +32,7 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
id: 0,
|
||||
name: '顶级分类',
|
||||
});
|
||||
return handleTree(data);
|
||||
return handleTree(data, 'id', 'parentId');
|
||||
},
|
||||
labelField: 'name',
|
||||
valueField: 'id',
|
||||
|
||||
+31
-31
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import type { StudentApi } from '#/api/infra/student';
|
||||
import type { StudentApi } from '#/api/infra/demo';
|
||||
import type { FormRules } from 'element-plus';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
@@ -11,7 +11,7 @@ import { ElMessage, ElTabs, ElTabPane, ElForm, ElFormItem, ElInput, ElSelect, El
|
||||
|
||||
import { computed, ref, reactive } from 'vue';
|
||||
import { $t } from '#/locales';
|
||||
import { getStudent, createStudent, updateStudent } from '#/api/infra/student';
|
||||
import { getStudent, createStudent, updateStudent } from '#/api/infra/demo';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
|
||||
@@ -102,56 +102,56 @@ const [Modal, modalApi] = useVbenModal({
|
||||
|
||||
<template>
|
||||
<Modal :title="getTitle">
|
||||
<el-form
|
||||
<ElForm
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="rules"
|
||||
label-width="120px"
|
||||
label-position="right"
|
||||
>
|
||||
<el-form-item label="名字" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入名字" />
|
||||
</el-form-item>
|
||||
<el-form-item label="简介" prop="description">
|
||||
<el-input v-model="formData.description" type="textarea" placeholder="请输入简介" />
|
||||
</el-form-item>
|
||||
<el-form-item label="出生日期" prop="birthday">
|
||||
<el-date-picker
|
||||
<ElFormItem label="名字" prop="name">
|
||||
<ElInput v-model="formData.name" placeholder="请输入名字" />
|
||||
</ElFormItem>
|
||||
<ElFormItem label="简介" prop="description">
|
||||
<ElInput v-model="formData.description" type="textarea" placeholder="请输入简介" />
|
||||
</ElFormItem>
|
||||
<ElFormItem label="出生日期" prop="birthday">
|
||||
<ElDatePicker
|
||||
v-model="formData.birthday"
|
||||
value-format="x"
|
||||
placeholder="选择出生日期"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="性别" prop="sex">
|
||||
<el-select v-model="formData.sex" placeholder="请选择性别">
|
||||
<el-option
|
||||
</ElFormItem>
|
||||
<ElFormItem label="性别" prop="sex">
|
||||
<ElSelect v-model="formData.sex" placeholder="请选择性别">
|
||||
<ElOption
|
||||
v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
:label="dict.label"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否有效" prop="enabled">
|
||||
<el-radio-group v-model="formData.enabled">
|
||||
<el-radio
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="是否有效" prop="enabled">
|
||||
<ElRadioGroup v-model="formData.enabled">
|
||||
<ElRadio
|
||||
v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="头像" prop="avatar">
|
||||
</ElRadio>
|
||||
</ElRadioGroup>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="头像" prop="avatar">
|
||||
<ImageUpload v-model="formData.avatar" />
|
||||
</el-form-item>
|
||||
<el-form-item label="附件" prop="video">
|
||||
</ElFormItem>
|
||||
<ElFormItem label="附件" prop="video">
|
||||
<FileUpload v-model="formData.video" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="memo">
|
||||
</ElFormItem>
|
||||
<ElFormItem label="备注" prop="memo">
|
||||
<RichTextarea v-model="formData.memo" height="500px" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
+79
-73
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import type { StudentApi } from '#/api/infra/student';
|
||||
import type { StudentApi } from '#/api/infra/demo';
|
||||
|
||||
import { ref, h, reactive, onMounted, nextTick } from 'vue';
|
||||
|
||||
@@ -19,7 +19,7 @@ import { VxeColumn, VxeTable } from '#/adapter/vxe-table';
|
||||
|
||||
import { $t } from '#/locales';
|
||||
import { isEmpty } from '@vben/utils';
|
||||
import { getStudentPage, deleteStudent, deleteStudentList, exportStudent } from '#/api/infra/student';
|
||||
import { getStudentPage, deleteStudent, deleteStudentList, exportStudent } from '#/api/infra/demo';
|
||||
|
||||
/** 子表的列表 */
|
||||
const subTabsName = ref('studentContact')
|
||||
@@ -35,12 +35,11 @@ const total = ref(0) // 列表的总页数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
name: undefined,
|
||||
birthday: undefined,
|
||||
birthday: undefined,
|
||||
sex: undefined,
|
||||
enabled: undefined,
|
||||
createTime: undefined,
|
||||
name: undefined,
|
||||
birthday: undefined,
|
||||
sex: undefined,
|
||||
enabled: undefined,
|
||||
createTime: undefined,
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
@@ -134,7 +133,14 @@ function handleRowCheckboxChange({
|
||||
async function handleExport() {
|
||||
try {
|
||||
exportLoading.value = true;
|
||||
const data = await exportStudent(queryParams);
|
||||
const params = cloneDeep(queryParams) as any;
|
||||
if (params.birthday && Array.isArray(params.birthday)) {
|
||||
params.birthday = (params.birthday as string[]).join(',');
|
||||
}
|
||||
if (params.createTime && Array.isArray(params.createTime)) {
|
||||
params.createTime = (params.createTime as string[]).join(',');
|
||||
}
|
||||
const data = await exportStudent(params);
|
||||
downloadFileFromBlobPart({ fileName: '学生.xls', source: data });
|
||||
}finally {
|
||||
exportLoading.value = false;
|
||||
@@ -156,61 +162,61 @@ onMounted(() => {
|
||||
|
||||
<ContentWrap v-if="!hiddenSearchBar">
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form
|
||||
<ElForm
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
inline
|
||||
>
|
||||
<el-form-item label="名字">
|
||||
<el-input
|
||||
<ElFormItem label="名字">
|
||||
<ElInput
|
||||
v-model="queryParams.name"
|
||||
placeholder="请输入名字"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-[240px]"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="出生日期">
|
||||
<el-date-picker
|
||||
</ElFormItem>
|
||||
<ElFormItem label="出生日期">
|
||||
<ElDatePicker
|
||||
v-model="queryParams.birthday"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="选择出生日期"
|
||||
clearable
|
||||
class="!w-[240px]"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="性别">
|
||||
<el-select
|
||||
</ElFormItem>
|
||||
<ElFormItem label="性别">
|
||||
<ElSelect
|
||||
v-model="queryParams.sex"
|
||||
placeholder="请选择性别"
|
||||
clearable
|
||||
class="!w-[240px]"
|
||||
>
|
||||
<el-option
|
||||
<ElOption
|
||||
v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
:label="dict.label"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否有效">
|
||||
<el-select
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="是否有效">
|
||||
<ElSelect
|
||||
v-model="queryParams.enabled"
|
||||
placeholder="请选择是否有效"
|
||||
clearable
|
||||
class="!w-[240px]"
|
||||
>
|
||||
<el-option
|
||||
<ElOption
|
||||
v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
:label="dict.label"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间">
|
||||
<el-date-picker
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="创建时间">
|
||||
<ElDatePicker
|
||||
v-model="queryParams.createTime"
|
||||
type="daterange"
|
||||
value-format="YYYY-MM-DD"
|
||||
@@ -219,24 +225,24 @@ onMounted(() => {
|
||||
end-placeholder="结束日期"
|
||||
class="!w-[240px]"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button class="ml-2" @click="resetQuery"> 重置 </el-button>
|
||||
<el-button class="ml-2" @click="handleQuery" type="primary">
|
||||
</ElFormItem>
|
||||
<ElFormItem>
|
||||
<ElButton class="ml-2" @click="resetQuery"> 重置 </ElButton>
|
||||
<ElButton class="ml-2" @click="handleQuery" type="primary">
|
||||
搜索
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ElButton>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap title="学生">
|
||||
<template #extra>
|
||||
<TableToolbar
|
||||
<VbenVxeTableToolbar
|
||||
ref="tableToolbarRef"
|
||||
v-model:hidden-search="hiddenSearchBar"
|
||||
>
|
||||
<el-button
|
||||
<ElButton
|
||||
class="ml-2"
|
||||
:icon="h(Plus)"
|
||||
type="primary"
|
||||
@@ -244,8 +250,8 @@ onMounted(() => {
|
||||
v-access:code="['infra:student:create']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.create', ['学生']) }}
|
||||
</el-button>
|
||||
<el-button
|
||||
</ElButton>
|
||||
<ElButton
|
||||
:icon="h(Download)"
|
||||
type="primary"
|
||||
class="ml-2"
|
||||
@@ -254,8 +260,8 @@ onMounted(() => {
|
||||
v-access:code="['infra:student:export']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.export') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
</ElButton>
|
||||
<ElButton
|
||||
:icon="h(Trash2)"
|
||||
type="danger"
|
||||
class="ml-2"
|
||||
@@ -264,10 +270,10 @@ onMounted(() => {
|
||||
v-access:code="['infra:student:delete']"
|
||||
>
|
||||
批量删除
|
||||
</el-button>
|
||||
</TableToolbar>
|
||||
</ElButton>
|
||||
</VbenVxeTableToolbar>
|
||||
</template>
|
||||
<vxe-table
|
||||
<VxeTable
|
||||
ref="tableRef"
|
||||
:data="list"
|
||||
@cell-click="onCellClick"
|
||||
@@ -281,36 +287,36 @@ onMounted(() => {
|
||||
@checkboxAll="handleRowCheckboxChange"
|
||||
@checkboxChange="handleRowCheckboxChange"
|
||||
>
|
||||
<vxe-column type="checkbox" width="40"></vxe-column>
|
||||
<vxe-column field="id" title="编号" align="center" />
|
||||
<vxe-column field="name" title="名字" align="center" />
|
||||
<vxe-column field="description" title="简介" align="center" />
|
||||
<vxe-column field="birthday" title="出生日期" align="center">
|
||||
<VxeColumn type="checkbox" width="40"></VxeColumn>
|
||||
<VxeColumn field="id" title="编号" align="center" />
|
||||
<VxeColumn field="name" title="名字" align="center" />
|
||||
<VxeColumn field="description" title="简介" align="center" />
|
||||
<VxeColumn field="birthday" title="出生日期" align="center">
|
||||
<template #default="{row}">
|
||||
{{formatDateTime(row.birthday)}}
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="sex" title="性别" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="sex" title="性别" align="center">
|
||||
<template #default="{row}">
|
||||
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="row.sex" />
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="enabled" title="是否有效" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="enabled" title="是否有效" align="center">
|
||||
<template #default="{row}">
|
||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="row.enabled" />
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="avatar" title="头像" align="center" />
|
||||
<vxe-column field="video" title="附件" align="center" />
|
||||
<vxe-column field="memo" title="备注" align="center" />
|
||||
<vxe-column field="createTime" title="创建时间" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="avatar" title="头像" align="center" />
|
||||
<VxeColumn field="video" title="附件" align="center" />
|
||||
<VxeColumn field="memo" title="备注" align="center" />
|
||||
<VxeColumn field="createTime" title="创建时间" align="center">
|
||||
<template #default="{row}">
|
||||
{{formatDateTime(row.createTime)}}
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="operation" title="操作" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="operation" title="操作" align="center">
|
||||
<template #default="{row}">
|
||||
<el-button
|
||||
<ElButton
|
||||
size="small"
|
||||
type="primary"
|
||||
link
|
||||
@@ -318,8 +324,8 @@ onMounted(() => {
|
||||
v-access:code="['infra:student:update']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.edit') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
</ElButton>
|
||||
<ElButton
|
||||
size="small"
|
||||
type="danger"
|
||||
link
|
||||
@@ -328,13 +334,13 @@ onMounted(() => {
|
||||
v-access:code="['infra:student:delete']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.delete') }}
|
||||
</el-button>
|
||||
</ElButton>
|
||||
</template>
|
||||
</vxe-column>
|
||||
</vxe-table>
|
||||
</VxeColumn>
|
||||
</VxeTable>
|
||||
<!-- 分页 -->
|
||||
<div class="mt-2 flex justify-end">
|
||||
<el-pagination
|
||||
<ElPagination
|
||||
:total="total"
|
||||
v-model:current-page="queryParams.pageNo"
|
||||
v-model:page-size="queryParams.pageSize"
|
||||
@@ -347,14 +353,14 @@ onMounted(() => {
|
||||
</ContentWrap>
|
||||
<ContentWrap>
|
||||
<!-- 子表的表单 -->
|
||||
<el-tabs v-model="subTabsName">
|
||||
<el-tab-pane name="studentContact" label="学生联系人">
|
||||
<ElTabs v-model="subTabsName">
|
||||
<ElTabPane name="studentContact" label="学生联系人">
|
||||
<StudentContactList :student-id="selectStudent?.id" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane name="studentTeacher" label="学生班主任">
|
||||
</ElTabPane>
|
||||
<ElTabPane name="studentTeacher" label="学生班主任">
|
||||
<StudentTeacherList :student-id="selectStudent?.id" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</ElTabPane>
|
||||
</ElTabs>
|
||||
</ContentWrap>
|
||||
</Page>
|
||||
</template>
|
||||
|
||||
+31
-31
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import type { StudentApi } from '#/api/infra/student';
|
||||
import type { StudentApi } from '#/api/infra/demo';
|
||||
import type { FormRules } from 'element-plus';
|
||||
|
||||
import { computed, ref, reactive } from 'vue';
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { getStudentContact, createStudentContact, updateStudentContact } from '#/api/infra/student';
|
||||
import { getStudentContact, createStudentContact, updateStudentContact } from '#/api/infra/demo';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
const getTitle = computed(() => {
|
||||
@@ -107,56 +107,56 @@
|
||||
|
||||
<template>
|
||||
<Modal :title="getTitle">
|
||||
<el-form
|
||||
<ElForm
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="rules"
|
||||
label-width="120px"
|
||||
label-position="right"
|
||||
>
|
||||
<el-form-item label="名字" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入名字" />
|
||||
</el-form-item>
|
||||
<el-form-item label="简介" prop="description">
|
||||
<el-input v-model="formData.description" type="textarea" placeholder="请输入简介" />
|
||||
</el-form-item>
|
||||
<el-form-item label="出生日期" prop="birthday">
|
||||
<el-date-picker
|
||||
<ElFormItem label="名字" prop="name">
|
||||
<ElInput v-model="formData.name" placeholder="请输入名字" />
|
||||
</ElFormItem>
|
||||
<ElFormItem label="简介" prop="description">
|
||||
<ElInput v-model="formData.description" type="textarea" placeholder="请输入简介" />
|
||||
</ElFormItem>
|
||||
<ElFormItem label="出生日期" prop="birthday">
|
||||
<ElDatePicker
|
||||
v-model="formData.birthday"
|
||||
value-format="x"
|
||||
placeholder="选择出生日期"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="性别" prop="sex">
|
||||
<el-select v-model="formData.sex" placeholder="请选择性别">
|
||||
<el-option
|
||||
</ElFormItem>
|
||||
<ElFormItem label="性别" prop="sex">
|
||||
<ElSelect v-model="formData.sex" placeholder="请选择性别">
|
||||
<ElOption
|
||||
v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
:label="dict.label"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否有效" prop="enabled">
|
||||
<el-radio-group v-model="formData.enabled">
|
||||
<el-radio
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="是否有效" prop="enabled">
|
||||
<ElRadioGroup v-model="formData.enabled">
|
||||
<ElRadio
|
||||
v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="头像" prop="avatar">
|
||||
</ElRadio>
|
||||
</ElRadioGroup>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="头像" prop="avatar">
|
||||
<ImageUpload v-model="formData.avatar" />
|
||||
</el-form-item>
|
||||
<el-form-item label="附件" prop="video">
|
||||
</ElFormItem>
|
||||
<ElFormItem label="附件" prop="video">
|
||||
<FileUpload v-model="formData.video" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="memo">
|
||||
</ElFormItem>
|
||||
<ElFormItem label="备注" prop="memo">
|
||||
<RichTextarea v-model="formData.memo" height="500px" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
+60
-59
@@ -1,11 +1,12 @@
|
||||
<script lang="ts" setup>
|
||||
import type { StudentApi } from '#/api/infra/student';
|
||||
import type { StudentApi } from '#/api/infra/demo';
|
||||
import type { VxeTableInstance } from '#/adapter/vxe-table';
|
||||
|
||||
import { reactive,ref, h, nextTick,watch,onMounted } from 'vue';
|
||||
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
import { getDictOptions } from '@vben/hooks';
|
||||
import { formatDateTime } from '@vben/utils';
|
||||
|
||||
import { DictTag } from '#/components/dict-tag';
|
||||
import { getRangePickerDefaultProps } from '#/utils';
|
||||
@@ -21,8 +22,8 @@
|
||||
import { Plus, Trash2 } from '@vben/icons';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { deleteStudentContact, deleteStudentContactList, getStudentContactPage } from '#/api/infra/student';
|
||||
import { isEmpty } from '@vben/utils';
|
||||
import { deleteStudentContact, deleteStudentContactList, getStudentContactPage } from '#/api/infra/demo';
|
||||
import { cloneDeep, isEmpty } from '@vben/utils';
|
||||
|
||||
const props = defineProps<{
|
||||
studentId?: number // 学生编号(主表的关联字段)
|
||||
@@ -159,61 +160,61 @@ onMounted(() => {
|
||||
<div class="h-[600px]">
|
||||
<ContentWrap v-if="!hiddenSearchBar">
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form
|
||||
<ElForm
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
inline
|
||||
>
|
||||
<el-form-item label="名字">
|
||||
<el-input
|
||||
<ElFormItem label="名字">
|
||||
<ElInput
|
||||
v-model="queryParams.name"
|
||||
placeholder="请输入名字"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-[240px]"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="出生日期">
|
||||
<el-date-picker
|
||||
</ElFormItem>
|
||||
<ElFormItem label="出生日期">
|
||||
<ElDatePicker
|
||||
v-model="queryParams.birthday"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="选择出生日期"
|
||||
clearable
|
||||
class="!w-[240px]"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="性别">
|
||||
<el-select
|
||||
</ElFormItem>
|
||||
<ElFormItem label="性别">
|
||||
<ElSelect
|
||||
v-model="queryParams.sex"
|
||||
placeholder="请选择性别"
|
||||
clearable
|
||||
class="!w-[240px]"
|
||||
>
|
||||
<el-option
|
||||
<ElOption
|
||||
v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
:label="dict.label"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否有效">
|
||||
<el-select
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="是否有效">
|
||||
<ElSelect
|
||||
v-model="queryParams.enabled"
|
||||
placeholder="请选择是否有效"
|
||||
clearable
|
||||
class="!w-[240px]"
|
||||
>
|
||||
<el-option
|
||||
<ElOption
|
||||
v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
:label="dict.label"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间">
|
||||
<el-date-picker
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="创建时间">
|
||||
<ElDatePicker
|
||||
v-model="queryParams.createTime"
|
||||
type="daterange"
|
||||
value-format="YYYY-MM-DD"
|
||||
@@ -222,14 +223,14 @@ onMounted(() => {
|
||||
end-placeholder="结束日期"
|
||||
class="!w-[240px]"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button class="ml-2" @click="resetQuery"> 重置 </el-button>
|
||||
<el-button class="ml-2" @click="handleQuery" type="primary">
|
||||
</ElFormItem>
|
||||
<ElFormItem>
|
||||
<ElButton class="ml-2" @click="resetQuery"> 重置 </ElButton>
|
||||
<ElButton class="ml-2" @click="handleQuery" type="primary">
|
||||
搜索
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ElButton>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
@@ -239,7 +240,7 @@ onMounted(() => {
|
||||
ref="tableToolbarRef"
|
||||
v-model:hidden-search="hiddenSearchBar"
|
||||
>
|
||||
<el-button
|
||||
<ElButton
|
||||
class="ml-2"
|
||||
:icon="h(Plus)"
|
||||
type="primary"
|
||||
@@ -247,8 +248,8 @@ onMounted(() => {
|
||||
v-access:code="['infra:student:create']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.create', ['学生']) }}
|
||||
</el-button>
|
||||
<el-button
|
||||
</ElButton>
|
||||
<ElButton
|
||||
:icon="h(Trash2)"
|
||||
type="danger"
|
||||
class="ml-2"
|
||||
@@ -257,10 +258,10 @@ onMounted(() => {
|
||||
v-access:code="['infra:student:delete']"
|
||||
>
|
||||
批量删除
|
||||
</el-button>
|
||||
</TableToolbar>
|
||||
</ElButton>
|
||||
</VbenVxeTableToolbar>
|
||||
</template>
|
||||
<vxe-table
|
||||
<VxeTable
|
||||
ref="tableRef"
|
||||
:data="list"
|
||||
show-overflow
|
||||
@@ -268,37 +269,37 @@ onMounted(() => {
|
||||
@checkboxAll="handleRowCheckboxChange"
|
||||
@checkboxChange="handleRowCheckboxChange"
|
||||
>
|
||||
<vxe-column type="checkbox" width="40"></vxe-column>
|
||||
<vxe-column field="id" title="编号" align="center" />
|
||||
<vxe-column field="studentId" title="学生编号" align="center" />
|
||||
<vxe-column field="name" title="名字" align="center" />
|
||||
<vxe-column field="description" title="简介" align="center" />
|
||||
<vxe-column field="birthday" title="出生日期" align="center">
|
||||
<VxeColumn type="checkbox" width="40"></VxeColumn>
|
||||
<VxeColumn field="id" title="编号" align="center" />
|
||||
<VxeColumn field="studentId" title="学生编号" align="center" />
|
||||
<VxeColumn field="name" title="名字" align="center" />
|
||||
<VxeColumn field="description" title="简介" align="center" />
|
||||
<VxeColumn field="birthday" title="出生日期" align="center">
|
||||
<template #default="{row}">
|
||||
{{formatDateTime(row.birthday)}}
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="sex" title="性别" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="sex" title="性别" align="center">
|
||||
<template #default="{row}">
|
||||
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="row.sex" />
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="enabled" title="是否有效" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="enabled" title="是否有效" align="center">
|
||||
<template #default="{row}">
|
||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="row.enabled" />
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="avatar" title="头像" align="center" />
|
||||
<vxe-column field="video" title="附件" align="center" />
|
||||
<vxe-column field="memo" title="备注" align="center" />
|
||||
<vxe-column field="createTime" title="创建时间" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="avatar" title="头像" align="center" />
|
||||
<VxeColumn field="video" title="附件" align="center" />
|
||||
<VxeColumn field="memo" title="备注" align="center" />
|
||||
<VxeColumn field="createTime" title="创建时间" align="center">
|
||||
<template #default="{row}">
|
||||
{{formatDateTime(row.createTime)}}
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="operation" title="操作" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="operation" title="操作" align="center">
|
||||
<template #default="{row}">
|
||||
<el-button
|
||||
<ElButton
|
||||
size="small"
|
||||
type="primary"
|
||||
link
|
||||
@@ -306,8 +307,8 @@ onMounted(() => {
|
||||
v-access:code="['infra:student:update']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.edit') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
</ElButton>
|
||||
<ElButton
|
||||
size="small"
|
||||
type="danger"
|
||||
link
|
||||
@@ -316,13 +317,13 @@ onMounted(() => {
|
||||
v-access:code="['infra:student:delete']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.delete') }}
|
||||
</el-button>
|
||||
</ElButton>
|
||||
</template>
|
||||
</vxe-column>
|
||||
</vxe-table>
|
||||
</VxeColumn>
|
||||
</VxeTable>
|
||||
<!-- 分页 -->
|
||||
<div class="mt-2 flex justify-end">
|
||||
<el-pagination
|
||||
<ElPagination
|
||||
:total="total"
|
||||
v-model:current-page="queryParams.pageNo"
|
||||
v-model:page-size="queryParams.pageSize"
|
||||
|
||||
+31
-31
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import type { StudentApi } from '#/api/infra/student';
|
||||
import type { StudentApi } from '#/api/infra/demo';
|
||||
import type { FormRules } from 'element-plus';
|
||||
|
||||
import { computed, ref, reactive } from 'vue';
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { getStudentTeacher, createStudentTeacher, updateStudentTeacher } from '#/api/infra/student';
|
||||
import { getStudentTeacher, createStudentTeacher, updateStudentTeacher } from '#/api/infra/demo';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
const getTitle = computed(() => {
|
||||
@@ -107,56 +107,56 @@
|
||||
|
||||
<template>
|
||||
<Modal :title="getTitle">
|
||||
<el-form
|
||||
<ElForm
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="rules"
|
||||
label-width="120px"
|
||||
label-position="right"
|
||||
>
|
||||
<el-form-item label="名字" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入名字" />
|
||||
</el-form-item>
|
||||
<el-form-item label="简介" prop="description">
|
||||
<el-input v-model="formData.description" type="textarea" placeholder="请输入简介" />
|
||||
</el-form-item>
|
||||
<el-form-item label="出生日期" prop="birthday">
|
||||
<el-date-picker
|
||||
<ElFormItem label="名字" prop="name">
|
||||
<ElInput v-model="formData.name" placeholder="请输入名字" />
|
||||
</ElFormItem>
|
||||
<ElFormItem label="简介" prop="description">
|
||||
<ElInput v-model="formData.description" type="textarea" placeholder="请输入简介" />
|
||||
</ElFormItem>
|
||||
<ElFormItem label="出生日期" prop="birthday">
|
||||
<ElDatePicker
|
||||
v-model="formData.birthday"
|
||||
value-format="x"
|
||||
placeholder="选择出生日期"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="性别" prop="sex">
|
||||
<el-select v-model="formData.sex" placeholder="请选择性别">
|
||||
<el-option
|
||||
</ElFormItem>
|
||||
<ElFormItem label="性别" prop="sex">
|
||||
<ElSelect v-model="formData.sex" placeholder="请选择性别">
|
||||
<ElOption
|
||||
v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
:label="dict.label"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否有效" prop="enabled">
|
||||
<el-radio-group v-model="formData.enabled">
|
||||
<el-radio
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="是否有效" prop="enabled">
|
||||
<ElRadioGroup v-model="formData.enabled">
|
||||
<ElRadio
|
||||
v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="头像" prop="avatar">
|
||||
</ElRadio>
|
||||
</ElRadioGroup>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="头像" prop="avatar">
|
||||
<ImageUpload v-model="formData.avatar" />
|
||||
</el-form-item>
|
||||
<el-form-item label="附件" prop="video">
|
||||
</ElFormItem>
|
||||
<ElFormItem label="附件" prop="video">
|
||||
<FileUpload v-model="formData.video" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="memo">
|
||||
</ElFormItem>
|
||||
<ElFormItem label="备注" prop="memo">
|
||||
<RichTextarea v-model="formData.memo" height="500px" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
+60
-59
@@ -1,11 +1,12 @@
|
||||
<script lang="ts" setup>
|
||||
import type { StudentApi } from '#/api/infra/student';
|
||||
import type { StudentApi } from '#/api/infra/demo';
|
||||
import type { VxeTableInstance } from '#/adapter/vxe-table';
|
||||
|
||||
import { reactive,ref, h, nextTick,watch,onMounted } from 'vue';
|
||||
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
import { getDictOptions } from '@vben/hooks';
|
||||
import { formatDateTime } from '@vben/utils';
|
||||
|
||||
import { DictTag } from '#/components/dict-tag';
|
||||
import { getRangePickerDefaultProps } from '#/utils';
|
||||
@@ -21,8 +22,8 @@
|
||||
import { Plus, Trash2 } from '@vben/icons';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { deleteStudentTeacher, deleteStudentTeacherList, getStudentTeacherPage } from '#/api/infra/student';
|
||||
import { isEmpty } from '@vben/utils';
|
||||
import { deleteStudentTeacher, deleteStudentTeacherList, getStudentTeacherPage } from '#/api/infra/demo';
|
||||
import { cloneDeep, isEmpty } from '@vben/utils';
|
||||
|
||||
const props = defineProps<{
|
||||
studentId?: number // 学生编号(主表的关联字段)
|
||||
@@ -159,61 +160,61 @@ onMounted(() => {
|
||||
<div class="h-[600px]">
|
||||
<ContentWrap v-if="!hiddenSearchBar">
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form
|
||||
<ElForm
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
inline
|
||||
>
|
||||
<el-form-item label="名字">
|
||||
<el-input
|
||||
<ElFormItem label="名字">
|
||||
<ElInput
|
||||
v-model="queryParams.name"
|
||||
placeholder="请输入名字"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-[240px]"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="出生日期">
|
||||
<el-date-picker
|
||||
</ElFormItem>
|
||||
<ElFormItem label="出生日期">
|
||||
<ElDatePicker
|
||||
v-model="queryParams.birthday"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="选择出生日期"
|
||||
clearable
|
||||
class="!w-[240px]"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="性别">
|
||||
<el-select
|
||||
</ElFormItem>
|
||||
<ElFormItem label="性别">
|
||||
<ElSelect
|
||||
v-model="queryParams.sex"
|
||||
placeholder="请选择性别"
|
||||
clearable
|
||||
class="!w-[240px]"
|
||||
>
|
||||
<el-option
|
||||
<ElOption
|
||||
v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
:label="dict.label"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否有效">
|
||||
<el-select
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="是否有效">
|
||||
<ElSelect
|
||||
v-model="queryParams.enabled"
|
||||
placeholder="请选择是否有效"
|
||||
clearable
|
||||
class="!w-[240px]"
|
||||
>
|
||||
<el-option
|
||||
<ElOption
|
||||
v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
:label="dict.label"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间">
|
||||
<el-date-picker
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="创建时间">
|
||||
<ElDatePicker
|
||||
v-model="queryParams.createTime"
|
||||
type="daterange"
|
||||
value-format="YYYY-MM-DD"
|
||||
@@ -222,14 +223,14 @@ onMounted(() => {
|
||||
end-placeholder="结束日期"
|
||||
class="!w-[240px]"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button class="ml-2" @click="resetQuery"> 重置 </el-button>
|
||||
<el-button class="ml-2" @click="handleQuery" type="primary">
|
||||
</ElFormItem>
|
||||
<ElFormItem>
|
||||
<ElButton class="ml-2" @click="resetQuery"> 重置 </ElButton>
|
||||
<ElButton class="ml-2" @click="handleQuery" type="primary">
|
||||
搜索
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ElButton>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
@@ -239,7 +240,7 @@ onMounted(() => {
|
||||
ref="tableToolbarRef"
|
||||
v-model:hidden-search="hiddenSearchBar"
|
||||
>
|
||||
<el-button
|
||||
<ElButton
|
||||
class="ml-2"
|
||||
:icon="h(Plus)"
|
||||
type="primary"
|
||||
@@ -247,8 +248,8 @@ onMounted(() => {
|
||||
v-access:code="['infra:student:create']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.create', ['学生']) }}
|
||||
</el-button>
|
||||
<el-button
|
||||
</ElButton>
|
||||
<ElButton
|
||||
:icon="h(Trash2)"
|
||||
type="danger"
|
||||
class="ml-2"
|
||||
@@ -257,10 +258,10 @@ onMounted(() => {
|
||||
v-access:code="['infra:student:delete']"
|
||||
>
|
||||
批量删除
|
||||
</el-button>
|
||||
</TableToolbar>
|
||||
</ElButton>
|
||||
</VbenVxeTableToolbar>
|
||||
</template>
|
||||
<vxe-table
|
||||
<VxeTable
|
||||
ref="tableRef"
|
||||
:data="list"
|
||||
show-overflow
|
||||
@@ -268,37 +269,37 @@ onMounted(() => {
|
||||
@checkboxAll="handleRowCheckboxChange"
|
||||
@checkboxChange="handleRowCheckboxChange"
|
||||
>
|
||||
<vxe-column type="checkbox" width="40"></vxe-column>
|
||||
<vxe-column field="id" title="编号" align="center" />
|
||||
<vxe-column field="studentId" title="学生编号" align="center" />
|
||||
<vxe-column field="name" title="名字" align="center" />
|
||||
<vxe-column field="description" title="简介" align="center" />
|
||||
<vxe-column field="birthday" title="出生日期" align="center">
|
||||
<VxeColumn type="checkbox" width="40"></VxeColumn>
|
||||
<VxeColumn field="id" title="编号" align="center" />
|
||||
<VxeColumn field="studentId" title="学生编号" align="center" />
|
||||
<VxeColumn field="name" title="名字" align="center" />
|
||||
<VxeColumn field="description" title="简介" align="center" />
|
||||
<VxeColumn field="birthday" title="出生日期" align="center">
|
||||
<template #default="{row}">
|
||||
{{formatDateTime(row.birthday)}}
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="sex" title="性别" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="sex" title="性别" align="center">
|
||||
<template #default="{row}">
|
||||
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="row.sex" />
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="enabled" title="是否有效" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="enabled" title="是否有效" align="center">
|
||||
<template #default="{row}">
|
||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="row.enabled" />
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="avatar" title="头像" align="center" />
|
||||
<vxe-column field="video" title="附件" align="center" />
|
||||
<vxe-column field="memo" title="备注" align="center" />
|
||||
<vxe-column field="createTime" title="创建时间" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="avatar" title="头像" align="center" />
|
||||
<VxeColumn field="video" title="附件" align="center" />
|
||||
<VxeColumn field="memo" title="备注" align="center" />
|
||||
<VxeColumn field="createTime" title="创建时间" align="center">
|
||||
<template #default="{row}">
|
||||
{{formatDateTime(row.createTime)}}
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="operation" title="操作" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="operation" title="操作" align="center">
|
||||
<template #default="{row}">
|
||||
<el-button
|
||||
<ElButton
|
||||
size="small"
|
||||
type="primary"
|
||||
link
|
||||
@@ -306,8 +307,8 @@ onMounted(() => {
|
||||
v-access:code="['infra:student:update']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.edit') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
</ElButton>
|
||||
<ElButton
|
||||
size="small"
|
||||
type="danger"
|
||||
link
|
||||
@@ -316,13 +317,13 @@ onMounted(() => {
|
||||
v-access:code="['infra:student:delete']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.delete') }}
|
||||
</el-button>
|
||||
</ElButton>
|
||||
</template>
|
||||
</vxe-column>
|
||||
</vxe-table>
|
||||
</VxeColumn>
|
||||
</VxeTable>
|
||||
<!-- 分页 -->
|
||||
<div class="mt-2 flex justify-end">
|
||||
<el-pagination
|
||||
<ElPagination
|
||||
:total="total"
|
||||
v-model:current-page="queryParams.pageNo"
|
||||
v-model:page-size="queryParams.pageSize"
|
||||
|
||||
+42
-38
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import type { StudentApi } from '#/api/infra/student';
|
||||
import type { StudentApi } from '#/api/infra/demo';
|
||||
import type { FormRules } from 'element-plus';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
@@ -13,7 +13,7 @@ import { ElMessage, ElTabs, ElTabPane, ElForm, ElFormItem, ElInput, ElSelect, El
|
||||
|
||||
import { computed, ref, reactive } from 'vue';
|
||||
import { $t } from '#/locales';
|
||||
import { getStudent, createStudent, updateStudent } from '#/api/infra/student';
|
||||
import { getStudent, createStudent, updateStudent } from '#/api/infra/demo';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
|
||||
@@ -71,7 +71,11 @@ const [Modal, modalApi] = useVbenModal({
|
||||
async onConfirm() {
|
||||
await formRef.value?.validate();
|
||||
// 校验子表单
|
||||
try {
|
||||
if (!studentContactFormRef.value?.validate()) {
|
||||
subTabsName.value = 'studentContact';
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await studentTeacherFormRef.value?.validate()
|
||||
} catch (e) {
|
||||
subTabsName.value = 'studentTeacher'
|
||||
@@ -118,65 +122,65 @@ const [Modal, modalApi] = useVbenModal({
|
||||
|
||||
<template>
|
||||
<Modal :title="getTitle">
|
||||
<el-form
|
||||
<ElForm
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="rules"
|
||||
label-width="120px"
|
||||
label-position="right"
|
||||
>
|
||||
<el-form-item label="名字" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入名字" />
|
||||
</el-form-item>
|
||||
<el-form-item label="简介" prop="description">
|
||||
<el-input v-model="formData.description" type="textarea" placeholder="请输入简介" />
|
||||
</el-form-item>
|
||||
<el-form-item label="出生日期" prop="birthday">
|
||||
<el-date-picker
|
||||
<ElFormItem label="名字" prop="name">
|
||||
<ElInput v-model="formData.name" placeholder="请输入名字" />
|
||||
</ElFormItem>
|
||||
<ElFormItem label="简介" prop="description">
|
||||
<ElInput v-model="formData.description" type="textarea" placeholder="请输入简介" />
|
||||
</ElFormItem>
|
||||
<ElFormItem label="出生日期" prop="birthday">
|
||||
<ElDatePicker
|
||||
v-model="formData.birthday"
|
||||
value-format="x"
|
||||
placeholder="选择出生日期"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="性别" prop="sex">
|
||||
<el-select v-model="formData.sex" placeholder="请选择性别">
|
||||
<el-option
|
||||
</ElFormItem>
|
||||
<ElFormItem label="性别" prop="sex">
|
||||
<ElSelect v-model="formData.sex" placeholder="请选择性别">
|
||||
<ElOption
|
||||
v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
:label="dict.label"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否有效" prop="enabled">
|
||||
<el-radio-group v-model="formData.enabled">
|
||||
<el-radio
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="是否有效" prop="enabled">
|
||||
<ElRadioGroup v-model="formData.enabled">
|
||||
<ElRadio
|
||||
v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="头像" prop="avatar">
|
||||
</ElRadio>
|
||||
</ElRadioGroup>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="头像" prop="avatar">
|
||||
<ImageUpload v-model="formData.avatar" />
|
||||
</el-form-item>
|
||||
<el-form-item label="附件" prop="video">
|
||||
</ElFormItem>
|
||||
<ElFormItem label="附件" prop="video">
|
||||
<FileUpload v-model="formData.video" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="memo">
|
||||
</ElFormItem>
|
||||
<ElFormItem label="备注" prop="memo">
|
||||
<RichTextarea v-model="formData.memo" height="500px" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
<!-- 子表的表单 -->
|
||||
<el-tabs v-model="subTabsName">
|
||||
<el-tab-pane name="studentContact" label="学生联系人">
|
||||
<ElTabs v-model="subTabsName">
|
||||
<ElTabPane name="studentContact" label="学生联系人">
|
||||
<StudentContactForm ref="studentContactFormRef" :student-id="formData?.id" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane name="studentTeacher" label="学生班主任">
|
||||
</ElTabPane>
|
||||
<ElTabPane name="studentTeacher" label="学生班主任">
|
||||
<StudentTeacherForm ref="studentTeacherFormRef" :student-id="formData?.id" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</ElTabPane>
|
||||
</ElTabs>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
+81
-75
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import type { StudentApi } from '#/api/infra/student';
|
||||
import type { StudentApi } from '#/api/infra/demo';
|
||||
|
||||
import { ref, h, reactive, onMounted, nextTick } from 'vue';
|
||||
|
||||
@@ -19,7 +19,7 @@ import { VxeColumn, VxeTable } from '#/adapter/vxe-table';
|
||||
|
||||
import { $t } from '#/locales';
|
||||
import { isEmpty } from '@vben/utils';
|
||||
import { getStudentPage, deleteStudent, deleteStudentList, exportStudent } from '#/api/infra/student';
|
||||
import { getStudentPage, deleteStudent, deleteStudentList, exportStudent } from '#/api/infra/demo';
|
||||
|
||||
/** 子表的列表 */
|
||||
const subTabsName = ref('studentContact')
|
||||
@@ -31,12 +31,11 @@ const total = ref(0) // 列表的总页数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
name: undefined,
|
||||
birthday: undefined,
|
||||
birthday: undefined,
|
||||
sex: undefined,
|
||||
enabled: undefined,
|
||||
createTime: undefined,
|
||||
name: undefined,
|
||||
birthday: undefined,
|
||||
sex: undefined,
|
||||
enabled: undefined,
|
||||
createTime: undefined,
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
@@ -130,7 +129,14 @@ function handleRowCheckboxChange({
|
||||
async function handleExport() {
|
||||
try {
|
||||
exportLoading.value = true;
|
||||
const data = await exportStudent(queryParams);
|
||||
const params = cloneDeep(queryParams) as any;
|
||||
if (params.birthday && Array.isArray(params.birthday)) {
|
||||
params.birthday = (params.birthday as string[]).join(',');
|
||||
}
|
||||
if (params.createTime && Array.isArray(params.createTime)) {
|
||||
params.createTime = (params.createTime as string[]).join(',');
|
||||
}
|
||||
const data = await exportStudent(params);
|
||||
downloadFileFromBlobPart({ fileName: '学生.xls', source: data });
|
||||
}finally {
|
||||
exportLoading.value = false;
|
||||
@@ -152,61 +158,61 @@ onMounted(() => {
|
||||
|
||||
<ContentWrap v-if="!hiddenSearchBar">
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form
|
||||
<ElForm
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
inline
|
||||
>
|
||||
<el-form-item label="名字">
|
||||
<el-input
|
||||
<ElFormItem label="名字">
|
||||
<ElInput
|
||||
v-model="queryParams.name"
|
||||
placeholder="请输入名字"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-[240px]"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="出生日期">
|
||||
<el-date-picker
|
||||
</ElFormItem>
|
||||
<ElFormItem label="出生日期">
|
||||
<ElDatePicker
|
||||
v-model="queryParams.birthday"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="选择出生日期"
|
||||
clearable
|
||||
class="!w-[240px]"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="性别">
|
||||
<el-select
|
||||
</ElFormItem>
|
||||
<ElFormItem label="性别">
|
||||
<ElSelect
|
||||
v-model="queryParams.sex"
|
||||
placeholder="请选择性别"
|
||||
clearable
|
||||
class="!w-[240px]"
|
||||
>
|
||||
<el-option
|
||||
<ElOption
|
||||
v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
:label="dict.label"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否有效">
|
||||
<el-select
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="是否有效">
|
||||
<ElSelect
|
||||
v-model="queryParams.enabled"
|
||||
placeholder="请选择是否有效"
|
||||
clearable
|
||||
class="!w-[240px]"
|
||||
>
|
||||
<el-option
|
||||
<ElOption
|
||||
v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
:label="dict.label"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间">
|
||||
<el-date-picker
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="创建时间">
|
||||
<ElDatePicker
|
||||
v-model="queryParams.createTime"
|
||||
type="daterange"
|
||||
value-format="YYYY-MM-DD"
|
||||
@@ -215,24 +221,24 @@ onMounted(() => {
|
||||
end-placeholder="结束日期"
|
||||
class="!w-[240px]"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button class="ml-2" @click="resetQuery"> 重置 </el-button>
|
||||
<el-button class="ml-2" @click="handleQuery" type="primary">
|
||||
</ElFormItem>
|
||||
<ElFormItem>
|
||||
<ElButton class="ml-2" @click="resetQuery"> 重置 </ElButton>
|
||||
<ElButton class="ml-2" @click="handleQuery" type="primary">
|
||||
搜索
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ElButton>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap title="学生">
|
||||
<template #extra>
|
||||
<TableToolbar
|
||||
<VbenVxeTableToolbar
|
||||
ref="tableToolbarRef"
|
||||
v-model:hidden-search="hiddenSearchBar"
|
||||
>
|
||||
<el-button
|
||||
<ElButton
|
||||
class="ml-2"
|
||||
:icon="h(Plus)"
|
||||
type="primary"
|
||||
@@ -240,8 +246,8 @@ onMounted(() => {
|
||||
v-access:code="['infra:student:create']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.create', ['学生']) }}
|
||||
</el-button>
|
||||
<el-button
|
||||
</ElButton>
|
||||
<ElButton
|
||||
:icon="h(Download)"
|
||||
type="primary"
|
||||
class="ml-2"
|
||||
@@ -250,8 +256,8 @@ onMounted(() => {
|
||||
v-access:code="['infra:student:export']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.export') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
</ElButton>
|
||||
<ElButton
|
||||
:icon="h(Trash2)"
|
||||
type="danger"
|
||||
class="ml-2"
|
||||
@@ -260,10 +266,10 @@ onMounted(() => {
|
||||
v-access:code="['infra:student:delete']"
|
||||
>
|
||||
批量删除
|
||||
</el-button>
|
||||
</TableToolbar>
|
||||
</ElButton>
|
||||
</VbenVxeTableToolbar>
|
||||
</template>
|
||||
<vxe-table
|
||||
<VxeTable
|
||||
ref="tableRef"
|
||||
:data="list"
|
||||
show-overflow
|
||||
@@ -271,50 +277,50 @@ onMounted(() => {
|
||||
@checkboxAll="handleRowCheckboxChange"
|
||||
@checkboxChange="handleRowCheckboxChange"
|
||||
>
|
||||
<vxe-column type="checkbox" width="40"></vxe-column>
|
||||
<VxeColumn type="checkbox" width="40"></VxeColumn>
|
||||
<!-- 子表的列表 -->
|
||||
<vxe-column type="expand" width="60">
|
||||
<VxeColumn type="expand" width="60">
|
||||
<template #content="{ row }">
|
||||
<!-- 子表的表单 -->
|
||||
<el-tabs v-model="subTabsName" class="mx-8">
|
||||
<el-tab-pane name="studentContact" label="学生联系人">
|
||||
<ElTabs v-model="subTabsName" class="mx-8">
|
||||
<ElTabPane name="studentContact" label="学生联系人">
|
||||
<StudentContactList :student-id="row?.id" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane name="studentTeacher" label="学生班主任">
|
||||
</ElTabPane>
|
||||
<ElTabPane name="studentTeacher" label="学生班主任">
|
||||
<StudentTeacherList :student-id="row?.id" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</ElTabPane>
|
||||
</ElTabs>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="id" title="编号" align="center" />
|
||||
<vxe-column field="name" title="名字" align="center" />
|
||||
<vxe-column field="description" title="简介" align="center" />
|
||||
<vxe-column field="birthday" title="出生日期" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="id" title="编号" align="center" />
|
||||
<VxeColumn field="name" title="名字" align="center" />
|
||||
<VxeColumn field="description" title="简介" align="center" />
|
||||
<VxeColumn field="birthday" title="出生日期" align="center">
|
||||
<template #default="{row}">
|
||||
{{formatDateTime(row.birthday)}}
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="sex" title="性别" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="sex" title="性别" align="center">
|
||||
<template #default="{row}">
|
||||
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="row.sex" />
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="enabled" title="是否有效" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="enabled" title="是否有效" align="center">
|
||||
<template #default="{row}">
|
||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="row.enabled" />
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="avatar" title="头像" align="center" />
|
||||
<vxe-column field="video" title="附件" align="center" />
|
||||
<vxe-column field="memo" title="备注" align="center" />
|
||||
<vxe-column field="createTime" title="创建时间" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="avatar" title="头像" align="center" />
|
||||
<VxeColumn field="video" title="附件" align="center" />
|
||||
<VxeColumn field="memo" title="备注" align="center" />
|
||||
<VxeColumn field="createTime" title="创建时间" align="center">
|
||||
<template #default="{row}">
|
||||
{{formatDateTime(row.createTime)}}
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="operation" title="操作" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="operation" title="操作" align="center">
|
||||
<template #default="{row}">
|
||||
<el-button
|
||||
<ElButton
|
||||
size="small"
|
||||
type="primary"
|
||||
link
|
||||
@@ -322,8 +328,8 @@ onMounted(() => {
|
||||
v-access:code="['infra:student:update']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.edit') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
</ElButton>
|
||||
<ElButton
|
||||
size="small"
|
||||
type="danger"
|
||||
link
|
||||
@@ -332,13 +338,13 @@ onMounted(() => {
|
||||
v-access:code="['infra:student:delete']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.delete') }}
|
||||
</el-button>
|
||||
</ElButton>
|
||||
</template>
|
||||
</vxe-column>
|
||||
</vxe-table>
|
||||
</VxeColumn>
|
||||
</VxeTable>
|
||||
<!-- 分页 -->
|
||||
<div class="mt-2 flex justify-end">
|
||||
<el-pagination
|
||||
<ElPagination
|
||||
:total="total"
|
||||
v-model:current-page="queryParams.pageNo"
|
||||
v-model:page-size="queryParams.pageSize"
|
||||
|
||||
+140
-59
@@ -1,19 +1,28 @@
|
||||
<script lang="ts" setup>
|
||||
import type { StudentApi } from '#/api/infra/student';
|
||||
import type { VxeTableInstance } from '#/adapter/vxe-table';
|
||||
import type { StudentApi } from '#/api/infra/demo';
|
||||
|
||||
import { computed, ref, reactive, h, onMounted,watch,nextTick } from 'vue';
|
||||
import { ref, watch } from 'vue';
|
||||
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
import { getDictOptions } from '@vben/hooks';
|
||||
import { Plus } from '@vben/icons';
|
||||
|
||||
import { ElMessage, ElTabs, ElTabPane, ElForm, ElFormItem, ElInput, ElButton, ElSelect, ElOption, ElRadioGroup, ElRadio, ElCheckboxGroup, ElCheckbox, ElDatePicker } from 'element-plus';
|
||||
import {
|
||||
ElButton,
|
||||
ElDatePicker,
|
||||
ElInput,
|
||||
ElMessage,
|
||||
ElOption,
|
||||
ElRadio,
|
||||
ElRadioGroup,
|
||||
ElSelect,
|
||||
} from 'element-plus';
|
||||
|
||||
import { VxeColumn, VxeTable } from '#/adapter/vxe-table';
|
||||
import { getStudentContactListByStudentId } from '#/api/infra/demo';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import type { VxeTableInstance } from '#/adapter/vxe-table';
|
||||
import { Plus } from "@vben/icons";
|
||||
import { VxeColumn, VxeTable } from '#/adapter/vxe-table';
|
||||
import { getStudentContactListByStudentId } from '#/api/infra/student';
|
||||
|
||||
const props = defineProps<{
|
||||
studentId?: number // 学生编号(主表的关联字段)
|
||||
}>()
|
||||
@@ -30,24 +39,96 @@ async function onDelete(row: StudentApi.StudentContact) {
|
||||
await tableRef.value?.remove(row);
|
||||
}
|
||||
|
||||
/** 提供获取表格数据的方法供父组件调用 */
|
||||
defineExpose({
|
||||
getData: (): StudentApi.StudentContact[] => {
|
||||
const data = list.value as StudentApi.StudentContact[];
|
||||
const removeRecords = tableRef.value?.getRemoveRecords() as StudentApi.StudentContact[];
|
||||
const insertRecords = tableRef.value?.getInsertRecords() as StudentApi.StudentContact[];
|
||||
return [
|
||||
...data.filter(
|
||||
(row) => !removeRecords.some((removed) => removed.id === row.id),
|
||||
),
|
||||
...insertRecords.map((row: any) => ({ ...row, id: undefined })),
|
||||
},
|
||||
});
|
||||
/** 获取当前子表数据,新增行不提交表格生成的临时编号 */
|
||||
function getData(): StudentApi.StudentContact[] {
|
||||
const table = tableRef.value;
|
||||
if (!table) {
|
||||
return [];
|
||||
}
|
||||
const { fullData } = table.getTableData();
|
||||
const inserted = new Set(table.getInsertRecords());
|
||||
return fullData.map((row) =>
|
||||
inserted.has(row) ? { ...row, id: undefined } : { ...row },
|
||||
);
|
||||
}
|
||||
|
||||
/** 校验子表必填字段 */
|
||||
function validate(): boolean {
|
||||
for (const [index, row] of getData().entries()) {
|
||||
if (
|
||||
row.name === null ||
|
||||
row.name === undefined ||
|
||||
(typeof row.name === 'string' && String(row.name).trim() === '') ||
|
||||
(Array.isArray(row.name) && row.name.length === 0)
|
||||
) {
|
||||
ElMessage.error(`第 ${index + 1} 行:名字不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.description === null ||
|
||||
row.description === undefined ||
|
||||
(typeof row.description === 'string' && String(row.description).trim() === '') ||
|
||||
(Array.isArray(row.description) && row.description.length === 0)
|
||||
) {
|
||||
ElMessage.error(`第 ${index + 1} 行:简介不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.birthday === null ||
|
||||
row.birthday === undefined ||
|
||||
(typeof row.birthday === 'string' && String(row.birthday).trim() === '') ||
|
||||
(Array.isArray(row.birthday) && row.birthday.length === 0)
|
||||
) {
|
||||
ElMessage.error(`第 ${index + 1} 行:出生日期不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.sex === null ||
|
||||
row.sex === undefined ||
|
||||
(typeof row.sex === 'string' && String(row.sex).trim() === '') ||
|
||||
(Array.isArray(row.sex) && row.sex.length === 0)
|
||||
) {
|
||||
ElMessage.error(`第 ${index + 1} 行:性别不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.enabled === null ||
|
||||
row.enabled === undefined ||
|
||||
(typeof row.enabled === 'string' && String(row.enabled).trim() === '') ||
|
||||
(Array.isArray(row.enabled) && row.enabled.length === 0)
|
||||
) {
|
||||
ElMessage.error(`第 ${index + 1} 行:是否有效不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.avatar === null ||
|
||||
row.avatar === undefined ||
|
||||
(typeof row.avatar === 'string' && String(row.avatar).trim() === '') ||
|
||||
(Array.isArray(row.avatar) && row.avatar.length === 0)
|
||||
) {
|
||||
ElMessage.error(`第 ${index + 1} 行:头像不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.memo === null ||
|
||||
row.memo === undefined ||
|
||||
(typeof row.memo === 'string' && String(row.memo).trim() === '') ||
|
||||
(Array.isArray(row.memo) && row.memo.length === 0)
|
||||
) {
|
||||
ElMessage.error(`第 ${index + 1} 行:备注不能为空`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
defineExpose({ getData, validate });
|
||||
|
||||
/** 监听主表的关联字段的变化,加载对应的子表数据 */
|
||||
watch(
|
||||
() => props.studentId,
|
||||
async (val) => {
|
||||
list.value = [];
|
||||
if (!val) {
|
||||
return;
|
||||
}
|
||||
@@ -58,70 +139,70 @@ watch(
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<vxe-table ref="tableRef" :data="list" show-overflow class="mx-4">
|
||||
<vxe-column field="name" title="名字" align="center">
|
||||
<VxeTable ref="tableRef" :data="list" show-overflow class="mx-4">
|
||||
<VxeColumn field="name" title="名字" align="center">
|
||||
<template #default="{row}">
|
||||
<el-input v-model="row.name" />
|
||||
<ElInput v-model="row.name" />
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="description" title="简介" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="description" title="简介" align="center">
|
||||
<template #default="{row}">
|
||||
<el-input v-model="row.description" type="textarea" />
|
||||
<ElInput v-model="row.description" type="textarea" />
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="birthday" title="出生日期" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="birthday" title="出生日期" align="center">
|
||||
<template #default="{row}">
|
||||
<el-date-picker
|
||||
<ElDatePicker
|
||||
v-model="row.birthday"
|
||||
type="datetime"
|
||||
value-format="x"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
/>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="sex" title="性别" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="sex" title="性别" align="center">
|
||||
<template #default="{row}">
|
||||
<el-select v-model="row.sex" placeholder="请选择性别">
|
||||
<el-option
|
||||
v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), 'number')"
|
||||
<ElSelect v-model="row.sex" placeholder="请选择性别">
|
||||
<ElOption
|
||||
v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
:label="dict.label"
|
||||
/>
|
||||
</el-select>
|
||||
</ElSelect>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="enabled" title="是否有效" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="enabled" title="是否有效" align="center">
|
||||
<template #default="{row}">
|
||||
<el-radio-group v-model="row.enabled">
|
||||
<el-radio
|
||||
v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), 'boolean')"
|
||||
<ElRadioGroup v-model="row.enabled">
|
||||
<ElRadio
|
||||
v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</ElRadio>
|
||||
</ElRadioGroup>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="avatar" title="头像" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="avatar" title="头像" align="center">
|
||||
<template #default="{row}">
|
||||
<ImageUpload v-model="row.avatar" />
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="video" title="附件" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="video" title="附件" align="center">
|
||||
<template #default="{row}">
|
||||
<FileUpload v-model="row.video" />
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="memo" title="备注" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="memo" title="备注" align="center">
|
||||
<template #default="{row}">
|
||||
<el-input v-model="row.memo" type="textarea" />
|
||||
<ElInput v-model="row.memo" type="textarea" />
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="operation" title="操作" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="operation" title="操作" align="center">
|
||||
<template #default="{row}">
|
||||
<el-button
|
||||
<ElButton
|
||||
size="small"
|
||||
type="danger"
|
||||
link
|
||||
@@ -129,13 +210,13 @@ watch(
|
||||
v-access:code="['infra:student:delete']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.delete') }}
|
||||
</el-button>
|
||||
</ElButton>
|
||||
</template>
|
||||
</vxe-column>
|
||||
</vxe-table>
|
||||
</VxeColumn>
|
||||
</VxeTable>
|
||||
<div class="flex justify-center mt-4">
|
||||
<el-button :icon="h(Plus)" type="primary" plain @click="handleAdd" v-access:code="['infra:student:create']">
|
||||
<ElButton :icon="Plus" type="primary" plain @click="handleAdd" v-access:code="['infra:student:create']">
|
||||
{{ $t('ui.actionTitle.create', ['学生联系人']) }}
|
||||
</el-button>
|
||||
</ElButton>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
+20
-19
@@ -1,11 +1,12 @@
|
||||
<script lang="ts" setup>
|
||||
import type { StudentApi } from '#/api/infra/student';
|
||||
import type { StudentApi } from '#/api/infra/demo';
|
||||
import type { VxeTableInstance } from '#/adapter/vxe-table';
|
||||
|
||||
import { reactive,ref, h, nextTick,watch,onMounted } from 'vue';
|
||||
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
import { getDictOptions } from '@vben/hooks';
|
||||
import { formatDateTime } from '@vben/utils';
|
||||
|
||||
import { DictTag } from '#/components/dict-tag';
|
||||
import { getRangePickerDefaultProps } from '#/utils';
|
||||
@@ -13,7 +14,7 @@
|
||||
import { ContentWrap } from '#/components/content-wrap';
|
||||
|
||||
|
||||
import { getStudentContactListByStudentId } from '#/api/infra/student';
|
||||
import { getStudentContactListByStudentId } from '#/api/infra/demo';
|
||||
|
||||
const props = defineProps<{
|
||||
studentId?: number // 学生编号(主表的关联字段)
|
||||
@@ -52,38 +53,38 @@ watch(
|
||||
|
||||
<template>
|
||||
<ContentWrap title="学生联系人列表">
|
||||
<vxe-table
|
||||
<VxeTable
|
||||
:data="list"
|
||||
show-overflow
|
||||
:loading="loading"
|
||||
>
|
||||
<vxe-column field="id" title="编号" align="center" />
|
||||
<vxe-column field="studentId" title="学生编号" align="center" />
|
||||
<vxe-column field="name" title="名字" align="center" />
|
||||
<vxe-column field="description" title="简介" align="center" />
|
||||
<vxe-column field="birthday" title="出生日期" align="center">
|
||||
<VxeColumn field="id" title="编号" align="center" />
|
||||
<VxeColumn field="studentId" title="学生编号" align="center" />
|
||||
<VxeColumn field="name" title="名字" align="center" />
|
||||
<VxeColumn field="description" title="简介" align="center" />
|
||||
<VxeColumn field="birthday" title="出生日期" align="center">
|
||||
<template #default="{row}">
|
||||
{{formatDateTime(row.birthday)}}
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="sex" title="性别" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="sex" title="性别" align="center">
|
||||
<template #default="{row}">
|
||||
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="row.sex" />
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="enabled" title="是否有效" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="enabled" title="是否有效" align="center">
|
||||
<template #default="{row}">
|
||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="row.enabled" />
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="avatar" title="头像" align="center" />
|
||||
<vxe-column field="video" title="附件" align="center" />
|
||||
<vxe-column field="memo" title="备注" align="center" />
|
||||
<vxe-column field="createTime" title="创建时间" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="avatar" title="头像" align="center" />
|
||||
<VxeColumn field="video" title="附件" align="center" />
|
||||
<VxeColumn field="memo" title="备注" align="center" />
|
||||
<VxeColumn field="createTime" title="创建时间" align="center">
|
||||
<template #default="{row}">
|
||||
{{formatDateTime(row.createTime)}}
|
||||
</template>
|
||||
</vxe-column>
|
||||
</vxe-table>
|
||||
</VxeColumn>
|
||||
</VxeTable>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
||||
+56
-35
@@ -1,17 +1,26 @@
|
||||
<script lang="ts" setup>
|
||||
import type { StudentApi } from '#/api/infra/student';
|
||||
import type { FormRules } from 'element-plus';
|
||||
import type { StudentApi } from '#/api/infra/demo';
|
||||
|
||||
import { computed, ref, reactive, h, onMounted,watch,nextTick } from 'vue';
|
||||
import { nextTick, reactive, ref, watch } from 'vue';
|
||||
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
import { getDictOptions } from '@vben/hooks';
|
||||
|
||||
import { ElMessage, ElTabs, ElTabPane, ElForm, ElFormItem, ElInput, ElButton, ElSelect, ElOption, ElRadioGroup, ElRadio, ElCheckboxGroup, ElCheckbox, ElDatePicker } from 'element-plus';
|
||||
import { $t } from '#/locales';
|
||||
import {
|
||||
ElForm,
|
||||
ElFormItem,
|
||||
ElDatePicker,
|
||||
ElInput,
|
||||
ElOption,
|
||||
ElRadio,
|
||||
ElRadioGroup,
|
||||
ElSelect,
|
||||
} from 'element-plus';
|
||||
|
||||
import type { FormRules } from 'element-plus';
|
||||
import { Tinymce as RichTextarea } from '#/components/tinymce';
|
||||
import { getStudentTeacherByStudentId } from '#/api/infra/student';
|
||||
import { getStudentTeacherByStudentId } from '#/api/infra/demo';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
const props = defineProps<{
|
||||
studentId?: number // 学生编号(主表的关联字段)
|
||||
@@ -50,6 +59,18 @@ defineExpose({
|
||||
watch(
|
||||
() => props.studentId,
|
||||
async (val) => {
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
studentId: undefined,
|
||||
name: undefined,
|
||||
description: undefined,
|
||||
birthday: undefined,
|
||||
sex: undefined,
|
||||
enabled: undefined,
|
||||
avatar: undefined,
|
||||
video: undefined,
|
||||
memo: undefined,
|
||||
};
|
||||
if (!val) {
|
||||
return;
|
||||
}
|
||||
@@ -61,7 +82,7 @@ watch(
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-form
|
||||
<ElForm
|
||||
ref="formRef"
|
||||
class="mx-4"
|
||||
:model="formData"
|
||||
@@ -69,48 +90,48 @@ watch(
|
||||
label-width="120px"
|
||||
label-position="right"
|
||||
>
|
||||
<el-form-item label="名字" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入名字" />
|
||||
</el-form-item>
|
||||
<el-form-item label="简介" prop="description">
|
||||
<el-input v-model="formData.description" type="textarea" placeholder="请输入简介" />
|
||||
</el-form-item>
|
||||
<el-form-item label="出生日期" prop="birthday">
|
||||
<el-date-picker
|
||||
<ElFormItem label="名字" prop="name">
|
||||
<ElInput v-model="formData.name" placeholder="请输入名字" />
|
||||
</ElFormItem>
|
||||
<ElFormItem label="简介" prop="description">
|
||||
<ElInput v-model="formData.description" type="textarea" placeholder="请输入简介" />
|
||||
</ElFormItem>
|
||||
<ElFormItem label="出生日期" prop="birthday">
|
||||
<ElDatePicker
|
||||
v-model="formData.birthday"
|
||||
value-format="x"
|
||||
placeholder="选择出生日期"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="性别" prop="sex">
|
||||
<el-select v-model="formData.sex" placeholder="请选择性别">
|
||||
<el-option
|
||||
</ElFormItem>
|
||||
<ElFormItem label="性别" prop="sex">
|
||||
<ElSelect v-model="formData.sex" placeholder="请选择性别">
|
||||
<ElOption
|
||||
v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
:label="dict.label"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否有效" prop="enabled">
|
||||
<el-radio-group v-model="formData.enabled">
|
||||
<el-radio
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="是否有效" prop="enabled">
|
||||
<ElRadioGroup v-model="formData.enabled">
|
||||
<ElRadio
|
||||
v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="头像" prop="avatar">
|
||||
</ElRadio>
|
||||
</ElRadioGroup>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="头像" prop="avatar">
|
||||
<ImageUpload v-model="formData.avatar" />
|
||||
</el-form-item>
|
||||
<el-form-item label="附件" prop="video">
|
||||
</ElFormItem>
|
||||
<ElFormItem label="附件" prop="video">
|
||||
<FileUpload v-model="formData.video" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="memo">
|
||||
</ElFormItem>
|
||||
<ElFormItem label="备注" prop="memo">
|
||||
<RichTextarea v-model="formData.memo" height="500px" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
</template>
|
||||
|
||||
+20
-19
@@ -1,11 +1,12 @@
|
||||
<script lang="ts" setup>
|
||||
import type { StudentApi } from '#/api/infra/student';
|
||||
import type { StudentApi } from '#/api/infra/demo';
|
||||
import type { VxeTableInstance } from '#/adapter/vxe-table';
|
||||
|
||||
import { reactive,ref, h, nextTick,watch,onMounted } from 'vue';
|
||||
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
import { getDictOptions } from '@vben/hooks';
|
||||
import { formatDateTime } from '@vben/utils';
|
||||
|
||||
import { DictTag } from '#/components/dict-tag';
|
||||
import { getRangePickerDefaultProps } from '#/utils';
|
||||
@@ -13,7 +14,7 @@
|
||||
import { ContentWrap } from '#/components/content-wrap';
|
||||
|
||||
|
||||
import { getStudentTeacherByStudentId } from '#/api/infra/student';
|
||||
import { getStudentTeacherByStudentId } from '#/api/infra/demo';
|
||||
|
||||
const props = defineProps<{
|
||||
studentId?: number // 学生编号(主表的关联字段)
|
||||
@@ -52,38 +53,38 @@ watch(
|
||||
|
||||
<template>
|
||||
<ContentWrap title="学生班主任列表">
|
||||
<vxe-table
|
||||
<VxeTable
|
||||
:data="list"
|
||||
show-overflow
|
||||
:loading="loading"
|
||||
>
|
||||
<vxe-column field="id" title="编号" align="center" />
|
||||
<vxe-column field="studentId" title="学生编号" align="center" />
|
||||
<vxe-column field="name" title="名字" align="center" />
|
||||
<vxe-column field="description" title="简介" align="center" />
|
||||
<vxe-column field="birthday" title="出生日期" align="center">
|
||||
<VxeColumn field="id" title="编号" align="center" />
|
||||
<VxeColumn field="studentId" title="学生编号" align="center" />
|
||||
<VxeColumn field="name" title="名字" align="center" />
|
||||
<VxeColumn field="description" title="简介" align="center" />
|
||||
<VxeColumn field="birthday" title="出生日期" align="center">
|
||||
<template #default="{row}">
|
||||
{{formatDateTime(row.birthday)}}
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="sex" title="性别" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="sex" title="性别" align="center">
|
||||
<template #default="{row}">
|
||||
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="row.sex" />
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="enabled" title="是否有效" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="enabled" title="是否有效" align="center">
|
||||
<template #default="{row}">
|
||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="row.enabled" />
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="avatar" title="头像" align="center" />
|
||||
<vxe-column field="video" title="附件" align="center" />
|
||||
<vxe-column field="memo" title="备注" align="center" />
|
||||
<vxe-column field="createTime" title="创建时间" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="avatar" title="头像" align="center" />
|
||||
<VxeColumn field="video" title="附件" align="center" />
|
||||
<VxeColumn field="memo" title="备注" align="center" />
|
||||
<VxeColumn field="createTime" title="创建时间" align="center">
|
||||
<template #default="{row}">
|
||||
{{formatDateTime(row.createTime)}}
|
||||
</template>
|
||||
</vxe-column>
|
||||
</vxe-table>
|
||||
</VxeColumn>
|
||||
</VxeTable>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
||||
+42
-38
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import type { StudentApi } from '#/api/infra/student';
|
||||
import type { StudentApi } from '#/api/infra/demo';
|
||||
import type { FormRules } from 'element-plus';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
@@ -13,7 +13,7 @@ import { ElMessage, ElTabs, ElTabPane, ElForm, ElFormItem, ElInput, ElSelect, El
|
||||
|
||||
import { computed, ref, reactive } from 'vue';
|
||||
import { $t } from '#/locales';
|
||||
import { getStudent, createStudent, updateStudent } from '#/api/infra/student';
|
||||
import { getStudent, createStudent, updateStudent } from '#/api/infra/demo';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
|
||||
@@ -71,7 +71,11 @@ const [Modal, modalApi] = useVbenModal({
|
||||
async onConfirm() {
|
||||
await formRef.value?.validate();
|
||||
// 校验子表单
|
||||
try {
|
||||
if (!studentContactFormRef.value?.validate()) {
|
||||
subTabsName.value = 'studentContact';
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await studentTeacherFormRef.value?.validate()
|
||||
} catch (e) {
|
||||
subTabsName.value = 'studentTeacher'
|
||||
@@ -118,65 +122,65 @@ const [Modal, modalApi] = useVbenModal({
|
||||
|
||||
<template>
|
||||
<Modal :title="getTitle">
|
||||
<el-form
|
||||
<ElForm
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="rules"
|
||||
label-width="120px"
|
||||
label-position="right"
|
||||
>
|
||||
<el-form-item label="名字" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入名字" />
|
||||
</el-form-item>
|
||||
<el-form-item label="简介" prop="description">
|
||||
<el-input v-model="formData.description" type="textarea" placeholder="请输入简介" />
|
||||
</el-form-item>
|
||||
<el-form-item label="出生日期" prop="birthday">
|
||||
<el-date-picker
|
||||
<ElFormItem label="名字" prop="name">
|
||||
<ElInput v-model="formData.name" placeholder="请输入名字" />
|
||||
</ElFormItem>
|
||||
<ElFormItem label="简介" prop="description">
|
||||
<ElInput v-model="formData.description" type="textarea" placeholder="请输入简介" />
|
||||
</ElFormItem>
|
||||
<ElFormItem label="出生日期" prop="birthday">
|
||||
<ElDatePicker
|
||||
v-model="formData.birthday"
|
||||
value-format="x"
|
||||
placeholder="选择出生日期"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="性别" prop="sex">
|
||||
<el-select v-model="formData.sex" placeholder="请选择性别">
|
||||
<el-option
|
||||
</ElFormItem>
|
||||
<ElFormItem label="性别" prop="sex">
|
||||
<ElSelect v-model="formData.sex" placeholder="请选择性别">
|
||||
<ElOption
|
||||
v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
:label="dict.label"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否有效" prop="enabled">
|
||||
<el-radio-group v-model="formData.enabled">
|
||||
<el-radio
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="是否有效" prop="enabled">
|
||||
<ElRadioGroup v-model="formData.enabled">
|
||||
<ElRadio
|
||||
v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="头像" prop="avatar">
|
||||
</ElRadio>
|
||||
</ElRadioGroup>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="头像" prop="avatar">
|
||||
<ImageUpload v-model="formData.avatar" />
|
||||
</el-form-item>
|
||||
<el-form-item label="附件" prop="video">
|
||||
</ElFormItem>
|
||||
<ElFormItem label="附件" prop="video">
|
||||
<FileUpload v-model="formData.video" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="memo">
|
||||
</ElFormItem>
|
||||
<ElFormItem label="备注" prop="memo">
|
||||
<RichTextarea v-model="formData.memo" height="500px" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
<!-- 子表的表单 -->
|
||||
<el-tabs v-model="subTabsName">
|
||||
<el-tab-pane name="studentContact" label="学生联系人">
|
||||
<ElTabs v-model="subTabsName">
|
||||
<ElTabPane name="studentContact" label="学生联系人">
|
||||
<StudentContactForm ref="studentContactFormRef" :student-id="formData?.id" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane name="studentTeacher" label="学生班主任">
|
||||
</ElTabPane>
|
||||
<ElTabPane name="studentTeacher" label="学生班主任">
|
||||
<StudentTeacherForm ref="studentTeacherFormRef" :student-id="formData?.id" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</ElTabPane>
|
||||
</ElTabs>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
+73
-67
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import type { StudentApi } from '#/api/infra/student';
|
||||
import type { StudentApi } from '#/api/infra/demo';
|
||||
|
||||
import { ref, h, reactive, onMounted, nextTick } from 'vue';
|
||||
|
||||
@@ -17,7 +17,7 @@ import { VxeColumn, VxeTable } from '#/adapter/vxe-table';
|
||||
|
||||
import { $t } from '#/locales';
|
||||
import { isEmpty } from '@vben/utils';
|
||||
import { getStudentPage, deleteStudent, deleteStudentList, exportStudent } from '#/api/infra/student';
|
||||
import { getStudentPage, deleteStudent, deleteStudentList, exportStudent } from '#/api/infra/demo';
|
||||
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
@@ -27,12 +27,11 @@ const total = ref(0) // 列表的总页数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
name: undefined,
|
||||
birthday: undefined,
|
||||
birthday: undefined,
|
||||
sex: undefined,
|
||||
enabled: undefined,
|
||||
createTime: undefined,
|
||||
name: undefined,
|
||||
birthday: undefined,
|
||||
sex: undefined,
|
||||
enabled: undefined,
|
||||
createTime: undefined,
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
@@ -126,7 +125,14 @@ function handleRowCheckboxChange({
|
||||
async function handleExport() {
|
||||
try {
|
||||
exportLoading.value = true;
|
||||
const data = await exportStudent(queryParams);
|
||||
const params = cloneDeep(queryParams) as any;
|
||||
if (params.birthday && Array.isArray(params.birthday)) {
|
||||
params.birthday = (params.birthday as string[]).join(',');
|
||||
}
|
||||
if (params.createTime && Array.isArray(params.createTime)) {
|
||||
params.createTime = (params.createTime as string[]).join(',');
|
||||
}
|
||||
const data = await exportStudent(params);
|
||||
downloadFileFromBlobPart({ fileName: '学生.xls', source: data });
|
||||
}finally {
|
||||
exportLoading.value = false;
|
||||
@@ -148,61 +154,61 @@ onMounted(() => {
|
||||
|
||||
<ContentWrap v-if="!hiddenSearchBar">
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form
|
||||
<ElForm
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
inline
|
||||
>
|
||||
<el-form-item label="名字">
|
||||
<el-input
|
||||
<ElFormItem label="名字">
|
||||
<ElInput
|
||||
v-model="queryParams.name"
|
||||
placeholder="请输入名字"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-[240px]"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="出生日期">
|
||||
<el-date-picker
|
||||
</ElFormItem>
|
||||
<ElFormItem label="出生日期">
|
||||
<ElDatePicker
|
||||
v-model="queryParams.birthday"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="选择出生日期"
|
||||
clearable
|
||||
class="!w-[240px]"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="性别">
|
||||
<el-select
|
||||
</ElFormItem>
|
||||
<ElFormItem label="性别">
|
||||
<ElSelect
|
||||
v-model="queryParams.sex"
|
||||
placeholder="请选择性别"
|
||||
clearable
|
||||
class="!w-[240px]"
|
||||
>
|
||||
<el-option
|
||||
<ElOption
|
||||
v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
:label="dict.label"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否有效">
|
||||
<el-select
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="是否有效">
|
||||
<ElSelect
|
||||
v-model="queryParams.enabled"
|
||||
placeholder="请选择是否有效"
|
||||
clearable
|
||||
class="!w-[240px]"
|
||||
>
|
||||
<el-option
|
||||
<ElOption
|
||||
v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
:label="dict.label"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间">
|
||||
<el-date-picker
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="创建时间">
|
||||
<ElDatePicker
|
||||
v-model="queryParams.createTime"
|
||||
type="daterange"
|
||||
value-format="YYYY-MM-DD"
|
||||
@@ -211,24 +217,24 @@ onMounted(() => {
|
||||
end-placeholder="结束日期"
|
||||
class="!w-[240px]"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button class="ml-2" @click="resetQuery"> 重置 </el-button>
|
||||
<el-button class="ml-2" @click="handleQuery" type="primary">
|
||||
</ElFormItem>
|
||||
<ElFormItem>
|
||||
<ElButton class="ml-2" @click="resetQuery"> 重置 </ElButton>
|
||||
<ElButton class="ml-2" @click="handleQuery" type="primary">
|
||||
搜索
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ElButton>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap title="学生">
|
||||
<template #extra>
|
||||
<TableToolbar
|
||||
<VbenVxeTableToolbar
|
||||
ref="tableToolbarRef"
|
||||
v-model:hidden-search="hiddenSearchBar"
|
||||
>
|
||||
<el-button
|
||||
<ElButton
|
||||
class="ml-2"
|
||||
:icon="h(Plus)"
|
||||
type="primary"
|
||||
@@ -236,8 +242,8 @@ onMounted(() => {
|
||||
v-access:code="['infra:student:create']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.create', ['学生']) }}
|
||||
</el-button>
|
||||
<el-button
|
||||
</ElButton>
|
||||
<ElButton
|
||||
:icon="h(Download)"
|
||||
type="primary"
|
||||
class="ml-2"
|
||||
@@ -246,8 +252,8 @@ onMounted(() => {
|
||||
v-access:code="['infra:student:export']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.export') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
</ElButton>
|
||||
<ElButton
|
||||
:icon="h(Trash2)"
|
||||
type="danger"
|
||||
class="ml-2"
|
||||
@@ -256,10 +262,10 @@ onMounted(() => {
|
||||
v-access:code="['infra:student:delete']"
|
||||
>
|
||||
批量删除
|
||||
</el-button>
|
||||
</TableToolbar>
|
||||
</ElButton>
|
||||
</VbenVxeTableToolbar>
|
||||
</template>
|
||||
<vxe-table
|
||||
<VxeTable
|
||||
ref="tableRef"
|
||||
:data="list"
|
||||
show-overflow
|
||||
@@ -267,36 +273,36 @@ onMounted(() => {
|
||||
@checkboxAll="handleRowCheckboxChange"
|
||||
@checkboxChange="handleRowCheckboxChange"
|
||||
>
|
||||
<vxe-column type="checkbox" width="40"></vxe-column>
|
||||
<vxe-column field="id" title="编号" align="center" />
|
||||
<vxe-column field="name" title="名字" align="center" />
|
||||
<vxe-column field="description" title="简介" align="center" />
|
||||
<vxe-column field="birthday" title="出生日期" align="center">
|
||||
<VxeColumn type="checkbox" width="40"></VxeColumn>
|
||||
<VxeColumn field="id" title="编号" align="center" />
|
||||
<VxeColumn field="name" title="名字" align="center" />
|
||||
<VxeColumn field="description" title="简介" align="center" />
|
||||
<VxeColumn field="birthday" title="出生日期" align="center">
|
||||
<template #default="{row}">
|
||||
{{formatDateTime(row.birthday)}}
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="sex" title="性别" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="sex" title="性别" align="center">
|
||||
<template #default="{row}">
|
||||
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="row.sex" />
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="enabled" title="是否有效" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="enabled" title="是否有效" align="center">
|
||||
<template #default="{row}">
|
||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="row.enabled" />
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="avatar" title="头像" align="center" />
|
||||
<vxe-column field="video" title="附件" align="center" />
|
||||
<vxe-column field="memo" title="备注" align="center" />
|
||||
<vxe-column field="createTime" title="创建时间" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="avatar" title="头像" align="center" />
|
||||
<VxeColumn field="video" title="附件" align="center" />
|
||||
<VxeColumn field="memo" title="备注" align="center" />
|
||||
<VxeColumn field="createTime" title="创建时间" align="center">
|
||||
<template #default="{row}">
|
||||
{{formatDateTime(row.createTime)}}
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="operation" title="操作" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="operation" title="操作" align="center">
|
||||
<template #default="{row}">
|
||||
<el-button
|
||||
<ElButton
|
||||
size="small"
|
||||
type="primary"
|
||||
link
|
||||
@@ -304,8 +310,8 @@ onMounted(() => {
|
||||
v-access:code="['infra:student:update']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.edit') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
</ElButton>
|
||||
<ElButton
|
||||
size="small"
|
||||
type="danger"
|
||||
link
|
||||
@@ -314,13 +320,13 @@ onMounted(() => {
|
||||
v-access:code="['infra:student:delete']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.delete') }}
|
||||
</el-button>
|
||||
</ElButton>
|
||||
</template>
|
||||
</vxe-column>
|
||||
</vxe-table>
|
||||
</VxeColumn>
|
||||
</VxeTable>
|
||||
<!-- 分页 -->
|
||||
<div class="mt-2 flex justify-end">
|
||||
<el-pagination
|
||||
<ElPagination
|
||||
:total="total"
|
||||
v-model:current-page="queryParams.pageNo"
|
||||
v-model:page-size="queryParams.pageSize"
|
||||
|
||||
+140
-59
@@ -1,19 +1,28 @@
|
||||
<script lang="ts" setup>
|
||||
import type { StudentApi } from '#/api/infra/student';
|
||||
import type { VxeTableInstance } from '#/adapter/vxe-table';
|
||||
import type { StudentApi } from '#/api/infra/demo';
|
||||
|
||||
import { computed, ref, reactive, h, onMounted,watch,nextTick } from 'vue';
|
||||
import { ref, watch } from 'vue';
|
||||
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
import { getDictOptions } from '@vben/hooks';
|
||||
import { Plus } from '@vben/icons';
|
||||
|
||||
import { ElMessage, ElTabs, ElTabPane, ElForm, ElFormItem, ElInput, ElButton, ElSelect, ElOption, ElRadioGroup, ElRadio, ElCheckboxGroup, ElCheckbox, ElDatePicker } from 'element-plus';
|
||||
import {
|
||||
ElButton,
|
||||
ElDatePicker,
|
||||
ElInput,
|
||||
ElMessage,
|
||||
ElOption,
|
||||
ElRadio,
|
||||
ElRadioGroup,
|
||||
ElSelect,
|
||||
} from 'element-plus';
|
||||
|
||||
import { VxeColumn, VxeTable } from '#/adapter/vxe-table';
|
||||
import { getStudentContactListByStudentId } from '#/api/infra/demo';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import type { VxeTableInstance } from '#/adapter/vxe-table';
|
||||
import { Plus } from "@vben/icons";
|
||||
import { VxeColumn, VxeTable } from '#/adapter/vxe-table';
|
||||
import { getStudentContactListByStudentId } from '#/api/infra/student';
|
||||
|
||||
const props = defineProps<{
|
||||
studentId?: number // 学生编号(主表的关联字段)
|
||||
}>()
|
||||
@@ -30,24 +39,96 @@ async function onDelete(row: StudentApi.StudentContact) {
|
||||
await tableRef.value?.remove(row);
|
||||
}
|
||||
|
||||
/** 提供获取表格数据的方法供父组件调用 */
|
||||
defineExpose({
|
||||
getData: (): StudentApi.StudentContact[] => {
|
||||
const data = list.value as StudentApi.StudentContact[];
|
||||
const removeRecords = tableRef.value?.getRemoveRecords() as StudentApi.StudentContact[];
|
||||
const insertRecords = tableRef.value?.getInsertRecords() as StudentApi.StudentContact[];
|
||||
return [
|
||||
...data.filter(
|
||||
(row) => !removeRecords.some((removed) => removed.id === row.id),
|
||||
),
|
||||
...insertRecords.map((row: any) => ({ ...row, id: undefined })),
|
||||
},
|
||||
});
|
||||
/** 获取当前子表数据,新增行不提交表格生成的临时编号 */
|
||||
function getData(): StudentApi.StudentContact[] {
|
||||
const table = tableRef.value;
|
||||
if (!table) {
|
||||
return [];
|
||||
}
|
||||
const { fullData } = table.getTableData();
|
||||
const inserted = new Set(table.getInsertRecords());
|
||||
return fullData.map((row) =>
|
||||
inserted.has(row) ? { ...row, id: undefined } : { ...row },
|
||||
);
|
||||
}
|
||||
|
||||
/** 校验子表必填字段 */
|
||||
function validate(): boolean {
|
||||
for (const [index, row] of getData().entries()) {
|
||||
if (
|
||||
row.name === null ||
|
||||
row.name === undefined ||
|
||||
(typeof row.name === 'string' && String(row.name).trim() === '') ||
|
||||
(Array.isArray(row.name) && row.name.length === 0)
|
||||
) {
|
||||
ElMessage.error(`第 ${index + 1} 行:名字不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.description === null ||
|
||||
row.description === undefined ||
|
||||
(typeof row.description === 'string' && String(row.description).trim() === '') ||
|
||||
(Array.isArray(row.description) && row.description.length === 0)
|
||||
) {
|
||||
ElMessage.error(`第 ${index + 1} 行:简介不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.birthday === null ||
|
||||
row.birthday === undefined ||
|
||||
(typeof row.birthday === 'string' && String(row.birthday).trim() === '') ||
|
||||
(Array.isArray(row.birthday) && row.birthday.length === 0)
|
||||
) {
|
||||
ElMessage.error(`第 ${index + 1} 行:出生日期不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.sex === null ||
|
||||
row.sex === undefined ||
|
||||
(typeof row.sex === 'string' && String(row.sex).trim() === '') ||
|
||||
(Array.isArray(row.sex) && row.sex.length === 0)
|
||||
) {
|
||||
ElMessage.error(`第 ${index + 1} 行:性别不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.enabled === null ||
|
||||
row.enabled === undefined ||
|
||||
(typeof row.enabled === 'string' && String(row.enabled).trim() === '') ||
|
||||
(Array.isArray(row.enabled) && row.enabled.length === 0)
|
||||
) {
|
||||
ElMessage.error(`第 ${index + 1} 行:是否有效不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.avatar === null ||
|
||||
row.avatar === undefined ||
|
||||
(typeof row.avatar === 'string' && String(row.avatar).trim() === '') ||
|
||||
(Array.isArray(row.avatar) && row.avatar.length === 0)
|
||||
) {
|
||||
ElMessage.error(`第 ${index + 1} 行:头像不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.memo === null ||
|
||||
row.memo === undefined ||
|
||||
(typeof row.memo === 'string' && String(row.memo).trim() === '') ||
|
||||
(Array.isArray(row.memo) && row.memo.length === 0)
|
||||
) {
|
||||
ElMessage.error(`第 ${index + 1} 行:备注不能为空`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
defineExpose({ getData, validate });
|
||||
|
||||
/** 监听主表的关联字段的变化,加载对应的子表数据 */
|
||||
watch(
|
||||
() => props.studentId,
|
||||
async (val) => {
|
||||
list.value = [];
|
||||
if (!val) {
|
||||
return;
|
||||
}
|
||||
@@ -58,70 +139,70 @@ watch(
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<vxe-table ref="tableRef" :data="list" show-overflow class="mx-4">
|
||||
<vxe-column field="name" title="名字" align="center">
|
||||
<VxeTable ref="tableRef" :data="list" show-overflow class="mx-4">
|
||||
<VxeColumn field="name" title="名字" align="center">
|
||||
<template #default="{row}">
|
||||
<el-input v-model="row.name" />
|
||||
<ElInput v-model="row.name" />
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="description" title="简介" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="description" title="简介" align="center">
|
||||
<template #default="{row}">
|
||||
<el-input v-model="row.description" type="textarea" />
|
||||
<ElInput v-model="row.description" type="textarea" />
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="birthday" title="出生日期" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="birthday" title="出生日期" align="center">
|
||||
<template #default="{row}">
|
||||
<el-date-picker
|
||||
<ElDatePicker
|
||||
v-model="row.birthday"
|
||||
type="datetime"
|
||||
value-format="x"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
/>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="sex" title="性别" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="sex" title="性别" align="center">
|
||||
<template #default="{row}">
|
||||
<el-select v-model="row.sex" placeholder="请选择性别">
|
||||
<el-option
|
||||
v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), 'number')"
|
||||
<ElSelect v-model="row.sex" placeholder="请选择性别">
|
||||
<ElOption
|
||||
v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
:label="dict.label"
|
||||
/>
|
||||
</el-select>
|
||||
</ElSelect>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="enabled" title="是否有效" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="enabled" title="是否有效" align="center">
|
||||
<template #default="{row}">
|
||||
<el-radio-group v-model="row.enabled">
|
||||
<el-radio
|
||||
v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), 'boolean')"
|
||||
<ElRadioGroup v-model="row.enabled">
|
||||
<ElRadio
|
||||
v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</ElRadio>
|
||||
</ElRadioGroup>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="avatar" title="头像" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="avatar" title="头像" align="center">
|
||||
<template #default="{row}">
|
||||
<ImageUpload v-model="row.avatar" />
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="video" title="附件" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="video" title="附件" align="center">
|
||||
<template #default="{row}">
|
||||
<FileUpload v-model="row.video" />
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="memo" title="备注" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="memo" title="备注" align="center">
|
||||
<template #default="{row}">
|
||||
<el-input v-model="row.memo" type="textarea" />
|
||||
<ElInput v-model="row.memo" type="textarea" />
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="operation" title="操作" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="operation" title="操作" align="center">
|
||||
<template #default="{row}">
|
||||
<el-button
|
||||
<ElButton
|
||||
size="small"
|
||||
type="danger"
|
||||
link
|
||||
@@ -129,13 +210,13 @@ watch(
|
||||
v-access:code="['infra:student:delete']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.delete') }}
|
||||
</el-button>
|
||||
</ElButton>
|
||||
</template>
|
||||
</vxe-column>
|
||||
</vxe-table>
|
||||
</VxeColumn>
|
||||
</VxeTable>
|
||||
<div class="flex justify-center mt-4">
|
||||
<el-button :icon="h(Plus)" type="primary" plain @click="handleAdd" v-access:code="['infra:student:create']">
|
||||
<ElButton :icon="Plus" type="primary" plain @click="handleAdd" v-access:code="['infra:student:create']">
|
||||
{{ $t('ui.actionTitle.create', ['学生联系人']) }}
|
||||
</el-button>
|
||||
</ElButton>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
+56
-35
@@ -1,17 +1,26 @@
|
||||
<script lang="ts" setup>
|
||||
import type { StudentApi } from '#/api/infra/student';
|
||||
import type { FormRules } from 'element-plus';
|
||||
import type { StudentApi } from '#/api/infra/demo';
|
||||
|
||||
import { computed, ref, reactive, h, onMounted,watch,nextTick } from 'vue';
|
||||
import { nextTick, reactive, ref, watch } from 'vue';
|
||||
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
import { getDictOptions } from '@vben/hooks';
|
||||
|
||||
import { ElMessage, ElTabs, ElTabPane, ElForm, ElFormItem, ElInput, ElButton, ElSelect, ElOption, ElRadioGroup, ElRadio, ElCheckboxGroup, ElCheckbox, ElDatePicker } from 'element-plus';
|
||||
import { $t } from '#/locales';
|
||||
import {
|
||||
ElForm,
|
||||
ElFormItem,
|
||||
ElDatePicker,
|
||||
ElInput,
|
||||
ElOption,
|
||||
ElRadio,
|
||||
ElRadioGroup,
|
||||
ElSelect,
|
||||
} from 'element-plus';
|
||||
|
||||
import type { FormRules } from 'element-plus';
|
||||
import { Tinymce as RichTextarea } from '#/components/tinymce';
|
||||
import { getStudentTeacherByStudentId } from '#/api/infra/student';
|
||||
import { getStudentTeacherByStudentId } from '#/api/infra/demo';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
const props = defineProps<{
|
||||
studentId?: number // 学生编号(主表的关联字段)
|
||||
@@ -50,6 +59,18 @@ defineExpose({
|
||||
watch(
|
||||
() => props.studentId,
|
||||
async (val) => {
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
studentId: undefined,
|
||||
name: undefined,
|
||||
description: undefined,
|
||||
birthday: undefined,
|
||||
sex: undefined,
|
||||
enabled: undefined,
|
||||
avatar: undefined,
|
||||
video: undefined,
|
||||
memo: undefined,
|
||||
};
|
||||
if (!val) {
|
||||
return;
|
||||
}
|
||||
@@ -61,7 +82,7 @@ watch(
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-form
|
||||
<ElForm
|
||||
ref="formRef"
|
||||
class="mx-4"
|
||||
:model="formData"
|
||||
@@ -69,48 +90,48 @@ watch(
|
||||
label-width="120px"
|
||||
label-position="right"
|
||||
>
|
||||
<el-form-item label="名字" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入名字" />
|
||||
</el-form-item>
|
||||
<el-form-item label="简介" prop="description">
|
||||
<el-input v-model="formData.description" type="textarea" placeholder="请输入简介" />
|
||||
</el-form-item>
|
||||
<el-form-item label="出生日期" prop="birthday">
|
||||
<el-date-picker
|
||||
<ElFormItem label="名字" prop="name">
|
||||
<ElInput v-model="formData.name" placeholder="请输入名字" />
|
||||
</ElFormItem>
|
||||
<ElFormItem label="简介" prop="description">
|
||||
<ElInput v-model="formData.description" type="textarea" placeholder="请输入简介" />
|
||||
</ElFormItem>
|
||||
<ElFormItem label="出生日期" prop="birthday">
|
||||
<ElDatePicker
|
||||
v-model="formData.birthday"
|
||||
value-format="x"
|
||||
placeholder="选择出生日期"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="性别" prop="sex">
|
||||
<el-select v-model="formData.sex" placeholder="请选择性别">
|
||||
<el-option
|
||||
</ElFormItem>
|
||||
<ElFormItem label="性别" prop="sex">
|
||||
<ElSelect v-model="formData.sex" placeholder="请选择性别">
|
||||
<ElOption
|
||||
v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
:label="dict.label"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否有效" prop="enabled">
|
||||
<el-radio-group v-model="formData.enabled">
|
||||
<el-radio
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="是否有效" prop="enabled">
|
||||
<ElRadioGroup v-model="formData.enabled">
|
||||
<ElRadio
|
||||
v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="头像" prop="avatar">
|
||||
</ElRadio>
|
||||
</ElRadioGroup>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="头像" prop="avatar">
|
||||
<ImageUpload v-model="formData.avatar" />
|
||||
</el-form-item>
|
||||
<el-form-item label="附件" prop="video">
|
||||
</ElFormItem>
|
||||
<ElFormItem label="附件" prop="video">
|
||||
<FileUpload v-model="formData.video" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="memo">
|
||||
</ElFormItem>
|
||||
<ElFormItem label="备注" prop="memo">
|
||||
<RichTextarea v-model="formData.memo" height="500px" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
</template>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import type { StudentApi } from '#/api/infra/student';
|
||||
import type { StudentApi } from '#/api/infra/demo';
|
||||
import type { FormRules } from 'element-plus';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
@@ -11,7 +11,7 @@ import { ElMessage, ElTabs, ElTabPane, ElForm, ElFormItem, ElInput, ElSelect, El
|
||||
|
||||
import { computed, ref, reactive } from 'vue';
|
||||
import { $t } from '#/locales';
|
||||
import { getStudent, createStudent, updateStudent } from '#/api/infra/student';
|
||||
import { getStudent, createStudent, updateStudent } from '#/api/infra/demo';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
|
||||
@@ -102,56 +102,56 @@ const [Modal, modalApi] = useVbenModal({
|
||||
|
||||
<template>
|
||||
<Modal :title="getTitle">
|
||||
<el-form
|
||||
<ElForm
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="rules"
|
||||
label-width="120px"
|
||||
label-position="right"
|
||||
>
|
||||
<el-form-item label="名字" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入名字" />
|
||||
</el-form-item>
|
||||
<el-form-item label="简介" prop="description">
|
||||
<el-input v-model="formData.description" type="textarea" placeholder="请输入简介" />
|
||||
</el-form-item>
|
||||
<el-form-item label="出生日期" prop="birthday">
|
||||
<el-date-picker
|
||||
<ElFormItem label="名字" prop="name">
|
||||
<ElInput v-model="formData.name" placeholder="请输入名字" />
|
||||
</ElFormItem>
|
||||
<ElFormItem label="简介" prop="description">
|
||||
<ElInput v-model="formData.description" type="textarea" placeholder="请输入简介" />
|
||||
</ElFormItem>
|
||||
<ElFormItem label="出生日期" prop="birthday">
|
||||
<ElDatePicker
|
||||
v-model="formData.birthday"
|
||||
value-format="x"
|
||||
placeholder="选择出生日期"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="性别" prop="sex">
|
||||
<el-select v-model="formData.sex" placeholder="请选择性别">
|
||||
<el-option
|
||||
</ElFormItem>
|
||||
<ElFormItem label="性别" prop="sex">
|
||||
<ElSelect v-model="formData.sex" placeholder="请选择性别">
|
||||
<ElOption
|
||||
v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
:label="dict.label"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否有效" prop="enabled">
|
||||
<el-radio-group v-model="formData.enabled">
|
||||
<el-radio
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="是否有效" prop="enabled">
|
||||
<ElRadioGroup v-model="formData.enabled">
|
||||
<ElRadio
|
||||
v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="头像" prop="avatar">
|
||||
</ElRadio>
|
||||
</ElRadioGroup>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="头像" prop="avatar">
|
||||
<ImageUpload v-model="formData.avatar" />
|
||||
</el-form-item>
|
||||
<el-form-item label="附件" prop="video">
|
||||
</ElFormItem>
|
||||
<ElFormItem label="附件" prop="video">
|
||||
<FileUpload v-model="formData.video" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="memo">
|
||||
</ElFormItem>
|
||||
<ElFormItem label="备注" prop="memo">
|
||||
<RichTextarea v-model="formData.memo" height="500px" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import type { StudentApi } from '#/api/infra/student';
|
||||
import type { StudentApi } from '#/api/infra/demo';
|
||||
|
||||
import { ref, h, reactive, onMounted, nextTick } from 'vue';
|
||||
|
||||
@@ -17,7 +17,7 @@ import { VxeColumn, VxeTable } from '#/adapter/vxe-table';
|
||||
|
||||
import { $t } from '#/locales';
|
||||
import { isEmpty } from '@vben/utils';
|
||||
import { getStudentPage, deleteStudent, deleteStudentList, exportStudent } from '#/api/infra/student';
|
||||
import { getStudentPage, deleteStudent, deleteStudentList, exportStudent } from '#/api/infra/demo';
|
||||
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
@@ -27,12 +27,11 @@ const total = ref(0) // 列表的总页数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
name: undefined,
|
||||
birthday: undefined,
|
||||
birthday: undefined,
|
||||
sex: undefined,
|
||||
enabled: undefined,
|
||||
createTime: undefined,
|
||||
name: undefined,
|
||||
birthday: undefined,
|
||||
sex: undefined,
|
||||
enabled: undefined,
|
||||
createTime: undefined,
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
@@ -126,7 +125,14 @@ function handleRowCheckboxChange({
|
||||
async function handleExport() {
|
||||
try {
|
||||
exportLoading.value = true;
|
||||
const data = await exportStudent(queryParams);
|
||||
const params = cloneDeep(queryParams) as any;
|
||||
if (params.birthday && Array.isArray(params.birthday)) {
|
||||
params.birthday = (params.birthday as string[]).join(',');
|
||||
}
|
||||
if (params.createTime && Array.isArray(params.createTime)) {
|
||||
params.createTime = (params.createTime as string[]).join(',');
|
||||
}
|
||||
const data = await exportStudent(params);
|
||||
downloadFileFromBlobPart({ fileName: '学生.xls', source: data });
|
||||
}finally {
|
||||
exportLoading.value = false;
|
||||
@@ -148,61 +154,61 @@ onMounted(() => {
|
||||
|
||||
<ContentWrap v-if="!hiddenSearchBar">
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form
|
||||
<ElForm
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
inline
|
||||
>
|
||||
<el-form-item label="名字">
|
||||
<el-input
|
||||
<ElFormItem label="名字">
|
||||
<ElInput
|
||||
v-model="queryParams.name"
|
||||
placeholder="请输入名字"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-[240px]"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="出生日期">
|
||||
<el-date-picker
|
||||
</ElFormItem>
|
||||
<ElFormItem label="出生日期">
|
||||
<ElDatePicker
|
||||
v-model="queryParams.birthday"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="选择出生日期"
|
||||
clearable
|
||||
class="!w-[240px]"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="性别">
|
||||
<el-select
|
||||
</ElFormItem>
|
||||
<ElFormItem label="性别">
|
||||
<ElSelect
|
||||
v-model="queryParams.sex"
|
||||
placeholder="请选择性别"
|
||||
clearable
|
||||
class="!w-[240px]"
|
||||
>
|
||||
<el-option
|
||||
<ElOption
|
||||
v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
:label="dict.label"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否有效">
|
||||
<el-select
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="是否有效">
|
||||
<ElSelect
|
||||
v-model="queryParams.enabled"
|
||||
placeholder="请选择是否有效"
|
||||
clearable
|
||||
class="!w-[240px]"
|
||||
>
|
||||
<el-option
|
||||
<ElOption
|
||||
v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
:label="dict.label"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间">
|
||||
<el-date-picker
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="创建时间">
|
||||
<ElDatePicker
|
||||
v-model="queryParams.createTime"
|
||||
type="daterange"
|
||||
value-format="YYYY-MM-DD"
|
||||
@@ -211,24 +217,24 @@ onMounted(() => {
|
||||
end-placeholder="结束日期"
|
||||
class="!w-[240px]"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button class="ml-2" @click="resetQuery"> 重置 </el-button>
|
||||
<el-button class="ml-2" @click="handleQuery" type="primary">
|
||||
</ElFormItem>
|
||||
<ElFormItem>
|
||||
<ElButton class="ml-2" @click="resetQuery"> 重置 </ElButton>
|
||||
<ElButton class="ml-2" @click="handleQuery" type="primary">
|
||||
搜索
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ElButton>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap title="学生">
|
||||
<template #extra>
|
||||
<TableToolbar
|
||||
<VbenVxeTableToolbar
|
||||
ref="tableToolbarRef"
|
||||
v-model:hidden-search="hiddenSearchBar"
|
||||
>
|
||||
<el-button
|
||||
<ElButton
|
||||
class="ml-2"
|
||||
:icon="h(Plus)"
|
||||
type="primary"
|
||||
@@ -236,8 +242,8 @@ onMounted(() => {
|
||||
v-access:code="['infra:student:create']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.create', ['学生']) }}
|
||||
</el-button>
|
||||
<el-button
|
||||
</ElButton>
|
||||
<ElButton
|
||||
:icon="h(Download)"
|
||||
type="primary"
|
||||
class="ml-2"
|
||||
@@ -246,8 +252,8 @@ onMounted(() => {
|
||||
v-access:code="['infra:student:export']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.export') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
</ElButton>
|
||||
<ElButton
|
||||
:icon="h(Trash2)"
|
||||
type="danger"
|
||||
class="ml-2"
|
||||
@@ -256,10 +262,10 @@ onMounted(() => {
|
||||
v-access:code="['infra:student:delete']"
|
||||
>
|
||||
批量删除
|
||||
</el-button>
|
||||
</TableToolbar>
|
||||
</ElButton>
|
||||
</VbenVxeTableToolbar>
|
||||
</template>
|
||||
<vxe-table
|
||||
<VxeTable
|
||||
ref="tableRef"
|
||||
:data="list"
|
||||
show-overflow
|
||||
@@ -267,36 +273,36 @@ onMounted(() => {
|
||||
@checkboxAll="handleRowCheckboxChange"
|
||||
@checkboxChange="handleRowCheckboxChange"
|
||||
>
|
||||
<vxe-column type="checkbox" width="40"></vxe-column>
|
||||
<vxe-column field="id" title="编号" align="center" />
|
||||
<vxe-column field="name" title="名字" align="center" />
|
||||
<vxe-column field="description" title="简介" align="center" />
|
||||
<vxe-column field="birthday" title="出生日期" align="center">
|
||||
<VxeColumn type="checkbox" width="40"></VxeColumn>
|
||||
<VxeColumn field="id" title="编号" align="center" />
|
||||
<VxeColumn field="name" title="名字" align="center" />
|
||||
<VxeColumn field="description" title="简介" align="center" />
|
||||
<VxeColumn field="birthday" title="出生日期" align="center">
|
||||
<template #default="{row}">
|
||||
{{formatDateTime(row.birthday)}}
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="sex" title="性别" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="sex" title="性别" align="center">
|
||||
<template #default="{row}">
|
||||
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="row.sex" />
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="enabled" title="是否有效" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="enabled" title="是否有效" align="center">
|
||||
<template #default="{row}">
|
||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="row.enabled" />
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="avatar" title="头像" align="center" />
|
||||
<vxe-column field="video" title="附件" align="center" />
|
||||
<vxe-column field="memo" title="备注" align="center" />
|
||||
<vxe-column field="createTime" title="创建时间" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="avatar" title="头像" align="center" />
|
||||
<VxeColumn field="video" title="附件" align="center" />
|
||||
<VxeColumn field="memo" title="备注" align="center" />
|
||||
<VxeColumn field="createTime" title="创建时间" align="center">
|
||||
<template #default="{row}">
|
||||
{{formatDateTime(row.createTime)}}
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="operation" title="操作" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="operation" title="操作" align="center">
|
||||
<template #default="{row}">
|
||||
<el-button
|
||||
<ElButton
|
||||
size="small"
|
||||
type="primary"
|
||||
link
|
||||
@@ -304,8 +310,8 @@ onMounted(() => {
|
||||
v-access:code="['infra:student:update']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.edit') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
</ElButton>
|
||||
<ElButton
|
||||
size="small"
|
||||
type="danger"
|
||||
link
|
||||
@@ -314,13 +320,13 @@ onMounted(() => {
|
||||
v-access:code="['infra:student:delete']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.delete') }}
|
||||
</el-button>
|
||||
</ElButton>
|
||||
</template>
|
||||
</vxe-column>
|
||||
</vxe-table>
|
||||
</VxeColumn>
|
||||
</VxeTable>
|
||||
<!-- 分页 -->
|
||||
<div class="mt-2 flex justify-end">
|
||||
<el-pagination
|
||||
<ElPagination
|
||||
:total="total"
|
||||
v-model:current-page="queryParams.pageNo"
|
||||
v-model:page-size="queryParams.pageSize"
|
||||
|
||||
+31
-31
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import type { StudentApi } from '#/api/infra/student';
|
||||
import type { StudentApi } from '#/api/infra/demo';
|
||||
import type { FormRules } from 'element-plus';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
@@ -11,7 +11,7 @@ import { ElMessage, ElTabs, ElTabPane, ElForm, ElFormItem, ElInput, ElSelect, El
|
||||
|
||||
import { computed, ref, reactive } from 'vue';
|
||||
import { $t } from '#/locales';
|
||||
import { getStudent, createStudent, updateStudent } from '#/api/infra/student';
|
||||
import { getStudent, createStudent, updateStudent } from '#/api/infra/demo';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
|
||||
@@ -102,56 +102,56 @@ const [Modal, modalApi] = useVbenModal({
|
||||
|
||||
<template>
|
||||
<Modal :title="getTitle">
|
||||
<el-form
|
||||
<ElForm
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="rules"
|
||||
label-width="120px"
|
||||
label-position="right"
|
||||
>
|
||||
<el-form-item label="名字" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入名字" />
|
||||
</el-form-item>
|
||||
<el-form-item label="简介" prop="description">
|
||||
<el-input v-model="formData.description" type="textarea" placeholder="请输入简介" />
|
||||
</el-form-item>
|
||||
<el-form-item label="出生日期" prop="birthday">
|
||||
<el-date-picker
|
||||
<ElFormItem label="名字" prop="name">
|
||||
<ElInput v-model="formData.name" placeholder="请输入名字" />
|
||||
</ElFormItem>
|
||||
<ElFormItem label="简介" prop="description">
|
||||
<ElInput v-model="formData.description" type="textarea" placeholder="请输入简介" />
|
||||
</ElFormItem>
|
||||
<ElFormItem label="出生日期" prop="birthday">
|
||||
<ElDatePicker
|
||||
v-model="formData.birthday"
|
||||
value-format="x"
|
||||
placeholder="选择出生日期"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="性别" prop="sex">
|
||||
<el-select v-model="formData.sex" placeholder="请选择性别">
|
||||
<el-option
|
||||
</ElFormItem>
|
||||
<ElFormItem label="性别" prop="sex">
|
||||
<ElSelect v-model="formData.sex" placeholder="请选择性别">
|
||||
<ElOption
|
||||
v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
:label="dict.label"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否有效" prop="enabled">
|
||||
<el-radio-group v-model="formData.enabled">
|
||||
<el-radio
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="是否有效" prop="enabled">
|
||||
<ElRadioGroup v-model="formData.enabled">
|
||||
<ElRadio
|
||||
v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="头像" prop="avatar">
|
||||
</ElRadio>
|
||||
</ElRadioGroup>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="头像" prop="avatar">
|
||||
<ImageUpload v-model="formData.avatar" />
|
||||
</el-form-item>
|
||||
<el-form-item label="附件" prop="video">
|
||||
</ElFormItem>
|
||||
<ElFormItem label="附件" prop="video">
|
||||
<FileUpload v-model="formData.video" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="memo">
|
||||
</ElFormItem>
|
||||
<ElFormItem label="备注" prop="memo">
|
||||
<RichTextarea v-model="formData.memo" height="500px" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
+12
@@ -47,6 +47,16 @@ function handleChange(file: any) {
|
||||
}
|
||||
}
|
||||
|
||||
/** 移除文件 */
|
||||
function handleRemove() {
|
||||
fileRef.value = null;
|
||||
}
|
||||
|
||||
/** 超出文件数量 */
|
||||
function handleExceed() {
|
||||
ElMessage.warning('只能选择一个文件,请先移除已选文件');
|
||||
}
|
||||
|
||||
/** 下载导入模板 */
|
||||
async function handleDownload() {
|
||||
const data = await importStudentTemplate();
|
||||
@@ -61,6 +71,8 @@ async function handleDownload() {
|
||||
:limit="1"
|
||||
accept=".xls,.xlsx"
|
||||
:on-change="handleChange"
|
||||
:on-remove="handleRemove"
|
||||
:on-exceed="handleExceed"
|
||||
:auto-upload="false"
|
||||
>
|
||||
<ElButton type="primary"> 选择 Excel 文件 </ElButton>
|
||||
|
||||
+75
-69
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import type { StudentApi } from '#/api/infra/student';
|
||||
import type { StudentApi } from '#/api/infra/demo';
|
||||
|
||||
import { ref, h, reactive, onMounted, nextTick } from 'vue';
|
||||
|
||||
@@ -17,7 +17,7 @@ import { VxeColumn, VxeTable } from '#/adapter/vxe-table';
|
||||
|
||||
import { $t } from '#/locales';
|
||||
import { isEmpty } from '@vben/utils';
|
||||
import { getStudentPage, deleteStudent, deleteStudentList, exportStudent } from '#/api/infra/student';
|
||||
import { getStudentPage, deleteStudent, deleteStudentList, exportStudent } from '#/api/infra/demo';
|
||||
import StudentImport from './modules/import-form.vue';
|
||||
|
||||
|
||||
@@ -28,12 +28,11 @@ const total = ref(0) // 列表的总页数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
name: undefined,
|
||||
birthday: undefined,
|
||||
birthday: undefined,
|
||||
sex: undefined,
|
||||
enabled: undefined,
|
||||
createTime: undefined,
|
||||
name: undefined,
|
||||
birthday: undefined,
|
||||
sex: undefined,
|
||||
enabled: undefined,
|
||||
createTime: undefined,
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
@@ -136,7 +135,14 @@ function handleRowCheckboxChange({
|
||||
async function handleExport() {
|
||||
try {
|
||||
exportLoading.value = true;
|
||||
const data = await exportStudent(queryParams);
|
||||
const params = cloneDeep(queryParams) as any;
|
||||
if (params.birthday && Array.isArray(params.birthday)) {
|
||||
params.birthday = (params.birthday as string[]).join(',');
|
||||
}
|
||||
if (params.createTime && Array.isArray(params.createTime)) {
|
||||
params.createTime = (params.createTime as string[]).join(',');
|
||||
}
|
||||
const data = await exportStudent(params);
|
||||
downloadFileFromBlobPart({ fileName: '学生.xls', source: data });
|
||||
}finally {
|
||||
exportLoading.value = false;
|
||||
@@ -159,61 +165,61 @@ onMounted(() => {
|
||||
|
||||
<ContentWrap v-if="!hiddenSearchBar">
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form
|
||||
<ElForm
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
inline
|
||||
>
|
||||
<el-form-item label="名字">
|
||||
<el-input
|
||||
<ElFormItem label="名字">
|
||||
<ElInput
|
||||
v-model="queryParams.name"
|
||||
placeholder="请输入名字"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-[240px]"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="出生日期">
|
||||
<el-date-picker
|
||||
</ElFormItem>
|
||||
<ElFormItem label="出生日期">
|
||||
<ElDatePicker
|
||||
v-model="queryParams.birthday"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="选择出生日期"
|
||||
clearable
|
||||
class="!w-[240px]"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="性别">
|
||||
<el-select
|
||||
</ElFormItem>
|
||||
<ElFormItem label="性别">
|
||||
<ElSelect
|
||||
v-model="queryParams.sex"
|
||||
placeholder="请选择性别"
|
||||
clearable
|
||||
class="!w-[240px]"
|
||||
>
|
||||
<el-option
|
||||
<ElOption
|
||||
v-for="dict in getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number')"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
:label="dict.label"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否有效">
|
||||
<el-select
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="是否有效">
|
||||
<ElSelect
|
||||
v-model="queryParams.enabled"
|
||||
placeholder="请选择是否有效"
|
||||
clearable
|
||||
class="!w-[240px]"
|
||||
>
|
||||
<el-option
|
||||
<ElOption
|
||||
v-for="dict in getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean')"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
:label="dict.label"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间">
|
||||
<el-date-picker
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="创建时间">
|
||||
<ElDatePicker
|
||||
v-model="queryParams.createTime"
|
||||
type="daterange"
|
||||
value-format="YYYY-MM-DD"
|
||||
@@ -222,24 +228,24 @@ onMounted(() => {
|
||||
end-placeholder="结束日期"
|
||||
class="!w-[240px]"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button class="ml-2" @click="resetQuery"> 重置 </el-button>
|
||||
<el-button class="ml-2" @click="handleQuery" type="primary">
|
||||
</ElFormItem>
|
||||
<ElFormItem>
|
||||
<ElButton class="ml-2" @click="resetQuery"> 重置 </ElButton>
|
||||
<ElButton class="ml-2" @click="handleQuery" type="primary">
|
||||
搜索
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ElButton>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap title="学生">
|
||||
<template #extra>
|
||||
<TableToolbar
|
||||
<VbenVxeTableToolbar
|
||||
ref="tableToolbarRef"
|
||||
v-model:hidden-search="hiddenSearchBar"
|
||||
>
|
||||
<el-button
|
||||
<ElButton
|
||||
class="ml-2"
|
||||
:icon="h(Plus)"
|
||||
type="primary"
|
||||
@@ -247,16 +253,16 @@ onMounted(() => {
|
||||
v-access:code="['infra:student:create']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.create', ['学生']) }}
|
||||
</el-button>
|
||||
<el-button
|
||||
</ElButton>
|
||||
<ElButton
|
||||
class="ml-2"
|
||||
type="primary"
|
||||
@click="handleImport"
|
||||
v-access:code="['infra:student:import']"
|
||||
>
|
||||
导入
|
||||
</el-button>
|
||||
<el-button
|
||||
</ElButton>
|
||||
<ElButton
|
||||
:icon="h(Download)"
|
||||
type="primary"
|
||||
class="ml-2"
|
||||
@@ -265,8 +271,8 @@ onMounted(() => {
|
||||
v-access:code="['infra:student:export']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.export') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
</ElButton>
|
||||
<ElButton
|
||||
:icon="h(Trash2)"
|
||||
type="danger"
|
||||
class="ml-2"
|
||||
@@ -275,10 +281,10 @@ onMounted(() => {
|
||||
v-access:code="['infra:student:delete']"
|
||||
>
|
||||
批量删除
|
||||
</el-button>
|
||||
</TableToolbar>
|
||||
</ElButton>
|
||||
</VbenVxeTableToolbar>
|
||||
</template>
|
||||
<vxe-table
|
||||
<VxeTable
|
||||
ref="tableRef"
|
||||
:data="list"
|
||||
show-overflow
|
||||
@@ -286,36 +292,36 @@ onMounted(() => {
|
||||
@checkboxAll="handleRowCheckboxChange"
|
||||
@checkboxChange="handleRowCheckboxChange"
|
||||
>
|
||||
<vxe-column type="checkbox" width="40"></vxe-column>
|
||||
<vxe-column field="id" title="编号" align="center" />
|
||||
<vxe-column field="name" title="名字" align="center" />
|
||||
<vxe-column field="description" title="简介" align="center" />
|
||||
<vxe-column field="birthday" title="出生日期" align="center">
|
||||
<VxeColumn type="checkbox" width="40"></VxeColumn>
|
||||
<VxeColumn field="id" title="编号" align="center" />
|
||||
<VxeColumn field="name" title="名字" align="center" />
|
||||
<VxeColumn field="description" title="简介" align="center" />
|
||||
<VxeColumn field="birthday" title="出生日期" align="center">
|
||||
<template #default="{row}">
|
||||
{{formatDateTime(row.birthday)}}
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="sex" title="性别" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="sex" title="性别" align="center">
|
||||
<template #default="{row}">
|
||||
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="row.sex" />
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="enabled" title="是否有效" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="enabled" title="是否有效" align="center">
|
||||
<template #default="{row}">
|
||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="row.enabled" />
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="avatar" title="头像" align="center" />
|
||||
<vxe-column field="video" title="附件" align="center" />
|
||||
<vxe-column field="memo" title="备注" align="center" />
|
||||
<vxe-column field="createTime" title="创建时间" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="avatar" title="头像" align="center" />
|
||||
<VxeColumn field="video" title="附件" align="center" />
|
||||
<VxeColumn field="memo" title="备注" align="center" />
|
||||
<VxeColumn field="createTime" title="创建时间" align="center">
|
||||
<template #default="{row}">
|
||||
{{formatDateTime(row.createTime)}}
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="operation" title="操作" align="center">
|
||||
</VxeColumn>
|
||||
<VxeColumn field="operation" title="操作" align="center">
|
||||
<template #default="{row}">
|
||||
<el-button
|
||||
<ElButton
|
||||
size="small"
|
||||
type="primary"
|
||||
link
|
||||
@@ -323,8 +329,8 @@ onMounted(() => {
|
||||
v-access:code="['infra:student:update']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.edit') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
</ElButton>
|
||||
<ElButton
|
||||
size="small"
|
||||
type="danger"
|
||||
link
|
||||
@@ -333,13 +339,13 @@ onMounted(() => {
|
||||
v-access:code="['infra:student:delete']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.delete') }}
|
||||
</el-button>
|
||||
</ElButton>
|
||||
</template>
|
||||
</vxe-column>
|
||||
</vxe-table>
|
||||
</VxeColumn>
|
||||
</VxeTable>
|
||||
<!-- 分页 -->
|
||||
<div class="mt-2 flex justify-end">
|
||||
<el-pagination
|
||||
<ElPagination
|
||||
:total="total"
|
||||
v-model:current-page="queryParams.pageNo"
|
||||
v-model:page-size="queryParams.pageSize"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import type { CategoryApi } from '#/api/infra/category';
|
||||
import type { CategoryApi } from '#/api/infra/demo';
|
||||
import type { FormRules } from 'element-plus';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
@@ -7,12 +7,12 @@ import { getDictOptions } from '@vben/hooks';
|
||||
import { Tinymce as RichTextarea } from '#/components/tinymce';
|
||||
import { ImageUpload, FileUpload } from "#/components/upload";
|
||||
import { ElMessage, ElTabs, ElTabPane, ElForm, ElFormItem, ElInput, ElSelect, ElOption, ElRadioGroup, ElRadio, ElCheckboxGroup, ElCheckbox, ElDatePicker, ElTreeSelect } from 'element-plus';
|
||||
import { getCategoryList } from '#/api/infra/category';
|
||||
import { getCategoryList } from '#/api/infra/demo';
|
||||
import { handleTree } from '@vben/utils'
|
||||
|
||||
import { computed, ref, reactive } from 'vue';
|
||||
import { $t } from '#/locales';
|
||||
import { getCategory, createCategory, updateCategory } from '#/api/infra/category';
|
||||
import { getCategory, createCategory, updateCategory } from '#/api/infra/demo';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
|
||||
@@ -98,18 +98,18 @@ const [Modal, modalApi] = useVbenModal({
|
||||
|
||||
<template>
|
||||
<Modal :title="getTitle">
|
||||
<el-form
|
||||
<ElForm
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="rules"
|
||||
label-width="120px"
|
||||
label-position="right"
|
||||
>
|
||||
<el-form-item label="名字" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入名字" />
|
||||
</el-form-item>
|
||||
<el-form-item label="父编号" prop="parentId">
|
||||
<el-tree-select
|
||||
<ElFormItem label="名字" prop="name">
|
||||
<ElInput v-model="formData.name" placeholder="请输入名字" />
|
||||
</ElFormItem>
|
||||
<ElFormItem label="父编号" prop="parentId">
|
||||
<ElTreeSelect
|
||||
v-model="formData.parentId"
|
||||
:data="categoryTree"
|
||||
:props="{
|
||||
@@ -121,7 +121,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
default-expand-all
|
||||
placeholder="请选择父编号"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
</Modal>
|
||||
</template>
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import type { CategoryApi } from '#/api/infra/category';
|
||||
import type { CategoryApi } from '#/api/infra/demo';
|
||||
|
||||
import { ref, h, reactive, onMounted, nextTick } from 'vue';
|
||||
|
||||
@@ -16,14 +16,14 @@ import { VxeColumn, VxeTable } from '#/adapter/vxe-table';
|
||||
|
||||
import { $t } from '#/locales';
|
||||
import { handleTree,isEmpty } from '@vben/utils'
|
||||
import { getCategoryList, deleteCategory, exportCategory } from '#/api/infra/category';
|
||||
import { getCategoryList, deleteCategory, exportCategory } from '#/api/infra/demo';
|
||||
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const list = ref<any[]>([]) // 树列表的数据
|
||||
|
||||
const queryParams = reactive({
|
||||
name: undefined,
|
||||
name: undefined,
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
@@ -89,7 +89,8 @@ async function handleDelete(row: CategoryApi.Category) {
|
||||
async function handleExport() {
|
||||
try {
|
||||
exportLoading.value = true;
|
||||
const data = await exportCategory(queryParams);
|
||||
const params = cloneDeep(queryParams) as any;
|
||||
const data = await exportCategory(params);
|
||||
downloadFileFromBlobPart({ fileName: '分类.xls', source: data });
|
||||
}finally {
|
||||
exportLoading.value = false;
|
||||
@@ -117,40 +118,40 @@ onMounted(() => {
|
||||
|
||||
<ContentWrap v-if="!hiddenSearchBar">
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form
|
||||
<ElForm
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
inline
|
||||
>
|
||||
<el-form-item label="名字">
|
||||
<el-input
|
||||
<ElFormItem label="名字">
|
||||
<ElInput
|
||||
v-model="queryParams.name"
|
||||
placeholder="请输入名字"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-[240px]"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button class="ml-2" @click="resetQuery"> 重置 </el-button>
|
||||
<el-button class="ml-2" @click="handleQuery" type="primary">
|
||||
</ElFormItem>
|
||||
<ElFormItem>
|
||||
<ElButton class="ml-2" @click="resetQuery"> 重置 </ElButton>
|
||||
<ElButton class="ml-2" @click="handleQuery" type="primary">
|
||||
搜索
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ElButton>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap title="分类">
|
||||
<template #extra>
|
||||
<TableToolbar
|
||||
<VbenVxeTableToolbar
|
||||
ref="tableToolbarRef"
|
||||
v-model:hidden-search="hiddenSearchBar"
|
||||
>
|
||||
<el-button @click="handleExpand" class="mr-2">
|
||||
<ElButton @click="handleExpand" class="mr-2">
|
||||
{{ isExpanded ? '收缩' : '展开' }}
|
||||
</el-button>
|
||||
<el-button
|
||||
</ElButton>
|
||||
<ElButton
|
||||
class="ml-2"
|
||||
:icon="h(Plus)"
|
||||
type="primary"
|
||||
@@ -158,8 +159,8 @@ onMounted(() => {
|
||||
v-access:code="['infra:category:create']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.create', ['分类']) }}
|
||||
</el-button>
|
||||
<el-button
|
||||
</ElButton>
|
||||
<ElButton
|
||||
:icon="h(Download)"
|
||||
type="primary"
|
||||
class="ml-2"
|
||||
@@ -168,10 +169,10 @@ onMounted(() => {
|
||||
v-access:code="['infra:category:export']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.export') }}
|
||||
</el-button>
|
||||
</TableToolbar>
|
||||
</ElButton>
|
||||
</VbenVxeTableToolbar>
|
||||
</template>
|
||||
<vxe-table
|
||||
<VxeTable
|
||||
ref="tableRef"
|
||||
:data="list"
|
||||
:tree-config="{
|
||||
@@ -184,12 +185,12 @@ onMounted(() => {
|
||||
show-overflow
|
||||
:loading="loading"
|
||||
>
|
||||
<vxe-column field="id" title="编号" align="center" />
|
||||
<vxe-column field="name" title="名字" align="center" tree-node/>
|
||||
<vxe-column field="parentId" title="父编号" align="center" />
|
||||
<vxe-column field="operation" title="操作" align="center">
|
||||
<VxeColumn field="id" title="编号" align="center" />
|
||||
<VxeColumn field="name" title="名字" align="center" tree-node/>
|
||||
<VxeColumn field="parentId" title="父编号" align="center" />
|
||||
<VxeColumn field="operation" title="操作" align="center">
|
||||
<template #default="{row}">
|
||||
<el-button
|
||||
<ElButton
|
||||
size="small"
|
||||
type="primary"
|
||||
link
|
||||
@@ -197,8 +198,8 @@ onMounted(() => {
|
||||
v-access:code="['infra:category:create']"
|
||||
>
|
||||
新增下级
|
||||
</el-button>
|
||||
<el-button
|
||||
</ElButton>
|
||||
<ElButton
|
||||
size="small"
|
||||
type="primary"
|
||||
link
|
||||
@@ -206,8 +207,8 @@ onMounted(() => {
|
||||
v-access:code="['infra:category:update']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.edit') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
</ElButton>
|
||||
<ElButton
|
||||
size="small"
|
||||
type="danger"
|
||||
link
|
||||
@@ -217,10 +218,10 @@ onMounted(() => {
|
||||
v-access:code="['infra:category:delete']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.delete') }}
|
||||
</el-button>
|
||||
</ElButton>
|
||||
</template>
|
||||
</vxe-column>
|
||||
</vxe-table>
|
||||
</VxeColumn>
|
||||
</VxeTable>
|
||||
</ContentWrap>
|
||||
</Page>
|
||||
</template>
|
||||
@@ -42,7 +42,7 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
rules: 'required',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
showTime: true,
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'x',
|
||||
class: '!w-full',
|
||||
@@ -65,8 +65,7 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'),
|
||||
buttonStyle: 'solid',
|
||||
optionType: 'button',
|
||||
isButton: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -277,7 +276,7 @@ export function useStudentContactFormSchema(): VbenFormSchema[] {
|
||||
rules: 'required',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
showTime: true,
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'x',
|
||||
class: '!w-full',
|
||||
@@ -300,8 +299,7 @@ export function useStudentContactFormSchema(): VbenFormSchema[] {
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'),
|
||||
buttonStyle: 'solid',
|
||||
optionType: 'button',
|
||||
isButton: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -520,7 +518,7 @@ export function useStudentTeacherFormSchema(): VbenFormSchema[] {
|
||||
rules: 'required',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
showTime: true,
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'x',
|
||||
class: '!w-full',
|
||||
@@ -543,8 +541,7 @@ export function useStudentTeacherFormSchema(): VbenFormSchema[] {
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'),
|
||||
buttonStyle: 'solid',
|
||||
optionType: 'button',
|
||||
isButton: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -5,7 +5,7 @@ import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { ElMessage, ElTabs, ElTabPane } from 'element-plus';
|
||||
import { ElMessage } from 'element-plus';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { createStudent, getStudent, updateStudent } from '#/api/infra/demo';
|
||||
|
||||
@@ -42,7 +42,7 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
rules: 'required',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
showTime: true,
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'x',
|
||||
class: '!w-full',
|
||||
@@ -65,8 +65,7 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'),
|
||||
buttonStyle: 'solid',
|
||||
optionType: 'button',
|
||||
isButton: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -270,7 +269,7 @@ export function useGridColumns(): VxeTableGridOptions<InfraStudentApi.Student>['
|
||||
minWidth: 120,
|
||||
slots: { default: 'sex' },
|
||||
params: {
|
||||
options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'boolean'),
|
||||
options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number'),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -310,7 +309,7 @@ export function useGridColumns(): VxeTableGridOptions<InfraStudentApi.Student>['
|
||||
}
|
||||
|
||||
/** 列表的字段 */
|
||||
export function useStudentContactGridColumns(): VxeTableGridOptions<StudentApi.StudentContact>['columns'] {
|
||||
export function useStudentContactGridColumns(): VxeTableGridOptions<InfraStudentApi.StudentContact>['columns'] {
|
||||
return [
|
||||
{
|
||||
field: 'id',
|
||||
@@ -444,7 +443,7 @@ export function useGridColumns(): VxeTableGridOptions<InfraStudentApi.Student>['
|
||||
rules: 'required',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
showTime: true,
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'x',
|
||||
class: '!w-full',
|
||||
@@ -467,8 +466,7 @@ export function useGridColumns(): VxeTableGridOptions<InfraStudentApi.Student>['
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'),
|
||||
buttonStyle: 'solid',
|
||||
optionType: 'button',
|
||||
isButton: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -492,7 +490,7 @@ export function useGridColumns(): VxeTableGridOptions<InfraStudentApi.Student>['
|
||||
}
|
||||
|
||||
/** 列表的字段 */
|
||||
export function useStudentTeacherGridColumns(): VxeTableGridOptions<StudentApi.StudentTeacher>['columns'] {
|
||||
export function useStudentTeacherGridColumns(): VxeTableGridOptions<InfraStudentApi.StudentTeacher>['columns'] {
|
||||
return [
|
||||
{
|
||||
field: 'id',
|
||||
|
||||
+7
-2
@@ -5,7 +5,7 @@ import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { ElMessage, ElTabPane, ElTabs } from 'element-plus';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { createStudent, getStudent, updateStudent } from '#/api/infra/demo';
|
||||
@@ -48,7 +48,12 @@ const [Modal, modalApi] = useVbenModal({
|
||||
return;
|
||||
}
|
||||
// 校验子表单
|
||||
const studentTeacherValid = await studentTeacherFormRef.value?.validate();
|
||||
const studentContactValid = await studentContactFormRef.value?.validate();
|
||||
if (!studentContactValid) {
|
||||
subTabsName.value = 'studentContact';
|
||||
return;
|
||||
}
|
||||
const studentTeacherValid = await studentTeacherFormRef.value?.validate();
|
||||
if (!studentTeacherValid) {
|
||||
subTabsName.value = 'studentTeacher';
|
||||
return;
|
||||
|
||||
+113
-44
@@ -6,10 +6,9 @@
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { Plus } from "@vben/icons";
|
||||
import { ElButton, ElTabs, ElTabPane, ElCheckbox, ElInput, ElSelect, ElOption, ElRadioGroup, ElCheckboxGroup, ElDatePicker } from 'element-plus';
|
||||
import { ElButton, ElCheckbox, ElCheckboxGroup, ElDatePicker, ElInput, ElMessage, ElOption, ElRadio, ElRadioGroup, ElSelect, ElTabPane, ElTabs } from 'element-plus';
|
||||
import { ImageUpload, FileUpload } from "#/components/upload";
|
||||
import type { OnActionClickParams } from '#/adapter/vxe-table';
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { useStudentContactGridEditColumns } from '../data';
|
||||
import { getStudentContactListByStudentId } from '#/api/infra/demo';
|
||||
|
||||
@@ -17,22 +16,9 @@ const props = defineProps<{
|
||||
studentId?: number // 学生编号(主表的关联字段)
|
||||
}>()
|
||||
|
||||
/** 表格操作按钮的回调函数 */
|
||||
function onActionClick({
|
||||
code,
|
||||
row,
|
||||
}: OnActionClickParams<InfraStudentApi.StudentContact>) {
|
||||
switch (code) {
|
||||
case 'delete': {
|
||||
onDelete(row);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
gridOptions: {
|
||||
columns: useStudentContactGridEditColumns(onActionClick),
|
||||
columns: useStudentContactGridEditColumns(),
|
||||
border: true,
|
||||
showOverflow: true,
|
||||
autoResize: true,
|
||||
@@ -51,7 +37,7 @@ gridOptions: {
|
||||
|
||||
/** 添加学生联系人 */
|
||||
const onAdd = async () => {
|
||||
await gridApi.grid.insertAt({} as StudentApi.StudentContact, -1);
|
||||
await gridApi.grid.insertAt({} as InfraStudentApi.StudentContact, -1);
|
||||
}
|
||||
|
||||
/** 删除学生联系人 */
|
||||
@@ -59,26 +45,96 @@ const onDelete = async (row: InfraStudentApi.StudentContact) => {
|
||||
await gridApi.grid.remove(row);
|
||||
}
|
||||
|
||||
/** 提供获取表格数据的方法供父组件调用 */
|
||||
defineExpose({
|
||||
getData: (): InfraStudentApi.StudentContact[] => {
|
||||
const data = gridApi.grid.getData() as InfraStudentApi.StudentContact[];
|
||||
const removeRecords = gridApi.grid.getRemoveRecords() as InfraStudentApi.StudentContact[];
|
||||
const insertRecords = gridApi.grid.getInsertRecords() as InfraStudentApi.StudentContact[];
|
||||
return data
|
||||
.filter((row) => !removeRecords.some((removed) => removed.id === row.id))
|
||||
.concat(insertRecords.map((row: any) => ({ ...row, id: undefined })));
|
||||
},
|
||||
});
|
||||
/** 获取当前子表数据,新增行不提交表格生成的临时编号 */
|
||||
function getData(): InfraStudentApi.StudentContact[] {
|
||||
const { fullData } = gridApi.grid.getTableData();
|
||||
const inserted = new Set(gridApi.grid.getInsertRecords());
|
||||
return fullData.map((row) =>
|
||||
inserted.has(row) ? { ...row, id: undefined } : { ...row },
|
||||
);
|
||||
}
|
||||
|
||||
/** 校验子表必填字段 */
|
||||
function validate(): boolean {
|
||||
for (const [index, row] of getData().entries()) {
|
||||
if (
|
||||
row.name === null ||
|
||||
row.name === undefined ||
|
||||
(typeof row.name === 'string' && String(row.name).trim() === '') ||
|
||||
(Array.isArray(row.name) && row.name.length === 0)
|
||||
) {
|
||||
ElMessage.error(`第 ${index + 1} 行:名字不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.description === null ||
|
||||
row.description === undefined ||
|
||||
(typeof row.description === 'string' && String(row.description).trim() === '') ||
|
||||
(Array.isArray(row.description) && row.description.length === 0)
|
||||
) {
|
||||
ElMessage.error(`第 ${index + 1} 行:简介不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.birthday === null ||
|
||||
row.birthday === undefined ||
|
||||
(typeof row.birthday === 'string' && String(row.birthday).trim() === '') ||
|
||||
(Array.isArray(row.birthday) && row.birthday.length === 0)
|
||||
) {
|
||||
ElMessage.error(`第 ${index + 1} 行:出生日期不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.sex === null ||
|
||||
row.sex === undefined ||
|
||||
(typeof row.sex === 'string' && String(row.sex).trim() === '') ||
|
||||
(Array.isArray(row.sex) && row.sex.length === 0)
|
||||
) {
|
||||
ElMessage.error(`第 ${index + 1} 行:性别不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.enabled === null ||
|
||||
row.enabled === undefined ||
|
||||
(typeof row.enabled === 'string' && String(row.enabled).trim() === '') ||
|
||||
(Array.isArray(row.enabled) && row.enabled.length === 0)
|
||||
) {
|
||||
ElMessage.error(`第 ${index + 1} 行:是否有效不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.avatar === null ||
|
||||
row.avatar === undefined ||
|
||||
(typeof row.avatar === 'string' && String(row.avatar).trim() === '') ||
|
||||
(Array.isArray(row.avatar) && row.avatar.length === 0)
|
||||
) {
|
||||
ElMessage.error(`第 ${index + 1} 行:头像不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.memo === null ||
|
||||
row.memo === undefined ||
|
||||
(typeof row.memo === 'string' && String(row.memo).trim() === '') ||
|
||||
(Array.isArray(row.memo) && row.memo.length === 0)
|
||||
) {
|
||||
ElMessage.error(`第 ${index + 1} 行:备注不能为空`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
defineExpose({ getData, validate });
|
||||
|
||||
/** 监听主表的关联字段的变化,加载对应的子表数据 */
|
||||
watch(
|
||||
() => props.studentId,
|
||||
async (val) => {
|
||||
await nextTick();
|
||||
if (!val) {
|
||||
await gridApi.grid.loadData([]);
|
||||
return;
|
||||
}
|
||||
await nextTick();
|
||||
await gridApi.grid.loadData(await getStudentContactListByStudentId(props.studentId!));
|
||||
},
|
||||
{ immediate: true },
|
||||
@@ -88,13 +144,13 @@ watch(
|
||||
<template>
|
||||
<Grid class="mx-4">
|
||||
<template #name="{ row }">
|
||||
<el-input v-model="row.name" />
|
||||
<ElInput v-model="row.name" />
|
||||
</template>
|
||||
<template #description="{ row }">
|
||||
<el-input v-model="row.description" type="textarea" />
|
||||
<ElInput v-model="row.description" type="textarea" />
|
||||
</template>
|
||||
<template #birthday="{ row }">
|
||||
<el-date-picker
|
||||
<ElDatePicker
|
||||
v-model="row.birthday"
|
||||
type="datetime"
|
||||
value-format="x"
|
||||
@@ -102,16 +158,16 @@ watch(
|
||||
/>
|
||||
</template>
|
||||
<template #sex="{ row, column }">
|
||||
<el-select v-model="row.sex" class="w-full">
|
||||
<el-option v-for="option in column.params.options" :key="option.value" :value="option.value" :label="option.label" />
|
||||
</el-select>
|
||||
<ElSelect v-model="row.sex" class="w-full">
|
||||
<ElOption v-for="option in column.params.options" :key="option.value" :value="option.value" :label="option.label" />
|
||||
</ElSelect>
|
||||
</template>
|
||||
<template #enabled="{ row, column }">
|
||||
<el-radio-group v-model="row.enabled">
|
||||
<el-radio v-for="option in column.params.options" :key="option.value" :label="option.value">
|
||||
<ElRadioGroup v-model="row.enabled">
|
||||
<ElRadio v-for="option in column.params.options" :key="option.value" :value="option.value">
|
||||
{{ option.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</ElRadio>
|
||||
</ElRadioGroup>
|
||||
</template>
|
||||
<template #avatar="{ row }">
|
||||
<ImageUpload v-model="row.avatar" />
|
||||
@@ -120,12 +176,25 @@ watch(
|
||||
<FileUpload v-model="row.video" />
|
||||
</template>
|
||||
<template #memo="{ row }">
|
||||
<el-input v-model="row.memo" type="textarea" />
|
||||
<ElInput v-model="row.memo" type="textarea" />
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: $t('common.delete'),
|
||||
type: 'danger', link: true,
|
||||
icon: ACTION_ICON.DELETE,
|
||||
auth: ['infra:student:delete'],
|
||||
onClick: onDelete.bind(null, row),
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
<div class="flex justify-center -mt-4">
|
||||
<el-button :icon="Plus" type="primary" plain @click="onAdd" v-access:code="['infra:student:create']">
|
||||
<ElButton :icon="Plus" type="primary" plain @click="onAdd" v-access:code="['infra:student:create']">
|
||||
{{ $t('ui.actionTitle.create', ['学生联系人']) }}
|
||||
</el-button>
|
||||
</ElButton>
|
||||
</div>
|
||||
</template>
|
||||
+2
-1
@@ -39,10 +39,11 @@ defineExpose({
|
||||
watch(
|
||||
() => props.studentId,
|
||||
async (val) => {
|
||||
await nextTick();
|
||||
await formApi.resetForm();
|
||||
if (!val) {
|
||||
return;
|
||||
}
|
||||
await nextTick();
|
||||
await formApi.setValues(await getStudentTeacherByStudentId(props.studentId!));
|
||||
},
|
||||
{ immediate: true },
|
||||
|
||||
+5
-7
@@ -42,7 +42,7 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
rules: 'required',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
showTime: true,
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'x',
|
||||
class: '!w-full',
|
||||
@@ -65,8 +65,7 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'),
|
||||
buttonStyle: 'solid',
|
||||
optionType: 'button',
|
||||
isButton: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -269,7 +268,7 @@ export function useGridColumns(): VxeTableGridOptions<InfraStudentApi.Student>['
|
||||
minWidth: 120,
|
||||
slots: { default: 'sex' },
|
||||
params: {
|
||||
options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'boolean'),
|
||||
options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number'),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -345,7 +344,7 @@ export function useGridColumns(): VxeTableGridOptions<InfraStudentApi.Student>['
|
||||
rules: 'required',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
showTime: true,
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'x',
|
||||
class: '!w-full',
|
||||
@@ -368,8 +367,7 @@ export function useGridColumns(): VxeTableGridOptions<InfraStudentApi.Student>['
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'),
|
||||
buttonStyle: 'solid',
|
||||
optionType: 'button',
|
||||
isButton: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
+7
-2
@@ -5,7 +5,7 @@ import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { ElMessage, ElTabPane, ElTabs } from 'element-plus';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { createStudent, getStudent, updateStudent } from '#/api/infra/demo';
|
||||
@@ -48,7 +48,12 @@ const [Modal, modalApi] = useVbenModal({
|
||||
return;
|
||||
}
|
||||
// 校验子表单
|
||||
const studentTeacherValid = await studentTeacherFormRef.value?.validate();
|
||||
const studentContactValid = await studentContactFormRef.value?.validate();
|
||||
if (!studentContactValid) {
|
||||
subTabsName.value = 'studentContact';
|
||||
return;
|
||||
}
|
||||
const studentTeacherValid = await studentTeacherFormRef.value?.validate();
|
||||
if (!studentTeacherValid) {
|
||||
subTabsName.value = 'studentTeacher';
|
||||
return;
|
||||
|
||||
+113
-44
@@ -6,10 +6,9 @@
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { Plus } from "@vben/icons";
|
||||
import { ElButton, ElTabs, ElTabPane, ElCheckbox, ElInput, ElSelect, ElOption, ElRadioGroup, ElCheckboxGroup, ElDatePicker } from 'element-plus';
|
||||
import { ElButton, ElCheckbox, ElCheckboxGroup, ElDatePicker, ElInput, ElMessage, ElOption, ElRadio, ElRadioGroup, ElSelect, ElTabPane, ElTabs } from 'element-plus';
|
||||
import { ImageUpload, FileUpload } from "#/components/upload";
|
||||
import type { OnActionClickParams } from '#/adapter/vxe-table';
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { useStudentContactGridEditColumns } from '../data';
|
||||
import { getStudentContactListByStudentId } from '#/api/infra/demo';
|
||||
|
||||
@@ -17,22 +16,9 @@ const props = defineProps<{
|
||||
studentId?: number // 学生编号(主表的关联字段)
|
||||
}>()
|
||||
|
||||
/** 表格操作按钮的回调函数 */
|
||||
function onActionClick({
|
||||
code,
|
||||
row,
|
||||
}: OnActionClickParams<InfraStudentApi.StudentContact>) {
|
||||
switch (code) {
|
||||
case 'delete': {
|
||||
onDelete(row);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
gridOptions: {
|
||||
columns: useStudentContactGridEditColumns(onActionClick),
|
||||
columns: useStudentContactGridEditColumns(),
|
||||
border: true,
|
||||
showOverflow: true,
|
||||
autoResize: true,
|
||||
@@ -51,7 +37,7 @@ gridOptions: {
|
||||
|
||||
/** 添加学生联系人 */
|
||||
const onAdd = async () => {
|
||||
await gridApi.grid.insertAt({} as StudentApi.StudentContact, -1);
|
||||
await gridApi.grid.insertAt({} as InfraStudentApi.StudentContact, -1);
|
||||
}
|
||||
|
||||
/** 删除学生联系人 */
|
||||
@@ -59,26 +45,96 @@ const onDelete = async (row: InfraStudentApi.StudentContact) => {
|
||||
await gridApi.grid.remove(row);
|
||||
}
|
||||
|
||||
/** 提供获取表格数据的方法供父组件调用 */
|
||||
defineExpose({
|
||||
getData: (): InfraStudentApi.StudentContact[] => {
|
||||
const data = gridApi.grid.getData() as InfraStudentApi.StudentContact[];
|
||||
const removeRecords = gridApi.grid.getRemoveRecords() as InfraStudentApi.StudentContact[];
|
||||
const insertRecords = gridApi.grid.getInsertRecords() as InfraStudentApi.StudentContact[];
|
||||
return data
|
||||
.filter((row) => !removeRecords.some((removed) => removed.id === row.id))
|
||||
.concat(insertRecords.map((row: any) => ({ ...row, id: undefined })));
|
||||
},
|
||||
});
|
||||
/** 获取当前子表数据,新增行不提交表格生成的临时编号 */
|
||||
function getData(): InfraStudentApi.StudentContact[] {
|
||||
const { fullData } = gridApi.grid.getTableData();
|
||||
const inserted = new Set(gridApi.grid.getInsertRecords());
|
||||
return fullData.map((row) =>
|
||||
inserted.has(row) ? { ...row, id: undefined } : { ...row },
|
||||
);
|
||||
}
|
||||
|
||||
/** 校验子表必填字段 */
|
||||
function validate(): boolean {
|
||||
for (const [index, row] of getData().entries()) {
|
||||
if (
|
||||
row.name === null ||
|
||||
row.name === undefined ||
|
||||
(typeof row.name === 'string' && String(row.name).trim() === '') ||
|
||||
(Array.isArray(row.name) && row.name.length === 0)
|
||||
) {
|
||||
ElMessage.error(`第 ${index + 1} 行:名字不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.description === null ||
|
||||
row.description === undefined ||
|
||||
(typeof row.description === 'string' && String(row.description).trim() === '') ||
|
||||
(Array.isArray(row.description) && row.description.length === 0)
|
||||
) {
|
||||
ElMessage.error(`第 ${index + 1} 行:简介不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.birthday === null ||
|
||||
row.birthday === undefined ||
|
||||
(typeof row.birthday === 'string' && String(row.birthday).trim() === '') ||
|
||||
(Array.isArray(row.birthday) && row.birthday.length === 0)
|
||||
) {
|
||||
ElMessage.error(`第 ${index + 1} 行:出生日期不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.sex === null ||
|
||||
row.sex === undefined ||
|
||||
(typeof row.sex === 'string' && String(row.sex).trim() === '') ||
|
||||
(Array.isArray(row.sex) && row.sex.length === 0)
|
||||
) {
|
||||
ElMessage.error(`第 ${index + 1} 行:性别不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.enabled === null ||
|
||||
row.enabled === undefined ||
|
||||
(typeof row.enabled === 'string' && String(row.enabled).trim() === '') ||
|
||||
(Array.isArray(row.enabled) && row.enabled.length === 0)
|
||||
) {
|
||||
ElMessage.error(`第 ${index + 1} 行:是否有效不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.avatar === null ||
|
||||
row.avatar === undefined ||
|
||||
(typeof row.avatar === 'string' && String(row.avatar).trim() === '') ||
|
||||
(Array.isArray(row.avatar) && row.avatar.length === 0)
|
||||
) {
|
||||
ElMessage.error(`第 ${index + 1} 行:头像不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.memo === null ||
|
||||
row.memo === undefined ||
|
||||
(typeof row.memo === 'string' && String(row.memo).trim() === '') ||
|
||||
(Array.isArray(row.memo) && row.memo.length === 0)
|
||||
) {
|
||||
ElMessage.error(`第 ${index + 1} 行:备注不能为空`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
defineExpose({ getData, validate });
|
||||
|
||||
/** 监听主表的关联字段的变化,加载对应的子表数据 */
|
||||
watch(
|
||||
() => props.studentId,
|
||||
async (val) => {
|
||||
await nextTick();
|
||||
if (!val) {
|
||||
await gridApi.grid.loadData([]);
|
||||
return;
|
||||
}
|
||||
await nextTick();
|
||||
await gridApi.grid.loadData(await getStudentContactListByStudentId(props.studentId!));
|
||||
},
|
||||
{ immediate: true },
|
||||
@@ -88,13 +144,13 @@ watch(
|
||||
<template>
|
||||
<Grid class="mx-4">
|
||||
<template #name="{ row }">
|
||||
<el-input v-model="row.name" />
|
||||
<ElInput v-model="row.name" />
|
||||
</template>
|
||||
<template #description="{ row }">
|
||||
<el-input v-model="row.description" type="textarea" />
|
||||
<ElInput v-model="row.description" type="textarea" />
|
||||
</template>
|
||||
<template #birthday="{ row }">
|
||||
<el-date-picker
|
||||
<ElDatePicker
|
||||
v-model="row.birthday"
|
||||
type="datetime"
|
||||
value-format="x"
|
||||
@@ -102,16 +158,16 @@ watch(
|
||||
/>
|
||||
</template>
|
||||
<template #sex="{ row, column }">
|
||||
<el-select v-model="row.sex" class="w-full">
|
||||
<el-option v-for="option in column.params.options" :key="option.value" :value="option.value" :label="option.label" />
|
||||
</el-select>
|
||||
<ElSelect v-model="row.sex" class="w-full">
|
||||
<ElOption v-for="option in column.params.options" :key="option.value" :value="option.value" :label="option.label" />
|
||||
</ElSelect>
|
||||
</template>
|
||||
<template #enabled="{ row, column }">
|
||||
<el-radio-group v-model="row.enabled">
|
||||
<el-radio v-for="option in column.params.options" :key="option.value" :label="option.value">
|
||||
<ElRadioGroup v-model="row.enabled">
|
||||
<ElRadio v-for="option in column.params.options" :key="option.value" :value="option.value">
|
||||
{{ option.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</ElRadio>
|
||||
</ElRadioGroup>
|
||||
</template>
|
||||
<template #avatar="{ row }">
|
||||
<ImageUpload v-model="row.avatar" />
|
||||
@@ -120,12 +176,25 @@ watch(
|
||||
<FileUpload v-model="row.video" />
|
||||
</template>
|
||||
<template #memo="{ row }">
|
||||
<el-input v-model="row.memo" type="textarea" />
|
||||
<ElInput v-model="row.memo" type="textarea" />
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: $t('common.delete'),
|
||||
type: 'danger', link: true,
|
||||
icon: ACTION_ICON.DELETE,
|
||||
auth: ['infra:student:delete'],
|
||||
onClick: onDelete.bind(null, row),
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
<div class="flex justify-center -mt-4">
|
||||
<el-button :icon="Plus" type="primary" plain @click="onAdd" v-access:code="['infra:student:create']">
|
||||
<ElButton :icon="Plus" type="primary" plain @click="onAdd" v-access:code="['infra:student:create']">
|
||||
{{ $t('ui.actionTitle.create', ['学生联系人']) }}
|
||||
</el-button>
|
||||
</ElButton>
|
||||
</div>
|
||||
</template>
|
||||
+2
-1
@@ -39,10 +39,11 @@ defineExpose({
|
||||
watch(
|
||||
() => props.studentId,
|
||||
async (val) => {
|
||||
await nextTick();
|
||||
await formApi.resetForm();
|
||||
if (!val) {
|
||||
return;
|
||||
}
|
||||
await nextTick();
|
||||
await formApi.setValues(await getStudentTeacherByStudentId(props.studentId!));
|
||||
},
|
||||
{ immediate: true },
|
||||
|
||||
@@ -42,7 +42,7 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
rules: 'required',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
showTime: true,
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'x',
|
||||
class: '!w-full',
|
||||
@@ -65,8 +65,7 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'),
|
||||
buttonStyle: 'solid',
|
||||
optionType: 'button',
|
||||
isButton: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
+2
-3
@@ -42,7 +42,7 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
rules: 'required',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
showTime: true,
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'x',
|
||||
class: '!w-full',
|
||||
@@ -65,8 +65,7 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'),
|
||||
buttonStyle: 'solid',
|
||||
optionType: 'button',
|
||||
isButton: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
+12
@@ -50,6 +50,16 @@ function handleChange(file: any) {
|
||||
}
|
||||
}
|
||||
|
||||
/** 移除文件 */
|
||||
function handleRemove() {
|
||||
formApi.setFieldValue('file', undefined);
|
||||
}
|
||||
|
||||
/** 超出文件数量 */
|
||||
function handleExceed() {
|
||||
ElMessage.warning('只能选择一个文件,请先移除已选文件');
|
||||
}
|
||||
|
||||
/** 下载模板 */
|
||||
async function handleDownload() {
|
||||
const data = await importStudentTemplate();
|
||||
@@ -66,6 +76,8 @@ async function handleDownload() {
|
||||
:limit="1"
|
||||
accept=".xls,.xlsx"
|
||||
:on-change="handleChange"
|
||||
:on-remove="handleRemove"
|
||||
:on-exceed="handleExceed"
|
||||
:auto-upload="false"
|
||||
>
|
||||
<ElButton type="primary"> 选择 Excel 文件 </ElButton>
|
||||
|
||||
@@ -32,13 +32,14 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
id: 0,
|
||||
name: '顶级分类',
|
||||
});
|
||||
return handleTree(data);
|
||||
return handleTree(data, 'id', 'parentId');
|
||||
},
|
||||
labelField: 'name',
|
||||
valueField: 'id',
|
||||
childrenField: 'children',
|
||||
placeholder: '请选择上级分类',
|
||||
treeDefaultExpandAll: true,
|
||||
defaultExpandAll: true,
|
||||
checkStrictly: true,
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
|
||||
+8
-4
@@ -64,7 +64,7 @@
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页组件 -->
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
|
||||
<Pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"/>
|
||||
<!-- 对话框(添加 / 修改) -->
|
||||
<StudentContactForm ref="formRef" @success="getList" />
|
||||
@@ -79,9 +79,13 @@
|
||||
components: {
|
||||
StudentContactForm
|
||||
},
|
||||
props:[
|
||||
'studentId'
|
||||
],// 学生编号(主表的关联字段)
|
||||
props: {
|
||||
// 学生编号(主表的关联字段)
|
||||
studentId: {
|
||||
type: Number,
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
|
||||
+8
-4
@@ -64,7 +64,7 @@
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页组件 -->
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
|
||||
<Pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"/>
|
||||
<!-- 对话框(添加 / 修改) -->
|
||||
<StudentTeacherForm ref="formRef" @success="getList" />
|
||||
@@ -79,9 +79,13 @@
|
||||
components: {
|
||||
StudentTeacherForm
|
||||
},
|
||||
props:[
|
||||
'studentId'
|
||||
],// 学生编号(主表的关联字段)
|
||||
props: {
|
||||
// 学生编号(主表的关联字段)
|
||||
studentId: {
|
||||
type: Number,
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
批量删除
|
||||
</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
<RightToolbar :showSearch.sync="showSearch" @queryTable="getList"></RightToolbar>
|
||||
</el-row>
|
||||
|
||||
<el-table
|
||||
@@ -127,7 +127,7 @@
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页组件 -->
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
|
||||
<Pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"/>
|
||||
<!-- 对话框(添加 / 修改) -->
|
||||
<StudentForm ref="formRef" @success="getList" />
|
||||
|
||||
+7
-3
@@ -97,9 +97,13 @@
|
||||
FileUpload,
|
||||
Editor,
|
||||
},
|
||||
props:[
|
||||
'studentId'
|
||||
],// 学生编号(主表的关联字段)
|
||||
props: {
|
||||
// 学生编号(主表的关联字段)
|
||||
studentId: {
|
||||
type: Number,
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
|
||||
+7
-3
@@ -48,9 +48,13 @@
|
||||
import * as StudentApi from '@/api/infra/demo';
|
||||
export default {
|
||||
name: "StudentContactList",
|
||||
props:[
|
||||
'studentId'
|
||||
],// 学生编号(主表的关联字段)
|
||||
props: {
|
||||
// 学生编号(主表的关联字段)
|
||||
studentId: {
|
||||
type: Number,
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user