Bladeren bron

新增我的学习,视频学习

余思翰 3 maanden geleden
bovenliggende
commit
b3e021a765

+ 2
- 2
oa-back/ruoyi-admin/src/main/resources/application.yml Bestand weergeven

@@ -59,9 +59,9 @@ spring:
59 59
   servlet:
60 60
     multipart:
61 61
       # 单个文件大小
62
-      max-file-size: 100MB
62
+      max-file-size: 1024MB
63 63
       # 设置总上传的文件大小
64
-      max-request-size: 200MB
64
+      max-request-size: 10240MB
65 65
   # 服务模块
66 66
   devtools:
67 67
     restart:

+ 1
- 1
oa-back/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java Bestand weergeven

@@ -25,7 +25,7 @@ public class FileUploadUtils
25 25
     /**
26 26
      * 默认大小 50M
27 27
      */
28
-    public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024;
28
+    public static final long DEFAULT_MAX_SIZE = 1024 * 1024 * 1024;
29 29
 
30 30
     /**
31 31
      * 默认的文件名最大长度 100

+ 2
- 2
oa-back/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/MimeTypeUtils.java Bestand weergeven

@@ -24,7 +24,7 @@ public class MimeTypeUtils
24 24
     public static final String[] MEDIA_EXTENSION = { "swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg",
25 25
             "asf", "rm", "rmvb" };
26 26
 
27
-    public static final String[] VIDEO_EXTENSION = { "mp4", "avi", "rmvb" };
27
+    public static final String[] VIDEO_EXTENSION = { "mp4", "avi", "rmvb", "mkv" };
28 28
 
29 29
     public static final String[] DEFAULT_ALLOWED_EXTENSION = {
30 30
             // 图片
@@ -34,7 +34,7 @@ public class MimeTypeUtils
34 34
             // 压缩文件
35 35
             "rar", "zip", "gz", "bz2",
36 36
             // 视频格式
37
-            "mp4", "avi", "rmvb",
37
+            "mp4", "avi", "rmvb", "mkv",
38 38
             // pdf
39 39
             "pdf" };
40 40
 

+ 5
- 2
oa-back/ruoyi-system/src/main/resources/mapper/oa/CmcStudyMapper.xml Bestand weergeven

@@ -11,7 +11,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
11 11
         <result property="lastPoint"    column="last_point"    />
12 12
         <result property="lastTime"    column="last_time"    />
13 13
         <result property="getHours"    column="get_hours"    />
14
-        <association property="user"    javaType="SysUser"         resultMap="userResult" />
14
+        <association property="user"    javaType="SysUser"         resultMap="UserResult" />
15 15
         <association property="dept"    javaType="SysDept"         resultMap="SysDeptResult" />
16 16
         <association property="resource"    javaType="CmcResource"         resultMap="CmcResourceResult" />
17 17
     </resultMap>
@@ -29,10 +29,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
29 29
     <resultMap type="CmcResource" id="CmcResourceResult">
30 30
         <result property="resourceId"    column="resource_id"    />
31 31
         <result property="title"    column="title"    />
32
+        <result property="type"    column="type"    />
33
+        <result property="sourcePath"    column="source_path"    />
34
+        <result property="hours"    column="hours"    />
32 35
     </resultMap>
33 36
 
34 37
     <sql id="selectCmcStudyVo">
35
-        select s.study_id, s.resource_id, r.tile, s.user_id, u.nick_name, d.dept_name, s.last_point, s.last_time, s.get_hours from cmc_study as s
38
+        select s.study_id, s.resource_id, r.title, r.type, r.source_path, r.hours, s.user_id, u.nick_name, d.dept_name, s.last_point, s.last_time, s.get_hours from cmc_study as s
36 39
         left join sys_user as u on u.user_id = s.user_id
37 40
         left join sys_dept as d on d.dept_id = u.dept_id
38 41
         left join cmc_resource as r on r.resource_id = s.resource_id

+ 1
- 1
oa-ui-app/utils/storage.js Bestand weergeven

@@ -10,7 +10,7 @@ import constant from './constant'
10 10
 let storageKey = 'storage_data'
11 11
 
12 12
 // 存储节点变量名
13
-let storageNodeKeys = [constant.avatar, constant.name, constant.roles, constant.permissions, constant.userId]
13
+let storageNodeKeys = [constant.avatar, constant.name, constant.roles, constant.permissions, constant.userId, constant.deptId]
14 14
 
15 15
 // 存储的数据
16 16
 let storageData = uni.getStorageSync(storageKey) || {}

+ 1
- 0
oa-ui/package.json Bestand weergeven

@@ -64,6 +64,7 @@
64 64
     "quill": "1.3.7",
65 65
     "screenfull": "5.0.2",
66 66
     "sortablejs": "1.10.2",
67
+    "video.js": "^8.21.0",
67 68
     "vkbeautify": "^0.99.3",
68 69
     "vue": "2.6.12",
69 70
     "vue-count-to": "1.0.13",

+ 50
- 0
oa-ui/src/api/oa/study/myStudy.js Bestand weergeven

@@ -0,0 +1,50 @@
1
+/*
2
+ * @Author: ysh
3
+ * @Date: 2025-03-06 15:40:47
4
+ * @LastEditors: 
5
+ * @LastEditTime: 2025-03-06 15:40:53
6
+ */
7
+import request from '@/utils/request'
8
+
9
+// 查询cmc学习记录列表
10
+export function listStudy(query) {
11
+  return request({
12
+    url: '/oa/study/list',
13
+    method: 'get',
14
+    params: query
15
+  })
16
+}
17
+
18
+// 查询cmc学习记录详细
19
+export function getStudy(studyId) {
20
+  return request({
21
+    url: '/oa/study/' + studyId,
22
+    method: 'get'
23
+  })
24
+}
25
+
26
+// 新增cmc学习记录
27
+export function addStudy(data) {
28
+  return request({
29
+    url: '/oa/study',
30
+    method: 'post',
31
+    data: data
32
+  })
33
+}
34
+
35
+// 修改cmc学习记录
36
+export function updateStudy(data) {
37
+  return request({
38
+    url: '/oa/study',
39
+    method: 'put',
40
+    data: data
41
+  })
42
+}
43
+
44
+// 删除cmc学习记录
45
+export function delStudy(studyId) {
46
+  return request({
47
+    url: '/oa/study/' + studyId,
48
+    method: 'delete'
49
+  })
50
+}

+ 44
- 0
oa-ui/src/api/oa/study/resource.js Bestand weergeven

@@ -0,0 +1,44 @@
1
+import request from '@/utils/request'
2
+
3
+// 查询cmc学习资料列表
4
+export function listResource(query) {
5
+  return request({
6
+    url: '/oa/resource/list',
7
+    method: 'get',
8
+    params: query
9
+  })
10
+}
11
+
12
+// 查询cmc学习资料详细
13
+export function getResource(resourceId) {
14
+  return request({
15
+    url: '/oa/resource/' + resourceId,
16
+    method: 'get'
17
+  })
18
+}
19
+
20
+// 新增cmc学习资料
21
+export function addResource(data) {
22
+  return request({
23
+    url: '/oa/resource',
24
+    method: 'post',
25
+    data: data
26
+  })
27
+}
28
+
29
+// 修改cmc学习资料
30
+export function updateResource(data) {
31
+  return request({
32
+    url: '/oa/resource',
33
+    method: 'put',
34
+    data: data
35
+  })
36
+}
37
+
38
+// 删除cmc学习资料
39
+export function delResource(resourceId) {
40
+  return request({
41
+    url: '/oa/resource/' + resourceId,
42
+    method: 'delete'
43
+  })
44
+}

BIN
oa-ui/src/assets/images/temp1.png Bestand weergeven


+ 1
- 1
oa-ui/src/components/FileUpload/index.vue Bestand weergeven

@@ -3,7 +3,7 @@
3 3
     <el-upload :action="uploadFileUrl" :data="additionalData" :before-upload="handleBeforeUpload" :file-list="fileList"
4 4
       :limit="limit" :on-error="handleUploadError" :on-exceed="handleExceed" :on-success="handleUploadSuccess"
5 5
       :show-file-list="false" :headers="headers" class="upload-file-uploader" ref="fileUpload"
6
-      accept=".doc,.docx,.xls,.xlsx,.pdf,.rar,.zip">
6
+      accept=".doc,.docx,.xls,.xlsx,.pdf,.rar,.zip,.mp4,.mkv">
7 7
       <!-- 上传按钮 -->
8 8
       <el-button size="mini" type="primary" :disabled="disabled">选取文件</el-button>
9 9
       <!-- 上传提示 -->

+ 28
- 2
oa-ui/src/router/index.js Bestand weergeven

@@ -2,7 +2,7 @@
2 2
  * @Author: ysh
3 3
  * @Date: 2024-01-03 09:23:11
4 4
  * @LastEditors: Please set LastEditors
5
- * @LastEditTime: 2024-10-11 09:26:01
5
+ * @LastEditTime: 2025-03-06 14:59:47
6 6
  */
7 7
 import Vue from 'vue'
8 8
 import Router from 'vue-router'
@@ -338,7 +338,33 @@ export const constantRoutes = [
338 338
 
339 339
       }
340 340
     ]
341
-  }
341
+  },
342
+  {
343
+    path: '/videoStudy/:id',
344
+    component: Layout,
345
+    children: [
346
+      {
347
+        path: '',
348
+        component: () => import('@/views/oa/study/components/videoStudy'),
349
+        name: 'VideoStudy',
350
+        meta: { title: '视频学习', icon: '' }
351
+      }
352
+    ],
353
+    hidden: true
354
+  },
355
+  {
356
+    path: '/pdfStudy/:id',
357
+    component: Layout,
358
+    children: [
359
+      {
360
+        path: '',
361
+        component: () => import('@/views/oa/study/components/pdfStudy'),
362
+        name: 'PdfStudy',
363
+        meta: { title: '文档学习', icon: '' }
364
+      }
365
+    ],
366
+    hidden: true
367
+  },
342 368
 ]
343 369
 
344 370
 // 动态路由,基于用户权限动态去加载

+ 5
- 0
oa-ui/src/utils/ruoyi.js Bestand weergeven

@@ -252,6 +252,9 @@ export function setArray(arr) {
252 252
 
253 253
 export function getUserName(userId) {
254 254
   if (userId) {
255
+    if (userId == 1) {
256
+      return '管理员'
257
+    }
255 258
     let user = this.$store.state.user.userList.find(item => item.userId === userId);
256 259
     // 如果找到了匹配的元素,返回其nickName  
257 260
     if (user) {
@@ -295,6 +298,7 @@ export function getDeptNames(ids) {
295 298
   }).join(',');
296 299
 }
297 300
 
301
+// 获取文件名
298 302
 export function getFileName(name) {
299 303
   if (name) {
300 304
     let arr = name.split("/");
@@ -302,6 +306,7 @@ export function getFileName(name) {
302 306
   }
303 307
 }
304 308
 
309
+// 在线预览
305 310
 export function reviewWord(url) {
306 311
   this.$router.push({
307 312
     path: '/preview',

+ 3
- 3
oa-ui/src/views/oa/study/approval.vue Bestand weergeven

@@ -29,7 +29,7 @@
29 29
     <el-row :gutter="10" class="mb8">
30 30
       <el-col :span="1.5">
31 31
         <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport"
32
-          v-hasPermi="['oa:trainApproval:export']">导出</el-button>
32
+          v-hasPermi="['oa:study:export']">导出</el-button>
33 33
       </el-col>
34 34
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
35 35
     </el-row>
@@ -57,9 +57,9 @@
57 57
       <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
58 58
         <template slot-scope="scope">
59 59
           <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
60
-            v-hasPermi="['oa:trainApproval:edit']">修改</el-button>
60
+            v-hasPermi="['oa:study:edit']">修改</el-button>
61 61
           <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
62
-            v-hasPermi="['oa:trainApproval:remove']">删除</el-button>
62
+            v-hasPermi="['oa:study:remove']">删除</el-button>
63 63
         </template>
64 64
       </el-table-column>
65 65
     </el-table>

+ 0
- 0
oa-ui/src/views/oa/study/components/pdfStudy.vue Bestand weergeven


+ 134
- 0
oa-ui/src/views/oa/study/components/studyHead.vue Bestand weergeven

@@ -0,0 +1,134 @@
1
+<!--
2
+ * @Author: ysh
3
+ * @Date: 2025-03-05 14:19:02
4
+ * @LastEditors: Please set LastEditors
5
+ * @LastEditTime: 2025-03-06 14:26:32
6
+-->
7
+<template>
8
+  <div class="head-bg">
9
+    <div class="user-info">
10
+      <div class="user-pic" data-is-fans="0" data-is-follows="">
11
+        <div class="user-pic-bg">
12
+          <img class="img" :src="$store.getters.avatar">
13
+        </div>
14
+      </div>
15
+      <div class="user-name">
16
+        {{ $store.getters.name }}
17
+      </div>
18
+      <div class="user-dept">
19
+        {{ getDeptName($store.getters.deptId) }}
20
+      </div>
21
+      <div class="user-study">
22
+        <div class="user-item">
23
+          <div class="u-info-learn" style="cursor:pointer;">
24
+            <em>{{ hours }}h</em>
25
+            <span>2025学时</span>
26
+          </div>
27
+        </div>
28
+      </div>
29
+    </div>
30
+  </div>
31
+</template>
32
+
33
+<script>
34
+export default {
35
+  data() {
36
+    return {
37
+      hours: 0,
38
+    }
39
+  },
40
+}
41
+</script>
42
+
43
+<style lang="scss" scoped>
44
+.head-bg {
45
+  background: url('~@/assets/images/temp1.png') no-repeat center top #000;
46
+  background-size: cover;
47
+  height: 100px;
48
+
49
+  .user-info {
50
+    display: flex;
51
+    position: relative;
52
+    width: 1200px;
53
+    margin: 0 auto;
54
+
55
+    .user-pic {
56
+      float: left;
57
+      width: 148px;
58
+      height: 148px;
59
+
60
+      .user-pic-bg {
61
+        border: 4px solid #fff;
62
+        box-shadow: 0 4px 8px 0 rgba(7, 17, 27, .1);
63
+        width: 120px;
64
+        height: 120px;
65
+        position: relative;
66
+        border-radius: 50%;
67
+        background: #fff;
68
+        top: 24px;
69
+
70
+        img {
71
+          text-align: center;
72
+          width: 115px;
73
+          height: 115px;
74
+          border-radius: 50%;
75
+        }
76
+      }
77
+    }
78
+
79
+    .user-name {
80
+      font-weight: 600;
81
+      text-align: left;
82
+      font-size: 24px;
83
+      color: #fff;
84
+      line-height: 28px;
85
+      margin-top: 48px;
86
+    }
87
+
88
+    .user-dept {
89
+      font-weight: 600;
90
+      text-align: left;
91
+      font-size: 16px;
92
+      color: #ebe7e7;
93
+      font-family: 'puhuiti';
94
+      line-height: 28px;
95
+      margin-top: 52px;
96
+      margin-left:20px;
97
+    }
98
+
99
+    .user-study {
100
+      position: absolute;
101
+      top: 31px;
102
+      right: 185px;
103
+      min-width: 200px;
104
+      text-align: right;
105
+
106
+      .user-item {
107
+        line-height: 48px;
108
+        vertical-align: middle;
109
+        height: 48px;
110
+        float: left;
111
+
112
+        em {
113
+          display: block;
114
+          text-align: center;
115
+          font-weight: 700;
116
+          font-size: 24px;
117
+          color: rgba(255, 255, 255, .8);
118
+          line-height: 28px;
119
+        }
120
+
121
+        span {
122
+          display: block;
123
+          text-align: center;
124
+          font-size: 14px;
125
+          color: rgba(255, 255, 255, .8);
126
+          line-height: 20px;
127
+          margin-top: 4px;
128
+        }
129
+      }
130
+    }
131
+  }
132
+
133
+}
134
+</style>

+ 96
- 0
oa-ui/src/views/oa/study/components/studyLeft.vue Bestand weergeven

@@ -0,0 +1,96 @@
1
+<!--
2
+ * @Author: ysh
3
+ * @Date: 2025-03-05 15:01:52
4
+ * @LastEditors: Please set LastEditors
5
+ * @LastEditTime: 2025-03-07 15:03:55
6
+-->
7
+<template>
8
+  <div>
9
+    <right-toolbar :search="false" @queryTable="getRecordsList"></right-toolbar>
10
+    <el-table :data="studyList" style="width: 100%" v-loading="loading">
11
+      <el-table-column align="center" label="序号" type="index" />
12
+      <el-table-column align="center" label="资料名称" prop="resource.title" />
13
+      <el-table-column align="center" label="类型" prop="resource.type"></el-table-column>
14
+      <el-table-column align="center" label="学习进度" prop="lastPoint">
15
+        <template slot-scope="scope">
16
+          <el-progress :text-inside="true" :stroke-width="26" :status="formatStatus(scope.row.lastPoint)"
17
+            :percentage="Number(scope.row.lastPoint)" text-color="#fff"></el-progress>
18
+        </template>
19
+      </el-table-column>
20
+      <el-table-column align="center" label="上次学习时间" prop="lastTime" />
21
+      <el-table-column align="center" label="获得学时" prop="getHours" />
22
+      <el-table-column align="center" label="操作" width="100px">
23
+        <template slot-scope="{row}">
24
+          <el-button type="text" @click="startStudy(row)">
25
+            {{ row.lastPoint == 0 ? '开始学习' : '继续学习' }}
26
+          </el-button>
27
+          <el-button style="color: #F56C6C;" type="text" @click="cancelStudy(row)">
28
+            取消学习
29
+          </el-button>
30
+        </template>
31
+      </el-table-column>
32
+    </el-table>
33
+  </div>
34
+</template>
35
+
36
+<script>
37
+import { listStudy, getStudy, delStudy, addStudy, updateStudy } from "@/api/oa/study/myStudy";
38
+
39
+export default {
40
+  data() {
41
+    return {
42
+      loading: true,
43
+      studyList: [],
44
+      total: 0,
45
+      queryParams: {}
46
+    }
47
+  },
48
+  created() {
49
+    this.getRecordsList();
50
+  },
51
+  methods: {
52
+    getRecordsList() {
53
+      this.loading = true;
54
+      this.queryParams.userId = this.$store.getters.userId;
55
+      listStudy(this.queryParams).then(response => {
56
+        this.studyList = response.rows;
57
+        this.total = response.total;
58
+        this.loading = false;
59
+      });
60
+    },
61
+    startStudy(record) {
62
+      const routeName = record.resource.type === '视频' ? 'VideoStudy' : 'PdfStudy';
63
+      console.log(record);
64
+      this.$router.push({
65
+        name: routeName,
66
+        params: { id: record.studyId }
67
+      })
68
+    },
69
+    cancelStudy(row) {
70
+      this.$modal.confirm('是否删除资料名称为"' + row.resource.title + '"的学习记录?').then(res => {
71
+        delStudy(row.studyId).then(res => {
72
+          this.$message.success(res.msg);
73
+          this.getRecordsList();
74
+        });
75
+      })
76
+    },
77
+    formatStatus(row) {
78
+      if (!row) {
79
+        row = 0
80
+        return 'exception'
81
+      }
82
+      if (row <= 20) {
83
+        return 'exception'
84
+      } else if (row > 20 && row <= 50) {
85
+        return 'warning'
86
+      } else if (row > 50 && row <= 80) {
87
+        return null
88
+      } else {
89
+        return 'success'
90
+      }
91
+    },
92
+  },
93
+}
94
+</script>
95
+
96
+<style lang="scss" scoped></style>

+ 158
- 0
oa-ui/src/views/oa/study/components/studyRight.vue Bestand weergeven

@@ -0,0 +1,158 @@
1
+<!--
2
+ * @Author: ysh
3
+ * @Date: 2025-03-05 15:36:17
4
+ * @LastEditors: Please set LastEditors
5
+ * @LastEditTime: 2025-03-06 16:00:55
6
+-->
7
+<template>
8
+  <div class="right-box">
9
+    <!-- <div class="material-list">
10
+      <el-card v-for="item in resourceList" :key="item.id" class="material-item">
11
+        <div class="material-content">
12
+          <i :class="item.type === '视频' ? 'el-icon-video-camera' : 'el-icon-document'"
13
+            style="font-size: 24px;margin-right: 15px" />
14
+          <div class="material-info">
15
+            <h4>名称:{{ item.title }}</h4>
16
+            <span>学时:{{ item.hours }}个学时</span>
17
+          </div>
18
+          <el-button type="primary" size="mini" @click="addToStudy(item)">
19
+            添加
20
+          </el-button>
21
+        </div>
22
+      </el-card>
23
+    </div> -->
24
+    <div class="video">
25
+      <el-tabs v-model="videoTab" type="card">
26
+        <el-tab-pane label="视频" name="video">
27
+          <el-table :data="videoList" style="width: 100%">
28
+            <el-table-column align="center" label="序号" type="index" />
29
+            <el-table-column align="center" label="资料名称" prop="title" />
30
+            <el-table-column align="center" label="学时" prop="hours" />
31
+            <el-table-column align="center" label="操作">
32
+              <template slot-scope="{row}">
33
+                <el-button type="text" @click="addToStudy(row)">
34
+                  添加课程
35
+                </el-button>
36
+              </template>
37
+            </el-table-column>
38
+          </el-table>
39
+          <pagination v-show="videoTotal > 0" :total="videoTotal" :page.sync="videoParams.pageNum"
40
+            :layout="'total, prev, pager, next,jumper'" :limit.sync="videoParams.pageSize" :autoScroll="false"
41
+            @pagination="getVideoList" />
42
+        </el-tab-pane>
43
+      </el-tabs>
44
+    </div>
45
+    <div class="pdf">
46
+      <el-tabs v-model="pdfTab" type="card">
47
+        <el-tab-pane label="文档" name="pdf">
48
+          <el-table :data="pdfList" style="width: 100%">
49
+            <el-table-column align="center" label="序号" type="index" />
50
+            <el-table-column align="center" label="资料名称" prop="title" />
51
+            <el-table-column align="center" label="学时" prop="hours" />
52
+            <el-table-column align="center" label="操作">
53
+              <template slot-scope="{row}">
54
+                <el-button type="text" @click="addToStudy(row)">
55
+                  添加课程
56
+                </el-button>
57
+              </template>
58
+            </el-table-column>
59
+          </el-table>
60
+          <pagination v-show="pdfTotal > 0" :total="pdfTotal" :page.sync="pdfParams.pageNum"
61
+            :layout="'total, prev, pager, next,jumper'" :limit.sync="pdfParams.pageSize" :autoScroll="false"
62
+            @pagination="getPdfList" />
63
+        </el-tab-pane>
64
+      </el-tabs>
65
+    </div>
66
+  </div>
67
+</template>
68
+
69
+<script>
70
+import { listResource, getResource, delResource, addResource, updateResource } from "@/api/oa/study/resource";
71
+import { listStudy, getStudy, delStudy, addStudy, updateStudy } from "@/api/oa/study/myStudy";
72
+
73
+export default {
74
+  data() {
75
+    return {
76
+      videoList: [],
77
+      pdfList: [],
78
+      videoTab: 'video',
79
+      pdfTab: 'pdf',
80
+      videoParams: {},
81
+      pdfParams: {},
82
+      videoTotal: 0,
83
+      pdfTotal: 0,
84
+    }
85
+  },
86
+  created() {
87
+    this.getVideoList();
88
+    this.getPdfList();
89
+  },
90
+  methods: {
91
+    getVideoList() {
92
+      this.videoParams.type = '视频'
93
+      listResource(this.videoParams).then(response => {
94
+        this.videoList = response.rows;
95
+        this.videoTotal = response.total;
96
+      });
97
+    },
98
+    getPdfList() {
99
+      this.pdfParams.type = '文档'
100
+      listResource(this.pdfParams).then(response => {
101
+        this.pdfList = response.rows;
102
+        this.pdfTotal = response.total;
103
+      });
104
+    },
105
+    addToStudy(row) {
106
+      console.log(row);
107
+      this.$modal.confirm('是否添加资料名称为"' + row.title + '"到我的学习记录?').then(res => {
108
+        let obj = {
109
+          resourceId: row.resourceId,
110
+          userId: this.$store.getters.userId,
111
+          lastPoint: 0,
112
+          getHours: 0
113
+        }
114
+        addStudy(obj).then(res => {
115
+          this.$message.success(res.msg);
116
+          this.$emit('refreshList');
117
+        });
118
+      })
119
+
120
+    }
121
+  },
122
+}
123
+</script>
124
+
125
+<style lang="scss" scoped>
126
+.material-list {
127
+  display: grid;
128
+  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
129
+  gap: 15px;
130
+}
131
+
132
+.material-content {
133
+  display: flex;
134
+  align-items: center;
135
+  justify-content: space-between;
136
+}
137
+
138
+.material-info {
139
+  flex: 1;
140
+  margin-right: 15px;
141
+}
142
+
143
+
144
+.right-box {
145
+  display: flex;
146
+
147
+  .video {
148
+    flex: 1;
149
+    border-right: 1px solid #ececec;
150
+    padding: 5px;
151
+  }
152
+
153
+  .pdf {
154
+    flex: 1;
155
+    padding: 5px;
156
+  }
157
+}
158
+</style>

+ 216
- 0
oa-ui/src/views/oa/study/components/videoStudy.vue Bestand weergeven

@@ -0,0 +1,216 @@
1
+<template>
2
+  <div class="video-container">
3
+    <el-button type="primary" @click="$router.push({ path: '/oa/study/myStudy' })">返回我的学习</el-button>
4
+    <video ref="videoPlayer" class="video-js vjs-big-play-centered" @play="handlePlay" @pause="handlePause"
5
+      @ended="handleEnd">
6
+    </video>
7
+    <el-alert v-if="showProgressTip" type="info" :closable="false" class="progress-alert">
8
+      已为您定位到上次播放位置:{{ formatTime(lastPosition) }}
9
+    </el-alert>
10
+  </div>
11
+</template>
12
+
13
+<script>
14
+import videojs from 'video.js';
15
+import 'video.js/dist/video-js.css';
16
+import { listResource, getResource, delResource, addResource, updateResource } from "@/api/oa/study/resource";
17
+import { listStudy, getStudy, delStudy, addStudy, updateStudy } from "@/api/oa/study/myStudy";
18
+
19
+export default {
20
+  data() {
21
+    return {
22
+      baseUrl: process.env.VUE_APP_BASE_API,
23
+      videoId: '',
24
+      videoPath: '',
25
+      player: null,
26
+      lastPosition: 0,
27
+      getHours: '', //可获得的学时
28
+      isComplete: false, //是否已经看完
29
+      showProgressTip: false,
30
+      // 禁用功能配置
31
+      disabledControls: [
32
+        'progressControl', // 隐藏进度条
33
+        'seekBar',         // 禁用拖拽
34
+        'remainingTimeDisplay',
35
+        'playbackRateMenuButton'
36
+      ],
37
+      videoDuration: undefined,
38
+    };
39
+  },
40
+  watch: {
41
+    videoPath() {
42
+      this.$nextTick(() => {
43
+        const video = this.$refs.videoPlayer;
44
+        if (!video) return;
45
+        video.pause();
46
+        video.removeAttribute("src");
47
+        video.load();
48
+        setTimeout(() => {
49
+          video.src = this.fullVideoPath;
50
+          video.load();
51
+        }, 100);
52
+      });
53
+    }
54
+  },
55
+  computed: {
56
+    fullVideoPath() {
57
+      return `/dev-api/profile/upload/${this.videoPath}`
58
+    }
59
+  },
60
+  created() {
61
+    this.getData();
62
+  },
63
+  mounted() {
64
+    this.initPlayer();
65
+    this.loadLastPosition();
66
+  },
67
+  methods: {
68
+    async getData() {
69
+      let studyId = this.$route.params.id;
70
+      let resData = await getStudy(studyId)
71
+      let data = resData.data
72
+      this.videoPath = data.resource.sourcePath;
73
+      if (Number(data.lastPoint) == 100) {
74
+        this.isComplete = true;
75
+        this.getHours = data.resource.hours;
76
+        console.log(data.resource);
77
+
78
+        this.$message.warning('视频已学习完毕,若要再次学习,请在学习记录里重新添加该课程。')
79
+      }
80
+    },
81
+    // 初始化播放器
82
+    initPlayer() {
83
+      const self = this;
84
+      this.player = videojs(this.$refs.videoPlayer, {
85
+        // 增加错误处理回调
86
+        errorDisplay: false,
87
+        autoplay: false,
88
+        controls: true,
89
+        sources: [{ src: this.fullVideoPath, type: 'video/mp4' }],
90
+        controlBar: {
91
+          // children: this.disabledControls,
92
+          // 自定义禁止拖拽的进度条
93
+          progressControl: false, // 关闭进度条
94
+          volumePanel: true      // 启用音量控制
95
+        },
96
+        userActions: {
97
+          hotkeys: false,
98
+          // 禁用双击全屏
99
+          doubleClick: false
100
+        }
101
+      });
102
+      let retryCount = 0;
103
+      // 增加错误监听
104
+      this.player.on('error', () => {
105
+        console.error('播放器错误:', this.player.error());
106
+        if (retryCount < 3) {
107
+          this.player.src(this.fullVideoPath);
108
+          this.player.load();
109
+          retryCount++;
110
+        }
111
+      });
112
+
113
+      // 节流保存(每10秒保存一次)
114
+      this.player.on('timeupdate', _.throttle(() => {
115
+        this.saveCurrentPosition();
116
+      }, 10000));
117
+    },
118
+    // 加载上次播放位置
119
+    async loadLastPosition() {
120
+      this.videoId = this.$route.params.id;
121
+      try {
122
+        const { data } = await getStudy(this.videoId);
123
+        const percentage = data.lastPoint || 0;
124
+        // 添加 readyState 检查
125
+        const checkReady = () => {
126
+          return new Promise(resolve => {
127
+            if (this.player.readyState() >= 2) { // HAVE_CURRENT_DATA
128
+              resolve();
129
+            } else {
130
+              this.player.one('loadedmetadata', resolve);
131
+            }
132
+          });
133
+        };
134
+        await checkReady();
135
+        const duration = this.player.duration();
136
+        const targetTime = duration * (percentage / 100);
137
+
138
+        // 使用播放器原生方法
139
+        this.player.currentTime(targetTime);
140
+        this.lastPosition = targetTime;
141
+
142
+      } catch (error) {
143
+        console.error('加载进度失败:', error);
144
+      }
145
+    },
146
+
147
+    // 处理播放事件
148
+    handlePlay() {
149
+      this.player.currentTime(this.lastPosition);
150
+    },
151
+
152
+    // 处理暂停事件
153
+    handlePause() {
154
+      this.saveCurrentPosition();
155
+    },
156
+
157
+    // 处理视频结束
158
+    handleEnd() {
159
+      this.saveCurrentPosition(true);
160
+    },
161
+    // 保存当前进度
162
+    async saveCurrentPosition(isEnd = false) {
163
+      const currentTime = this.player.currentTime();
164
+      const duration = this.player.duration();
165
+      let percentage = isEnd ? 100 : Number(((currentTime / duration) * 100).toFixed(2));
166
+      if (isNaN(percentage)) {
167
+        return
168
+      }
169
+      try {
170
+        if (!this.isComplete) {
171
+          await updateStudy({
172
+            studyId: this.videoId,
173
+            lastPoint: percentage
174
+          });
175
+        } else {
176
+          await updateStudy({
177
+            studyId: this.videoId,
178
+            getHours: this.getHours
179
+          });
180
+        }
181
+        // 始终更新 lastPosition(即使播放结束)
182
+        this.lastPosition = currentTime;
183
+      } catch (error) {
184
+        console.error('保存进度失败:', error);
185
+      }
186
+    },
187
+
188
+    // 时间格式化
189
+    formatTime(seconds) {
190
+      const date = new Date(0);
191
+      date.setSeconds(seconds);
192
+      return date.toISOString().substr(11, 8);
193
+    },
194
+  },
195
+  beforeDestroy() {
196
+    if (this.player) {
197
+      this.player.dispose();
198
+    }
199
+  }
200
+};
201
+</script>
202
+
203
+<style lang="scss" scoped>
204
+.video-container {
205
+  position: relative;
206
+  width: 1000px;
207
+  // margin: 0 auto;
208
+
209
+  .progress-alert {
210
+    position: absolute;
211
+    bottom: 40px;
212
+    width: 100%;
213
+    z-index: 1;
214
+  }
215
+}
216
+</style>

+ 87
- 0
oa-ui/src/views/oa/study/myStudy.vue Bestand weergeven

@@ -0,0 +1,87 @@
1
+<!--
2
+ * @Author: ysh
3
+ * @Date: 2025-03-05 14:00:18
4
+ * @LastEditors: Please set LastEditors
5
+ * @LastEditTime: 2025-03-05 15:26:03
6
+-->
7
+<template>
8
+  <div>
9
+    <study-head></study-head>
10
+    <div class="main app-contrainer">
11
+      <div class="left">
12
+        <div class="title">
13
+          我的学习记录
14
+          <div class="line"></div>
15
+        </div>
16
+        <study-left ref="studyLeft"></study-left>
17
+      </div>
18
+      <!-- <div class="center">
19
+      </div> -->
20
+      <div class="right">
21
+        <div class="title">
22
+          学习资料库
23
+          <div class="line"></div>
24
+        </div>
25
+        <study-right ref="studyRight" @refreshList="getStudyList"></study-right>
26
+      </div>
27
+    </div>
28
+  </div>
29
+</template>
30
+
31
+<script>
32
+import StudyHead from './components/studyHead.vue'
33
+import StudyLeft from './components/studyLeft.vue'
34
+import StudyRight from './components/studyRight.vue'
35
+export default {
36
+  name: 'myStudy',
37
+  components: { StudyHead, StudyLeft, StudyRight },
38
+  methods: {
39
+    getStudyList() {
40
+      this.$refs.studyLeft.getRecordsList();
41
+    }
42
+  }
43
+}
44
+</script>
45
+
46
+<style lang="scss" scoped>
47
+.main {
48
+  display: flex;
49
+  margin-top: 50px;
50
+
51
+  .left {
52
+    flex: 1;
53
+    padding: 10px;
54
+    border-right: 1px solid #ececec;
55
+  }
56
+
57
+  .center {
58
+    flex: 1;
59
+    padding: 10px;
60
+    border-right: 1px solid #ececec;
61
+  }
62
+
63
+  .right {
64
+    flex: 1;
65
+    padding: 10px;
66
+  }
67
+}
68
+
69
+
70
+.title {
71
+  position: relative;
72
+  font-weight: bold;
73
+  font-size: 18px;
74
+  padding-left: 40px;
75
+  padding-bottom: 20px;
76
+
77
+  .line {
78
+    position: absolute;
79
+    left: 22px;
80
+    top: 3px;
81
+    width: 5px;
82
+    height: 20px;
83
+    border-radius: 10px;
84
+    background-color: #2893e5;
85
+  }
86
+}
87
+</style>

+ 317
- 0
oa-ui/src/views/oa/study/resource.vue Bestand weergeven

@@ -0,0 +1,317 @@
1
+<!--
2
+ * @Author: ysh
3
+ * @Date: 2025-03-05 09:15:54
4
+ * @LastEditors: Please set LastEditors
5
+ * @LastEditTime: 2025-03-05 13:58:28
6
+-->
7
+<template>
8
+  <div class="app-container">
9
+    <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
10
+      <el-form-item label="资料名称" prop="title">
11
+        <el-input v-model="queryParams.title" placeholder="请输入资料名称" clearable @keyup.enter.native="handleQuery" />
12
+      </el-form-item>
13
+      <el-form-item label="上传人" prop="uploader">
14
+        <el-input v-model="queryParams.uploader" placeholder="请输入上传人" clearable @keyup.enter.native="handleQuery" />
15
+      </el-form-item>
16
+      <el-form-item label="上传部门" prop="uploadDept">
17
+        <el-input v-model="queryParams.uploadDept" placeholder="请输入上传部门" clearable @keyup.enter.native="handleQuery" />
18
+      </el-form-item>
19
+      <el-form-item label="专业领域" prop="field">
20
+        <el-input v-model="queryParams.field" placeholder="请输入专业领域" clearable @keyup.enter.native="handleQuery" />
21
+      </el-form-item>
22
+      <el-form-item label="上传时间" prop="uploadTime">
23
+        <el-date-picker clearable v-model="queryParams.uploadTime" type="date" value-format="yyyy-MM-dd"
24
+          placeholder="请选择上传时间">
25
+        </el-date-picker>
26
+      </el-form-item>
27
+      <el-form-item>
28
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
29
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
30
+      </el-form-item>
31
+    </el-form>
32
+
33
+    <el-row :gutter="10" class="mb8">
34
+      <el-col :span="1.5">
35
+        <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
36
+          v-hasPermi="['oa:resource:add']">新增</el-button>
37
+      </el-col>
38
+      <el-col :span="1.5">
39
+        <el-button type="success" plain icon="el-icon-edit" size="mini" :disabled="single" @click="handleUpdate"
40
+          v-hasPermi="['oa:resource:edit']">修改</el-button>
41
+      </el-col>
42
+      <el-col :span="1.5">
43
+        <el-button type="danger" plain icon="el-icon-delete" size="mini" :disabled="multiple" @click="handleDelete"
44
+          v-hasPermi="['oa:resource:remove']">删除</el-button>
45
+      </el-col>
46
+      <el-col :span="1.5">
47
+        <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport"
48
+          v-hasPermi="['oa:resource:export']">导出</el-button>
49
+      </el-col>
50
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
51
+    </el-row>
52
+
53
+    <el-table v-loading="loading" :data="resourceList" @selection-change="handleSelectionChange">
54
+      <el-table-column type="selection" width="55" align="center" />
55
+      <el-table-column label="资料id" align="center" prop="resourceId" />
56
+      <el-table-column label="上传人" align="center" prop="uploader">
57
+        <template slot-scope="scope">
58
+          {{ getUserName(scope.row.uploader) }}
59
+        </template>
60
+      </el-table-column>
61
+      <el-table-column label="上传部门" align="center" prop="uploadDept">
62
+        <template slot-scope="scope">
63
+          {{ getDeptName(scope.row.uploadDept) }}
64
+        </template>
65
+      </el-table-column>
66
+      <el-table-column label="标题" align="center" prop="title" />
67
+      <el-table-column label="上传附件" align="center" prop="sourcePath" :formatter="formatterPath" />
68
+      <el-table-column label="专业领域" align="center" prop="field" />
69
+      <el-table-column label="类型" align="center" prop="type" />
70
+      <el-table-column label="上传时间" align="center" prop="uploadTime" width="180">
71
+        <template slot-scope="scope">
72
+          <span>{{ parseTime(scope.row.uploadTime, '{y}-{m}-{d}') }}</span>
73
+        </template>
74
+      </el-table-column>
75
+      <el-table-column label="学时" align="center" prop="hours" />
76
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
77
+        <template slot-scope="scope">
78
+          <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
79
+            v-hasPermi="['oa:resource:edit']">修改</el-button>
80
+          <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
81
+            v-hasPermi="['oa:resource:remove']">删除</el-button>
82
+        </template>
83
+      </el-table-column>
84
+    </el-table>
85
+
86
+    <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
87
+      @pagination="getList" />
88
+
89
+    <!-- 添加或修改cmc学习资料对话框 -->
90
+    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
91
+      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
92
+        <el-form-item label="资料名称" prop="title">
93
+          <el-input v-model="form.title" placeholder="请输入资料名称" />
94
+        </el-form-item>
95
+        <el-form-item label="上传附件" prop="sourcePath">
96
+          <FileUpload v-if="form.sourcePath == null || form.sourcePath == ''" :isShowTip="false" :limit="1"
97
+            :filePathName="'教育学习/学习资料'" :fileType="['pdf', 'mp4', 'mkv']" :fileSize="10240" @input="getDocumentPath">
98
+          </FileUpload>
99
+          <div v-if="form.sourcePath">
100
+            <el-link type="primary" @click="reviewWord(`${baseUrl}${'/profile/upload' + form.sourcePath}`)">
101
+              {{ getFileName(form.sourcePath) }}
102
+            </el-link>
103
+            <el-link class="ml20" type="warning" :href="`${baseUrl}${'/profile/upload' + form.sourcePath}`"
104
+              :underline="false" target="_blank">
105
+              <span class="el-icon-download">下载文件</span>
106
+            </el-link>
107
+            <el-link class="ml20" type="danger" @click="handleDeleteDocument" :underline="false"
108
+              v-hasPermi="['oa:resource:edit']">
109
+              <span class="el-icon-delete">删除文件</span>
110
+            </el-link>
111
+            <!-- <FileUpload v-if="taskName == '参培申请'" :limit="1" :filePathName="'参培/佐证附件'"
112
+              :fileType="['doc', 'docx', 'xls', 'xlsx', 'pdf', 'rar', 'zip']" @input="getDocumentPath"></FileUpload> -->
113
+          </div>
114
+          <div style="font-size:12px;color:#e45656">
115
+            tips:文档请上传pdf格式,视频请上传mp4格式
116
+          </div>
117
+        </el-form-item>
118
+        <el-form-item label="资料类型" prop="type">
119
+          <el-select v-model="form.type" clearable>
120
+            <el-option label="视频" value="视频"></el-option>
121
+            <el-option label="文档" value="文档"></el-option>
122
+          </el-select>
123
+        </el-form-item>
124
+        <el-form-item label="专业领域" prop="field">
125
+          <el-input v-model="form.field" placeholder="请输入专业领域" />
126
+        </el-form-item>
127
+        <el-form-item label="学时" prop="hours">
128
+          <el-input-number v-model="form.hours" :min="0" :precision="1" />
129
+        </el-form-item>
130
+        <el-form-item label="上传人" prop="uploader">
131
+          {{ getUserName(form.uploader) }}
132
+        </el-form-item>
133
+        <el-form-item label="上传部门" prop="uploadDept">
134
+          {{ getDeptName(form.uploadDept) }}
135
+        </el-form-item>
136
+        <el-form-item label="上传时间" prop="uploadTime">
137
+          {{ form.uploadTime }}
138
+        </el-form-item>
139
+      </el-form>
140
+      <div slot="footer" class="dialog-footer">
141
+        <el-button type="primary" @click="submitForm">确 定</el-button>
142
+        <el-button @click="cancel">取 消</el-button>
143
+      </div>
144
+    </el-dialog>
145
+  </div>
146
+</template>
147
+
148
+<script>
149
+import { listResource, getResource, delResource, addResource, updateResource } from "@/api/oa/study/resource";
150
+import { parseTime } from "@/utils/ruoyi";
151
+
152
+export default {
153
+  name: "Resource",
154
+  data() {
155
+    return {
156
+      baseUrl: process.env.VUE_APP_BASE_API,
157
+      // 遮罩层
158
+      loading: true,
159
+      // 选中数组
160
+      ids: [],
161
+      // 非单个禁用
162
+      single: true,
163
+      // 非多个禁用
164
+      multiple: true,
165
+      // 显示搜索条件
166
+      showSearch: true,
167
+      // 总条数
168
+      total: 0,
169
+      // cmc学习资料表格数据
170
+      resourceList: [],
171
+      // 弹出层标题
172
+      title: "",
173
+      // 是否显示弹出层
174
+      open: false,
175
+      // 查询参数
176
+      queryParams: {
177
+        pageNum: 1,
178
+        pageSize: 10,
179
+        uploader: null,
180
+        uploadDept: null,
181
+        title: null,
182
+        sourcePath: null,
183
+        field: null,
184
+        type: null,
185
+        uploadTime: null,
186
+        hours: null
187
+      },
188
+      // 表单参数
189
+      form: {},
190
+      // 表单校验
191
+      rules: {
192
+      }
193
+    };
194
+  },
195
+  created() {
196
+    this.getList();
197
+  },
198
+  methods: {
199
+    /** 查询cmc学习资料列表 */
200
+    getList() {
201
+      this.loading = true;
202
+      listResource(this.queryParams).then(response => {
203
+        this.resourceList = response.rows;
204
+        this.total = response.total;
205
+        this.loading = false;
206
+      });
207
+    },
208
+    // 取消按钮
209
+    cancel() {
210
+      this.open = false;
211
+      this.reset();
212
+    },
213
+    // 表单重置
214
+    reset() {
215
+      this.form = {
216
+        resourceId: null,
217
+        uploader: null,
218
+        uploadDept: null,
219
+        title: null,
220
+        sourcePath: null,
221
+        field: null,
222
+        type: null,
223
+        uploadTime: null,
224
+        hours: null
225
+      };
226
+      this.resetForm("form");
227
+    },
228
+    /** 搜索按钮操作 */
229
+    handleQuery() {
230
+      this.queryParams.pageNum = 1;
231
+      this.getList();
232
+    },
233
+    /** 重置按钮操作 */
234
+    resetQuery() {
235
+      this.resetForm("queryForm");
236
+      this.handleQuery();
237
+    },
238
+    // 多选框选中数据
239
+    handleSelectionChange(selection) {
240
+      this.ids = selection.map(item => item.resourceId)
241
+      this.single = selection.length !== 1
242
+      this.multiple = !selection.length
243
+    },
244
+    /** 新增按钮操作 */
245
+    handleAdd() {
246
+      this.reset();
247
+      this.open = true;
248
+      this.form.uploader = this.$store.getters.userId;
249
+      this.form.uploadDept = this.$store.getters.deptId;
250
+      this.form.uploadTime = parseTime(new Date(), '{y}-{m}-{d}');
251
+      this.title = "新增学习资料";
252
+    },
253
+    /** 修改按钮操作 */
254
+    handleUpdate(row) {
255
+      this.reset();
256
+      const resourceId = row.resourceId || this.ids
257
+      getResource(resourceId).then(response => {
258
+        this.form = response.data;
259
+        this.open = true;
260
+        this.title = "修改cmc学习资料";
261
+      });
262
+    },
263
+    /** 提交按钮 */
264
+    submitForm() {
265
+      this.$refs["form"].validate(valid => {
266
+        if (valid) {
267
+          if (this.form.resourceId != null) {
268
+            updateResource(this.form).then(response => {
269
+              this.$modal.msgSuccess("修改成功");
270
+              this.open = false;
271
+              this.getList();
272
+            });
273
+          } else {
274
+            addResource(this.form).then(response => {
275
+              this.$modal.msgSuccess("新增成功");
276
+              this.open = false;
277
+              this.getList();
278
+            });
279
+          }
280
+        }
281
+      });
282
+    },
283
+    /** 删除按钮操作 */
284
+    handleDelete(row) {
285
+      const resourceIds = row.resourceId || this.ids;
286
+      this.$modal.confirm('是否确认删除cmc学习资料编号为"' + resourceIds + '"的数据项?').then(function () {
287
+        return delResource(resourceIds);
288
+      }).then(() => {
289
+        this.getList();
290
+        this.$modal.msgSuccess("删除成功");
291
+      }).catch(() => { });
292
+    },
293
+    /** 导出按钮操作 */
294
+    handleExport() {
295
+      this.download('oa/resource/export', {
296
+        ...this.queryParams
297
+      }, `resource_${new Date().getTime()}.xlsx`)
298
+    },
299
+    getDocumentPath(val) {
300
+      let arr = val.split('/upload')
301
+      this.form.sourcePath = arr[1]
302
+      if (val == "") {
303
+        this.form.sourcePath = ""
304
+      }
305
+    },
306
+    handleDeleteDocument() {
307
+      this.$modal.confirm('是否确认删除该附件?').then(() => {
308
+        this.form.sourcePath = '';
309
+      }).catch(() => { });
310
+    },
311
+    formatterPath(row){
312
+      let name = this.getFileName(row.sourcePath)
313
+      return name
314
+    }
315
+  }
316
+};
317
+</script>

Laden…
Annuleren
Opslaan