Sfoglia il codice sorgente

```
feat(note): 实现学习笔记的增删改功能

- 修改BlockNoteEditorDialog组件中的字段名,将title和content改为noteTitle和noteContent
- 添加表单验证功能,使用国际化消息提示
- 实现笔记的编辑和删除功能,包括确认对话框
- 更新国际化文件,添加标题和内容的输入提示信息
- 修复课程评论组件的分页参数传递问题
- 优化学习笔记组件的数据绑定和分页逻辑
- 添加表单提交验证和错误提示功能
- 实现课程ID变化时的自动数据刷新
```

zhangningning 1 mese fa
parent
commit
770428f70d

+ 22 - 0
src/api/note.js

@@ -0,0 +1,22 @@
+import request from './request.js'
+
+// 新增笔记
+export function noteAdd(data = {}) {
+  return request.post('/note',data)
+}
+// 新增笔记
+export function noteEdit(data = {}) {
+  return request.put('/note',data)
+}
+// 获取笔记列表
+export function getnoteList(data = {}) {
+  return request.get('/note/list',data)
+}
+// 删除笔记
+export function noteDel(data = {}) {
+  return request.del('/note/'+data.id)
+}
+// 获取笔记详情
+export function getnoteDetail(data = {}) {
+  return request.get('/note/'+data.id)
+}

+ 42 - 14
src/components/BlockNoteEditorDialog.vue

@@ -5,10 +5,10 @@
     width="800"
   >
     <el-form :model="form" :rules="rules" ref="formRef" label-position="top" class="edit_note">
-      <el-form-item label="" prop="title">
-        <el-input v-model="form.title" placeholder="请输入标题" size="large" show-word-limit maxlength="20"/>
+      <el-form-item label="" prop="noteTitle">
+        <el-input v-model="form.noteTitle" placeholder="请输入标题" size="large" show-word-limit maxlength="20"/>
       </el-form-item>
-      <el-form-item label="" prop="content">
+      <el-form-item label="" prop="noteContent">
         <BlockNoteEditor v-model="editorContent" />
       </el-form-item>
     </el-form>
@@ -27,30 +27,58 @@ import BlockNoteEditor from '@/components/BlockNoteEditor.vue'
 import { ref, reactive, watchEffect } from 'vue'
 import { useI18n } from 'vue-i18n' 
 const { t } = useI18n()
-
-
+const emit = defineEmits(['submit']);
 
 const dialogVisible = ref(false)
-const title = ref(t('common.add'));
+const title = ref('');
 // 编辑器内容
+// 表单引用
+const formRef = ref(null);
+
 const editorContent = ref(null);
 const form = reactive({
-  title: '',
-  content: ''
+  noteId: '',
+  noteTitle: '',
+  noteContent: ''
 })
 watchEffect(() => {
   // 将编辑器内容赋值给表单的workflowContent
-  form.content = JSON.stringify(editorContent.value);
+  form.noteContent = JSON.stringify(editorContent.value);
 })
 const rules = {
-  title: [{ required: true, message: '请输入标题', trigger: 'blur' }],
-  content: [{ required: true, message: '请输入内容', trigger: 'change' }]
+  noteTitle: [{ required: true, message: t('common.pleaseInputTitle'), trigger: 'blur' }],
+  noteContent: [{ required: true, message: t('common.pleaseInputContent'), trigger: 'change' }]
 }
 const submitForm = async () => {
-  
+   await formRef.value.validate((valid, fields,name) => {
+    console.log(valid, fields,name)
+    if (!valid) {
+      //报错第一个key
+      let firstKey = Object.keys(fields)[0]
+      DGTMessage.warning(fields[firstKey][0].message)
+      return
+    }
+    emit('submit', form);
+    dialogVisible.value = false
+  })
 };
-const openDialog = () => {
-  dialogVisible.value = true
+const openDialog = (item) => {
+  dialogVisible.value = true;
+  editorContent.value = null;
+  if(item){
+    form.noteId = item.noteId;
+    form.noteTitle = item.noteTitle;
+    form.noteContent = "";
+    // 解析JSON字符串为对象
+    if(item.noteContent){
+      editorContent.value = JSON.parse(item.noteContent);
+    }
+  }
+  if(item.noteId){
+    title.value = t('common.edit')
+  }else{
+    title.value = t('common.add')
+  }
 };
 
 defineExpose({

+ 5 - 3
src/locales/en.js

@@ -22,12 +22,12 @@ export default {
     kechengmulu: 'Course Directory',
     pinglun: 'Comments',
     xuxibiji: 'Learning Notes',
-    add: 'Add',
-    edit: 'Edit',
+    add: 'Add ',
+    edit: 'Edit ',
     delete: 'Delete',
     genxinyu: 'Updated at',
     cancel: 'Cancel',
-    confirm: 'Confirm',
+    confirm: 'Confirm ',
     orderConfirmTip: 'Please pay within 30 minutes, or the order will be automatically cancelled',
     payWay: 'Pay Way',
     orderInfo: 'Order Info',
@@ -43,6 +43,8 @@ export default {
     pleaseInputCommentContent: 'Please input comment content',
     commentSuccess: 'Comment Success',
     send: 'Send',
+    pleaseInputTitle: 'Please input title',
+    pleaseInputContent: 'Please input content',
   },
   login: {
     smsLogin: 'SMS Login',

+ 2 - 0
src/locales/zh-CN.js

@@ -44,6 +44,8 @@ export default {
     pleaseInputCommentContent: '请输入评论内容',
     commentSuccess: '评论成功',
     send: '发送',
+    pleaseInputTitle: '请输入标题',
+    pleaseInputContent: '请输入内容',
   },
   login: {
     smsLogin: '短信登录',

+ 8 - 7
src/pages/LearningSystem/components/pinglun.vue

@@ -25,12 +25,12 @@
         </div>
       </div>
     </div>
-    <!-- <Pagination 
+    <Pagination 
       :total="listTotal"
       :page-size="searchFom.pageSize"
       :current-page="searchFom.pageNum"
       @page-change="handlePageChange"
-    /> -->
+    />
   </div>
 </template>
 <script setup>
@@ -42,6 +42,7 @@ import { useI18n } from 'vue-i18n'
 const { t } = useI18n() 
 import { useAppStore } from '@/pinia/appStore'
 const appStore = useAppStore()
+
 const props = defineProps({
   info: {
     type: Object,
@@ -52,6 +53,8 @@ const props = defineProps({
 //监听props.info.courseId变化
 watch(() => props.info.courseId, (newVal, oldVal) => {
   if(newVal !== oldVal){
+    // 课程ID变化时,重置分页参数
+    searchFom.courseId = newVal
     getList('init');
   }
 })
@@ -60,6 +63,7 @@ watch(() => props.info.courseId, (newVal, oldVal) => {
 const comments = ref('');
 const listTotal = ref(0);
 const searchFom = reactive({
+  courseId: props.info.courseId,
   pageNum: 1,
   pageSize: 10,
 })
@@ -78,12 +82,9 @@ const getList = async (type) => {
   if(type === 'init'){
     searchFom.pageNum = 1
   }
-  const res = await getCommentList({
-    ...searchFom,
-    courseId: props.info.courseId
-  })
+  const res = await getCommentList(searchFom)
   if(res.code === 200){
-    listTotal.value = res.rows.length
+    listTotal.value = res.total
     list.value = res.rows
   }
 };

+ 72 - 22
src/pages/LearningSystem/components/xuxibiji.vue

@@ -4,24 +4,24 @@
       <div class="line_vertical"></div>
       <div class="font_size18 bold">{{$t('common.xuxibiji')}}</div>
     </div>
-    <div class="addBtn flex-center gradient border_radius_10">
-      <div class="gap10" @click="openAddDialog">
+    <div class="addBtn flex-center gradient border_radius_10" @click="openAddDialog">
+      <div class="gap10">
         <img :src="addIcon" alt="" style="width:30px;height:30px">
         <span class="font_size18">{{$t('common.add')}}{{$t('common.xuxibiji')}}</span>
       </div>
     </div>
     <div class="list_item padding16 border_radius_16 mt16 flex-center-between" 
-    v-for="(item, index) in 4" :key="index">
+    v-for="(item, index) in list" :key="index">
       <div class="flex_1">
-        <div class="bold font_size18">这是笔记名称</div>
-        <div class="gray999 mt5">{{$t('common.genxinyu')}} 2025-11-08 03:26</div>
+        <div class="bold font_size18">{{item.noteTitle}}</div>
+        <div class="gray999 mt5">{{$t('common.genxinyu')}} {{item.updateTime || item.createTime}}</div>
       </div>
       <div>
-        <el-button type="primary" plain size="large">
+        <el-button type="primary" plain size="large" @click="openAddDialog(item)">
           <el-icon><EditPen /></el-icon>
           <span class="ml10">{{$t('common.edit')}}</span>
         </el-button>
-        <el-button type="danger" plain size="large">
+        <el-button type="danger" plain size="large" @click="handleDelete(item)">
           <el-icon><Delete /></el-icon>
           <span class="ml10">{{$t('common.delete')}}</span>
         </el-button>
@@ -33,35 +33,46 @@
       :current-page="searchFom.pageNum"
       @page-change="handlePageChange"
     />
-    <BlockNoteEditorDialog ref="blockNoteEditorDialogRef" />
+    <BlockNoteEditorDialog ref="blockNoteEditorDialogRef" @submit="submit"/>
   </div>
 </template>
 <script setup>
 import addIcon from '@/assets/imgs/add.png'
 import BlockNoteEditorDialog from '@/components/BlockNoteEditorDialog.vue'
 import Pagination from '@/components/Pagination.vue'
-import { getCourseList } from '@/api/course.js'
-import { ref, onMounted,reactive } from 'vue'
+import { getnoteList,noteAdd,noteEdit,noteDel, getnoteDetail } from '@/api/note.js'
+import { ref, onMounted,reactive,watch } from 'vue'
+import DGTMessage from '@/utils/message'
+import { ElMessageBox } from 'element-plus'
+import { useI18n } from 'vue-i18n' 
+const { t } = useI18n() 
 import { useAppStore } from '@/pinia/appStore'
 const appStore = useAppStore()
-defineProps({
+const props =defineProps({
   info: {
     type: Object,
     default: () => ({})
   }
 })
+
+//监听props.info.courseId变化
+watch(() => props.info.courseId, (newVal, oldVal) => {
+  if(newVal !== oldVal){
+    // 课程ID变化时,重置分页参数
+    searchFom.courseId = newVal
+    getList('init');
+  }
+})
+
 const comments = ref('');
 const listTotal = ref(0);
 const searchFom = reactive({
+  courseId: props.info.courseId,
   pageNum: 1,
   pageSize: 10,
 })
 const list = ref([]);
 
-onMounted(() => {
-  getList();
-});
-
 const handlePageChange = (page) => {
   searchFom.pageNum = page
   // 这里可以添加获取数据的逻辑
@@ -73,17 +84,56 @@ const getList = async (type) => {
   if(type === 'init'){
     searchFom.pageNum = 1
   }
-  // const res = await getQuestList(searchFom)
-  // if(res.code === 200){
-  //   listTotal.value = res.total
-  //   list.value = res.rows
-  // }
+  const res = await getnoteList(searchFom)
+  if(res.code === 200){
+    listTotal.value = res.total
+    list.value = res.rows
+  }
 };
 // 打开添加对话框
 const blockNoteEditorDialogRef = ref(null)
-const openAddDialog = () => {
-  blockNoteEditorDialogRef.value.openDialog();
+const openAddDialog = (item={}) => {
+  blockNoteEditorDialogRef.value.openDialog(item);
 };
+// 提交表单
+const submit = (form) => {
+  let params = {
+    noteId: form.noteId,
+    courseId: searchFom.courseId,
+    noteTitle: form.noteTitle,
+    noteContent: form.noteContent,
+  }
+  let req = noteAdd
+  if(form.noteId){
+    req = noteEdit
+  }
+  req(params).then(res => {
+    console.log(res)
+    if(res.code === 200){
+      DGTMessage.success(t('workflowTrade.publishSuccess'))
+      getList('init');
+    }
+  })
+};
+// 删除笔记
+const handleDelete = (item) => {
+  ElMessageBox.confirm(t('common.confirm')+t('common.delete')+'?', t('common.delete'), {
+    confirmButtonText: t('common.confirm'),
+    cancelButtonText: t('common.cancel'),
+    type: 'warning'
+  }).then(() => {
+    noteDel({id: item.noteId}).then(res => {
+      if(res.code === 200){
+        DGTMessage.success(t('common.delete')+t('common.success'))
+        getList('init');
+      }
+    })
+  }).catch(() => {
+    DGTMessage.info(t('common.cancel')+t('common.delete'));
+  });
+};
+
+
 
 </script>
 <style scoped lang="scss">

+ 1 - 1
src/utils/util.js

@@ -1,4 +1,4 @@
-import DGTMessage from '@/utils/message'//浏览器下载文件
+import DGTMessage from '@/utils/message'
 import { signUp } from '@/api/apply.js'
 import { ElMessageBox } from 'element-plus'
 import { createOrder } from '@/api/order.js'