Merge branch 'main' into cys

This commit is contained in:
cys 2024-07-17 14:20:31 +08:00
commit fbb9fed298
27 changed files with 1387 additions and 373 deletions

View File

@ -22,8 +22,8 @@ export default defineConfig({
server: { server: {
proxy: { proxy: {
'/dev-api': { '/dev-api': {
// target: 'http://27.128.240.72:7865', target: 'http://27.128.240.72:7865',
target: 'http://192.168.2.52:7863', // target: 'http://192.168.2.52:7863',
changeOrigin: true, changeOrigin: true,
rewrite: (p) => p.replace(/^\/dev-api/, '') rewrite: (p) => p.replace(/^\/dev-api/, '')
}, },

View File

@ -1,9 +1,9 @@
const fs = require('fs') const fs = require('fs')
const path = require('path') const path = require('path')
import { ElectronDownloadManager } from 'electron-dl-manager'; import { ElectronDownloadManager } from 'electron-dl-manager'
const manager = new ElectronDownloadManager(); import { dialog } from 'electron'
const manager = new ElectronDownloadManager()
export default async function ({ app, shell, BrowserWindow, ipcMain }) { export default async function ({ app, shell, BrowserWindow, ipcMain }) {
const userDataPath = app.getPath('userData') const userDataPath = app.getPath('userData')
//默认浏览器打开url //默认浏览器打开url
ipcMain.on('open-url-browser', (e, url) => { ipcMain.on('open-url-browser', (e, url) => {
@ -22,8 +22,8 @@ export default async function ({ app, shell, BrowserWindow, ipcMain }) {
}) })
//下载文件 //下载文件
ipcMain.on('download-file-default', (e,url) => { ipcMain.on('download-file-default', (e, url) => {
createFolder('selfFile').then(async ()=>{ createFolder('selfFile').then(async () => {
const browserWindow = BrowserWindow.fromId(e.sender.id) const browserWindow = BrowserWindow.fromId(e.sender.id)
const id = await manager.download({ const id = await manager.download({
window: browserWindow, window: browserWindow,
@ -38,17 +38,65 @@ export default async function ({ app, shell, BrowserWindow, ipcMain }) {
id, id,
percentCompleted, percentCompleted,
// Get the number of bytes received so far // Get the number of bytes received so far
bytesReceived: item.getReceivedBytes(), bytesReceived: item.getReceivedBytes()
}); })
}, },
onDownloadCompleted: async ({ id, item }) => { onDownloadCompleted: async ({ id, item }) => {
console.log(item) console.log(item)
}, },
onDownloadCancelled: async () => {}, onDownloadCancelled: async () => {},
onDownloadInterrupted: async () => {}, onDownloadInterrupted: async () => {},
onError: (err, data) => {}, onError: (err, data) => {}
} }
}); })
})
})
/**...
* 接收渲染进程 保存文件的 的通知
* @param {Object} event
* @param {String} url 下载链接
* @param {String} fileName 文件名称包括后缀名例如图1.png
*/
ipcMain.on('save-as', function (event, url, fileName) {
let win = BrowserWindow.getFocusedWindow();
//通过扩展名识别文件类型
let filters = [{ name: '全部文件', extensions: ['*'] }]
let ext = path.extname(fileName) //获取扩展名
if (ext && ext !== '.') {
const name = ext.slice(1, ext.length)
if (name) {
filters.unshift({
name: '',
extensions: [name]
})
}
}
let filePath = null //用户选择存放文件的路径
//1- 弹出另存为弹框,用于获取保存路径
dialog
.showSaveDialog(win, {
title: '另存为',
filters,
defaultPath: fileName
})
.then((result) => {
//点击保存后开始下载
filePath = result.filePath
if (filePath) {
win.webContents.downloadURL(url) // 触发will-download事件
}
})
.catch(() => {
console.log('另存为--catch')
})
//2- 准备下载的时候触发
win.webContents.session.once('will-download', (event, item, webContents) => {
if (!filePath) return
//设置下载项的保存文件路径
item.setSavePath(filePath)
}) })
}) })

View File

@ -8,8 +8,8 @@ File({ app, shell, BrowserWindow, ipcMain })
function createWindow() { function createWindow() {
// Create the browser window. // Create the browser window.
const mainWindow = new BrowserWindow({ const mainWindow = new BrowserWindow({
width: 888, width: 1050,
height: 520, height: 650,
show: false, show: false,
frame: false, frame: false,
autoHideMenuBar: true, autoHideMenuBar: true,

View File

@ -8,3 +8,18 @@ export const getSmarttalkPage = (params) => {
params params
}) })
} }
export function deleteSmarttalk(id) {
return request({
url: '/smarttalk/file/' + id,
method: 'delete'
})
}
export const updateSmarttalk = (params) => {
return request({
url: '/smarttalk/file/updateSmarttalk',
method: 'post',
params
})
}

View File

@ -1,9 +1,9 @@
@font-face { @font-face {
font-family: "iconfont"; /* Project id 2794390 */ font-family: "iconfont"; /* Project id 2794390 */
src: url('iconfont.woff2?t=1720953486579') format('woff2'), src: url('iconfont.woff2?t=1721179711733') format('woff2'),
url('iconfont.woff?t=1720953486579') format('woff'), url('iconfont.woff?t=1721179711733') format('woff'),
url('iconfont.ttf?t=1720953486579') format('truetype'), url('iconfont.ttf?t=1721179711733') format('truetype'),
url('iconfont.svg?t=1720953486579#iconfont') format('svg'); url('iconfont.svg?t=1721179711733#iconfont') format('svg');
} }
.iconfont { .iconfont {
@ -14,6 +14,10 @@
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
.icon-yidongdaozu:before {
content: "\e67d";
}
.icon-shanchu:before { .icon-shanchu:before {
content: "\e852"; content: "\e852";
} }

File diff suppressed because one or more lines are too long

View File

@ -5,6 +5,13 @@
"css_prefix_text": "icon-", "css_prefix_text": "icon-",
"description": "", "description": "",
"glyphs": [ "glyphs": [
{
"icon_id": "1207918",
"name": "移动到组",
"font_class": "yidongdaozu",
"unicode": "e67d",
"unicode_decimal": 59005
},
{ {
"icon_id": "8288874", "icon_id": "8288874",
"name": "删除", "name": "删除",

View File

@ -14,6 +14,8 @@
/> />
<missing-glyph /> <missing-glyph />
<glyph glyph-name="yidongdaozu" unicode="&#59005;" d="M904.448 270.272 119.616 270.272c-23.68 0-44.736-14.656-52.48-36.48C65.024 227.968 64 221.952 64 216c0-16.32 7.616-32.192 21.184-42.688l293.248-225.728c24.128-18.56 59.008-14.464 78.016 9.088 18.944 23.552 14.848 57.664-9.28 76.224L288 156.544l616 0c30.72 0 56 29.44 56 59.456C960 246.016 935.168 270.272 904.448 270.272zM119.552 497.728l784.832 0c23.68 0 44.736 14.656 52.48 36.48C958.976 540.032 960 546.048 960 552c0 16.32-7.616 32.192-21.184 42.688l-293.248 225.728c-24.128 18.56-59.008 14.464-78.016-9.088C548.608 787.776 552.64 753.6 576.832 735.04L736 611.456 120 611.456C89.28 611.456 64 582.016 64 552 64 521.984 88.832 497.728 119.552 497.728z" horiz-adv-x="1024" />
<glyph glyph-name="shanchu" unicode="&#59474;" d="M736.653061-33.959184H287.346939c-45.97551 0-83.591837 37.616327-83.591837 83.591837V540.734694h616.489796v-491.102041c0-45.97551-37.616327-83.591837-83.591837-83.591837zM245.55102 498.938776v-449.306123c0-22.987755 18.808163-41.795918 41.795919-41.795918h449.306122c22.987755 0 41.795918 18.808163 41.795919 41.795918V498.938776H245.55102zM407.510204 101.877551c-11.493878 0-20.897959 9.404082-20.897959 20.897959V384c0 11.493878 9.404082 20.897959 20.897959 20.897959s20.897959-9.404082 20.897959-20.897959v-261.22449c0-11.493878-9.404082-20.897959-20.897959-20.897959zM616.489796 101.877551c-11.493878 0-20.897959 9.404082-20.897959 20.897959V384c0 11.493878 9.404082 20.897959 20.897959 20.897959s20.897959-9.404082 20.897959-20.897959v-261.22449c0-11.493878-9.404082-20.897959-20.897959-20.897959zM846.367347 498.938776H177.632653c-45.97551 0-83.591837 37.616327-83.591837 83.591836v31.346939c0 45.97551 37.616327 83.591837 83.591837 83.591837h668.734694c45.97551 0 83.591837-37.616327 83.591837-83.591837v-31.346939c0-45.97551-37.616327-83.591837-83.591837-83.591836zM177.632653 655.673469c-22.987755 0-41.795918-18.808163-41.795918-41.795918v-31.346939c0-22.987755 18.808163-41.795918 41.795918-41.795918h668.734694c22.987755 0 41.795918 18.808163 41.795918 41.795918v31.346939c0 22.987755-18.808163 41.795918-41.795918 41.795918H177.632653zM650.44898 655.673469h-276.89796c-28.734694 0-52.244898 23.510204-52.244898 52.244898v41.795919c0 28.734694 23.510204 52.244898 52.244898 52.244898h276.89796c28.734694 0 52.244898-23.510204 52.244898-52.244898v-41.795919c0-28.734694-23.510204-52.244898-52.244898-52.244898z m-276.89796 104.489796c-5.746939 0-10.44898-4.702041-10.448979-10.448979v-41.795919c0-5.746939 4.702041-10.44898 10.448979-10.448979h276.89796c5.746939 0 10.44898 4.702041 10.448979 10.448979v41.795919c0 5.746939-4.702041 10.44898-10.448979 10.448979h-276.89796z" horiz-adv-x="1024" /> <glyph glyph-name="shanchu" unicode="&#59474;" d="M736.653061-33.959184H287.346939c-45.97551 0-83.591837 37.616327-83.591837 83.591837V540.734694h616.489796v-491.102041c0-45.97551-37.616327-83.591837-83.591837-83.591837zM245.55102 498.938776v-449.306123c0-22.987755 18.808163-41.795918 41.795919-41.795918h449.306122c22.987755 0 41.795918 18.808163 41.795919 41.795918V498.938776H245.55102zM407.510204 101.877551c-11.493878 0-20.897959 9.404082-20.897959 20.897959V384c0 11.493878 9.404082 20.897959 20.897959 20.897959s20.897959-9.404082 20.897959-20.897959v-261.22449c0-11.493878-9.404082-20.897959-20.897959-20.897959zM616.489796 101.877551c-11.493878 0-20.897959 9.404082-20.897959 20.897959V384c0 11.493878 9.404082 20.897959 20.897959 20.897959s20.897959-9.404082 20.897959-20.897959v-261.22449c0-11.493878-9.404082-20.897959-20.897959-20.897959zM846.367347 498.938776H177.632653c-45.97551 0-83.591837 37.616327-83.591837 83.591836v31.346939c0 45.97551 37.616327 83.591837 83.591837 83.591837h668.734694c45.97551 0 83.591837-37.616327 83.591837-83.591837v-31.346939c0-45.97551-37.616327-83.591837-83.591837-83.591836zM177.632653 655.673469c-22.987755 0-41.795918-18.808163-41.795918-41.795918v-31.346939c0-22.987755 18.808163-41.795918 41.795918-41.795918h668.734694c22.987755 0 41.795918 18.808163 41.795918 41.795918v31.346939c0 22.987755-18.808163 41.795918-41.795918 41.795918H177.632653zM650.44898 655.673469h-276.89796c-28.734694 0-52.244898 23.510204-52.244898 52.244898v41.795919c0 28.734694 23.510204 52.244898 52.244898 52.244898h276.89796c28.734694 0 52.244898-23.510204 52.244898-52.244898v-41.795919c0-28.734694-23.510204-52.244898-52.244898-52.244898z m-276.89796 104.489796c-5.746939 0-10.44898-4.702041-10.448979-10.448979v-41.795919c0-5.746939 4.702041-10.44898 10.448979-10.448979h276.89796c5.746939 0 10.44898 4.702041 10.448979 10.448979v41.795919c0 5.746939-4.702041 10.44898-10.448979 10.448979h-276.89796z" horiz-adv-x="1024" />
<glyph glyph-name="xiazai" unicode="&#58973;" d="M795.694545 500.363636a289.047273 289.047273 0 0 1-567.38909 0 197.585455 197.585455 0 0 1 18.385454-395.636363h30.952727a23.272727 23.272727 0 0 1 0 46.545454H246.690909a151.272727 151.272727 0 1 0-2.327273 302.545455l23.272728-5.352727 3.025454 25.6a242.269091 242.269091 0 0 0 480.814546 0l4.654545-25.134546 23.272727 4.887273a151.272727 151.272727 0 1 0-2.327272-302.545455h-34.909091a23.272727 23.272727 0 0 1 0-46.545454h35.141818a197.585455 197.585455 0 0 1 18.385454 395.636363zM628.363636 206.196364l-91.927272-93.090909v286.254545a23.272727 23.272727 0 0 1-46.545455 0v-285.090909l-91.927273 93.090909A23.272727 23.272727 0 1 1 365.149091 174.545455l131.490909-131.723637a23.272727 23.272727 0 0 1 33.047273 0L661.178182 174.545455A23.272727 23.272727 0 1 1 628.363636 206.196364z" horiz-adv-x="1024" /> <glyph glyph-name="xiazai" unicode="&#58973;" d="M795.694545 500.363636a289.047273 289.047273 0 0 1-567.38909 0 197.585455 197.585455 0 0 1 18.385454-395.636363h30.952727a23.272727 23.272727 0 0 1 0 46.545454H246.690909a151.272727 151.272727 0 1 0-2.327273 302.545455l23.272728-5.352727 3.025454 25.6a242.269091 242.269091 0 0 0 480.814546 0l4.654545-25.134546 23.272727 4.887273a151.272727 151.272727 0 1 0-2.327272-302.545455h-34.909091a23.272727 23.272727 0 0 1 0-46.545454h35.141818a197.585455 197.585455 0 0 1 18.385454 395.636363zM628.363636 206.196364l-91.927272-93.090909v286.254545a23.272727 23.272727 0 0 1-46.545455 0v-285.090909l-91.927273 93.090909A23.272727 23.272727 0 1 1 365.149091 174.545455l131.490909-131.723637a23.272727 23.272727 0 0 1 33.047273 0L661.178182 174.545455A23.272727 23.272727 0 1 1 628.363636 206.196364z" horiz-adv-x="1024" />

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 49 KiB

View File

@ -32,3 +32,27 @@ html,body{
fill: currentColor; fill: currentColor;
overflow: hidden; overflow: hidden;
} }
::-webkit-scrollbar {
width:5px;
height:5px;
background-color:#F5F5F5;
}
/* 滚动条上的滚动滑块. */
::-webkit-scrollbar-thumb {
background-color: #a3b7cb;
border-radius: 50px;
}
/* 滚动条轨道. */
::-webkit-scrollbar-track {
-webkit-box-shadow:inset 0 0 6px rgba(0, 142, 255, 1);
box-shadow:inset 0 0 6px rgba(0, 142, 255, 1);
background-color:#E7E3DF;
}
/* 滚动条没有滑块的轨道部分 */
::-webkit-scrollbar-track-piece {
background-color: #E7E3DF;
}
/* 当同时有垂直滚动条和水平滚动条时交汇的部分. */
::-webkit-scrollbar-corner {
background:transparent;
}

View File

@ -1,16 +1,20 @@
<template> <template>
<div class="book-wrap"> <div class="book-wrap">
<div class="book-name flex" @click="dialogVisible = true"> <el-scrollbar height="100%">
<span>{{ curBookName }}</span> <div class="book-name flex" @click="dialogVisible = true">
<i class="iconfont icon-xiangyou"></i> <span>{{ curBookName }}</span>
</div> <i class="iconfont icon-xiangyou"></i>
<div class="book-list"> </div>
<el-tree :data="treeData" :props="defaultProps" highlight-current @node-click="handleNodeClick"> <div class="book-list">
<template #default="{ node }"> <el-tree ref="refTree" :data="treeData" :props="defaultProps" node-key="id"
<span :title="node.label" class="tree-label">{{ node.label }}</span> :default-expanded-keys="defaultExpandedKeys" :current-node-key="currentNode" highlight-current
</template> @node-click="handleNodeClick">
</el-tree> <template #default="{ node }">
</div> <span :title="node.label" class="tree-label">{{ node.label }}</span>
</template>
</el-tree>
</div>
</el-scrollbar>
</div> </div>
<!--弹窗 选择教材--> <!--弹窗 选择教材-->
<el-dialog v-model="dialogVisible" append-to-body :show-close="false" width="550" <el-dialog v-model="dialogVisible" append-to-body :show-close="false" width="550"
@ -35,12 +39,12 @@
</template> </template>
<script setup> <script setup>
import { onMounted, ref } from 'vue'; import { onMounted, ref, nextTick, toRaw } from 'vue';
import useUserStore from '@/store/modules/user' import useUserStore from '@/store/modules/user'
import { listEvaluation } from '@/api/subject' import { listEvaluation } from '@/api/subject'
// emit // emit
const emit = defineEmits(['nodeClick']) const emit = defineEmits(['nodeClick', 'changeBook'])
// store // store
const userStore = useUserStore() const userStore = useUserStore()
const { edustage, edusubject, userId } = userStore.user const { edustage, edusubject, userId } = userStore.user
@ -63,6 +67,14 @@ const curBookName = ref('')
const volumeOne = ref([]) const volumeOne = ref([])
// //
const volumeTwo = ref([]) const volumeTwo = ref([])
// ID
const currentNode = ref(0)
//
const currentNodeName = ref('')
//
const defaultExpandedKeys = ref([])
// tree
const refTree = ref(null)
// //
@ -73,14 +85,25 @@ const getSubjectContent = async () => {
entpcourseedituserid: userId, entpcourseedituserid: userId,
pageSize: 500 pageSize: 500
} }
const { rows } = await listEvaluation(params)
evaluationList.value = rows let data;
if (localStorage.getItem('evaluationList')) {
evaluationList.value = JSON.parse(localStorage.getItem('evaluationList'))
data = evaluationList.value
}
else {
const { rows } = await listEvaluation(params)
localStorage.setItem('evaluationList', JSON.stringify(rows))
evaluationList.value = rows
data = rows
}
// //
await getSubject() await getSubject()
// //
volumeOne.value = rows.filter(item => item.level == 1 && item.semester == '上册') volumeOne.value = data.filter(item => item.level == 1 && item.semester == '上册')
// //
volumeTwo.value = rows.filter(item => item.level == 1 && item.semester == '下册') volumeTwo.value = data.filter(item => item.level == 1 && item.semester == '下册')
getTreeData() getTreeData()
} }
@ -93,7 +116,6 @@ const changeBook = ({ id, itemtitle }) => {
setTimeout(() => { setTimeout(() => {
dialogVisible.value = false dialogVisible.value = false
}, 100); }, 100);
} }
const getTreeData = () => { const getTreeData = () => {
@ -101,8 +123,76 @@ const getTreeData = () => {
let upData = transData(volumeOne.value) let upData = transData(volumeOne.value)
let downData = transData(volumeTwo.value) let downData = transData(volumeTwo.value)
treeData.value = upData.length ? upData : downData treeData.value = upData.length ? upData : downData
defaultExpandedKeys.value = [treeData.value[0].id]
nextTick(() => {
currentNode.value = getLastLevelData(treeData.value)[0].id
currentNodeName.value = getLastLevelData(treeData.value)[0].label
emitChangeBook()
})
} }
const emitChangeBook = () => {
let curNode = {
id: currentNode.value,
label: currentNodeName.value
}
let parentNode = findParentByChildId(treeData.value, currentNode.value)
curNode.parentNode = toRaw(parentNode)
const data = {
textBook: {
curBookId: curBookId.value,
curBookName: curBookName.value
},
node: curNode
}
emit('changeBook', data)
}
const getLastLevelData = (tree) => {
let lastLevelData = [];
//
function traverseTree(nodes) {
nodes.forEach((node) => {
//
if (node.children && node.children.length > 0) {
traverseTree(node.children);
} else {
//
lastLevelData.push(node);
}
});
}
//
traverseTree(tree);
//
return lastLevelData;
}
// id
const findParentByChildId = (treeData, targetNodeId) => {
//
//
for (let node of treeData) {
// ID
if (node.children && node.children.some(child => child.id === targetNodeId)) {
// ID ID
return node;
}
//
if (node.children) {
let parentNode = findParentByChildId(node.children, targetNodeId);
if (parentNode) {
return parentNode;
}
}
}
// null
return null;
}
const transData = (data) => { const transData = (data) => {
let ary = [] let ary = []
@ -133,8 +223,16 @@ const transData = (data) => {
// //
const getSubject = async () => { const getSubject = async () => {
const { rows } = await listEvaluation({ itemkey: "version", pageSize: 500 }) if (localStorage.getItem('subjectList')) {
subjectList.value = rows.filter(item => item.edustage == edustage && item.edusubject == edusubject && isHaveUnit(item.id)) subjectList.value = JSON.parse(localStorage.getItem('subjectList'))
}
else {
const { rows } = await listEvaluation({ itemkey: "version", pageSize: 500 })
subjectList.value = rows.filter(item => item.edustage == edustage && item.edusubject == edusubject && isHaveUnit(item.id))
localStorage.setItem('subjectList', JSON.stringify(subjectList.value))
}
//
curBookName.value = subjectList.value[0].itemtitle curBookName.value = subjectList.value[0].itemtitle
curBookId.value = subjectList.value[0].id curBookId.value = subjectList.value[0].id
} }
@ -146,12 +244,32 @@ const isHaveUnit = (id) => {
}) })
} }
const renderContent = (h, { node, data }) => {
console.log(node, data)
}
const handleNodeClick = (data) => { const handleNodeClick = (data, node) => {
emit('nodeClick', data) /**
* data : 当前节点数据
* node : 当前节点对象 包含当前节点所有数据 parent属性 指向父节点Node对象
*/
const currentNode = data;
const parentNode = node.parent.data;
if (Array.isArray(parentNode)) {
currentNode.parentNode = null
}
else {
currentNode.parentNode = parentNode
}
let curData = {
textBook: {
curBookId: curBookId.value,
curBookName: curBookName.value
},
node: toRaw(currentNode)
}
emit('nodeClick', curData)
} }
onMounted(() => { onMounted(() => {
@ -161,7 +279,6 @@ onMounted(() => {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.book-wrap { .book-wrap {
width: 300px; width: 300px;
height: 100%; height: 100%;
@ -170,10 +287,17 @@ onMounted(() => {
box-shadow: 0px 0px 20px 0px rgba(99, 99, 99, 0.06); box-shadow: 0px 0px 20px 0px rgba(99, 99, 99, 0.06);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
position: relative;
.book-name { .book-name {
background-color: #ffffff;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 45px; height: 45px;
padding: 0 15px; padding: 0 15px;
z-index: 1;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
color: #3b3b3b; color: #3b3b3b;
@ -184,7 +308,7 @@ onMounted(() => {
} }
.book-list { .book-list {
padding-left: 10px; padding: 45px 10px 0 10px;
flex: 1; flex: 1;
} }
} }
@ -240,12 +364,12 @@ onMounted(() => {
:deep(.el-tree-node) { :deep(.el-tree-node) {
.el-tree-node__content { .el-tree-node__content {
height: 40px; height: 40px;
border-radius: 10px;
&:hover { &:hover {
background-color: #eaf3ff; background-color: #eaf3ff;
} }
} }
} }
.tree-label { .tree-label {
@ -254,9 +378,8 @@ onMounted(() => {
text-overflow: ellipsis; text-overflow: ellipsis;
} }
:deep(.el-tree--highlight-current .el-tree-node.is-current>.el-tree-node__content){ :deep(.el-tree--highlight-current .el-tree-node.is-current>.el-tree-node__content) {
background-color: #d9e8fe !important; background-color: #eaf3ff !important;
color: #409EFF color: #409EFF
} }
</style> </style>

View File

@ -0,0 +1,47 @@
<template>
<svg class="icon file-icon" aria-hidden="true" :style="{'font-size': size + 'px'}">
<use :xlink:href="getFileTypeIcon()"></use>
</svg>
</template>
<script setup>
import { defineProps } from 'vue'
const props = defineProps({
fileName: {
type: String,
default: ''
},
size: {
type: Number,
default: 30
}
})
const getFileTypeIcon = () => {
const name = props.fileName.substr(props.fileName.lastIndexOf('.') + 1);
const iconObj = {
pdf: 'icon-pdf',
ppt: 'icon-ppt',
pptx: 'icon-pptx',
doc: 'icon-word',
docx: 'icon-word',
mp4: 'icon-video',
mov: 'icon-mov',
avi: 'icon-avi',
jpeg: 'icon-jpeg',
jpg: 'icon-jpg',
png: 'icon-png',
gif: 'icon-gif',
txt: 'icon-txt',
rar: 'icon-rar',
}
return '#' + iconObj[name]
}
</script>
<style lang="scss" scoped>
</style>

View File

@ -0,0 +1,384 @@
<template>
<el-dialog v-model="dialogVisible" append-to-body :show-close="false" width="630" :before-close="beforeClose"
style="border-radius: 5px;padding-top: 0">
<div class="dialog-title flex">
<span>{{ title }}</span>
<i class="iconfont icon-close" @click="closeDialog"></i>
</div>
<div class="dialog-content">
<div class="book-name flex" @click="bookVisible = true">
<span>{{ curBookName }}</span>
<i class="iconfont icon-yidongdaozu"></i>
</div>
<el-scrollbar height="400px">
<div class="book-data">
<el-tree ref="refTree" :data="treeData" :props="defaultProps" node-key="id"
:default-expanded-keys="defaultExpandedKeys" :current-node-key="currentNodeId" highlight-current
@node-click="handleNodeClick">
<template #default="{ node }">
<span :title="node.label" class="tree-label">{{ node.label }}</span>
</template>
</el-tree>
</div>
</el-scrollbar>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="closeDialog">取消</el-button>
<el-button type="primary" @click="onSubmit">
确定
</el-button>
</div>
</template>
</el-dialog>
<!--选择教材弹窗-->
<el-dialog v-model="bookVisible" append-to-body :show-close="false" width="550"
style="border-radius: 10px; padding: 10px 15px;">
<template #header>
<div class="choose-book-header flex">
<span>切换教材</span>
<i class="iconfont icon-guanbi" @click="bookVisible = false"></i>
</div>
</template>
<div class="textbook-container">
<el-scrollbar height="450px">
<div class="textbook-item flex" v-for="item in subjectList" :class="curBookId == item.id ? 'active-item' : ''"
:key="item.id" @click="changeBook(item)">
<img :src="item.avartar" class="textbook-img" alt="">
<span class="book-name">{{ item.itemtitle }}</span>
</div>
</el-scrollbar>
</div>
</el-dialog>
</template>
<script setup>
import { ref, reactive, toRaw, onMounted, nextTick, watch, defineProps, defineEmits } from 'vue'
const props = defineProps({
modelValue: {
type: Boolean,
default: false
},
title: {
type: String,
default: '移动至'
}
})
const dialogVisible = ref(false)
const bookVisible = ref(false)
//
const subjectList = ref([])
const evaluationList = ref([])
const treeData = ref([])
const defaultProps = {
children: 'children',
label: 'label',
class: 'textbook-tree'
}
//ID
const curBookId = ref(-1)
//
const curBookName = ref('')
//
const volumeOne = ref([])
//
const volumeTwo = ref([])
//
const currentNode = reactive({
data: {}
})
// ID
const currentNodeId = ref(0)
//
const currentNodeName = ref('')
//
const defaultExpandedKeys = ref([])
// tree
const refTree = ref(null)
// emit
const emit = defineEmits(['update:modelValue', 'onSubmit'])
watch(() => props.modelValue, (newVal) => {
dialogVisible.value = newVal
})
const getSubjectContent = () => {
evaluationList.value = JSON.parse(localStorage.getItem('evaluationList'))
let data = evaluationList.value
//
getSubject()
//
volumeOne.value = data.filter(item => item.level == 1 && item.semester == '上册')
//
volumeTwo.value = data.filter(item => item.level == 1 && item.semester == '下册')
getTreeData()
}
const getSubject = () => {
subjectList.value = JSON.parse(localStorage.getItem('subjectList'))
//
curBookName.value = subjectList.value[0].itemtitle
curBookId.value = subjectList.value[0].id
}
const getTreeData = () => {
//
let upData = transData(volumeOne.value)
let downData = transData(volumeTwo.value)
treeData.value = upData.length ? upData : downData
defaultExpandedKeys.value = [treeData.value[0].id]
nextTick(() => {
currentNodeId.value = getLastLevelData(treeData.value)[0].id
currentNodeName.value = getLastLevelData(treeData.value)[0].label
emitChangeBook()
})
}
const emitChangeBook = () => {
let curNode = {
id: currentNodeId.value,
label: currentNodeName.value
}
let parentNode = findParentByChildId(treeData.value, currentNodeId.value)
curNode.parentNode = toRaw(parentNode)
const data = {
textBook: {
curBookId: curBookId.value,
curBookName: curBookName.value
},
node: curNode
}
currentNode.data = data
}
// id
const findParentByChildId = (treeData, targetNodeId) => {
//
//
for (let node of treeData) {
// ID
if (node.children && node.children.some(child => child.id === targetNodeId)) {
// ID ID
return node;
}
//
if (node.children) {
let parentNode = findParentByChildId(node.children, targetNodeId);
if (parentNode) {
return parentNode;
}
}
}
// null
return null;
}
const handleNodeClick = (data, node) => {
/**
* data : 当前节点数据
* node : 当前节点对象 包含当前节点所有数据 parent属性 指向父节点Node对象
*/
const nodeData = data;
const parentNode = node.parent.data;
if (Array.isArray(parentNode)) {
nodeData.parentNode = null
}
else {
nodeData.parentNode = parentNode
}
let curData = {
textBook: {
curBookId: curBookId.value,
curBookName: curBookName.value
},
node: toRaw(nodeData)
}
currentNode.data = curData
// emit('nodeClick', curData)
}
const transData = (data) => {
let ary = []
data.forEach(item => {
let obj = {}
if (item.rootid == curBookId.value) {
obj.label = item.itemtitle
obj.id = item.id
let ary2 = []
evaluationList.value.forEach(el => {
let obj2 = {}
if (item.id == el.parentid) {
obj2 = {
label: el.itemtitle,
id: el.id
}
ary2.push(obj2)
}
obj.children = ary2
})
ary.push(obj)
}
})
return ary
}
//
const getLastLevelData = (tree) => {
let lastLevelData = [];
//
function traverseTree(nodes) {
nodes.forEach((node) => {
//
if (node.children && node.children.length > 0) {
traverseTree(node.children);
} else {
//
lastLevelData.push(node);
}
});
}
//
traverseTree(tree);
//
return lastLevelData;
}
//
const changeBook = ({ id, itemtitle }) => {
curBookId.value = id
curBookName.value = itemtitle
getTreeData()
setTimeout(() => {
bookVisible.value = false
}, 0);
}
const onSubmit = () => {
emit('onSubmit', toRaw(currentNode.data))
closeDialog()
}
const closeDialog = () => {
emit('update:modelValue', false)
}
//
const beforeClose = (done) => {
emit('update:modelValue', false)
done()
}
onMounted(() => {
getSubjectContent()
})
</script>
<style lang="scss" scoped>
.dialog-title {
justify-content: space-between;
font-size: 16px;
font-weight: 600;
color: #000;
.icon-close{
cursor: pointer;
}
}
.dialog-content {
padding: 10px 20px;
.book-name {
justify-content: space-between;
font-size: 14px;
font-weight: 600;
color: #0f0f0f;
margin: 15px 0;
cursor: pointer;
.icon-yidongdaozu {
color: #3a3a3a;
}
}
}
.choose-book-header {
justify-content: space-between;
font-size: 15px;
font-weight: bold;
.icon-guanbi {
font-size: 20px;
cursor: pointer;
}
}
.textbook-container {
.textbook-item {
padding: 10px 20px;
align-items: center;
border-radius: 5px;
cursor: pointer;
.book-name {
margin-left: 20px;
color: #3b3b3b;
font-size: 13px;
}
&:hover {
background: #f4f7f9;
}
}
.active-item {
background-color: #f4f7f9;
.book-name {
color: #368fff;
font-weight: bold
}
}
.textbook-img {
width: 55px;
height: 70px;
}
}
:deep(.el-tree-node) {
.el-tree-node__content {
height: 40px;
border-radius: 10px;
&:hover {
background-color: #eaf3ff;
}
}
}
:deep(.el-tree--highlight-current .el-tree-node.is-current>.el-tree-node__content) {
background-color: #eaf3ff !important;
color: #409EFF;
font-weight: bold
}
</style>

View File

@ -1,10 +1,11 @@
<template> <template>
<el-dialog v-model="dialogValue" width="630" :before-close="beforeClose"> <el-dialog v-model="dialogValue" width="630" :before-close="beforeClose" style="border-radius: 5px;">
<div class="file-dialog"> <div class="file-dialog">
<el-form> <el-form>
<el-form-item label="文件"> <el-form-item label="文件">
<div class="create-item file-item flex"> <div class="create-item file-item flex">
<el-upload action="" multiple :before-upload="hanleFileBefore" :auto-upload="true"> <el-upload :file-list="fileList" :show-file-list="false" :auto-upload="false" multiple
:on-change="hanleFileChange">
<el-button slot="trigger">选择文件</el-button> <el-button slot="trigger">选择文件</el-button>
</el-upload> </el-upload>
<span class="upload-desc">说明一次最多上传5个文件单个文件大小不能大于100M</span> <span class="upload-desc">说明一次最多上传5个文件单个文件大小不能大于100M</span>
@ -15,14 +16,13 @@
<div class="file-list-item flex" v-for="(item, index) in fileList" :key="item.uid"> <div class="file-list-item flex" v-for="(item, index) in fileList" :key="item.uid">
<div class="file-name"> <div class="file-name">
<span class="name">标题</span> <span class="name">标题</span>
<svg class="icon icon-ppt" aria-hidden="true"> <FileImage :fileName="item.name" size="50"/>
<use :xlink:href="getFileTypeIcon(item.name)"></use> <el-input class="file-input" v-model="item.fileData.name" placeholder="请输入文件名" />
</svg> <span>.{{ getFileSuffix(item.name) }}</span>
<span class="text">{{ item.name }}</span>
</div> </div>
<div class="flex-type flex"> <div class="flex-type flex">
<span class="name">类别</span> <span class="name">类别</span>
<el-select v-model="item.fileType" placeholder="Select" style="width: 100px"> <el-select v-model="item.fileData.fileFlag" placeholder="Select" style="width: 100px">
<el-option v-for="item in resourceType" :key="item.alue" :label="item.label" :value="item.value" /> <el-option v-for="item in resourceType" :key="item.alue" :label="item.label" :value="item.value" />
</el-select> </el-select>
@ -46,7 +46,11 @@
<script setup> <script setup>
import { ref, defineProps, defineEmits, watch } from 'vue' import { ref, defineProps, defineEmits, watch } from 'vue'
import FileImage from '@/components/file-image/index.vue'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { resourceType } from '@/utils/resourceDict'
import { getFileSuffix, getFileName } from '@/utils/ruoyi'
const props = defineProps({ const props = defineProps({
modelValue: { modelValue: {
@ -60,30 +64,14 @@ const emit = defineEmits(['update:modelValue', 'submitFile'])
// //
const fileList = ref([]) const fileList = ref([])
const fileType = ref(1)
//
const resourceType = ref([
{
label: '课件',
value: 1
},
{
label: '教案',
value: 2
},
{
label: '素材',
value: 3
}
])
watch(() => props.modelValue, (newVal) => { watch(() => props.modelValue, (newVal) => {
dialogValue.value = newVal dialogValue.value = newVal
}) })
const hanleFileBefore = (rawFile) => {
// const hanleFileChange = (file) => {
console.log(file)
//
const audioTypes = ['audio/mpeg', 'audio/wav', 'audio/ogg', 'audio/aac'] const audioTypes = ['audio/mpeg', 'audio/wav', 'audio/ogg', 'audio/aac']
// //
const videoTypes = ['video/mp4', 'video/webm', 'video/ogg'] const videoTypes = ['video/mp4', 'video/webm', 'video/ogg']
@ -93,36 +81,33 @@ const hanleFileBefore = (rawFile) => {
const pptTypes = ['application/vnd.ms-powerpoint', 'application/vnd.openxmlformats-officedocument.presentationml.presentation'] const pptTypes = ['application/vnd.ms-powerpoint', 'application/vnd.openxmlformats-officedocument.presentationml.presentation']
// pdf // pdf
const pdfTypes = ['application/pdf'] const pdfTypes = ['application/pdf']
// zip
const zipTypes = ['application/x-zip-compressed','application/x-compressed']
//
const imgTypes = ['image/jpeg','image/gif', 'image/png']
// text
const textTypes = ['text/plain']
const fileType = rawFile.type const fileType = file.raw.type
if (!(audioTypes.includes(fileType) || videoTypes.includes(fileType) || wordTypes.includes(fileType) || pptTypes.includes(fileType) || pdfTypes.includes(fileType))) { if (!(audioTypes.includes(fileType) || videoTypes.includes(fileType) || wordTypes.includes(fileType) || pptTypes.includes(fileType) || pdfTypes.includes(fileType) || zipTypes.includes(fileType) || imgTypes.includes(fileType) || textTypes.includes(fileType))) {
ElMessage.error('文件格式错误! 请上传音频、视频、word、ppt、pdf文件!') ElMessage.error('文件格式错误! 请上传图片、音频、视频、word、ppt、pdf、txt、zip文件!')
return false return false
} }
// //
const fileSize = rawFile.size / 1024 / 1024 > 100 const fileSize = file.raw.size / 1024 / 1024 > 100
if (fileSize) { if (fileSize) {
ElMessage.error('文件大小错误! 请上传小于100M的文件!') ElMessage.error('文件大小错误! 请上传小于100M的文件!')
return false return false
} }
console.log(rawFile) if (file.status === 'ready') {
rawFile.fileType = 1
fileList.value.push(rawFile)
return false // fileData
} file.fileData = {
fileFlag: '课件',
const getFileTypeIcon = (fileName)=>{ name: getFileName(file.name),
const name = fileName.substr(fileName.lastIndexOf('.') + 1); }
console.log(name) fileList.value.push(file)
const iconObj = {
pdf: 'icon-pdf',
ppt: 'icon-ppt',
doc: 'icon-word',
docx: 'icon-word',
mp4: 'icon-video'
} }
return '#' + iconObj[name]
} }
// //
@ -143,11 +128,16 @@ const closeDialog = () => {
} }
// //
const submitFile = () => { const submitFile = () => {
emit('submitFile', fileList.value) //
fileList.value.forEach((item) => {
let suffix = getFileSuffix(item.name)
item.fileData.fileShowName = item.fileData.name + '.' + suffix
delete item.fileData.name
})
emit('submitFile',fileList.value)
closeDialog()
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -172,15 +162,18 @@ const submitFile = () => {
width: 100%; width: 100%;
justify-content: space-between; justify-content: space-between;
margin-bottom: 15px; margin-bottom: 15px;
.file-name { .file-name {
width: 50%; width: 55%;
margin-right: 20px; margin-right: 20px;
display: flex; display: flex;
align-items: center align-items: center
} }
.icon{
.icon {
font-size: 30px; font-size: 30px;
} }
.flex-type { .flex-type {
align-items: center; align-items: center;
margin-right: 20px; margin-right: 20px;
@ -192,6 +185,7 @@ const submitFile = () => {
.name { .name {
position: relative; position: relative;
flex-shrink: 0; flex-shrink: 0;
&::after { &::after {
content: '*'; content: '*';
position: absolute; position: absolute;
@ -210,4 +204,24 @@ const submitFile = () => {
} }
} }
} }
.file-input {
border-bottom: solid #dfdfdf 1px;
&:hover {
border-color: #409EFF;
}
&:focus{
border-color: #409EFF;
}
}
:deep(.el-input__wrapper){
box-shadow: none
}
:deep(.el-input__wrapper.is-focus){
box-shadow: none
}
:deep(.el-input__wrapper:hover){
box-shadow: none
}
</style> </style>

View File

@ -1,5 +1,5 @@
<template> <template>
<el-popover placement="left-end" width="300px" trigger="click"> <el-popover placement="left-end" width="300px" title="文件上传" trigger="click">
<template #default> <template #default>
<el-upload <el-upload
ref="talk_uploader_core" ref="talk_uploader_core"
@ -22,7 +22,7 @@
<div class="talk-uploader-body"> <div class="talk-uploader-body">
<div v-for="(item, index) in uploaderStore.uploadList" :key="index"> <div v-for="(item, index) in uploaderStore.uploadList" :key="index">
<div class="prepare-body-main-item"> <div class="prepare-body-main-item">
<div class="prepare-uploader-progress" :style="{'width': item.percentage+'%'}"></div> <div class="prepare-uploader-progress" :style="{ width: item.percentage + '%' }"></div>
<div class="prepare-body-main-item-icon"> <div class="prepare-body-main-item-icon">
<svg <svg
class="icon" class="icon"
@ -35,11 +35,11 @@
</svg> </svg>
</div> </div>
<div class="prepare-body-main-item-info"> <div class="prepare-body-main-item-info">
<div class="prepare-item-info-title">平面向量基本定理及坐标表示</div> <div class="prepare-item-info-title">{{ item.raw.name }}</div>
<div class="prepare-item-info-message"> <div class="prepare-item-info-message">
<div>1.6MB</div> <div>{{formatFileSize(item.raw.size)}}</div>
&nbsp;&nbsp;|&nbsp;&nbsp; <!-- &nbsp;&nbsp;|&nbsp;&nbsp;-->
<div>古诗词诵读 > 静女</div> <!-- <div>古诗词诵读 > 静女</div>-->
</div> </div>
</div> </div>
<div class="prepare-body-main-item-tool" @click="removeUploadFile(item.uid)"> <div class="prepare-body-main-item-tool" @click="removeUploadFile(item.uid)">
@ -99,6 +99,23 @@ export default {
}, 1000) }, 1000)
}, },
methods: { methods: {
formatFileSize(fileSize) {
if (fileSize < 1024) {
return fileSize + 'B'
} else if (fileSize < 1024 * 1024) {
let temp = fileSize / 1024
temp = temp.toFixed(2)
return temp + 'KB'
} else if (fileSize < 1024 * 1024 * 1024) {
let temp = fileSize / (1024 * 1024)
temp = temp.toFixed(2)
return temp + 'MB'
} else {
let temp = fileSize / (1024 * 1024 * 1024)
temp = temp.toFixed(2)
return temp + 'GB'
}
},
onSuccess(res, file, files) { onSuccess(res, file, files) {
this.removeUploadFile(file.uid) this.removeUploadFile(file.uid)
file.callback(res) file.callback(res)
@ -119,7 +136,7 @@ export default {
this.uploadDatas = this.uploadNow.fileData this.uploadDatas = this.uploadNow.fileData
this.getFileMD5(this.uploadNow.raw).then((md5) => { this.getFileMD5(this.uploadNow.raw).then((md5) => {
this.uploadDatas.md5 = md5 this.uploadDatas.md5 = md5
this.$refs.talk_uploader_core.handleStart(this.uploadNow.raw) // this.$refs.talk_uploader_core.handleStart(this.uploadNow.raw)
this.$refs.talk_uploader_core.submit() this.$refs.talk_uploader_core.submit()
}) })
} }
@ -158,10 +175,15 @@ export default {
.talk-uploader-body { .talk-uploader-body {
width: 100%; width: 100%;
height: 300px; height: 300px;
overflow: auto;
} }
.prepare-body-main-item { .prepare-body-main-item {
position: relative; position: relative;
.prepare-uploader-progress{ display: flex;
align-items: center;
border-bottom: 1px solid rgba(131, 131, 127, 0.17);
padding: 10px 0;
.prepare-uploader-progress {
height: 100%; height: 100%;
position: absolute; position: absolute;
background-color: #83bb67; background-color: #83bb67;
@ -171,10 +193,7 @@ export default {
&:hover { &:hover {
background-color: rgba(144, 147, 153, 0.2); background-color: rgba(144, 147, 153, 0.2);
} }
display: flex;
align-items: center;
border-bottom: 1px solid rgba(131, 131, 127, 0.17);
padding: 10px 0;
.prepare-body-main-item-icon { .prepare-body-main-item-icon {
width: 80px; width: 80px;
} }

View File

@ -84,6 +84,7 @@ const useUserStore = defineStore(
this.token = '' this.token = ''
this.roles = [] this.roles = []
this.permissions = [] this.permissions = []
localStorage.clear()
removeToken() removeToken()
resolve() resolve()
}).catch(error => { }).catch(error => {

View File

@ -0,0 +1,78 @@
// 判断是不是昨天
const isYestday = (date) => {
var yesterday = new Date(new Date() - 1000 * 60 * 60 * 24)
return (
yesterday.getYear() === date.getYear() &&
yesterday.getMonth() === date.getMonth() &&
yesterday.getDate() === date.getDate()
)
}
// 判断是不是今年
const isYear = (date) => {
return date.getYear() === new Date().getYear()
}
//将时间转为 yy/mm/dd hh:mm:ss
const formatDateTime = (date) => {
if (!date) {
return ''
}
var dateObject = new Date(date)
var y = dateObject.getFullYear()
var m = dateObject.getMonth() + 1
m = m < 10 ? '0' + m : m
var d = dateObject.getDate()
d = d < 10 ? '0' + d : d
var h = dateObject.getHours()
h = h < 10 ? '0' + h : h
var minute = dateObject.getMinutes()
minute = minute < 10 ? '0' + minute : minute
var second = dateObject.getSeconds()
second = second < 10 ? '0' + second : second
return y + '/' + m + '/' + d + ' ' + h + ':' + minute + ':' + second
}
/**
* 将时间戳转换为文本描述
*
* 此函数旨在将时间戳转换为更易读的文本格式以便用户可以更直观地理解时间信息
* 通过传入的时间函数将生成对应的时间文本"刚刚""5分钟前""昨天"
* 如果设置了simple参数为true则会返回更简单的23/06/25只显示年月日如果是今年只返回月和日
*
* @param {string} timeStamp - 需要转换的时间
* @param {boolean} simple - 是否使用简单的格式默认为false23/06/2506/25
* @returns {string} - 转换后的时间文本
*/
export const toTimeText = (timeStamp, simple) => {
if (!timeStamp) {
return ''
}
var dateTime = new Date(timeStamp)
var currentTime = Date.parse(new Date()) //当前时间
var timeDiff = currentTime - dateTime //与当前时间误差
var timeText = ''
if (timeDiff <= 60000) {
//一分钟内
timeText = '刚刚'
} else if (timeDiff > 60000 && timeDiff < 3600000) {
//1小时内
timeText = Math.floor(timeDiff / 60000) + '分钟前'
} else if (timeDiff >= 3600000 && timeDiff < 86400000 && !isYestday(dateTime)) {
//今日
timeText = formatDateTime(dateTime).substr(11, 5)
} else if (isYestday(dateTime)) {
//昨天
timeText = '昨天' + formatDateTime(dateTime).substr(11, 5)
} else if (isYear(dateTime)) {
//今年
timeText = formatDateTime(dateTime).substr(5, simple ? 5 : 14)
} else {
//不属于今年
timeText = formatDateTime(dateTime)
if (simple) {
timeText = timeText.substr(2, 8)
}
}
return timeText
}

View File

@ -0,0 +1,65 @@
export const tabs = [
{
label: '平台资源',
value: '平台'
},
{
label: '校本资源',
value: '校本'
}
]
// 资源格式
export const resourceFormat = [
{
label: 'word',
value: 'word'
},
{
label: 'ppt',
value: 'ppt'
},
{
label: 'mp3',
value: 'mp3'
},
{
label: 'mp4',
value: 'mp4'
},
{
label: 'JPG',
value: 'jpg'
},
{
label: 'PNG',
value: 'png'
},
{
label: 'RAR',
value: 'rar'
},
{
label: 'TXT',
value: 'txt'
}
]
// 资源类型
export const resourceType = [
{
label: '素材',
value: '素材'
},
{
label: '课件',
value: '课件'
},
{
label: '教案',
value: '教案'
}
]

View File

@ -245,3 +245,15 @@ export function getNormalPath(p) {
export function blobValidate(data) { export function blobValidate(data) {
return data.type !== 'application/json' return data.type !== 'application/json'
} }
// 获取文件后缀
export const getFileSuffix = (filename) => {
return filename.substring(filename.lastIndexOf('.') + 1);
}
// 获取文件名(不带后缀)
export const getFileName = (filename) => {
// 使用正则表达式匹配文件名部分(不包括后缀)
if(!filename) return
return filename.replace(/\.[^/.]+$/, "");
}

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="page-resource flex"> <div class="page-resource flex" v-loading="isLoading">
<ChooseTextbook @node-click="nodeClick" /> <ChooseTextbook @changeBook="changeBook" @node-click="nodeClick" />
<div class="page-right"> <div class="page-right">
<div class="prepare-body-header"> <div class="prepare-body-header">
<div> <div>
@ -8,62 +8,84 @@
<el-popover placement="top-start" :width="250" trigger="hover"> <el-popover placement="top-start" :width="250" trigger="hover">
<template #default> <template #default>
<div> <div>
<el-button type="success" size="small" :icon="Check" circle /> 2024-07-11 16:15 <el-button type="success" size="small" :icon="Check" circle />
2024-07-11 16:15
同步成功 同步成功
</div> </div>
</template> </template>
<template #reference> <template #reference>
<el-button size="small" text <el-button size="small" text
><el-icon><Refresh /></el-icon></el-button >
<el-icon>
<Refresh />
</el-icon>
云同步
</el-button
> >
</template> </template>
</el-popover> </el-popover>
</div> </div>
<div style="display: flex"> <div style="display: flex">
<el-upload <el-button @click="isDialogOpen=true">上传资料</el-button>
ref="choosefile" <el-button type="primary" style="margin-left: 10px">新建课件</el-button>
v-model:file-list="fileList"
name="file"
:show-file-list="false"
:auto-upload="false"
:multiple="true"
:on-change="chooseFile"
class="editor-img-uploader"
>
<el-button>上传资料</el-button>
</el-upload>
<el-button type="primary" @click="changeFile">新建课件</el-button>
<el-button type="primary" @click="clearFile">crear</el-button>
</div> </div>
</div> </div>
<div class="prepare-body-main"> <div class="prepare-body-main">
<div v-for="index in 10" :key="index" class="prepare-body-main-item"> <div v-for="(item,index) in currentFileList" :key="index" class="prepare-body-main-item">
<div class="prepare-body-main-item-icon"> <div class="prepare-body-main-item-icon">
<svg class="icon" aria-hidden="true" font-size="50px" color="red" style="margin: auto"> <svg class="icon" aria-hidden="true" font-size="50px" color="red" style="margin: auto">
<use xlink:href="#icon-ppt"></use> <use xlink:href="#icon-ppt"></use>
</svg> </svg>
</div> </div>
<div class="prepare-body-main-item-info"> <div class="prepare-body-main-item-info">
<div class="prepare-item-info-title">平面向量基本定理及坐标表示</div> <div class="prepare-item-info-title">{{ item.fileShowName }}</div>
<div class="prepare-item-info-message"> <div class="prepare-item-info-message">
<div> <div style="width: 80px">
<el-icon <el-icon
style="background-color: green; border-radius: 20px; color: white; top: 2px" style="background-color: green; border-radius: 20px; color: white; top: 2px"
><Check /></el-icon >
>已同步 <Check />
</el-icon
>
已同步
</div>
|
<div style="width: 80px">{{ formatFileSize(item.fileSize) }}</div>
|
<div style="width: 100px">{{ toTimeText(item.uploadTime, true) }}</div>
|
<div style="white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;" :title="item.levelFirstName + (item.levelSecondName ? ' > ' + item.levelSecondName : '') + (item.levelThirdNmae ? ' > ' + item.levelThirdNmae : '')">
{{ item.levelFirstName + (item.levelSecondName ? ' > ' + item.levelSecondName : '') + (item.levelThirdNmae ? ' > ' + item.levelThirdNmae : '') }}
</div> </div>
&nbsp;&nbsp;|&nbsp;&nbsp;
<div>1.6MB</div>
&nbsp;&nbsp;|&nbsp;&nbsp;
<div>2024-07-10</div>
&nbsp;&nbsp;|&nbsp;&nbsp;
<div>古诗词诵读 > 静女</div>
</div> </div>
</div> </div>
<div class="prepare-body-main-item-tool"> <div class="prepare-body-main-item-tool">
<el-popover placement="left-start" popper-class="prepare-popper" trigger="click"> <el-popover placement="left-start" :hide-after="100" :ref="'popover_'+index" popper-class="prepare-popper" trigger="click">
<template #default> <template #default>
<div style="width: 100%; height: 100px; background-color: #003b94"></div> <div style="width: 100%;">
<div class="item-popover">
<div class="item-popover-item">
<el-button text @click="editTalk(item,index)">
<i class="iconfont icon-bianji"></i>
<span>重命名</span>
</el-button>
</div>
<div class="item-popover-item">
<el-button text @click="deleteTalk(item);closePopver(index)">
<i class="iconfont icon-shanchu"></i>
<span>删除</span>
</el-button>
</div>
<div class="item-popover-item">
<el-button text @click="downloadFile(item)">
<i class="iconfont icon-xiazai"></i>
<span>下载</span>
</el-button>
</div>
</div>
</div>
</template> </template>
<template #reference> <template #reference>
<span class="iconfont icon-shenglvehao" style="cursor: pointer"></span> <span class="iconfont icon-shenglvehao" style="cursor: pointer"></span>
@ -73,35 +95,47 @@
</div> </div>
</div> </div>
</div> </div>
<uploadDialog v-model="isDialogOpen" @submitFile="submitFile" />
</div> </div>
</template> </template>
<script setup> <script setup>
import { Check } from '@element-plus/icons-vue' import { Check } from '@element-plus/icons-vue'
import ChooseTextbook from '@/components/choose-textbook/index.vue'
import { deleteSmarttalk } from '@/api/file'
</script> </script>
<script> <script>
import FileUpload from '@/components/file-upload/index.vue' import FileUpload from '@/components/file-upload/index.vue'
import ChooseTextbook from '@/components/choose-textbook/index.vue' import ChooseTextbook from '@/components/choose-textbook/index.vue'
import ResoureList from '@/views/resource/container/resoure-list.vue' import ResoureList from '@/views/resource/container/resoure-list.vue'
import ResoureSearch from '@/views/resource/container/resoure-search.vue' import ResoureSearch from '@/views/resource/container/resoure-search.vue'
import uploadDialog from '@/components/upload-dialog/index.vue'
import { Refresh } from '@element-plus/icons-vue' import { Refresh } from '@element-plus/icons-vue'
import uploaderState from '@/store/modules/uploader' import uploaderState from '@/store/modules/uploader'
import { deleteSmarttalk, getSmarttalkPage, updateSmarttalk } from '@/api/file'
import { toTimeText } from '@/utils/date'
import { ElMessage, ElMessageBox } from 'element-plus'
// import { getSmarttalkPage } from '@/api/file' // import { getSmarttalkPage } from '@/api/file'
const { ipcRenderer } = window.electron || {} const { ipcRenderer } = window.electron || {}
export default { export default {
name: 'Prepare', name: 'Prepare',
components: { ResoureSearch, ResoureList, ChooseTextbook, FileUpload, Refresh }, components: { ResoureSearch, ResoureList, ChooseTextbook, FileUpload, Refresh, uploadDialog },
data() { data() {
return { return {
fileList:[], isLoading: false,
fileUrl: isDialogOpen: false,
'https://wzyzoss.eos-chongqing-3.cmecloud.cn/2024/7/10/117cdf208c6b4e58bf2b73369eaf3cb5.pptx', showToolVisible: false,
filePath: 'C:/Users/zhuhao/Desktop/工作文档/0901高一【数学(人教A版)】集合的概念-PPT课件.pptx', fileList: [],
currentNode: {},
currentFileList: [],
// fileUrl:
// 'https://wzyzoss.eos-chongqing-3.cmecloud.cn/2024/7/10/117cdf208c6b4e58bf2b73369eaf3cb5.pptx',
// filePath: 'C:/Users/zhuhao/Desktop//0901(A)-PPT.pptx',
uploadData: { uploadData: {
textbookId: '123', textbookId: null,
levelFirstId: '123', levelFirstId: 39103,
levelSecondId: '123', levelSecondId: null,
fileSource: '平台', fileSource: '个人',
fileFlag: '课件' fileRoot: '备课'
} }
} }
}, },
@ -112,6 +146,8 @@ export default {
}) })
}, },
mounted() { mounted() {
//
// const destination = '0901(A)-PPT.pptx' // const destination = '0901(A)-PPT.pptx'
// ipcRenderer.send('open-path-app',this.filePath) // ipcRenderer.send('open-path-app',this.filePath)
// const source = 'D:\\edufile\\0901(A)-PPT.pptx' // const source = 'D:\\edufile\\0901(A)-PPT.pptx'
@ -122,6 +158,67 @@ export default {
// }) // })
}, },
methods: { methods: {
formatFileSize(fileSize) {
if (fileSize < 1024) {
return fileSize + 'B'
} else if (fileSize < (1024 * 1024)) {
var temp = fileSize / 1024
temp = temp.toFixed(2)
return temp + 'KB'
} else if (fileSize < (1024 * 1024 * 1024)) {
var temp = fileSize / (1024 * 1024)
temp = temp.toFixed(2)
return temp + 'MB'
} else {
var temp = fileSize / (1024 * 1024 * 1024)
temp = temp.toFixed(2)
return temp + 'GB'
}
},
editTalk(item) {
ElMessageBox.prompt('请输入新的名称', '重命名', {
confirmButtonText: '确认',
cancelButtonText: '取消',
inputValue: item.fileShowName.substring(0,item.fileShowName.lastIndexOf('.')),
})
.then(({ value }) => {
item.fileShowName = value;
updateSmarttalk({id:item.id,fileShowName:item.fileShowName}).then(res=>{
if (res.data===true) {
ElMessage({
type: 'success',
message: `修改成功!`,
})
}
})
})
.catch(() => {
})
},
downloadFile(item) {
ipcRenderer.send('save-as',item.fileFullPath,item.fileShowName)
},
deleteTalk(item) {
deleteSmarttalk(item.id).then(res=>{
let index = this.currentFileList.indexOf(item);
this.currentFileList.splice(index,1)
})
},
closePopver(index){
this.$refs['popover_'+index][0].hide();
},
submitFile(files) {
let _this = this;
files.filter(file => {
file.fileData = Object.assign(this.uploadData, file.fileData)
file.callback = function(res) {
_this.currentFileList.unshift(res.resData);
}
})
console.log(files)
uploaderState().pushFile(files)
this.fileList = []
},
callback({ error, filePath }) { callback({ error, filePath }) {
if (error) { if (error) {
console.error('An error occurred:', error) console.error('An error occurred:', error)
@ -129,27 +226,36 @@ export default {
} }
console.log('File copied to:', filePath) console.log('File copied to:', filePath)
}, },
changeBook(data) {
this.nodeClick(data)
},
nodeClick(data) { nodeClick(data) {
console.log(data) let cata = this.parseCataByNode(data.node)
this.currentNode = data.node
this.uploadData.levelFirstId = cata.length > 0 ? cata[0] : null
this.uploadData.levelSecondId = cata.length > 1 ? cata[1] : null
this.uploadData.levelThirdId = cata.length > 2 ? cata[2] : null
this.uploadData.textbookId = data.textBook.curBookId
this.isLoading = true
getSmarttalkPage({
...this.uploadData,
orderByColumn: 'uploadTime',
isAsc: 'desc'
}).then(res => {
this.currentFileList = [...res.rows]
this.isLoading = false
}).catch(res => {
this.isLoading = false
})
}, },
chooseFile(file) { parseCataByNode(node) {
file.fileData = { if (node.parentNode) {
textbookId: '123', let arr = this.parseCataByNode(node.parentNode)
levelFirstId: '123', arr.push(node.id)
levelSecondId: '123', return arr
fileSource: '平台', } else {
fileFlag: '课件' return [node.id]
} }
file.callback = function(res){
console.log(res)
}
},
changeFile() {
uploaderState().pushFile(this.fileList)
this.fileList = [];
},
clearFile() {
this.fileList = [];
} }
} }
} }
@ -160,6 +266,20 @@ export default {
min-width: 80px !important; min-width: 80px !important;
padding: 5px !important; padding: 5px !important;
} }
.item-popover-item {
padding: 5px 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
cursor: pointer;
.iconfont {
margin-right: 5px;
color: #a2a2a2;
}
}
</style> </style>
<style scoped lang="scss"> <style scoped lang="scss">
.page-resource { .page-resource {
@ -167,6 +287,7 @@ export default {
height: 100%; height: 100%;
.page-right { .page-right {
min-width: 0;
flex: 1; flex: 1;
margin-left: 20px; margin-left: 20px;
height: 100%; height: 100%;
@ -175,6 +296,7 @@ export default {
box-shadow: 0px 0px 20px 0px rgba(99, 99, 99, 0.06); box-shadow: 0px 0px 20px 0px rgba(99, 99, 99, 0.06);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
.prepare-body-header { .prepare-body-header {
height: 60px; height: 60px;
width: 100%; width: 100%;
@ -184,44 +306,54 @@ export default {
justify-content: space-between; justify-content: space-between;
padding: 0 20px; padding: 0 20px;
} }
.prepare-body-main { .prepare-body-main {
flex: 1; flex: 1;
width: 100%; width: 100%;
overflow: auto; overflow: auto;
padding: 0 30px; padding: 0 30px;
.prepare-body-main-item { .prepare-body-main-item {
&:hover{ &:hover {
background-color: rgba(144, 147, 153, 0.2); background-color: rgba(144, 147, 153, 0.2);
cursor: pointer; cursor: pointer;
} }
display: flex; display: flex;
align-items: center; align-items: center;
border-bottom: 1px solid rgba(131, 131, 127, 0.17); border-bottom: 1px solid rgba(131, 131, 127, 0.17);
padding: 10px 0; padding: 10px 0;
.prepare-body-main-item-icon { .prepare-body-main-item-icon {
width: 80px; width: 80px;
} }
.prepare-body-main-item-tool { .prepare-body-main-item-tool {
font-size: 18px !important; font-size: 18px !important;
font-weight: bold; font-weight: bold;
flex: 1;
text-align: right; text-align: right;
padding-right: 30px; padding-right: 30px;
} }
.prepare-body-main-item-info { .prepare-body-main-item-info {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
min-width: 0;
flex: 1;
.prepare-item-info-title { .prepare-item-info-title {
text-align: left; text-align: left;
font-size: 16px; font-size: 16px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
} }
.prepare-item-info-message { .prepare-item-info-message {
font-size: 12px; font-size: 12px;
line-height: 23px; line-height: 23px;
color: #909399; color: #909399;
display: flex; display: flex;
flex-wrap: wrap;
justify-content: space-between;
} }
} }
} }

View File

@ -1,80 +0,0 @@
<template>
<div class="create-resoure">
<el-page-header @back="goBack">
<template #content>
<span class="font-600 mr-3 header-name"> 上传备课资源 </span>
</template>
</el-page-header>
<div class="create-main">
<el-form>
<el-form-item label="目录">
<div class="create-item">第二章 地球上的大气 / 大气的组成和垂直分层</div>
</el-form-item>
<el-form-item label="文件">
<div class="create-item file-item flex">
<FileUpload/>
<!-- <el-button round size="small" color="#eeeeee" class="add-btn">
<i class="iconfont icon-jiahao"></i>
添加文件
</el-button> -->
<span class="upload-desc">说明一次最多上传5个文件单个文件大小不能大于100M</span>
</div>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script setup>
import useResoureStore from '../store'
import FileUpload from '@/components/file-upload/index.vue'
const sourceStore = useResoureStore()
const goBack = () => {
sourceStore.isCreate = false
}
</script>
<style lang="scss" scoped>
.create-resoure {
.el-page-header {
padding: 10px 15px;
border-bottom: solid #ececec 1px;
.header-name {
font-size: 16px;
}
}
.create-main{
font-size: 12px;
padding: 40px;
.el-form-item{
margin-bottom: 30px;
}
.create-item{
font-size: 13px;
width: 100%;
text-align: left;
border-bottom: solid #e0e0e0 1px;
.add-btn{
padding: 13px 11px;
}
.icon-jiahao{
font-size: 14px;
margin-right: 5px;
}
.upload-desc{
margin-left: 10px;
font-size: 12px;
color: #b7b7b7;
}
}
.file-item{
padding-bottom: 15px;
}
}
}
</style>

View File

@ -1,43 +1,46 @@
<template> <template>
<div class="resource-list"> <div class="resource-list" v-loading="sourceStore.loading">
<el-scrollbar height="400px"> <el-scrollbar height="400px">
<el-empty description="暂无数据" v-if="!sourceStore.result.list.length" />
<ul> <ul>
<li class="list-item"> <li class="list-item" v-for="item in sourceStore.result.list" :key="item.id">
<div class="item-left flex"> <div class="item-left flex">
<svg class="icon icon-ppt" aria-hidden="true"> <FileImage :fileName="item.fileName" :size="50" />
<use xlink:href="#icon-ppt"></use>
</svg>
<div class="flex item-left-content"> <div class="flex item-left-content">
<div class="name flex">第1课课后巩固练习 2022-2023学年部编版语文七年级上册</div> <div class="name flex">{{ item.fileShowName }}</div>
<div class="item-tags flex"> <div class="item-tags flex">
<el-tag type="info" class="mr-10">教案</el-tag> <el-tag type="info" class="mr-10">{{ item.fileFlag }}</el-tag>
<el-tag type="info" class="mr-10">PPT</el-tag> <el-tag type="info" class="mr-10">{{ getFileSuffix(item.fileName) }}</el-tag>
<span class="gray-text mr-10">2024-07-13上传</span> <span class="gray-text mr-10">{{ item.uploadTime }}上传</span>
<span class="line mr-10"></span> <!-- <span class="line mr-10"></span>
<span class="gray-text mr-10">下载3次</span> <span class="gray-text mr-10">下载3次</span> -->
</div> </div>
</div> </div>
</div> </div>
<div class="item-btns" @click.stop> <div class="item-btns" @click.stop>
<el-popover placement="bottom-end" trigger="click" popper-class="custom-popover"> <el-popover placement="bottom-end" trigger="hover" popper-class="custom-popover"
:visible="item.showPopover">
<template #reference> <template #reference>
<el-button link type="primary"> <i class="iconfont icon-shenglvehao"></i></el-button> <el-button link type="primary"> <i class="iconfont icon-shenglvehao"></i></el-button>
</template> </template>
<template #default> <template #default>
<div class="item-popover"> <div class="item-popover">
<div class="item-popover-item"> <div class="item-popover-item" @click="editRow(item)">
<i class="iconfont icon-bianji"></i> <i class="iconfont icon-bianji"></i>
<span>编辑</span> <span>编辑</span>
</div> </div>
<div class="item-popover-item"> <div class="item-popover-item" @click="delRow(item)">
<i class="iconfont icon-shanchu"></i> <i class="iconfont icon-shanchu"></i>
<span>删除</span> <span>删除</span>
</div> </div>
<div class="item-popover-item"> <div class="item-popover-item" @click="downloadFile(item)">
<i class="iconfont icon-xiazai"></i> <i class="iconfont icon-xiazai"></i>
<span>下载</span> <span>下载</span>
</div> </div>
<div class="item-popover-item" @click="moveFile(item)">
<i class="iconfont icon-xiazai"></i>
<span>移动至</span>
</div>
</div> </div>
</template> </template>
</el-popover> </el-popover>
@ -48,24 +51,73 @@
</div> </div>
</li> </li>
</ul> </ul>
<div class="pagination-box">
<el-pagination v-model:current-page="currentPage" v-model:page-size="pageSize"
:page-sizes="[100, 200, 300, 400]" background layout="total, sizes, prev, pager, next, jumper" :total="400"
@size-change="handleSizeChange" @current-change="handleCurrentChange" />
</div>
</el-scrollbar> </el-scrollbar>
<div class="pagination-box">
<el-pagination v-model:current-page="sourceStore.query.pageNum" v-model:page-size="sourceStore.query.pageSize"
:page-sizes="[10, 20, 30, 50]" background layout="total, sizes, prev, pager, next, jumper"
:total="sourceStore.result.total" @size-change="handleSizeChange" @current-change="handleCurrentChange" />
</div>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref } from 'vue' import { ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import FileImage from '@/components/file-image/index.vue'
import { deleteSmarttalk, updateSmarttalk } from '@/api/file'
import { getFileSuffix } from '@/utils/ruoyi'
import useResoureStore from '../store'
const currentPage = ref(1) const { ipcRenderer } = window.electron || {}
const pageSize = ref(100) const sourceStore = useResoureStore()
const handleSizeChange = () => { } const handleSizeChange = () => { }
const handleCurrentChange = () => { } const handleCurrentChange = () => { }
//
const downloadFile = (item) => {
ipcRenderer.send('save-as', item.fileFullPath, item.fileShowName)
}
const editRow = (item) => {
console.log(item.fileShowName.substring(0, item.fileShowName.lastIndexOf('.')))
ElMessageBox.prompt('请输入新的名称', '重命名', {
confirmButtonText: '确认',
cancelButtonText: '取消',
inputValue: item.fileShowName.substring(0, item.fileShowName.lastIndexOf('.'))
})
.then(({ value }) => {
item.fileShowName = value + '.' + item.fileSuffix
updateSmarttalk({ id: item.id, fileShowName: item.fileShowName }).then((res) => {
if (res.data === true) {
ElMessage({
type: 'success',
message: `修改成功!`
})
sourceStore.handleQuery()
}
})
})
.catch(() => { })
}
//
const delRow = (item) => {
sourceStore.loading = true
try {
deleteSmarttalk(item.id).then(() => {
ElMessage.success('操作成功')
sourceStore.handleQuery()
})
} finally {
sourceStore.loading = false
}
}
//
const moveFile = (item) => {
moveDialogVisible.value = true
}
</script> </script>
@ -82,6 +134,7 @@ const handleCurrentChange = () => { }
justify-content: center; justify-content: center;
font-size: 12px; font-size: 12px;
cursor: pointer; cursor: pointer;
.iconfont { .iconfont {
margin-right: 5px; margin-right: 5px;
color: #a2a2a2; color: #a2a2a2;
@ -97,6 +150,7 @@ const handleCurrentChange = () => { }
.resource-list { .resource-list {
.list-item { .list-item {
flex: 1;
padding: 10px 20px; padding: 10px 20px;
border-bottom: solid #f1f1f1 1px; border-bottom: solid #f1f1f1 1px;
display: flex; display: flex;

View File

@ -3,7 +3,8 @@
<el-row justify="space-between"> <el-row justify="space-between">
<el-col :span="12" class="tab-btns flex"> <el-col :span="12" class="tab-btns flex">
<el-button text v-for="item in sourceStore.tabs" :key="item.id" <el-button text v-for="item in sourceStore.tabs" :key="item.id"
:type="sourceStore.curTab == item.id ? 'primary' : ''" @click="sourceStore.changeTab(item.id)">{{ item.text :type="sourceStore.query.fileSource == item.value ? 'primary' : ''"
@click="sourceStore.changeTab(item.value)">{{ item.label
}}</el-button> }}</el-button>
</el-col> </el-col>
<el-col :span="12" class="search-box flex"> <el-col :span="12" class="search-box flex">
@ -12,46 +13,30 @@
</el-row> </el-row>
<el-row class="resoure-btns"> <el-row class="resoure-btns">
<el-col :span="24" class="query-row flex"> <el-col :span="24" class="query-row flex">
<div class="flex"> <el-select v-model="sourceStore.curFormat" placeholder="Select" size="small" <div class="flex row-left"> <el-select v-model="sourceStore.query.fileSuffix"
style="width: 100px"> style="width: 100px">
<el-option v-for="item in sourceStore.formatList" :key="item.value" :label="item.label" <el-option v-for="item in sourceStore.resourceFormatList" :key="item.value" :label="item.label"
:value="item.value" /> :value="item.value" />
</el-select> </el-select>
<div class="line"></div> <div class="line"></div>
<el-button size="small" v-for="item in sourceStore.typeList" :key="item.id" <el-button v-for="item in sourceStore.resourceTypeList" :key="item.id"
:type="sourceStore.curType == item.id ? 'primary' : ''" round @click="sourceStore.changeType(item.id)">{{ :type="sourceStore.query.fileFlag == item.value ? 'primary' : ''" round
item.text }}</el-button> @click="sourceStore.changeType(item.value)">{{
item.label }}</el-button>
</div> </div>
<div> <div>
<slot name="add" />
<el-button type="primary" round size="small" @click="openDialog">
<i class="iconfont icon-jiahao"></i>
新建资源</el-button>
</div> </div>
</el-col> </el-col>
</el-row> </el-row>
<uploadDialog v-model="isDialogOpen" @submitFile="submitFile"/>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref, toRaw } from 'vue'
import useResoureStore from '../store' import useResoureStore from '../store'
import uploadDialog from '@/components/upload-dialog/index.vue'
import uploaderState from '@/store/modules/uploader'
const sourceStore = useResoureStore() const sourceStore = useResoureStore()
const isDialogOpen = ref(false)
const openDialog = ()=>{
isDialogOpen.value = true
}
const submitFile = (fileList)=>{
console.log(toRaw(fileList))
uploaderState().pushFile(toRaw(fileList))
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.resoure-search { .resoure-search {
@ -74,6 +59,9 @@ const submitFile = (fileList)=>{
.query-row { .query-row {
justify-content: space-between; justify-content: space-between;
.row-left{
align-items: center;
}
} }
.line { .line {
@ -83,12 +71,12 @@ const submitFile = (fileList)=>{
margin: 0 10px; margin: 0 10px;
} }
.icon-jiahao {
font-size: 12px;
margin-right: 3px;
font-weight: bold;
}
} }
} }
.el-button.is-round{
padding: 3px 15px;
font-size: 13px;
}
</style> </style>

View File

@ -1,33 +1,94 @@
<template> <template>
<div class="page-resource flex"> <div class="page-resource flex">
<ChooseTextbook @nodeClick="nodeClick" /> <!--左侧 教材 目录-->
<ChooseTextbook @changeBook="changeBook" @nodeClick="nodeClick" />
<div class="page-right"> <div class="page-right">
<template v-if="!sourceStore.isCreate"> <!-- 搜索 -->
<ResoureSearch /> <ResoureSearch #add>
<ResoureList /> <el-button type="primary" round @click="openDialog" class="create-btn">
</template> <i class="iconfont icon-jiahao"></i>
<template v-else> 新建资源</el-button>
<CreateResoure/> </ResoureSearch>
</template> <!-- 列表 -->
<ResoureList />
</div> </div>
</div> </div>
<!-- 上传弹窗 -->
<uploadDialog v-model="isDialogOpen" @submitFile="submitFile" />
<!-- <MoveFile v-model="isDialogOpen" @onSubmit="onSubmit" /> -->
</template> </template>
<script setup> <script setup>
import { toRaw } from 'vue' import { ref, toRaw } from 'vue'
import useResoureStore from './store' import useResoureStore from './store'
import ChooseTextbook from '@/components/choose-textbook/index.vue' import ChooseTextbook from '@/components/choose-textbook/index.vue'
import ResoureSearch from './container/resoure-search.vue' import ResoureSearch from './container/resoure-search.vue'
import ResoureList from './container/resoure-list.vue' import ResoureList from './container/resoure-list.vue'
import CreateResoure from './container/create-resoure.vue' import uploadDialog from '@/components/upload-dialog/index.vue'
import MoveFile from '@/components/move-file/index.vue'
import uploaderState from '@/store/modules/uploader'
const sourceStore = useResoureStore() const sourceStore = useResoureStore()
const isDialogOpen = ref(false)
const { ipcRenderer } = window.electron || {} const { ipcRenderer } = window.electron || {}
// ipcRenderer.send('set-winsize',{x:1100,y: 700}) // ipcRenderer.send('set-winsize',{x:1100,y: 700})
const openDialog = () => {
isDialogOpen.value = true
}
const onSubmit = (data)=>{
console.log(data)
}
//
const changeBook = (data) => {
getData(data)
}
//
const nodeClick = (data) => { const nodeClick = (data) => {
console.log(toRaw(data)) getData(data)
}
//
const getData = (data) => {
const { textBook, node } = data
let textBookId = textBook.curBookId
let levelFirstId = node.id
let levelSecondId = node.parentNode ? node.parentNode.id : ''
sourceStore.query = {
textBookId,
levelFirstId,
levelSecondId,
...sourceStore.query
}
sourceStore.handleQuery()
}
//
const submitFile = (data) => {
let fileList = toRaw(data)
const { textBookId, levelFirstId, levelSecondId, fileSource, fileRoot } = sourceStore.query
//
let fileData = { textBookId, levelFirstId, levelSecondId, fileSource, fileRoot }
fileList.forEach(item => {
fileData.fileShowName = item.fileData.fileShowName
fileData.fileFlag = item.fileData.fileFlag
item.fileData = fileData
item.callback = fileCallBack
})
// console.log(fileList)
uploaderState().pushFile(fileList)
}
const fileCallBack = (res) => {
console.log(res)
if (res.code == 200) {
sourceStore.handleQuery()
}
} }
</script> </script>
@ -38,6 +99,7 @@ const nodeClick = (data) => {
height: 100%; height: 100%;
.page-right { .page-right {
min-width: 0;
flex: 1; flex: 1;
margin-left: 20px; margin-left: 20px;
height: 100%; height: 100%;
@ -45,5 +107,14 @@ const nodeClick = (data) => {
border-radius: 10px; border-radius: 10px;
box-shadow: 0px 0px 20px 0px rgba(99, 99, 99, 0.06); box-shadow: 0px 0px 20px 0px rgba(99, 99, 99, 0.06);
} }
.icon-jiahao {
font-size: 12px;
margin-right: 3px;
font-weight: bold;
}
}
.create-btn{
font-size: 13px;
padding: 5px 13px;
} }
</style> </style>

View File

@ -1,76 +1,82 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { getSmarttalkPage } from '@/api/file/index'
import { tabs, resourceType, resourceFormat } from '@/utils/resourceDict'
import useUserStore from '@/store/modules/user'
const tabs = [ const userStore = useUserStore()
const resourceTypeList = [
{ {
type: '', label: '全部',
text: '平台资源', value: ''
id: 1
}, },
{ ...resourceType
type: '',
text: '校本资源',
id: 2
}
] ]
const resourceFormatList = [
const typeList = [
{
type: '',
text: '全部',
id: 1
},
{
type: '',
text: '素材',
id: 2
},
{
type: '',
text: '课件',
id: 4
},
{
type: '',
text: '教案',
id: 6
},
]
// 资源格式
const formatList = [
{ {
label: '资源格式', label: '资源格式',
value: -1 value: ''
}, },
{ ...resourceFormat
label: 'word',
value: 1
},
{
label: 'ppt',
value: 2
}
] ]
// 校本资源为学校ID
tabs.forEach(item =>{
if( item.label == "校本资源"){
item.value = userStore.user.deptId
}
})
const structQuery = {
pageNum: 1,
pageSize: 10
}
export default defineStore('resource', { export default defineStore('resource', {
state: () => ({ state: () => ({
tabs, tabs,
typeList, resourceTypeList,
formatList, resourceFormatList,
curTab: 1,
curType: 1,
curFormat: -1, curFormat: -1,
searchKey: '', searchKey: '',
// 新建资源 // 新建资源
isCreate: false, isCreate: false,
loading: false,
//查询条件
query: {
fileSource: '平台',
fileSuffix: '',
fileFlag: '',
fileRoot: '资源',
orderByColumn: 'uploadTime',
isAsc: 'desc',
...structQuery
},
result: {
list: [],
total: 0
}
}), }),
actions: { actions: {
handleQuery() {
try {
this.loading = true
getSmarttalkPage(this.query).then((res) => {
this.result.total = res.total
this.result.list = res.rows
})
} finally {
this.loading = false
}
},
changeTab(val) { changeTab(val) {
this.curTab = val this.query.fileSource = val
this.handleQuery()
}, },
changeType(val) { changeType(val) {
this.curType = val this.query.fileFlag = val
this.handleQuery()
} }
} }
}) })