From fc7d1f2ee13a6c0fe46f4d27f9b6d6ee6b9a76ee Mon Sep 17 00:00:00 2001 From: GoCo Date: Sat, 26 Jul 2025 18:51:25 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E8=AF=BE=E7=A8=8B=E9=9A=BE=E5=BA=A6=E3=80=81=E8=AF=BE=E7=A8=8B?= =?UTF-8?q?=E6=89=80=E5=B1=9E=E4=B8=93=E9=A2=98=E5=AD=97=E6=AE=B5&?= =?UTF-8?q?=E8=AF=BE=E7=A8=8B=E5=88=97=E8=A1=A8=E6=9F=A5=E8=AF=A2=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E6=9D=A1=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 5 +- server/api/api/lesson/v1/lesson.go | 3 +- .../api/lesson/lesson_v1_lesson_list.go | 16 ++++- server/internal/dao/internal/lesson.go | 4 ++ .../internal/dao/internal/lesson_section.go | 2 +- server/internal/logic/sys/lesson.go | 5 ++ server/internal/model/do/lesson.go | 2 + server/internal/model/do/lesson_section.go | 2 +- server/internal/model/entity/lesson.go | 2 + .../internal/model/entity/lesson_section.go | 2 +- server/internal/model/input/sysin/lesson.go | 42 ++++++++++--- web/src/views/lesson/edit.vue | 13 +++- web/src/views/lesson/model.ts | 60 ++++++++++++++++++- web/src/views/lesson/view.vue | 12 ++++ 14 files changed, 150 insertions(+), 20 deletions(-) diff --git a/Dockerfile b/Dockerfile index 413eee8..9e1bbe9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,8 +11,6 @@ RUN npm install -g pnpm # 设置工作目录 WORKDIR /app -# 复制后端 -COPY ./server ./server # 复制前端 COPY ./web ./web @@ -21,6 +19,9 @@ WORKDIR /app/web RUN echo "y" |pnpm install RUN pnpm run build +# 复制后端 +COPY ./server ./server + # 构建后端项目 WORKDIR /app/server diff --git a/server/api/api/lesson/v1/lesson.go b/server/api/api/lesson/v1/lesson.go index 31fa269..e937db9 100644 --- a/server/api/api/lesson/v1/lesson.go +++ b/server/api/api/lesson/v1/lesson.go @@ -10,7 +10,8 @@ import ( type LessonListReq struct { g.Meta `path:"/lesson/list" method:"get" tags:"课程" summary:"获取课程列表"` CategoryId int64 `json:"categoryId" dc:"分类ID"` - Tag string `json:"tag" dc:"标签"` + Difficuty int `json:"difficuty" dc:"难度"` + Subject string `json:"subject" dc:"专题"` } // 获取课程列表响应 diff --git a/server/internal/controller/api/lesson/lesson_v1_lesson_list.go b/server/internal/controller/api/lesson/lesson_v1_lesson_list.go index 5ee79b4..985cfe4 100644 --- a/server/internal/controller/api/lesson/lesson_v1_lesson_list.go +++ b/server/internal/controller/api/lesson/lesson_v1_lesson_list.go @@ -9,23 +9,33 @@ import ( "github.com/gogf/gf/v2/errors/gcode" "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/frame/g" ) func (c *ControllerV1) LessonList(ctx context.Context, req *v1.LessonListReq) (res *v1.LessonListRes, err error) { // 构建查询条件 query := dao.Lesson.Ctx(ctx) - // 按分类ID过滤 - if req.CategoryId > 0 { + // 分类过滤 + if req.CategoryId != -1 && req.CategoryId > 0 { query = query.Where("category_id = ?", req.CategoryId) } - // TODO 按标签过滤 + // 难度过滤 + if req.Difficuty != -1 { + query = query.Where("difficulty = ?", req.Difficuty) + } + + // 专题过滤(subject为all时不过滤,否则用JSON_CONTAINS) + if req.Subject != "" && req.Subject != "all" { + query = query.WhereLike("subject", "%"+req.Subject+"%") + } // 查询课程列表 var list []*entity.Lesson err = query.Scan(&list) if err != nil { + g.Log().Error(ctx, err) return nil, gerror.NewCode(gcode.CodeDbOperationError, "数据库错误") } diff --git a/server/internal/dao/internal/lesson.go b/server/internal/dao/internal/lesson.go index 2218805..c864fa1 100755 --- a/server/internal/dao/internal/lesson.go +++ b/server/internal/dao/internal/lesson.go @@ -41,6 +41,8 @@ type LessonColumns struct { CreatedTime string // 创建时间 UpdatedBy string // 更新人 UpdatedTime string // 更新时间 + Difficulty string // 课程难度 + Subject string // 所属专题 } // lessonColumns holds the columns for the table hg_lesson. @@ -65,6 +67,8 @@ var lessonColumns = LessonColumns{ CreatedTime: "created_time", UpdatedBy: "updated_by", UpdatedTime: "updated_time", + Difficulty: "difficulty", + Subject: "subject", } // NewLessonDao creates and returns a new DAO object for table data access. diff --git a/server/internal/dao/internal/lesson_section.go b/server/internal/dao/internal/lesson_section.go index 992af2f..28b9842 100755 --- a/server/internal/dao/internal/lesson_section.go +++ b/server/internal/dao/internal/lesson_section.go @@ -27,7 +27,7 @@ type LessonSectionColumns struct { Name string // 章节名 SortOrder string // 排序号 ParentId string // 父章节id - Level string // 章节层级;1=章,2=节 + Level string // 章节层级 Revision string // 乐观锁 CreatedBy string // 创建人 CreatedTime string // 创建时间 diff --git a/server/internal/logic/sys/lesson.go b/server/internal/logic/sys/lesson.go index 791e460..faa5b67 100644 --- a/server/internal/logic/sys/lesson.go +++ b/server/internal/logic/sys/lesson.go @@ -52,6 +52,11 @@ func (s *sSysLesson) List(ctx context.Context, in *sysin.LessonListInp) (list [] // 字段过滤 mod = mod.Fields(sysin.LessonListModel{}) + // 查询id + if in.Id > 0 { + mod = mod.Where(dao.Lesson.Columns().Id, in.Id) + } + // 查询课程名 if in.Name != "" { mod = mod.WhereLike(dao.Lesson.Columns().Name, in.Name) diff --git a/server/internal/model/do/lesson.go b/server/internal/model/do/lesson.go index 1a61d0e..adc1ae7 100755 --- a/server/internal/model/do/lesson.go +++ b/server/internal/model/do/lesson.go @@ -32,4 +32,6 @@ type Lesson struct { CreatedTime *gtime.Time // 创建时间 UpdatedBy interface{} // 更新人 UpdatedTime *gtime.Time // 更新时间 + Difficulty interface{} // 课程难度 + Subject interface{} // 所属专题 } diff --git a/server/internal/model/do/lesson_section.go b/server/internal/model/do/lesson_section.go index 65405ce..3c2ee7d 100755 --- a/server/internal/model/do/lesson_section.go +++ b/server/internal/model/do/lesson_section.go @@ -18,7 +18,7 @@ type LessonSection struct { Name interface{} // 章节名 SortOrder interface{} // 排序号 ParentId interface{} // 父章节id - Level interface{} // 章节层级;1=章,2=节 + Level interface{} // 章节层级 Revision interface{} // 乐观锁 CreatedBy interface{} // 创建人 CreatedTime *gtime.Time // 创建时间 diff --git a/server/internal/model/entity/lesson.go b/server/internal/model/entity/lesson.go index f7aa139..e54676e 100755 --- a/server/internal/model/entity/lesson.go +++ b/server/internal/model/entity/lesson.go @@ -30,4 +30,6 @@ type Lesson struct { CreatedTime *gtime.Time `json:"createdTime" orm:"created_time" description:"创建时间"` UpdatedBy int64 `json:"updatedBy" orm:"updated_by" description:"更新人"` UpdatedTime *gtime.Time `json:"updatedTime" orm:"updated_time" description:"更新时间"` + Difficulty int `json:"difficulty" orm:"difficulty" description:"课程难度"` + Subject string `json:"subject" orm:"subject" description:"所属专题"` } diff --git a/server/internal/model/entity/lesson_section.go b/server/internal/model/entity/lesson_section.go index b02f97f..8552bfc 100755 --- a/server/internal/model/entity/lesson_section.go +++ b/server/internal/model/entity/lesson_section.go @@ -16,7 +16,7 @@ type LessonSection struct { Name string `json:"name" orm:"name" description:"章节名"` SortOrder int `json:"sortOrder" orm:"sort_order" description:"排序号"` ParentId int `json:"parentId" orm:"parent_id" description:"父章节id"` - Level int `json:"level" orm:"level" description:"章节层级;1=章,2=节"` + Level int `json:"level" orm:"level" description:"章节层级"` Revision int `json:"revision" orm:"revision" description:"乐观锁"` CreatedBy int64 `json:"createdBy" orm:"created_by" description:"创建人"` CreatedTime *gtime.Time `json:"createdTime" orm:"created_time" description:"创建时间"` diff --git a/server/internal/model/input/sysin/lesson.go b/server/internal/model/input/sysin/lesson.go index 49f3c2d..e5a6b6b 100644 --- a/server/internal/model/input/sysin/lesson.go +++ b/server/internal/model/input/sysin/lesson.go @@ -11,6 +11,7 @@ import ( "hotgo/internal/model/entity" "hotgo/internal/model/input/form" + "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/gtime" ) @@ -18,7 +19,7 @@ import ( type LessonUpdateFields struct { Name string `json:"name" dc:"课程名"` Cover string `json:"cover" dc:"封面图"` - CategoryId int `json:"categoryId" dc:"所属分类"` + CategoryId int64 `json:"categoryId" dc:"所属分类"` Video string `json:"video" dc:"介绍视频"` School string `json:"school" dc:"所属学校"` Description string `json:"description" dc:"课程概述"` @@ -30,14 +31,16 @@ type LessonUpdateFields struct { StartTime *gtime.Time `json:"startTime" dc:"开课时间"` EndTime *gtime.Time `json:"endTime" dc:"结课时间"` Question string `json:"question" dc:"常见问题"` - UpdatedBy int `json:"updatedBy" dc:"更新人"` + UpdatedBy int64 `json:"updatedBy" dc:"更新人"` + Difficulty int `json:"difficulty" dc:"课程难度"` + Subject string `json:"subject" dc:"所属专题"` } // LessonInsertFields 新增课程管理字段过滤 type LessonInsertFields struct { Name string `json:"name" dc:"课程名"` Cover string `json:"cover" dc:"封面图"` - CategoryId int `json:"categoryId" dc:"所属分类"` + CategoryId int64 `json:"categoryId" dc:"所属分类"` Video string `json:"video" dc:"介绍视频"` School string `json:"school" dc:"所属学校"` Description string `json:"description" dc:"课程概述"` @@ -49,7 +52,9 @@ type LessonInsertFields struct { StartTime *gtime.Time `json:"startTime" dc:"开课时间"` EndTime *gtime.Time `json:"endTime" dc:"结课时间"` Question string `json:"question" dc:"常见问题"` - CreatedBy int `json:"createdBy" dc:"创建人"` + CreatedBy int64 `json:"createdBy" dc:"创建人"` + Difficulty int `json:"difficulty" dc:"课程难度"` + Subject string `json:"subject" dc:"所属专题"` } // LessonEditInp 修改/新增课程管理 @@ -58,6 +63,20 @@ type LessonEditInp struct { } func (in *LessonEditInp) Filter(ctx context.Context) (err error) { + // 验证课程名 + if err := g.Validator().Rules("required").Data(in.Name).Messages("课程名不能为空").Run(ctx); err != nil { + return err.Current() + } + + // 验证封面图 + if err := g.Validator().Rules("required").Data(in.Cover).Messages("封面图不能为空").Run(ctx); err != nil { + return err.Current() + } + + // 验证所属分类 + if err := g.Validator().Rules("required").Data(in.CategoryId).Messages("所属分类不能为空").Run(ctx); err != nil { + return err.Current() + } return } @@ -77,7 +96,7 @@ type LessonDeleteModel struct{} // LessonViewInp 获取指定课程管理信息 type LessonViewInp struct { - Id int `json:"id" v:"required#id不能为空" dc:"id"` + Id int64 `json:"id" v:"required#id不能为空" dc:"id"` } func (in *LessonViewInp) Filter(ctx context.Context) (err error) { @@ -91,6 +110,7 @@ type LessonViewModel struct { // LessonListInp 获取课程管理列表 type LessonListInp struct { form.PageReq + Id int64 `json:"id" dc:"id"` Name string `json:"name" dc:"课程名"` School string `json:"school" dc:"所属学校"` } @@ -100,22 +120,24 @@ func (in *LessonListInp) Filter(ctx context.Context) (err error) { } type LessonListModel struct { - Id int `json:"id" dc:"id"` + Id int64 `json:"id" dc:"id"` Name string `json:"name" dc:"课程名"` Cover string `json:"cover" dc:"封面图"` - CategoryId int `json:"categoryId" dc:"所属分类"` + CategoryId int64 `json:"categoryId" dc:"所属分类"` Video string `json:"video" dc:"介绍视频"` School string `json:"school" dc:"所属学校"` StartTime *gtime.Time `json:"startTime" dc:"开课时间"` EndTime *gtime.Time `json:"endTime" dc:"结课时间"` + Difficulty int `json:"difficulty" dc:"课程难度"` + Subject string `json:"subject" dc:"所属专题"` } // LessonExportModel 导出课程管理 type LessonExportModel struct { - Id int `json:"id" dc:"id"` + Id int64 `json:"id" dc:"id"` Name string `json:"name" dc:"课程名"` Cover string `json:"cover" dc:"封面图"` - CategoryId int `json:"categoryId" dc:"所属分类"` + CategoryId int64 `json:"categoryId" dc:"所属分类"` Video string `json:"video" dc:"介绍视频"` School string `json:"school" dc:"所属学校"` Description string `json:"description" dc:"课程概述"` @@ -127,4 +149,6 @@ type LessonExportModel struct { StartTime *gtime.Time `json:"startTime" dc:"开课时间"` EndTime *gtime.Time `json:"endTime" dc:"结课时间"` Question string `json:"question" dc:"常见问题"` + Difficulty int `json:"difficulty" dc:"课程难度"` + Subject string `json:"subject" dc:"所属专题"` } \ No newline at end of file diff --git a/web/src/views/lesson/edit.vue b/web/src/views/lesson/edit.vue index befce7e..3d657eb 100644 --- a/web/src/views/lesson/edit.vue +++ b/web/src/views/lesson/edit.vue @@ -16,6 +16,7 @@ + + + + + + + + + + @@ -113,7 +124,7 @@ import { ref, computed } from 'vue'; import { useDictStore } from '@/store/modules/dict'; import { Edit, View } from '@/api/lesson'; - import { State, newState } from './model'; + import { State, newState, rules } from './model'; import UploadImage from '@/components/Upload/uploadImage.vue'; import UploadFile from '@/components/Upload/uploadFile.vue'; import Editor from '@/components/Editor/editor.vue'; diff --git a/web/src/views/lesson/model.ts b/web/src/views/lesson/model.ts index 8394c2f..826e070 100644 --- a/web/src/views/lesson/model.ts +++ b/web/src/views/lesson/model.ts @@ -1,6 +1,8 @@ import { h, ref } from 'vue'; +import { NTag } from 'naive-ui'; import { cloneDeep } from 'lodash-es'; import { FormSchema } from '@/components/Form'; +import { isNullObject } from '@/utils/is'; import { renderImage, renderOptionTag, renderFile } from '@/utils'; import { useDictStore } from '@/store/modules/dict'; @@ -27,6 +29,8 @@ export class State { public createdTime = ''; // 创建时间 public updatedBy = 0; // 更新人 public updatedTime = ''; // 更新时间 + public difficulty = null; // 课程难度 + public subject = null; // 所属专题 constructor(state?: Partial) { if (state) { @@ -46,9 +50,40 @@ export function newState(state: State | Record | null): State { } // 表单验证规则 +export const rules = { + name: { + required: true, + trigger: ['blur', 'input'], + type: 'string', + message: '请输入课程名', + }, + cover: { + required: true, + trigger: ['blur', 'input'], + type: 'string', + message: '请输入封面图', + }, + categoryId: { + required: true, + trigger: ['blur', 'input'], + type: 'number', + message: '请输入所属分类', + }, +}; // 表格搜索表单 export const schemas = ref([ + { + field: 'id', + component: 'NInputNumber', + label: 'id', + componentProps: { + placeholder: '请输入id', + onUpdateValue: (e: any) => { + console.log(e); + }, + }, + }, { field: 'name', component: 'NInput', @@ -132,9 +167,32 @@ export const columns = [ align: 'left', width: -1, }, + { + title: '课程难度', + key: 'difficulty', + align: 'left', + width: -1, + render(row: State) { + return renderOptionTag('lesson_difficulty', row.difficulty); + }, + }, + { + title: '所属专题', + key: 'subject', + align: 'left', + width: -1, + render(row: State) { + if (isNullObject(row.subject) || !isArray(row.subject)) { + return ``; + } + return row.subject.map((tagKey) => { + return renderOptionTag('lesson_subject', row.tagKey) + }); + }, + }, ]; // 加载字典数据选项 export function loadOptions() { - dict.loadOptions(['lessonCategoryOption']); + dict.loadOptions(['lesson_subject', 'lesson_difficulty', 'lessonCategoryOption']); } \ No newline at end of file diff --git a/web/src/views/lesson/view.vue b/web/src/views/lesson/view.vue index 7640fe6..9791d8b 100644 --- a/web/src/views/lesson/view.vue +++ b/web/src/views/lesson/view.vue @@ -97,6 +97,18 @@ + + + {{ dict.getLabel('lesson_difficulty', formValue.difficulty) }} + + + + +