diff --git a/.eslintrc.js b/.eslintrc.js index f2977df6..70c91784 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -21,7 +21,7 @@ module.exports = defineConfig({ 'plugin:vue/vue3-recommended', 'plugin:@typescript-eslint/recommended', 'prettier', - 'plugin:prettier/recommended', + 'plugin:prettier/recommended', '@unocss' ], rules: { @@ -67,6 +67,7 @@ module.exports = defineConfig({ } ], 'vue/multi-word-component-names': 'off', - 'vue/no-v-html': 'off' + 'vue/no-v-html': 'off', + 'prettier/prettier': 'off' // 芋艿:默认关闭 prettier 的 ESLint 校验,因为我们使用的是 IDE 的 Prettier 插件 } }) diff --git a/src/api/infra/codegen/index.ts b/src/api/infra/codegen/index.ts index 64701efe..1b91e917 100644 --- a/src/api/infra/codegen/index.ts +++ b/src/api/infra/codegen/index.ts @@ -67,6 +67,11 @@ export type CodegenCreateListReqVO = { tableNames: string[] } +// 查询列表代码生成表定义 +export const getCodegenTableList = (dataSourceConfigId: number) => { + return request.get({ url: '/infra/codegen/table/list?dataSourceConfigId=' + dataSourceConfigId }) +} + // 查询列表代码生成表定义 export const getCodegenTablePage = (params: PageParam) => { return request.get({ url: '/infra/codegen/table/page', params }) @@ -92,11 +97,6 @@ export const syncCodegenFromDB = (id: number) => { return request.put({ url: '/infra/codegen/sync-from-db?tableId=' + id }) } -// 基于 SQL 建表语句,同步数据库的表和字段定义 -export const syncCodegenFromSQL = (id: number, sql: string) => { - return request.put({ url: '/infra/codegen/sync-from-sql?tableId=' + id + '&sql=' + sql }) -} - // 预览生成代码 export const previewCodegen = (id: number) => { return request.get({ url: '/infra/codegen/preview?tableId=' + id }) diff --git a/src/api/infra/demo/demo01/index.ts b/src/api/infra/demo/demo01/index.ts new file mode 100644 index 00000000..1a4b01ca --- /dev/null +++ b/src/api/infra/demo/demo01/index.ts @@ -0,0 +1,40 @@ +import request from '@/config/axios' + +export interface Demo01ContactVO { + id: number + name: string + sex: number + birthday: Date + description: string + avatar: string +} + +// 查询示例联系人分页 +export const getDemo01ContactPage = async (params) => { + return await request.get({ url: `/infra/demo01-contact/page`, params }) +} + +// 查询示例联系人详情 +export const getDemo01Contact = async (id: number) => { + return await request.get({ url: `/infra/demo01-contact/get?id=` + id }) +} + +// 新增示例联系人 +export const createDemo01Contact = async (data: Demo01ContactVO) => { + return await request.post({ url: `/infra/demo01-contact/create`, data }) +} + +// 修改示例联系人 +export const updateDemo01Contact = async (data: Demo01ContactVO) => { + return await request.put({ url: `/infra/demo01-contact/update`, data }) +} + +// 删除示例联系人 +export const deleteDemo01Contact = async (id: number) => { + return await request.delete({ url: `/infra/demo01-contact/delete?id=` + id }) +} + +// 导出示例联系人 Excel +export const exportDemo01Contact = async (params) => { + return await request.download({ url: `/infra/demo01-contact/export-excel`, params }) +} \ No newline at end of file diff --git a/src/api/infra/demo/demo02/index.ts b/src/api/infra/demo/demo02/index.ts new file mode 100644 index 00000000..21e45c90 --- /dev/null +++ b/src/api/infra/demo/demo02/index.ts @@ -0,0 +1,37 @@ +import request from '@/config/axios' + +export interface Demo02CategoryVO { + id: number + name: string + parentId: number +} + +// 查询示例分类列表 +export const getDemo02CategoryList = async (params) => { + return await request.get({ url: `/infra/demo02-category/list`, params }) +} + +// 查询示例分类详情 +export const getDemo02Category = async (id: number) => { + return await request.get({ url: `/infra/demo02-category/get?id=` + id }) +} + +// 新增示例分类 +export const createDemo02Category = async (data: Demo02CategoryVO) => { + return await request.post({ url: `/infra/demo02-category/create`, data }) +} + +// 修改示例分类 +export const updateDemo02Category = async (data: Demo02CategoryVO) => { + return await request.put({ url: `/infra/demo02-category/update`, data }) +} + +// 删除示例分类 +export const deleteDemo02Category = async (id: number) => { + return await request.delete({ url: `/infra/demo02-category/delete?id=` + id }) +} + +// 导出示例分类 Excel +export const exportDemo02Category = async (params) => { + return await request.download({ url: `/infra/demo02-category/export-excel`, params }) +} \ No newline at end of file diff --git a/src/api/infra/demo/demo03/erp/index.ts b/src/api/infra/demo/demo03/erp/index.ts new file mode 100644 index 00000000..d408b630 --- /dev/null +++ b/src/api/infra/demo/demo03/erp/index.ts @@ -0,0 +1,91 @@ +import request from '@/config/axios' + +export interface Demo03StudentVO { + id: number + name: string + sex: number + birthday: Date + description: string +} + +// 查询学生分页 +export const getDemo03StudentPage = async (params) => { + return await request.get({ url: `/infra/demo03-student/page`, params }) +} + +// 查询学生详情 +export const getDemo03Student = async (id: number) => { + return await request.get({ url: `/infra/demo03-student/get?id=` + id }) +} + +// 新增学生 +export const createDemo03Student = async (data: Demo03StudentVO) => { + return await request.post({ url: `/infra/demo03-student/create`, data }) +} + +// 修改学生 +export const updateDemo03Student = async (data: Demo03StudentVO) => { + return await request.put({ url: `/infra/demo03-student/update`, data }) +} + +// 删除学生 +export const deleteDemo03Student = async (id: number) => { + return await request.delete({ url: `/infra/demo03-student/delete?id=` + id }) +} + +// 导出学生 Excel +export const exportDemo03Student = async (params) => { + return await request.download({ url: `/infra/demo03-student/export-excel`, params }) +} + +// ==================== 子表(学生课程) ==================== + +// 获得学生课程分页 +export const getDemo03CoursePage = async (params) => { + return await request.get({ url: `/infra/demo03-student/demo03-course/page`, params }) +} +// 新增学生课程 +export const createDemo03Course = async (data) => { + return await request.post({ url: `/infra/demo03-student/demo03-course/create`, data }) +} + +// 修改学生课程 +export const updateDemo03Course = async (data) => { + return await request.put({ url: `/infra/demo03-student/demo03-course/update`, data }) +} + +// 删除学生课程 +export const deleteDemo03Course = async (id: number) => { + return await request.delete({ url: `/infra/demo03-student/demo03-course/delete?id=` + id }) +} + +// 获得学生课程 +export const getDemo03Course = async (id: number) => { + return await request.get({ url: `/infra/demo03-student/demo03-course/get?id=` + id }) +} + +// ==================== 子表(学生班级) ==================== + +// 获得学生班级分页 +export const getDemo03GradePage = async (params) => { + return await request.get({ url: `/infra/demo03-student/demo03-grade/page`, params }) +} +// 新增学生班级 +export const createDemo03Grade = async (data) => { + return await request.post({ url: `/infra/demo03-student/demo03-grade/create`, data }) +} + +// 修改学生班级 +export const updateDemo03Grade = async (data) => { + return await request.put({ url: `/infra/demo03-student/demo03-grade/update`, data }) +} + +// 删除学生班级 +export const deleteDemo03Grade = async (id: number) => { + return await request.delete({ url: `/infra/demo03-student/demo03-grade/delete?id=` + id }) +} + +// 获得学生班级 +export const getDemo03Grade = async (id: number) => { + return await request.get({ url: `/infra/demo03-student/demo03-grade/get?id=` + id }) +} \ No newline at end of file diff --git a/src/api/infra/demo/demo03/inner/index.ts b/src/api/infra/demo/demo03/inner/index.ts new file mode 100644 index 00000000..f15ee1dc --- /dev/null +++ b/src/api/infra/demo/demo03/inner/index.ts @@ -0,0 +1,53 @@ +import request from '@/config/axios' + +export interface Demo03StudentVO { + id: number + name: string + sex: number + birthday: Date + description: string +} + +// 查询学生分页 +export const getDemo03StudentPage = async (params) => { + return await request.get({ url: `/infra/demo03-student/page`, params }) +} + +// 查询学生详情 +export const getDemo03Student = async (id: number) => { + return await request.get({ url: `/infra/demo03-student/get?id=` + id }) +} + +// 新增学生 +export const createDemo03Student = async (data: Demo03StudentVO) => { + return await request.post({ url: `/infra/demo03-student/create`, data }) +} + +// 修改学生 +export const updateDemo03Student = async (data: Demo03StudentVO) => { + return await request.put({ url: `/infra/demo03-student/update`, data }) +} + +// 删除学生 +export const deleteDemo03Student = async (id: number) => { + return await request.delete({ url: `/infra/demo03-student/delete?id=` + id }) +} + +// 导出学生 Excel +export const exportDemo03Student = async (params) => { + return await request.download({ url: `/infra/demo03-student/export-excel`, params }) +} + +// ==================== 子表(学生课程) ==================== + +// 获得学生课程列表 +export const getDemo03CourseListByStudentId = async (studentId) => { + return await request.get({ url: `/infra/demo03-student/demo03-course/list-by-student-id?studentId=` + studentId }) +} + +// ==================== 子表(学生班级) ==================== + +// 获得学生班级 +export const getDemo03GradeByStudentId = async (studentId) => { + return await request.get({ url: `/infra/demo03-student/demo03-grade/get-by-student-id?studentId=` + studentId }) +} \ No newline at end of file diff --git a/src/api/infra/demo/demo03/normal/index.ts b/src/api/infra/demo/demo03/normal/index.ts new file mode 100644 index 00000000..f15ee1dc --- /dev/null +++ b/src/api/infra/demo/demo03/normal/index.ts @@ -0,0 +1,53 @@ +import request from '@/config/axios' + +export interface Demo03StudentVO { + id: number + name: string + sex: number + birthday: Date + description: string +} + +// 查询学生分页 +export const getDemo03StudentPage = async (params) => { + return await request.get({ url: `/infra/demo03-student/page`, params }) +} + +// 查询学生详情 +export const getDemo03Student = async (id: number) => { + return await request.get({ url: `/infra/demo03-student/get?id=` + id }) +} + +// 新增学生 +export const createDemo03Student = async (data: Demo03StudentVO) => { + return await request.post({ url: `/infra/demo03-student/create`, data }) +} + +// 修改学生 +export const updateDemo03Student = async (data: Demo03StudentVO) => { + return await request.put({ url: `/infra/demo03-student/update`, data }) +} + +// 删除学生 +export const deleteDemo03Student = async (id: number) => { + return await request.delete({ url: `/infra/demo03-student/delete?id=` + id }) +} + +// 导出学生 Excel +export const exportDemo03Student = async (params) => { + return await request.download({ url: `/infra/demo03-student/export-excel`, params }) +} + +// ==================== 子表(学生课程) ==================== + +// 获得学生课程列表 +export const getDemo03CourseListByStudentId = async (studentId) => { + return await request.get({ url: `/infra/demo03-student/demo03-course/list-by-student-id?studentId=` + studentId }) +} + +// ==================== 子表(学生班级) ==================== + +// 获得学生班级 +export const getDemo03GradeByStudentId = async (studentId) => { + return await request.get({ url: `/infra/demo03-student/demo03-grade/get-by-student-id?studentId=` + studentId }) +} \ No newline at end of file diff --git a/src/components/UploadFile/src/UploadFile.vue b/src/components/UploadFile/src/UploadFile.vue index 6895440b..c1f3e4e2 100644 --- a/src/components/UploadFile/src/UploadFile.vue +++ b/src/components/UploadFile/src/UploadFile.vue @@ -144,6 +144,8 @@ watch( } else if (isArray(props.modelValue)) { // 情况2:字符串 files.concat(props.modelValue) + } else if (props.modelValue == null) { + // 情况3:undefined 不处理 } else { throw new Error('不支持的 modelValue 类型') } diff --git a/src/views/infra/codegen/EditTable.vue b/src/views/infra/codegen/EditTable.vue index 9c4e7657..c94e0da6 100644 --- a/src/views/infra/codegen/EditTable.vue +++ b/src/views/infra/codegen/EditTable.vue @@ -8,7 +8,7 @@ <colum-info-form ref="columInfoRef" :columns="formData.columns" /> </el-tab-pane> <el-tab-pane label="生成信息" name="generateInfo"> - <generate-info-form ref="generateInfoRef" :table="formData.table" /> + <generate-info-form ref="generateInfoRef" :table="formData.table" :columns="formData.columns" /> </el-tab-pane> </el-tabs> <el-form> diff --git a/src/views/infra/codegen/components/GenerateInfoForm.vue b/src/views/infra/codegen/components/GenerateInfoForm.vue index 744edfe6..d2a01cc0 100644 --- a/src/views/infra/codegen/components/GenerateInfoForm.vue +++ b/src/views/infra/codegen/components/GenerateInfoForm.vue @@ -3,7 +3,7 @@ <el-row> <el-col :span="12"> <el-form-item label="生成模板" prop="templateType"> - <el-select v-model="formData.templateType" @change="tplSelectChange"> + <el-select v-model="formData.templateType"> <el-option v-for="dict in getIntDictOptions(DICT_TYPE.INFRA_CODEGEN_TEMPLATE_TYPE)" :key="dict.value" @@ -182,50 +182,33 @@ </el-col> </el-row> - <el-row v-show="formData.tplCategory === 'tree'"> - <h4 class="form-header">其他信息</h4> - <el-col :span="12"> - <el-form-item> - <template #label> - <span> - 树编码字段 - <el-tooltip content="树显示的编码字段名, 如:dept_id" placement="top"> - <Icon icon="ep:question-filled" /> - </el-tooltip> - </span> - </template> - <el-select v-model="formData.treeCode" placeholder="请选择"> - <el-option - v-for="(column, index) in formData.columns" - :key="index" - :label="column.columnName + ':' + column.columnComment" - :value="column.columnName" - /> - </el-select> - </el-form-item> + <!-- 树表信息 --> + <el-row v-if="formData.templateType == 2"> + <el-col :span="24"> + <h4 class="form-header">树表信息</h4> </el-col> <el-col :span="12"> - <el-form-item> + <el-form-item prop="treeParentColumnId"> <template #label> <span> - 树父编码字段 + 父编号字段 <el-tooltip content="树显示的父编码字段名, 如:parent_Id" placement="top"> <Icon icon="ep:question-filled" /> </el-tooltip> </span> </template> - <el-select v-model="formData.treeParentCode" placeholder="请选择"> + <el-select v-model="formData.treeParentColumnId" placeholder="请选择"> <el-option - v-for="(column, index) in formData.columns" + v-for="(column, index) in props.columns" :key="index" :label="column.columnName + ':' + column.columnComment" - :value="column.columnName" + :value="column.id" /> </el-select> </el-form-item> </el-col> <el-col :span="12"> - <el-form-item> + <el-form-item prop="treeNameColumnId"> <template #label> <span> 树名称字段 @@ -234,60 +217,79 @@ </el-tooltip> </span> </template> - - <el-select v-model="formData.treeName" placeholder="请选择"> + <el-select v-model="formData.treeNameColumnId" placeholder="请选择"> <el-option - v-for="(column, index) in formData.columns" + v-for="(column, index) in props.columns" :key="index" :label="column.columnName + ':' + column.columnComment" - :value="column.columnName" + :value="column.id" /> </el-select> </el-form-item> </el-col> </el-row> - <el-row v-show="formData.tplCategory === 'sub'"> - <h4 class="form-header">关联信息</h4> + + <!-- 主表信息 --> + <el-row v-if="formData.templateType == 15"> + <el-col :span="24"> + <h4 class="form-header">主表信息</h4> + </el-col> <el-col :span="12"> - <el-form-item> + <el-form-item prop="masterTableId"> <template #label> <span> - 关联子表的表名 - <el-tooltip content="关联子表的表名, 如:sys_user" placement="top"> + 关联的主表 + <el-tooltip content="关联主表(父表)的表名, 如:system_user" placement="top"> <Icon icon="ep:question-filled" /> </el-tooltip> </span> </template> - <el-select v-model="formData.subTableName" placeholder="请选择" @change="subSelectChange"> + <el-select v-model="formData.masterTableId" placeholder="请选择"> <el-option v-for="(table0, index) in tables" :key="index" :label="table0.tableName + ':' + table0.tableComment" - :value="table0.tableName" + :value="table0.id" /> </el-select> </el-form-item> </el-col> <el-col :span="12"> - <el-form-item> + <el-form-item prop="subJoinColumnId"> <template #label> <span> - 子表关联的外键名 - <el-tooltip content="子表关联的外键名, 如:user_id" placement="top"> + 子表关联的字段 + <el-tooltip content="子表关联的字段, 如:user_id" placement="top"> <Icon icon="ep:question-filled" /> </el-tooltip> </span> </template> - <el-select v-model="formData.subTableFkName" placeholder="请选择"> + <el-select v-model="formData.subJoinColumnId" placeholder="请选择"> <el-option - v-for="(column, index) in subColumns" + v-for="(column, index) in props.columns" :key="index" :label="column.columnName + ':' + column.columnComment" - :value="column.columnName" + :value="column.id" /> </el-select> </el-form-item> </el-col> + <el-col :span="12"> + <el-form-item prop="subJoinMany"> + <template #label> + <span> + 关联关系 + <el-tooltip content="主表与子表的关联关系" placement="top"> + <Icon icon="ep:question-filled" /> + </el-tooltip> + </span> + </template> + <el-radio-group v-model="formData.subJoinMany" placeholder="请选择"> + <el-radio :label="true">一对多</el-radio> + <el-radio :label="false">一对一</el-radio> + </el-radio-group> + </el-form-item> + </el-col> </el-row> </el-form> </template> @@ -305,6 +307,10 @@ const props = defineProps({ table: { type: Object as PropType<Nullable<CodegenApi.CodegenTableVO>>, default: () => null + }, + columns: { + type: Array as unknown as PropType<CodegenApi.CodegenColumnVO[]>, + default: () => null } }) @@ -319,13 +325,12 @@ const formData = ref({ classComment: '', parentMenuId: null, genPath: '', - treeCode: '', - treeParentCode: '', - treeName: '', - tplCategory: '', - subTableName: '', - subTableFkName: '', - genType: '' + genType: '', + masterTableId: undefined, + subJoinColumnId: undefined, + subJoinMany: undefined, + treeParentColumnId: undefined, + treeNameColumnId: undefined }) const rules = reactive({ @@ -336,41 +341,29 @@ const rules = reactive({ businessName: [required], businessPackage: [required], className: [required], - classComment: [required] + classComment: [required], + masterTableId: [required], + subJoinColumnId: [required], + subJoinMany: [required], + treeParentColumnId: [required], + treeNameColumnId: [required] }) -const tables = ref([]) -const subColumns = ref([]) +const tables = ref([]) // 表定义列表 const menus = ref<any[]>([]) const menuTreeProps = { label: 'name' } -/** 选择子表名触发 */ -const subSelectChange = () => { - formData.value.subTableFkName = '' -} - -/** 选择生成模板触发 */ -const tplSelectChange = (value) => { - if (value !== 1) { - // TODO 芋艿:暂时不考虑支持树形结构 - message.error( - '暂时不考虑支持【树形】和【主子表】的代码生成。原因是:导致 vm 模板过于复杂,不利于胖友二次开发' - ) - return false - } - if (value !== 'sub') { - formData.value.subTableName = '' - formData.value.subTableFkName = '' - } -} - watch( () => props.table, - (table) => { + async (table) => { if (!table) return formData.value = table as any + // 加载表列表 + if (table.dataSourceConfigId >= 0) { + tables.value = await CodegenApi.getCodegenTableList(formData.value.dataSourceConfigId) + } }, { deep: true, @@ -380,6 +373,7 @@ watch( onMounted(async () => { try { + // 加载菜单 const resp = await MenuApi.getSimpleMenusList() menus.value = handleTree(resp) } catch {} diff --git a/src/views/infra/demo/demo01/Demo01ContactForm.vue b/src/views/infra/demo/demo01/Demo01ContactForm.vue new file mode 100644 index 00000000..0452a3c0 --- /dev/null +++ b/src/views/infra/demo/demo01/Demo01ContactForm.vue @@ -0,0 +1,126 @@ +<template> + <Dialog :title="dialogTitle" v-model="dialogVisible"> + <el-form + ref="formRef" + :model="formData" + :rules="formRules" + label-width="100px" + v-loading="formLoading" + > + <el-form-item label="名字" prop="name"> + <el-input v-model="formData.name" placeholder="请输入名字" /> + </el-form-item> + <el-form-item label="性别" prop="sex"> + <el-radio-group v-model="formData.sex"> + <el-radio + v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)" + :key="dict.value" + :label="dict.value" + > + {{ dict.label }} + </el-radio> + </el-radio-group> + </el-form-item> + <el-form-item label="出生年" prop="birthday"> + <el-date-picker + v-model="formData.birthday" + type="date" + value-format="x" + placeholder="选择出生年" + /> + </el-form-item> + <el-form-item label="简介" prop="description"> + <Editor v-model="formData.description" height="150px" /> + </el-form-item> + <el-form-item label="头像" prop="avatar"> + <UploadImg v-model="formData.avatar" /> + </el-form-item> + </el-form> + <template #footer> + <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> + </template> + </Dialog> +</template> +<script setup lang="ts"> +import { getIntDictOptions, DICT_TYPE } from '@/utils/dict' +import * as Demo01ContactApi from '@/api/infra/demo/demo01' + +const { t } = useI18n() // 国际化 +const message = useMessage() // 消息弹窗 + +const dialogVisible = ref(false) // 弹窗的是否展示 +const dialogTitle = ref('') // 弹窗的标题 +const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 +const formType = ref('') // 表单的类型:create - 新增;update - 修改 +const formData = ref({ + id: undefined, + name: undefined, + sex: undefined, + birthday: undefined, + description: undefined, + avatar: undefined +}) +const formRules = reactive({ + name: [{ required: true, message: '名字不能为空', trigger: 'blur' }], + sex: [{ required: true, message: '性别不能为空', trigger: 'blur' }], + birthday: [{ required: true, message: '出生年不能为空', trigger: 'blur' }], + description: [{ required: true, message: '简介不能为空', trigger: 'blur' }] +}) +const formRef = ref() // 表单 Ref + +/** 打开弹窗 */ +const open = async (type: string, id?: number) => { + dialogVisible.value = true + dialogTitle.value = t('action.' + type) + formType.value = type + resetForm() + // 修改时,设置数据 + if (id) { + formLoading.value = true + try { + formData.value = await Demo01ContactApi.getDemo01Contact(id) + } finally { + formLoading.value = false + } + } +} +defineExpose({ open }) // 提供 open 方法,用于打开弹窗 + +/** 提交表单 */ +const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 +const submitForm = async () => { + // 校验表单 + await formRef.value.validate() + // 提交请求 + formLoading.value = true + try { + const data = formData.value as unknown as Demo01ContactApi.Demo01ContactVO + if (formType.value === 'create') { + await Demo01ContactApi.createDemo01Contact(data) + message.success(t('common.createSuccess')) + } else { + await Demo01ContactApi.updateDemo01Contact(data) + message.success(t('common.updateSuccess')) + } + dialogVisible.value = false + // 发送操作成功的事件 + emit('success') + } finally { + formLoading.value = false + } +} + +/** 重置表单 */ +const resetForm = () => { + formData.value = { + id: undefined, + name: undefined, + sex: undefined, + birthday: undefined, + description: undefined, + avatar: undefined + } + formRef.value?.resetFields() +} +</script> \ No newline at end of file diff --git a/src/views/infra/demo/demo01/index.vue b/src/views/infra/demo/demo01/index.vue new file mode 100644 index 00000000..55751e1b --- /dev/null +++ b/src/views/infra/demo/demo01/index.vue @@ -0,0 +1,217 @@ +<template> + <ContentWrap> + <!-- 搜索工作栏 --> + <el-form + class="-mb-15px" + :model="queryParams" + ref="queryFormRef" + :inline="true" + label-width="68px" + > + <el-form-item label="名字" prop="name"> + <el-input + v-model="queryParams.name" + placeholder="请输入名字" + clearable + @keyup.enter="handleQuery" + class="!w-240px" + /> + </el-form-item> + <el-form-item label="性别" prop="sex"> + <el-select + v-model="queryParams.sex" + placeholder="请选择性别" + clearable + class="!w-240px" + > + <el-option + v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item label="创建时间" prop="createTime"> + <el-date-picker + v-model="queryParams.createTime" + value-format="YYYY-MM-DD HH:mm:ss" + type="daterange" + start-placeholder="开始日期" + end-placeholder="结束日期" + :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]" + class="!w-240px" + /> + </el-form-item> + <el-form-item> + <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button> + <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> + <el-button + type="primary" + plain + @click="openForm('create')" + v-hasPermi="['infra:demo01-contact:create']" + > + <Icon icon="ep:plus" class="mr-5px" /> 新增 + </el-button> + <el-button + type="success" + plain + @click="handleExport" + :loading="exportLoading" + v-hasPermi="['infra:demo01-contact:export']" + > + <Icon icon="ep:download" class="mr-5px" /> 导出 + </el-button> + </el-form-item> + </el-form> + </ContentWrap> + + <!-- 列表 --> + <ContentWrap> + <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> + <el-table-column label="编号" align="center" prop="id" /> + <el-table-column label="名字" align="center" prop="name" /> + <el-table-column label="性别" align="center" prop="sex"> + <template #default="scope"> + <dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="scope.row.sex" /> + </template> + </el-table-column> + <el-table-column + label="出生年" + align="center" + prop="birthday" + :formatter="dateFormatter" + width="180px" + /> + <el-table-column label="简介" align="center" prop="description" /> + <el-table-column label="头像" align="center" prop="avatar" /> + <el-table-column + label="创建时间" + align="center" + prop="createTime" + :formatter="dateFormatter" + width="180px" + /> + <el-table-column label="操作" align="center"> + <template #default="scope"> + <el-button + link + type="primary" + @click="openForm('update', scope.row.id)" + v-hasPermi="['infra:demo01-contact:update']" + > + 编辑 + </el-button> + <el-button + link + type="danger" + @click="handleDelete(scope.row.id)" + v-hasPermi="['infra:demo01-contact:delete']" + > + 删除 + </el-button> + </template> + </el-table-column> + </el-table> + <!-- 分页 --> + <Pagination + :total="total" + v-model:page="queryParams.pageNo" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + </ContentWrap> + + <!-- 表单弹窗:添加/修改 --> + <Demo01ContactForm ref="formRef" @success="getList" /> +</template> + +<script setup lang="ts"> +import { getIntDictOptions, DICT_TYPE } from '@/utils/dict' +import { dateFormatter } from '@/utils/formatTime' +import download from '@/utils/download' +import * as Demo01ContactApi from '@/api/infra/demo/demo01' +import Demo01ContactForm from './Demo01ContactForm.vue' + +defineOptions({ name: 'Demo01Contact' }) + +const message = useMessage() // 消息弹窗 +const { t } = useI18n() // 国际化 + +const loading = ref(true) // 列表的加载中 +const list = ref([]) // 列表的数据 +const total = ref(0) // 列表的总页数 +const queryParams = reactive({ + pageNo: 1, + pageSize: 10, + name: null, + sex: null, + createTime: [] +}) +const queryFormRef = ref() // 搜索的表单 +const exportLoading = ref(false) // 导出的加载中 + +/** 查询列表 */ +const getList = async () => { + loading.value = true + try { + const data = await Demo01ContactApi.getDemo01ContactPage(queryParams) + list.value = data.list + total.value = data.total + } finally { + loading.value = false + } +} + +/** 搜索按钮操作 */ +const handleQuery = () => { + queryParams.pageNo = 1 + getList() +} + +/** 重置按钮操作 */ +const resetQuery = () => { + queryFormRef.value.resetFields() + handleQuery() +} + +/** 添加/修改操作 */ +const formRef = ref() +const openForm = (type: string, id?: number) => { + formRef.value.open(type, id) +} + +/** 删除按钮操作 */ +const handleDelete = async (id: number) => { + try { + // 删除的二次确认 + await message.delConfirm() + // 发起删除 + await Demo01ContactApi.deleteDemo01Contact(id) + message.success(t('common.delSuccess')) + // 刷新列表 + await getList() + } catch {} +} + +/** 导出按钮操作 */ +const handleExport = async () => { + try { + // 导出的二次确认 + await message.exportConfirm() + // 发起导出 + exportLoading.value = true + const data = await Demo01ContactApi.exportDemo01Contact(queryParams) + download.excel(data, '示例联系人.xls') + } catch { + } finally { + exportLoading.value = false + } +} + +/** 初始化 **/ +onMounted(() => { + getList() +}) +</script> \ No newline at end of file diff --git a/src/views/infra/demo/demo02/Demo02CategoryForm.vue b/src/views/infra/demo/demo02/Demo02CategoryForm.vue new file mode 100644 index 00000000..9002d5ee --- /dev/null +++ b/src/views/infra/demo/demo02/Demo02CategoryForm.vue @@ -0,0 +1,114 @@ +<template> + <Dialog :title="dialogTitle" v-model="dialogVisible"> + <el-form + ref="formRef" + :model="formData" + :rules="formRules" + label-width="100px" + v-loading="formLoading" + > + <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 + v-model="formData.parentId" + :data="demo02CategoryTree" + :props="defaultProps" + check-strictly + default-expand-all + placeholder="请选择父级编号" + /> + </el-form-item> + </el-form> + <template #footer> + <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> + </template> + </Dialog> +</template> +<script setup lang="ts"> +import * as Demo02CategoryApi from '@/api/infra/demo/demo02' +import { defaultProps, handleTree } from '@/utils/tree' + +const { t } = useI18n() // 国际化 +const message = useMessage() // 消息弹窗 + +const dialogVisible = ref(false) // 弹窗的是否展示 +const dialogTitle = ref('') // 弹窗的标题 +const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 +const formType = ref('') // 表单的类型:create - 新增;update - 修改 +const formData = ref({ + id: undefined, + name: undefined, + parentId: undefined +}) +const formRules = reactive({ + name: [{ required: true, message: '名字不能为空', trigger: 'blur' }], + parentId: [{ required: true, message: '父级编号不能为空', trigger: 'blur' }] +}) +const formRef = ref() // 表单 Ref +const demo02CategoryTree = ref() // 树形结构 + +/** 打开弹窗 */ +const open = async (type: string, id?: number) => { + dialogVisible.value = true + dialogTitle.value = t('action.' + type) + formType.value = type + resetForm() + // 修改时,设置数据 + if (id) { + formLoading.value = true + try { + formData.value = await Demo02CategoryApi.getDemo02Category(id) + } finally { + formLoading.value = false + } + } + await getDemo02CategoryTree() +} +defineExpose({ open }) // 提供 open 方法,用于打开弹窗 + +/** 提交表单 */ +const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 +const submitForm = async () => { + // 校验表单 + await formRef.value.validate() + // 提交请求 + formLoading.value = true + try { + const data = formData.value as unknown as Demo02CategoryApi.Demo02CategoryVO + if (formType.value === 'create') { + await Demo02CategoryApi.createDemo02Category(data) + message.success(t('common.createSuccess')) + } else { + await Demo02CategoryApi.updateDemo02Category(data) + message.success(t('common.updateSuccess')) + } + dialogVisible.value = false + // 发送操作成功的事件 + emit('success') + } finally { + formLoading.value = false + } +} + +/** 重置表单 */ +const resetForm = () => { + formData.value = { + id: undefined, + name: undefined, + parentId: undefined + } + formRef.value?.resetFields() +} + +/** 获得示例分类树 */ +const getDemo02CategoryTree = async () => { + demo02CategoryTree.value = [] + const data = await Demo02CategoryApi.getDemo02CategoryList() + const root: Tree = { id: 0, name: '顶级示例分类', children: [] } + root.children = handleTree(data, 'id', 'parentId') + demo02CategoryTree.value.push(root) +} +</script> \ No newline at end of file diff --git a/src/views/infra/demo/demo02/index.vue b/src/views/infra/demo/demo02/index.vue new file mode 100644 index 00000000..e46c77dc --- /dev/null +++ b/src/views/infra/demo/demo02/index.vue @@ -0,0 +1,205 @@ +<template> + <ContentWrap> + <!-- 搜索工作栏 --> + <el-form + class="-mb-15px" + :model="queryParams" + ref="queryFormRef" + :inline="true" + label-width="68px" + > + <el-form-item label="名字" prop="name"> + <el-input + v-model="queryParams.name" + placeholder="请输入名字" + clearable + @keyup.enter="handleQuery" + class="!w-240px" + /> + </el-form-item> + <el-form-item label="创建时间" prop="createTime"> + <el-date-picker + v-model="queryParams.createTime" + value-format="YYYY-MM-DD HH:mm:ss" + type="daterange" + start-placeholder="开始日期" + end-placeholder="结束日期" + :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]" + class="!w-240px" + /> + </el-form-item> + <el-form-item> + <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button> + <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> + <el-button + type="primary" + plain + @click="openForm('create')" + v-hasPermi="['infra:demo02-category:create']" + > + <Icon icon="ep:plus" class="mr-5px" /> 新增 + </el-button> + <el-button + type="success" + plain + @click="handleExport" + :loading="exportLoading" + v-hasPermi="['infra:demo02-category:export']" + > + <Icon icon="ep:download" class="mr-5px" /> 导出 + </el-button> + <el-button type="danger" plain @click="toggleExpandAll"> + <Icon icon="ep:sort" class="mr-5px" /> 展开/折叠 + </el-button> + </el-form-item> + </el-form> + </ContentWrap> + + <!-- 列表 --> + <ContentWrap> + <el-table + v-loading="loading" + :data="list" + :stripe="true" + :show-overflow-tooltip="true" + row-key="id" + :default-expand-all="isExpandAll" + v-if="refreshTable" + > + <el-table-column label="编号" align="center" prop="id" /> + <el-table-column label="名字" align="center" prop="name" /> + <el-table-column + label="创建时间" + align="center" + prop="createTime" + :formatter="dateFormatter" + width="180px" + /> + <el-table-column label="操作" align="center"> + <template #default="scope"> + <el-button + link + type="primary" + @click="openForm('update', scope.row.id)" + v-hasPermi="['infra:demo02-category:update']" + > + 编辑 + </el-button> + <el-button + link + type="danger" + @click="handleDelete(scope.row.id)" + v-hasPermi="['infra:demo02-category:delete']" + > + 删除 + </el-button> + </template> + </el-table-column> + </el-table> + <!-- 分页 --> + <Pagination + :total="total" + v-model:page="queryParams.pageNo" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + </ContentWrap> + + <!-- 表单弹窗:添加/修改 --> + <Demo02CategoryForm ref="formRef" @success="getList" /> +</template> + +<script setup lang="ts"> +import { dateFormatter } from '@/utils/formatTime' +import { handleTree } from '@/utils/tree' +import download from '@/utils/download' +import * as Demo02CategoryApi from '@/api/infra/demo/demo02' +import Demo02CategoryForm from './Demo02CategoryForm.vue' + +defineOptions({ name: 'Demo02Category' }) + +const message = useMessage() // 消息弹窗 +const { t } = useI18n() // 国际化 + +const loading = ref(true) // 列表的加载中 +const list = ref([]) // 列表的数据 +const queryParams = reactive({ + name: null, + parentId: null, + createTime: [] +}) +const queryFormRef = ref() // 搜索的表单 +const exportLoading = ref(false) // 导出的加载中 + +/** 查询列表 */ +const getList = async () => { + loading.value = true + try { + const data = await Demo02CategoryApi.getDemo02CategoryList(queryParams) + list.value = handleTree(data, 'id', 'parentId') + } finally { + loading.value = false + } +} + +/** 搜索按钮操作 */ +const handleQuery = () => { + queryParams.pageNo = 1 + getList() +} + +/** 重置按钮操作 */ +const resetQuery = () => { + queryFormRef.value.resetFields() + handleQuery() +} + +/** 添加/修改操作 */ +const formRef = ref() +const openForm = (type: string, id?: number) => { + formRef.value.open(type, id) +} + +/** 删除按钮操作 */ +const handleDelete = async (id: number) => { + try { + // 删除的二次确认 + await message.delConfirm() + // 发起删除 + await Demo02CategoryApi.deleteDemo02Category(id) + message.success(t('common.delSuccess')) + // 刷新列表 + await getList() + } catch {} +} + +/** 导出按钮操作 */ +const handleExport = async () => { + try { + // 导出的二次确认 + await message.exportConfirm() + // 发起导出 + exportLoading.value = true + const data = await Demo02CategoryApi.exportDemo02Category(queryParams) + download.excel(data, '示例分类.xls') + } catch { + } finally { + exportLoading.value = false + } +} + +/** 展开/折叠操作 */ +const isExpandAll = ref(true) // 是否展开,默认全部展开 +const refreshTable = ref(true) // 重新渲染表格状态 +const toggleExpandAll = async () => { + refreshTable.value = false + isExpandAll.value = !isExpandAll.value + await nextTick() + refreshTable.value = true +} + +/** 初始化 **/ +onMounted(() => { + getList() +}) +</script> \ No newline at end of file diff --git a/src/views/infra/demo/demo03/erp/Demo03StudentForm.vue b/src/views/infra/demo/demo03/erp/Demo03StudentForm.vue new file mode 100644 index 00000000..29f1370d --- /dev/null +++ b/src/views/infra/demo/demo03/erp/Demo03StudentForm.vue @@ -0,0 +1,121 @@ +<template> + <Dialog :title="dialogTitle" v-model="dialogVisible"> + <el-form + ref="formRef" + :model="formData" + :rules="formRules" + label-width="100px" + v-loading="formLoading" + > + <el-form-item label="名字" prop="name"> + <el-input v-model="formData.name" placeholder="请输入名字" /> + </el-form-item> + <el-form-item label="性别" prop="sex"> + <el-radio-group v-model="formData.sex"> + <el-radio + v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)" + :key="dict.value" + :label="dict.value" + > + {{ dict.label }} + </el-radio> + </el-radio-group> + </el-form-item> + <el-form-item label="出生日期" prop="birthday"> + <el-date-picker + v-model="formData.birthday" + type="date" + value-format="x" + placeholder="选择出生日期" + /> + </el-form-item> + <el-form-item label="简介" prop="description"> + <Editor v-model="formData.description" height="150px" /> + </el-form-item> + </el-form> + <template #footer> + <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> + </template> + </Dialog> +</template> +<script setup lang="ts"> +import { getIntDictOptions, DICT_TYPE } from '@/utils/dict' +import * as Demo03StudentApi from '@/api/infra/demo/demo03/erp' + +const { t } = useI18n() // 国际化 +const message = useMessage() // 消息弹窗 + +const dialogVisible = ref(false) // 弹窗的是否展示 +const dialogTitle = ref('') // 弹窗的标题 +const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 +const formType = ref('') // 表单的类型:create - 新增;update - 修改 +const formData = ref({ + id: undefined, + name: undefined, + sex: undefined, + birthday: undefined, + description: undefined +}) +const formRules = reactive({ + name: [{ required: true, message: '名字不能为空', trigger: 'blur' }], + sex: [{ required: true, message: '性别不能为空', trigger: 'blur' }], + birthday: [{ required: true, message: '出生日期不能为空', trigger: 'blur' }], + description: [{ required: true, message: '简介不能为空', trigger: 'blur' }] +}) +const formRef = ref() // 表单 Ref + +/** 打开弹窗 */ +const open = async (type: string, id?: number) => { + dialogVisible.value = true + dialogTitle.value = t('action.' + type) + formType.value = type + resetForm() + // 修改时,设置数据 + if (id) { + formLoading.value = true + try { + formData.value = await Demo03StudentApi.getDemo03Student(id) + } finally { + formLoading.value = false + } + } +} +defineExpose({ open }) // 提供 open 方法,用于打开弹窗 + +/** 提交表单 */ +const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 +const submitForm = async () => { + // 校验表单 + await formRef.value.validate() + // 提交请求 + formLoading.value = true + try { + const data = formData.value as unknown as Demo03StudentApi.Demo03StudentVO + if (formType.value === 'create') { + await Demo03StudentApi.createDemo03Student(data) + message.success(t('common.createSuccess')) + } else { + await Demo03StudentApi.updateDemo03Student(data) + message.success(t('common.updateSuccess')) + } + dialogVisible.value = false + // 发送操作成功的事件 + emit('success') + } finally { + formLoading.value = false + } +} + +/** 重置表单 */ +const resetForm = () => { + formData.value = { + id: undefined, + name: undefined, + sex: undefined, + birthday: undefined, + description: undefined + } + formRef.value?.resetFields() +} +</script> \ No newline at end of file diff --git a/src/views/infra/demo/demo03/erp/components/Demo03CourseForm.vue b/src/views/infra/demo/demo03/erp/components/Demo03CourseForm.vue new file mode 100644 index 00000000..de1c06de --- /dev/null +++ b/src/views/infra/demo/demo03/erp/components/Demo03CourseForm.vue @@ -0,0 +1,99 @@ +<template> + <Dialog :title="dialogTitle" v-model="dialogVisible"> + <el-form + ref="formRef" + :model="formData" + :rules="formRules" + label-width="100px" + v-loading="formLoading" + > + <el-form-item label="名字" prop="name"> + <el-input v-model="formData.name" placeholder="请输入名字" /> + </el-form-item> + <el-form-item label="分数" prop="score"> + <el-input v-model="formData.score" placeholder="请输入分数" /> + </el-form-item> + </el-form> + <template #footer> + <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> + </template> + </Dialog> +</template> +<script setup lang="ts"> +import * as Demo03StudentApi from '@/api/infra/demo/demo03/erp' + +const { t } = useI18n() // 国际化 +const message = useMessage() // 消息弹窗 + +const dialogVisible = ref(false) // 弹窗的是否展示 +const dialogTitle = ref('') // 弹窗的标题 +const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 +const formType = ref('') // 表单的类型:create - 新增;update - 修改 +const formData = ref({ + id: undefined, + studentId: undefined, + name: undefined, + score: undefined +}) +const formRules = reactive({ + studentId: [{ required: true, message: '学生编号不能为空', trigger: 'blur' }], + name: [{ required: true, message: '名字不能为空', trigger: 'blur' }], + score: [{ required: true, message: '分数不能为空', trigger: 'blur' }] +}) +const formRef = ref() // 表单 Ref + +/** 打开弹窗 */ +const open = async (type: string, id?: number, studentId: number) => { + dialogVisible.value = true + dialogTitle.value = t('action.' + type) + formType.value = type + resetForm() + formData.value.studentId = studentId + // 修改时,设置数据 + if (id) { + formLoading.value = true + try { + formData.value = await Demo03StudentApi.getDemo03Course(id) + } finally { + formLoading.value = false + } + } +} +defineExpose({ open }) // 提供 open 方法,用于打开弹窗 + +/** 提交表单 */ +const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 +const submitForm = async () => { + // 校验表单 + await formRef.value.validate() + // 提交请求 + formLoading.value = true + try { + const data = formData.value + if (formType.value === 'create') { + await Demo03StudentApi.createDemo03Course(data) + message.success(t('common.createSuccess')) + } else { + await Demo03StudentApi.updateDemo03Course(data) + message.success(t('common.updateSuccess')) + } + dialogVisible.value = false + // 发送操作成功的事件 + emit('success') + } finally { + formLoading.value = false + } +} + +/** 重置表单 */ +const resetForm = () => { + formData.value = { + id: undefined, + studentId: undefined, + name: undefined, + score: undefined + } + formRef.value?.resetFields() +} +</script> \ No newline at end of file diff --git a/src/views/infra/demo/demo03/erp/components/Demo03CourseList.vue b/src/views/infra/demo/demo03/erp/components/Demo03CourseList.vue new file mode 100644 index 00000000..7e06ee64 --- /dev/null +++ b/src/views/infra/demo/demo03/erp/components/Demo03CourseList.vue @@ -0,0 +1,126 @@ +<template> + <!-- 列表 --> + <ContentWrap> + <el-button + type="primary" + plain + @click="openForm('create')" + v-hasPermi="['infra:demo03-student:create']" + > + <Icon icon="ep:plus" class="mr-5px" /> 新增 + </el-button> + <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> + <el-table-column label="编号" align="center" prop="id" /> + <el-table-column label="名字" align="center" prop="name" /> + <el-table-column label="分数" align="center" prop="score" /> + <el-table-column + label="创建时间" + align="center" + prop="createTime" + :formatter="dateFormatter" + width="180px" + /> + <el-table-column label="操作" align="center"> + <template #default="scope"> + <el-button + link + type="primary" + @click="openForm('update', scope.row.id)" + v-hasPermi="['infra:demo03-student:update']" + > + 编辑 + </el-button> + <el-button + link + type="danger" + @click="handleDelete(scope.row.id)" + v-hasPermi="['infra:demo03-student:delete']" + > + 删除 + </el-button> + </template> + </el-table-column> + </el-table> + <!-- 分页 --> + <Pagination + :total="total" + v-model:page="queryParams.pageNo" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + </ContentWrap> + <!-- 表单弹窗:添加/修改 --> + <Demo03CourseForm ref="formRef" @success="getList" /> +</template> + +<script setup lang="ts"> +import { dateFormatter } from '@/utils/formatTime' +import * as Demo03StudentApi from '@/api/infra/demo/demo03/erp' +import Demo03CourseForm from './Demo03CourseForm.vue' + +const { t } = useI18n() // 国际化 +const message = useMessage() // 消息弹窗 + +const props = defineProps<{ + studentId: undefined // 学生编号(主表的关联字段) +}>() +const loading = ref(false) // 列表的加载中 +const list = ref([]) // 列表的数据 +const total = ref(0) // 列表的总页数 +const queryParams = reactive({ + pageNo: 1, + pageSize: 10, + studentId: undefined +}) + +/** 监听主表的关联字段的变化,加载对应的子表数据 */ +watch( + () => props.studentId, + (val) => { + queryParams.studentId = val + handleQuery() + }, + { immediate: false } +) + +/** 查询列表 */ +const getList = async () => { + loading.value = true + try { + const data = await Demo03StudentApi.getDemo03CoursePage(queryParams) + list.value = data.list + total.value = data.total + } finally { + loading.value = false + } +} + +/** 搜索按钮操作 */ +const handleQuery = () => { + queryParams.pageNo = 1 + getList() +} + +/** 添加/修改操作 */ +const formRef = ref() +const openForm = (type: string, id?: number) => { + if (!props.studentId) { + message.error('请选择一个学生') + return + } + formRef.value.open(type, id, props.studentId) +} + +/** 删除按钮操作 */ +const handleDelete = async (id: number) => { + try { + // 删除的二次确认 + await message.delConfirm() + // 发起删除 + await Demo03StudentApi.deleteDemo03Course(id) + message.success(t('common.delSuccess')) + // 刷新列表 + await getList() + } catch {} +} +</script> \ No newline at end of file diff --git a/src/views/infra/demo/demo03/erp/components/Demo03GradeForm.vue b/src/views/infra/demo/demo03/erp/components/Demo03GradeForm.vue new file mode 100644 index 00000000..abba0032 --- /dev/null +++ b/src/views/infra/demo/demo03/erp/components/Demo03GradeForm.vue @@ -0,0 +1,99 @@ +<template> + <Dialog :title="dialogTitle" v-model="dialogVisible"> + <el-form + ref="formRef" + :model="formData" + :rules="formRules" + label-width="100px" + v-loading="formLoading" + > + <el-form-item label="名字" prop="name"> + <el-input v-model="formData.name" placeholder="请输入名字" /> + </el-form-item> + <el-form-item label="班主任" prop="teacher"> + <el-input v-model="formData.teacher" placeholder="请输入班主任" /> + </el-form-item> + </el-form> + <template #footer> + <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> + </template> + </Dialog> +</template> +<script setup lang="ts"> +import * as Demo03StudentApi from '@/api/infra/demo/demo03/erp' + +const { t } = useI18n() // 国际化 +const message = useMessage() // 消息弹窗 + +const dialogVisible = ref(false) // 弹窗的是否展示 +const dialogTitle = ref('') // 弹窗的标题 +const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 +const formType = ref('') // 表单的类型:create - 新增;update - 修改 +const formData = ref({ + id: undefined, + studentId: undefined, + name: undefined, + teacher: undefined +}) +const formRules = reactive({ + studentId: [{ required: true, message: '学生编号不能为空', trigger: 'blur' }], + name: [{ required: true, message: '名字不能为空', trigger: 'blur' }], + teacher: [{ required: true, message: '班主任不能为空', trigger: 'blur' }] +}) +const formRef = ref() // 表单 Ref + +/** 打开弹窗 */ +const open = async (type: string, id?: number, studentId: number) => { + dialogVisible.value = true + dialogTitle.value = t('action.' + type) + formType.value = type + resetForm() + formData.value.studentId = studentId + // 修改时,设置数据 + if (id) { + formLoading.value = true + try { + formData.value = await Demo03StudentApi.getDemo03Grade(id) + } finally { + formLoading.value = false + } + } +} +defineExpose({ open }) // 提供 open 方法,用于打开弹窗 + +/** 提交表单 */ +const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 +const submitForm = async () => { + // 校验表单 + await formRef.value.validate() + // 提交请求 + formLoading.value = true + try { + const data = formData.value + if (formType.value === 'create') { + await Demo03StudentApi.createDemo03Grade(data) + message.success(t('common.createSuccess')) + } else { + await Demo03StudentApi.updateDemo03Grade(data) + message.success(t('common.updateSuccess')) + } + dialogVisible.value = false + // 发送操作成功的事件 + emit('success') + } finally { + formLoading.value = false + } +} + +/** 重置表单 */ +const resetForm = () => { + formData.value = { + id: undefined, + studentId: undefined, + name: undefined, + teacher: undefined + } + formRef.value?.resetFields() +} +</script> \ No newline at end of file diff --git a/src/views/infra/demo/demo03/erp/components/Demo03GradeList.vue b/src/views/infra/demo/demo03/erp/components/Demo03GradeList.vue new file mode 100644 index 00000000..b12f1889 --- /dev/null +++ b/src/views/infra/demo/demo03/erp/components/Demo03GradeList.vue @@ -0,0 +1,126 @@ +<template> + <!-- 列表 --> + <ContentWrap> + <el-button + type="primary" + plain + @click="openForm('create')" + v-hasPermi="['infra:demo03-student:create']" + > + <Icon icon="ep:plus" class="mr-5px" /> 新增 + </el-button> + <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> + <el-table-column label="编号" align="center" prop="id" /> + <el-table-column label="名字" align="center" prop="name" /> + <el-table-column label="班主任" align="center" prop="teacher" /> + <el-table-column + label="创建时间" + align="center" + prop="createTime" + :formatter="dateFormatter" + width="180px" + /> + <el-table-column label="操作" align="center"> + <template #default="scope"> + <el-button + link + type="primary" + @click="openForm('update', scope.row.id)" + v-hasPermi="['infra:demo03-student:update']" + > + 编辑 + </el-button> + <el-button + link + type="danger" + @click="handleDelete(scope.row.id)" + v-hasPermi="['infra:demo03-student:delete']" + > + 删除 + </el-button> + </template> + </el-table-column> + </el-table> + <!-- 分页 --> + <Pagination + :total="total" + v-model:page="queryParams.pageNo" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + </ContentWrap> + <!-- 表单弹窗:添加/修改 --> + <Demo03GradeForm ref="formRef" @success="getList" /> +</template> + +<script setup lang="ts"> +import { dateFormatter } from '@/utils/formatTime' +import * as Demo03StudentApi from '@/api/infra/demo/demo03/erp' +import Demo03GradeForm from './Demo03GradeForm.vue' + +const { t } = useI18n() // 国际化 +const message = useMessage() // 消息弹窗 + +const props = defineProps<{ + studentId: undefined // 学生编号(主表的关联字段) +}>() +const loading = ref(false) // 列表的加载中 +const list = ref([]) // 列表的数据 +const total = ref(0) // 列表的总页数 +const queryParams = reactive({ + pageNo: 1, + pageSize: 10, + studentId: undefined +}) + +/** 监听主表的关联字段的变化,加载对应的子表数据 */ +watch( + () => props.studentId, + (val) => { + queryParams.studentId = val + handleQuery() + }, + { immediate: false } +) + +/** 查询列表 */ +const getList = async () => { + loading.value = true + try { + const data = await Demo03StudentApi.getDemo03GradePage(queryParams) + list.value = data.list + total.value = data.total + } finally { + loading.value = false + } +} + +/** 搜索按钮操作 */ +const handleQuery = () => { + queryParams.pageNo = 1 + getList() +} + +/** 添加/修改操作 */ +const formRef = ref() +const openForm = (type: string, id?: number) => { + if (!props.studentId) { + message.error('请选择一个学生') + return + } + formRef.value.open(type, id, props.studentId) +} + +/** 删除按钮操作 */ +const handleDelete = async (id: number) => { + try { + // 删除的二次确认 + await message.delConfirm() + // 发起删除 + await Demo03StudentApi.deleteDemo03Grade(id) + message.success(t('common.delSuccess')) + // 刷新列表 + await getList() + } catch {} +} +</script> \ No newline at end of file diff --git a/src/views/infra/demo/demo03/erp/index.vue b/src/views/infra/demo/demo03/erp/index.vue new file mode 100644 index 00000000..8fdc7b42 --- /dev/null +++ b/src/views/infra/demo/demo03/erp/index.vue @@ -0,0 +1,243 @@ +<template> + <ContentWrap> + <!-- 搜索工作栏 --> + <el-form + class="-mb-15px" + :model="queryParams" + ref="queryFormRef" + :inline="true" + label-width="68px" + > + <el-form-item label="名字" prop="name"> + <el-input + v-model="queryParams.name" + placeholder="请输入名字" + clearable + @keyup.enter="handleQuery" + class="!w-240px" + /> + </el-form-item> + <el-form-item label="性别" prop="sex"> + <el-select + v-model="queryParams.sex" + placeholder="请选择性别" + clearable + class="!w-240px" + > + <el-option + v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item label="创建时间" prop="createTime"> + <el-date-picker + v-model="queryParams.createTime" + value-format="YYYY-MM-DD HH:mm:ss" + type="daterange" + start-placeholder="开始日期" + end-placeholder="结束日期" + :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]" + class="!w-240px" + /> + </el-form-item> + <el-form-item> + <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button> + <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> + <el-button + type="primary" + plain + @click="openForm('create')" + v-hasPermi="['infra:demo03-student:create']" + > + <Icon icon="ep:plus" class="mr-5px" /> 新增 + </el-button> + <el-button + type="success" + plain + @click="handleExport" + :loading="exportLoading" + v-hasPermi="['infra:demo03-student:export']" + > + <Icon icon="ep:download" class="mr-5px" /> 导出 + </el-button> + </el-form-item> + </el-form> + </ContentWrap> + + <!-- 列表 --> + <ContentWrap> + <el-table + v-loading="loading" + :data="list" + :stripe="true" + :show-overflow-tooltip="true" + highlight-current-row + @current-change="handleCurrentChange" + > + <el-table-column label="编号" align="center" prop="id" /> + <el-table-column label="名字" align="center" prop="name" /> + <el-table-column label="性别" align="center" prop="sex"> + <template #default="scope"> + <dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="scope.row.sex" /> + </template> + </el-table-column> + <el-table-column + label="出生日期" + align="center" + prop="birthday" + :formatter="dateFormatter" + width="180px" + /> + <el-table-column label="简介" align="center" prop="description" /> + <el-table-column + label="创建时间" + align="center" + prop="createTime" + :formatter="dateFormatter" + width="180px" + /> + <el-table-column label="操作" align="center"> + <template #default="scope"> + <el-button + link + type="primary" + @click="openForm('update', scope.row.id)" + v-hasPermi="['infra:demo03-student:update']" + > + 编辑 + </el-button> + <el-button + link + type="danger" + @click="handleDelete(scope.row.id)" + v-hasPermi="['infra:demo03-student:delete']" + > + 删除 + </el-button> + </template> + </el-table-column> + </el-table> + <!-- 分页 --> + <Pagination + :total="total" + v-model:page="queryParams.pageNo" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + </ContentWrap> + + <!-- 表单弹窗:添加/修改 --> + <Demo03StudentForm ref="formRef" @success="getList" /> + <!-- 子表的列表 --> + <ContentWrap> + <el-tabs model-value="demo03Course"> + <el-tab-pane label="学生课程" name="demo03Course"> + <Demo03CourseList :student-id="currentRow.id" /> + </el-tab-pane> + <el-tab-pane label="学生班级" name="demo03Grade"> + <Demo03GradeList :student-id="currentRow.id" /> + </el-tab-pane> + </el-tabs> + </ContentWrap> +</template> + +<script setup lang="ts"> +import { getIntDictOptions, DICT_TYPE } from '@/utils/dict' +import { dateFormatter } from '@/utils/formatTime' +import download from '@/utils/download' +import * as Demo03StudentApi from '@/api/infra/demo/demo03/erp' +import Demo03StudentForm from './Demo03StudentForm.vue' +import Demo03CourseList from './components/Demo03CourseList.vue' +import Demo03GradeList from './components/Demo03GradeList.vue' + +defineOptions({ name: 'Demo03Student' }) + +const message = useMessage() // 消息弹窗 +const { t } = useI18n() // 国际化 + +const loading = ref(true) // 列表的加载中 +const list = ref([]) // 列表的数据 +const total = ref(0) // 列表的总页数 +const queryParams = reactive({ + pageNo: 1, + pageSize: 10, + name: null, + sex: null, + description: null, + createTime: [] +}) +const queryFormRef = ref() // 搜索的表单 +const exportLoading = ref(false) // 导出的加载中 + +/** 查询列表 */ +const getList = async () => { + loading.value = true + try { + const data = await Demo03StudentApi.getDemo03StudentPage(queryParams) + list.value = data.list + total.value = data.total + } finally { + loading.value = false + } +} + +/** 搜索按钮操作 */ +const handleQuery = () => { + queryParams.pageNo = 1 + getList() +} + +/** 重置按钮操作 */ +const resetQuery = () => { + queryFormRef.value.resetFields() + handleQuery() +} + +/** 添加/修改操作 */ +const formRef = ref() +const openForm = (type: string, id?: number) => { + formRef.value.open(type, id) +} + +/** 删除按钮操作 */ +const handleDelete = async (id: number) => { + try { + // 删除的二次确认 + await message.delConfirm() + // 发起删除 + await Demo03StudentApi.deleteDemo03Student(id) + message.success(t('common.delSuccess')) + // 刷新列表 + await getList() + } catch {} +} + +/** 导出按钮操作 */ +const handleExport = async () => { + try { + // 导出的二次确认 + await message.exportConfirm() + // 发起导出 + exportLoading.value = true + const data = await Demo03StudentApi.exportDemo03Student(queryParams) + download.excel(data, '学生.xls') + } catch { + } finally { + exportLoading.value = false + } +} + +/** 选中行操作 */ +const currentRow = ref({}) // 选中行 +const handleCurrentChange = (row) => { + currentRow.value = row +} + +/** 初始化 **/ +onMounted(() => { + getList() +}) +</script> \ No newline at end of file diff --git a/src/views/infra/demo/demo03/inner/Demo03StudentForm.vue b/src/views/infra/demo/demo03/inner/Demo03StudentForm.vue new file mode 100644 index 00000000..fe9327b9 --- /dev/null +++ b/src/views/infra/demo/demo03/inner/Demo03StudentForm.vue @@ -0,0 +1,153 @@ +<template> + <Dialog :title="dialogTitle" v-model="dialogVisible"> + <el-form + ref="formRef" + :model="formData" + :rules="formRules" + label-width="100px" + v-loading="formLoading" + > + <el-form-item label="名字" prop="name"> + <el-input v-model="formData.name" placeholder="请输入名字" /> + </el-form-item> + <el-form-item label="性别" prop="sex"> + <el-radio-group v-model="formData.sex"> + <el-radio + v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)" + :key="dict.value" + :label="dict.value" + > + {{ dict.label }} + </el-radio> + </el-radio-group> + </el-form-item> + <el-form-item label="出生日期" prop="birthday"> + <el-date-picker + v-model="formData.birthday" + type="date" + value-format="x" + placeholder="选择出生日期" + /> + </el-form-item> + <el-form-item label="简介" prop="description"> + <Editor v-model="formData.description" height="150px" /> + </el-form-item> + </el-form> + <!-- 子表的表单 --> + <el-tabs v-model="subTabsName"> + <el-tab-pane label="学生课程" name="demo03Course"> + <Demo03CourseForm ref="demo03CourseFormRef" :student-id="formData.id" /> + </el-tab-pane> + <el-tab-pane label="学生班级" name="demo03Grade"> + <Demo03GradeForm ref="demo03GradeFormRef" :student-id="formData.id" /> + </el-tab-pane> + </el-tabs> + <template #footer> + <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> + </template> + </Dialog> +</template> +<script setup lang="ts"> +import { getIntDictOptions, DICT_TYPE } from '@/utils/dict' +import * as Demo03StudentApi from '@/api/infra/demo/demo03/inner' +import Demo03CourseForm from './components/Demo03CourseForm.vue' +import Demo03GradeForm from './components/Demo03GradeForm.vue' + +const { t } = useI18n() // 国际化 +const message = useMessage() // 消息弹窗 + +const dialogVisible = ref(false) // 弹窗的是否展示 +const dialogTitle = ref('') // 弹窗的标题 +const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 +const formType = ref('') // 表单的类型:create - 新增;update - 修改 +const formData = ref({ + id: undefined, + name: undefined, + sex: undefined, + birthday: undefined, + description: undefined +}) +const formRules = reactive({ + name: [{ required: true, message: '名字不能为空', trigger: 'blur' }], + sex: [{ required: true, message: '性别不能为空', trigger: 'blur' }], + birthday: [{ required: true, message: '出生日期不能为空', trigger: 'blur' }], + description: [{ required: true, message: '简介不能为空', trigger: 'blur' }] +}) +const formRef = ref() // 表单 Ref + +/** 子表的表单 */ +const subTabsName = ref('demo03Course') +const demo03CourseFormRef = ref() +const demo03GradeFormRef = ref() + +/** 打开弹窗 */ +const open = async (type: string, id?: number) => { + dialogVisible.value = true + dialogTitle.value = t('action.' + type) + formType.value = type + resetForm() + // 修改时,设置数据 + if (id) { + formLoading.value = true + try { + formData.value = await Demo03StudentApi.getDemo03Student(id) + } finally { + formLoading.value = false + } + } +} +defineExpose({ open }) // 提供 open 方法,用于打开弹窗 + +/** 提交表单 */ +const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 +const submitForm = async () => { + // 校验表单 + await formRef.value.validate() + // 校验子表单 + try { + await demo03CourseFormRef.value.validate() + } catch (e) { + subTabsName.value = 'demo03Course' + return + } + try { + await demo03GradeFormRef.value.validate() + } catch (e) { + subTabsName.value = 'demo03Grade' + return + } + // 提交请求 + formLoading.value = true + try { + const data = formData.value as unknown as Demo03StudentApi.Demo03StudentVO + // 拼接子表的数据 + data.demo03Courses = demo03CourseFormRef.value.getData() + data.demo03Grade = demo03GradeFormRef.value.getData() + if (formType.value === 'create') { + await Demo03StudentApi.createDemo03Student(data) + message.success(t('common.createSuccess')) + } else { + await Demo03StudentApi.updateDemo03Student(data) + message.success(t('common.updateSuccess')) + } + dialogVisible.value = false + // 发送操作成功的事件 + emit('success') + } finally { + formLoading.value = false + } +} + +/** 重置表单 */ +const resetForm = () => { + formData.value = { + id: undefined, + name: undefined, + sex: undefined, + birthday: undefined, + description: undefined + } + formRef.value?.resetFields() +} +</script> \ No newline at end of file diff --git a/src/views/infra/demo/demo03/inner/components/Demo03CourseForm.vue b/src/views/infra/demo/demo03/inner/components/Demo03CourseForm.vue new file mode 100644 index 00000000..87057513 --- /dev/null +++ b/src/views/infra/demo/demo03/inner/components/Demo03CourseForm.vue @@ -0,0 +1,100 @@ +<template> + <el-form + ref="formRef" + :model="formData" + :rules="formRules" + v-loading="formLoading" + label-width="0px" + :inline-message="true" + > + <el-table :data="formData" class="-mt-10px"> + <el-table-column label="序号" type="index" width="100" /> + <el-table-column label="名字" min-width="150"> + <template #default="{ row, $index }"> + <el-form-item :prop="`${$index}.name`" :rules="formRules.name" class="mb-0px!"> + <el-input v-model="row.name" placeholder="请输入名字" /> + </el-form-item> + </template> + </el-table-column> + <el-table-column label="分数" min-width="150"> + <template #default="{ row, $index }"> + <el-form-item :prop="`${$index}.score`" :rules="formRules.score" class="mb-0px!"> + <el-input v-model="row.score" placeholder="请输入分数" /> + </el-form-item> + </template> + </el-table-column> + <el-table-column align="center" fixed="right" label="操作" width="60"> + <template #default="{ $index }"> + <el-button @click="handleDelete($index)" link>—</el-button> + </template> + </el-table-column> + </el-table> + </el-form> + <el-row justify="center" class="mt-3"> + <el-button @click="handleAdd" round>+ 添加学生课程</el-button> + </el-row> +</template> +<script setup lang="ts"> +import * as Demo03StudentApi from '@/api/infra/demo/demo03/inner' + +const props = defineProps<{ + studentId: undefined // 学生编号(主表的关联字段) +}>() +const formLoading = ref(false) // 表单的加载中 +const formData = ref([]) +const formRules = reactive({ + studentId: [{ required: true, message: '学生编号不能为空', trigger: 'blur' }], + name: [{ required: true, message: '名字不能为空', trigger: 'blur' }], + score: [{ required: true, message: '分数不能为空', trigger: 'blur' }] +}) +const formRef = ref() // 表单 Ref + +/** 监听主表的关联字段的变化,加载对应的子表数据 */ +watch( + () => props.studentId, + async (val) => { + // 1. 重置表单 + formData.value = [] + // 2. val 非空,则加载数据 + if (!val) { + return; + } + try { + formLoading.value = true + formData.value = await Demo03StudentApi.getDemo03CourseListByStudentId(val) + } finally { + formLoading.value = false + } + }, + { immediate: true } +) + +/** 新增按钮操作 */ +const handleAdd = () => { + const row = { + id: undefined, + studentId: undefined, + name: undefined, + score: undefined + } + row.studentId = props.studentId + formData.value.push(row) +} + +/** 删除按钮操作 */ +const handleDelete = (index) => { + formData.value.splice(index, 1) +} + +/** 表单校验 */ +const validate = () => { + return formRef.value.validate() +} + +/** 表单值 */ +const getData = () => { + return formData.value +} + +defineExpose({ validate, getData }) +</script> \ No newline at end of file diff --git a/src/views/infra/demo/demo03/inner/components/Demo03CourseList.vue b/src/views/infra/demo/demo03/inner/components/Demo03CourseList.vue new file mode 100644 index 00000000..d912fc5d --- /dev/null +++ b/src/views/infra/demo/demo03/inner/components/Demo03CourseList.vue @@ -0,0 +1,51 @@ +<template> + <!-- 列表 --> + <ContentWrap> + <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> + <el-table-column label="编号" align="center" prop="id" /> + <el-table-column label="名字" align="center" prop="name" /> + <el-table-column label="分数" align="center" prop="score" /> + <el-table-column + label="创建时间" + align="center" + prop="createTime" + :formatter="dateFormatter" + width="180px" + /> + </el-table> + </ContentWrap> +</template> +<script setup lang="ts"> +import { dateFormatter } from '@/utils/formatTime' +import * as Demo03StudentApi from '@/api/infra/demo/demo03/inner' + +const { t } = useI18n() // 国际化 +const message = useMessage() // 消息弹窗 + +const props = defineProps<{ + studentId: undefined // 学生编号(主表的关联字段) +}>() +const loading = ref(false) // 列表的加载中 +const list = ref([]) // 列表的数据 + +/** 查询列表 */ +const getList = async () => { + loading.value = true + try { + list.value = await Demo03StudentApi.getDemo03CourseListByStudentId(props.studentId) + } finally { + loading.value = false + } +} + +/** 搜索按钮操作 */ +const handleQuery = () => { + queryParams.pageNo = 1 + getList() +} + +/** 初始化 **/ +onMounted(() => { + getList() +}) +</script> \ No newline at end of file diff --git a/src/views/infra/demo/demo03/inner/components/Demo03GradeForm.vue b/src/views/infra/demo/demo03/inner/components/Demo03GradeForm.vue new file mode 100644 index 00000000..e0eeb192 --- /dev/null +++ b/src/views/infra/demo/demo03/inner/components/Demo03GradeForm.vue @@ -0,0 +1,72 @@ +<template> + <el-form + ref="formRef" + :model="formData" + :rules="formRules" + label-width="100px" + v-loading="formLoading" + > + <el-form-item label="名字" prop="name"> + <el-input v-model="formData.name" placeholder="请输入名字" /> + </el-form-item> + <el-form-item label="班主任" prop="teacher"> + <el-input v-model="formData.teacher" placeholder="请输入班主任" /> + </el-form-item> + </el-form> +</template> +<script setup lang="ts"> +import * as Demo03StudentApi from '@/api/infra/demo/demo03/inner' + +const props = defineProps<{ + studentId: undefined // 学生编号(主表的关联字段) +}>() +const formLoading = ref(false) // 表单的加载中 +const formData = ref([]) +const formRules = reactive({ + studentId: [{ required: true, message: '学生编号不能为空', trigger: 'blur' }], + name: [{ required: true, message: '名字不能为空', trigger: 'blur' }], + teacher: [{ required: true, message: '班主任不能为空', trigger: 'blur' }] +}) +const formRef = ref() // 表单 Ref + +/** 监听主表的关联字段的变化,加载对应的子表数据 */ +watch( + () => props.studentId, + async (val) => { + // 1. 重置表单 + formData.value = { + id: undefined, + studentId: undefined, + name: undefined, + teacher: undefined, + } + // 2. val 非空,则加载数据 + if (!val) { + return; + } + try { + formLoading.value = true + const data = await Demo03StudentApi.getDemo03GradeByStudentId(val) + if (!data) { + return + } + formData.value = data + } finally { + formLoading.value = false + } + }, + { immediate: true } +) + +/** 表单校验 */ +const validate = () => { + return formRef.value.validate() +} + +/** 表单值 */ +const getData = () => { + return formData.value +} + +defineExpose({ validate, getData }) +</script> \ No newline at end of file diff --git a/src/views/infra/demo/demo03/inner/components/Demo03GradeList.vue b/src/views/infra/demo/demo03/inner/components/Demo03GradeList.vue new file mode 100644 index 00000000..96905414 --- /dev/null +++ b/src/views/infra/demo/demo03/inner/components/Demo03GradeList.vue @@ -0,0 +1,55 @@ +<template> + <!-- 列表 --> + <ContentWrap> + <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> + <el-table-column label="编号" align="center" prop="id" /> + <el-table-column label="名字" align="center" prop="name" /> + <el-table-column label="班主任" align="center" prop="teacher" /> + <el-table-column + label="创建时间" + align="center" + prop="createTime" + :formatter="dateFormatter" + width="180px" + /> + </el-table> + </ContentWrap> +</template> +<script setup lang="ts"> +import { dateFormatter } from '@/utils/formatTime' +import * as Demo03StudentApi from '@/api/infra/demo/demo03/inner' + +const { t } = useI18n() // 国际化 +const message = useMessage() // 消息弹窗 + +const props = defineProps<{ + studentId: undefined // 学生编号(主表的关联字段) +}>() +const loading = ref(false) // 列表的加载中 +const list = ref([]) // 列表的数据 + +/** 查询列表 */ +const getList = async () => { + loading.value = true + try { + const data = await Demo03StudentApi.getDemo03GradeByStudentId(props.studentId) + if (!data) { + return + } + list.value.push(data) + } finally { + loading.value = false + } +} + +/** 搜索按钮操作 */ +const handleQuery = () => { + queryParams.pageNo = 1 + getList() +} + +/** 初始化 **/ +onMounted(() => { + getList() +}) +</script> \ No newline at end of file diff --git a/src/views/infra/demo/demo03/inner/index.vue b/src/views/infra/demo/demo03/inner/index.vue new file mode 100644 index 00000000..4ce6037d --- /dev/null +++ b/src/views/infra/demo/demo03/inner/index.vue @@ -0,0 +1,232 @@ +<template> + <ContentWrap> + <!-- 搜索工作栏 --> + <el-form + class="-mb-15px" + :model="queryParams" + ref="queryFormRef" + :inline="true" + label-width="68px" + > + <el-form-item label="名字" prop="name"> + <el-input + v-model="queryParams.name" + placeholder="请输入名字" + clearable + @keyup.enter="handleQuery" + class="!w-240px" + /> + </el-form-item> + <el-form-item label="性别" prop="sex"> + <el-select + v-model="queryParams.sex" + placeholder="请选择性别" + clearable + class="!w-240px" + > + <el-option + v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item label="创建时间" prop="createTime"> + <el-date-picker + v-model="queryParams.createTime" + value-format="YYYY-MM-DD HH:mm:ss" + type="daterange" + start-placeholder="开始日期" + end-placeholder="结束日期" + :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]" + class="!w-240px" + /> + </el-form-item> + <el-form-item> + <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button> + <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> + <el-button + type="primary" + plain + @click="openForm('create')" + v-hasPermi="['infra:demo03-student:create']" + > + <Icon icon="ep:plus" class="mr-5px" /> 新增 + </el-button> + <el-button + type="success" + plain + @click="handleExport" + :loading="exportLoading" + v-hasPermi="['infra:demo03-student:export']" + > + <Icon icon="ep:download" class="mr-5px" /> 导出 + </el-button> + </el-form-item> + </el-form> + </ContentWrap> + + <!-- 列表 --> + <ContentWrap> + <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> + <!-- 子表的列表 --> + <el-table-column type="expand"> + <template #default="scope"> + <el-tabs model-value="demo03Course"> + <el-tab-pane label="学生课程" name="demo03Course"> + <Demo03CourseList :student-id="scope.row.id" /> + </el-tab-pane> + <el-tab-pane label="学生班级" name="demo03Grade"> + <Demo03GradeList :student-id="scope.row.id" /> + </el-tab-pane> + </el-tabs> + </template> + </el-table-column> + <el-table-column label="编号" align="center" prop="id" /> + <el-table-column label="名字" align="center" prop="name" /> + <el-table-column label="性别" align="center" prop="sex"> + <template #default="scope"> + <dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="scope.row.sex" /> + </template> + </el-table-column> + <el-table-column + label="出生日期" + align="center" + prop="birthday" + :formatter="dateFormatter" + width="180px" + /> + <el-table-column label="简介" align="center" prop="description" /> + <el-table-column + label="创建时间" + align="center" + prop="createTime" + :formatter="dateFormatter" + width="180px" + /> + <el-table-column label="操作" align="center"> + <template #default="scope"> + <el-button + link + type="primary" + @click="openForm('update', scope.row.id)" + v-hasPermi="['infra:demo03-student:update']" + > + 编辑 + </el-button> + <el-button + link + type="danger" + @click="handleDelete(scope.row.id)" + v-hasPermi="['infra:demo03-student:delete']" + > + 删除 + </el-button> + </template> + </el-table-column> + </el-table> + <!-- 分页 --> + <Pagination + :total="total" + v-model:page="queryParams.pageNo" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + </ContentWrap> + + <!-- 表单弹窗:添加/修改 --> + <Demo03StudentForm ref="formRef" @success="getList" /> +</template> + +<script setup lang="ts"> +import { getIntDictOptions, DICT_TYPE } from '@/utils/dict' +import { dateFormatter } from '@/utils/formatTime' +import download from '@/utils/download' +import * as Demo03StudentApi from '@/api/infra/demo/demo03/inner' +import Demo03StudentForm from './Demo03StudentForm.vue' +import Demo03CourseList from './components/Demo03CourseList.vue' +import Demo03GradeList from './components/Demo03GradeList.vue' + +defineOptions({ name: 'Demo03Student' }) + +const message = useMessage() // 消息弹窗 +const { t } = useI18n() // 国际化 + +const loading = ref(true) // 列表的加载中 +const list = ref([]) // 列表的数据 +const total = ref(0) // 列表的总页数 +const queryParams = reactive({ + pageNo: 1, + pageSize: 10, + name: null, + sex: null, + description: null, + createTime: [] +}) +const queryFormRef = ref() // 搜索的表单 +const exportLoading = ref(false) // 导出的加载中 + +/** 查询列表 */ +const getList = async () => { + loading.value = true + try { + const data = await Demo03StudentApi.getDemo03StudentPage(queryParams) + list.value = data.list + total.value = data.total + } finally { + loading.value = false + } +} + +/** 搜索按钮操作 */ +const handleQuery = () => { + queryParams.pageNo = 1 + getList() +} + +/** 重置按钮操作 */ +const resetQuery = () => { + queryFormRef.value.resetFields() + handleQuery() +} + +/** 添加/修改操作 */ +const formRef = ref() +const openForm = (type: string, id?: number) => { + formRef.value.open(type, id) +} + +/** 删除按钮操作 */ +const handleDelete = async (id: number) => { + try { + // 删除的二次确认 + await message.delConfirm() + // 发起删除 + await Demo03StudentApi.deleteDemo03Student(id) + message.success(t('common.delSuccess')) + // 刷新列表 + await getList() + } catch {} +} + +/** 导出按钮操作 */ +const handleExport = async () => { + try { + // 导出的二次确认 + await message.exportConfirm() + // 发起导出 + exportLoading.value = true + const data = await Demo03StudentApi.exportDemo03Student(queryParams) + download.excel(data, '学生.xls') + } catch { + } finally { + exportLoading.value = false + } +} + +/** 初始化 **/ +onMounted(() => { + getList() +}) +</script> \ No newline at end of file diff --git a/src/views/infra/demo/demo03/normal/Demo03StudentForm.vue b/src/views/infra/demo/demo03/normal/Demo03StudentForm.vue new file mode 100644 index 00000000..00508228 --- /dev/null +++ b/src/views/infra/demo/demo03/normal/Demo03StudentForm.vue @@ -0,0 +1,153 @@ +<template> + <Dialog :title="dialogTitle" v-model="dialogVisible"> + <el-form + ref="formRef" + :model="formData" + :rules="formRules" + label-width="100px" + v-loading="formLoading" + > + <el-form-item label="名字" prop="name"> + <el-input v-model="formData.name" placeholder="请输入名字" /> + </el-form-item> + <el-form-item label="性别" prop="sex"> + <el-radio-group v-model="formData.sex"> + <el-radio + v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)" + :key="dict.value" + :label="dict.value" + > + {{ dict.label }} + </el-radio> + </el-radio-group> + </el-form-item> + <el-form-item label="出生日期" prop="birthday"> + <el-date-picker + v-model="formData.birthday" + type="date" + value-format="x" + placeholder="选择出生日期" + /> + </el-form-item> + <el-form-item label="简介" prop="description"> + <Editor v-model="formData.description" height="150px" /> + </el-form-item> + </el-form> + <!-- 子表的表单 --> + <el-tabs v-model="subTabsName"> + <el-tab-pane label="学生课程" name="demo03Course"> + <Demo03CourseForm ref="demo03CourseFormRef" :student-id="formData.id" /> + </el-tab-pane> + <el-tab-pane label="学生班级" name="demo03Grade"> + <Demo03GradeForm ref="demo03GradeFormRef" :student-id="formData.id" /> + </el-tab-pane> + </el-tabs> + <template #footer> + <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> + </template> + </Dialog> +</template> +<script setup lang="ts"> +import { getIntDictOptions, DICT_TYPE } from '@/utils/dict' +import * as Demo03StudentApi from '@/api/infra/demo/demo03/normal' +import Demo03CourseForm from './components/Demo03CourseForm.vue' +import Demo03GradeForm from './components/Demo03GradeForm.vue' + +const { t } = useI18n() // 国际化 +const message = useMessage() // 消息弹窗 + +const dialogVisible = ref(false) // 弹窗的是否展示 +const dialogTitle = ref('') // 弹窗的标题 +const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 +const formType = ref('') // 表单的类型:create - 新增;update - 修改 +const formData = ref({ + id: undefined, + name: undefined, + sex: undefined, + birthday: undefined, + description: undefined +}) +const formRules = reactive({ + name: [{ required: true, message: '名字不能为空', trigger: 'blur' }], + sex: [{ required: true, message: '性别不能为空', trigger: 'blur' }], + birthday: [{ required: true, message: '出生日期不能为空', trigger: 'blur' }], + description: [{ required: true, message: '简介不能为空', trigger: 'blur' }] +}) +const formRef = ref() // 表单 Ref + +/** 子表的表单 */ +const subTabsName = ref('demo03Course') +const demo03CourseFormRef = ref() +const demo03GradeFormRef = ref() + +/** 打开弹窗 */ +const open = async (type: string, id?: number) => { + dialogVisible.value = true + dialogTitle.value = t('action.' + type) + formType.value = type + resetForm() + // 修改时,设置数据 + if (id) { + formLoading.value = true + try { + formData.value = await Demo03StudentApi.getDemo03Student(id) + } finally { + formLoading.value = false + } + } +} +defineExpose({ open }) // 提供 open 方法,用于打开弹窗 + +/** 提交表单 */ +const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 +const submitForm = async () => { + // 校验表单 + await formRef.value.validate() + // 校验子表单 + try { + await demo03CourseFormRef.value.validate() + } catch (e) { + subTabsName.value = 'demo03Course' + return + } + try { + await demo03GradeFormRef.value.validate() + } catch (e) { + subTabsName.value = 'demo03Grade' + return + } + // 提交请求 + formLoading.value = true + try { + const data = formData.value as unknown as Demo03StudentApi.Demo03StudentVO + // 拼接子表的数据 + data.demo03Courses = demo03CourseFormRef.value.getData() + data.demo03Grade = demo03GradeFormRef.value.getData() + if (formType.value === 'create') { + await Demo03StudentApi.createDemo03Student(data) + message.success(t('common.createSuccess')) + } else { + await Demo03StudentApi.updateDemo03Student(data) + message.success(t('common.updateSuccess')) + } + dialogVisible.value = false + // 发送操作成功的事件 + emit('success') + } finally { + formLoading.value = false + } +} + +/** 重置表单 */ +const resetForm = () => { + formData.value = { + id: undefined, + name: undefined, + sex: undefined, + birthday: undefined, + description: undefined + } + formRef.value?.resetFields() +} +</script> \ No newline at end of file diff --git a/src/views/infra/demo/demo03/normal/components/Demo03CourseForm.vue b/src/views/infra/demo/demo03/normal/components/Demo03CourseForm.vue new file mode 100644 index 00000000..b6f58572 --- /dev/null +++ b/src/views/infra/demo/demo03/normal/components/Demo03CourseForm.vue @@ -0,0 +1,100 @@ +<template> + <el-form + ref="formRef" + :model="formData" + :rules="formRules" + v-loading="formLoading" + label-width="0px" + :inline-message="true" + > + <el-table :data="formData" class="-mt-10px"> + <el-table-column label="序号" type="index" width="100" /> + <el-table-column label="名字" min-width="150"> + <template #default="{ row, $index }"> + <el-form-item :prop="`${$index}.name`" :rules="formRules.name" class="mb-0px!"> + <el-input v-model="row.name" placeholder="请输入名字" /> + </el-form-item> + </template> + </el-table-column> + <el-table-column label="分数" min-width="150"> + <template #default="{ row, $index }"> + <el-form-item :prop="`${$index}.score`" :rules="formRules.score" class="mb-0px!"> + <el-input v-model="row.score" placeholder="请输入分数" /> + </el-form-item> + </template> + </el-table-column> + <el-table-column align="center" fixed="right" label="操作" width="60"> + <template #default="{ $index }"> + <el-button @click="handleDelete($index)" link>—</el-button> + </template> + </el-table-column> + </el-table> + </el-form> + <el-row justify="center" class="mt-3"> + <el-button @click="handleAdd" round>+ 添加学生课程</el-button> + </el-row> +</template> +<script setup lang="ts"> +import * as Demo03StudentApi from '@/api/infra/demo/demo03/normal' + +const props = defineProps<{ + studentId: undefined // 学生编号(主表的关联字段) +}>() +const formLoading = ref(false) // 表单的加载中 +const formData = ref([]) +const formRules = reactive({ + studentId: [{ required: true, message: '学生编号不能为空', trigger: 'blur' }], + name: [{ required: true, message: '名字不能为空', trigger: 'blur' }], + score: [{ required: true, message: '分数不能为空', trigger: 'blur' }] +}) +const formRef = ref() // 表单 Ref + +/** 监听主表的关联字段的变化,加载对应的子表数据 */ +watch( + () => props.studentId, + async (val) => { + // 1. 重置表单 + formData.value = [] + // 2. val 非空,则加载数据 + if (!val) { + return; + } + try { + formLoading.value = true + formData.value = await Demo03StudentApi.getDemo03CourseListByStudentId(val) + } finally { + formLoading.value = false + } + }, + { immediate: true } +) + +/** 新增按钮操作 */ +const handleAdd = () => { + const row = { + id: undefined, + studentId: undefined, + name: undefined, + score: undefined + } + row.studentId = props.studentId + formData.value.push(row) +} + +/** 删除按钮操作 */ +const handleDelete = (index) => { + formData.value.splice(index, 1) +} + +/** 表单校验 */ +const validate = () => { + return formRef.value.validate() +} + +/** 表单值 */ +const getData = () => { + return formData.value +} + +defineExpose({ validate, getData }) +</script> \ No newline at end of file diff --git a/src/views/infra/demo/demo03/normal/components/Demo03GradeForm.vue b/src/views/infra/demo/demo03/normal/components/Demo03GradeForm.vue new file mode 100644 index 00000000..12653b6c --- /dev/null +++ b/src/views/infra/demo/demo03/normal/components/Demo03GradeForm.vue @@ -0,0 +1,72 @@ +<template> + <el-form + ref="formRef" + :model="formData" + :rules="formRules" + label-width="100px" + v-loading="formLoading" + > + <el-form-item label="名字" prop="name"> + <el-input v-model="formData.name" placeholder="请输入名字" /> + </el-form-item> + <el-form-item label="班主任" prop="teacher"> + <el-input v-model="formData.teacher" placeholder="请输入班主任" /> + </el-form-item> + </el-form> +</template> +<script setup lang="ts"> +import * as Demo03StudentApi from '@/api/infra/demo/demo03/normal' + +const props = defineProps<{ + studentId: undefined // 学生编号(主表的关联字段) +}>() +const formLoading = ref(false) // 表单的加载中 +const formData = ref([]) +const formRules = reactive({ + studentId: [{ required: true, message: '学生编号不能为空', trigger: 'blur' }], + name: [{ required: true, message: '名字不能为空', trigger: 'blur' }], + teacher: [{ required: true, message: '班主任不能为空', trigger: 'blur' }] +}) +const formRef = ref() // 表单 Ref + +/** 监听主表的关联字段的变化,加载对应的子表数据 */ +watch( + () => props.studentId, + async (val) => { + // 1. 重置表单 + formData.value = { + id: undefined, + studentId: undefined, + name: undefined, + teacher: undefined, + } + // 2. val 非空,则加载数据 + if (!val) { + return; + } + try { + formLoading.value = true + const data = await Demo03StudentApi.getDemo03GradeByStudentId(val) + if (!data) { + return + } + formData.value = data + } finally { + formLoading.value = false + } + }, + { immediate: true } +) + +/** 表单校验 */ +const validate = () => { + return formRef.value.validate() +} + +/** 表单值 */ +const getData = () => { + return formData.value +} + +defineExpose({ validate, getData }) +</script> \ No newline at end of file diff --git a/src/views/infra/demo/demo03/normal/index.vue b/src/views/infra/demo/demo03/normal/index.vue new file mode 100644 index 00000000..52029107 --- /dev/null +++ b/src/views/infra/demo/demo03/normal/index.vue @@ -0,0 +1,217 @@ +<template> + <ContentWrap> + <!-- 搜索工作栏 --> + <el-form + class="-mb-15px" + :model="queryParams" + ref="queryFormRef" + :inline="true" + label-width="68px" + > + <el-form-item label="名字" prop="name"> + <el-input + v-model="queryParams.name" + placeholder="请输入名字" + clearable + @keyup.enter="handleQuery" + class="!w-240px" + /> + </el-form-item> + <el-form-item label="性别" prop="sex"> + <el-select + v-model="queryParams.sex" + placeholder="请选择性别" + clearable + class="!w-240px" + > + <el-option + v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item label="创建时间" prop="createTime"> + <el-date-picker + v-model="queryParams.createTime" + value-format="YYYY-MM-DD HH:mm:ss" + type="daterange" + start-placeholder="开始日期" + end-placeholder="结束日期" + :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]" + class="!w-240px" + /> + </el-form-item> + <el-form-item> + <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button> + <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> + <el-button + type="primary" + plain + @click="openForm('create')" + v-hasPermi="['infra:demo03-student:create']" + > + <Icon icon="ep:plus" class="mr-5px" /> 新增 + </el-button> + <el-button + type="success" + plain + @click="handleExport" + :loading="exportLoading" + v-hasPermi="['infra:demo03-student:export']" + > + <Icon icon="ep:download" class="mr-5px" /> 导出 + </el-button> + </el-form-item> + </el-form> + </ContentWrap> + + <!-- 列表 --> + <ContentWrap> + <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> + <el-table-column label="编号" align="center" prop="id" /> + <el-table-column label="名字" align="center" prop="name" /> + <el-table-column label="性别" align="center" prop="sex"> + <template #default="scope"> + <dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="scope.row.sex" /> + </template> + </el-table-column> + <el-table-column + label="出生日期" + align="center" + prop="birthday" + :formatter="dateFormatter" + width="180px" + /> + <el-table-column label="简介" align="center" prop="description" /> + <el-table-column + label="创建时间" + align="center" + prop="createTime" + :formatter="dateFormatter" + width="180px" + /> + <el-table-column label="操作" align="center"> + <template #default="scope"> + <el-button + link + type="primary" + @click="openForm('update', scope.row.id)" + v-hasPermi="['infra:demo03-student:update']" + > + 编辑 + </el-button> + <el-button + link + type="danger" + @click="handleDelete(scope.row.id)" + v-hasPermi="['infra:demo03-student:delete']" + > + 删除 + </el-button> + </template> + </el-table-column> + </el-table> + <!-- 分页 --> + <Pagination + :total="total" + v-model:page="queryParams.pageNo" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + </ContentWrap> + + <!-- 表单弹窗:添加/修改 --> + <Demo03StudentForm ref="formRef" @success="getList" /> +</template> + +<script setup lang="ts"> +import { getIntDictOptions, DICT_TYPE } from '@/utils/dict' +import { dateFormatter } from '@/utils/formatTime' +import download from '@/utils/download' +import * as Demo03StudentApi from '@/api/infra/demo/demo03/normal' +import Demo03StudentForm from './Demo03StudentForm.vue' + +defineOptions({ name: 'Demo03Student' }) + +const message = useMessage() // 消息弹窗 +const { t } = useI18n() // 国际化 + +const loading = ref(true) // 列表的加载中 +const list = ref([]) // 列表的数据 +const total = ref(0) // 列表的总页数 +const queryParams = reactive({ + pageNo: 1, + pageSize: 10, + name: null, + sex: null, + description: null, + createTime: [] +}) +const queryFormRef = ref() // 搜索的表单 +const exportLoading = ref(false) // 导出的加载中 + +/** 查询列表 */ +const getList = async () => { + loading.value = true + try { + const data = await Demo03StudentApi.getDemo03StudentPage(queryParams) + list.value = data.list + total.value = data.total + } finally { + loading.value = false + } +} + +/** 搜索按钮操作 */ +const handleQuery = () => { + queryParams.pageNo = 1 + getList() +} + +/** 重置按钮操作 */ +const resetQuery = () => { + queryFormRef.value.resetFields() + handleQuery() +} + +/** 添加/修改操作 */ +const formRef = ref() +const openForm = (type: string, id?: number) => { + formRef.value.open(type, id) +} + +/** 删除按钮操作 */ +const handleDelete = async (id: number) => { + try { + // 删除的二次确认 + await message.delConfirm() + // 发起删除 + await Demo03StudentApi.deleteDemo03Student(id) + message.success(t('common.delSuccess')) + // 刷新列表 + await getList() + } catch {} +} + +/** 导出按钮操作 */ +const handleExport = async () => { + try { + // 导出的二次确认 + await message.exportConfirm() + // 发起导出 + exportLoading.value = true + const data = await Demo03StudentApi.exportDemo03Student(queryParams) + download.excel(data, '学生.xls') + } catch { + } finally { + exportLoading.value = false + } +} + +/** 初始化 **/ +onMounted(() => { + getList() +}) +</script> \ No newline at end of file diff --git a/src/views/infra/testDemo/index.vue b/src/views/infra/testDemo/index.vue deleted file mode 100644 index ca6a5b07..00000000 --- a/src/views/infra/testDemo/index.vue +++ /dev/null @@ -1,4 +0,0 @@ -<template> - <div>index</div> -</template> -<script lang="ts" setup></script>