Merge branch 'main' into cys
This commit is contained in:
commit
fbb9fed298
|
@ -22,8 +22,8 @@ export default defineConfig({
|
|||
server: {
|
||||
proxy: {
|
||||
'/dev-api': {
|
||||
// target: 'http://27.128.240.72:7865',
|
||||
target: 'http://192.168.2.52:7863',
|
||||
target: 'http://27.128.240.72:7865',
|
||||
// target: 'http://192.168.2.52:7863',
|
||||
changeOrigin: true,
|
||||
rewrite: (p) => p.replace(/^\/dev-api/, '')
|
||||
},
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
import { ElectronDownloadManager } from 'electron-dl-manager';
|
||||
const manager = new ElectronDownloadManager();
|
||||
import { ElectronDownloadManager } from 'electron-dl-manager'
|
||||
import { dialog } from 'electron'
|
||||
const manager = new ElectronDownloadManager()
|
||||
export default async function ({ app, shell, BrowserWindow, ipcMain }) {
|
||||
|
||||
const userDataPath = app.getPath('userData')
|
||||
//默认浏览器打开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) => {
|
||||
createFolder('selfFile').then(async ()=>{
|
||||
ipcMain.on('download-file-default', (e, url) => {
|
||||
createFolder('selfFile').then(async () => {
|
||||
const browserWindow = BrowserWindow.fromId(e.sender.id)
|
||||
const id = await manager.download({
|
||||
window: browserWindow,
|
||||
|
@ -38,17 +38,65 @@ export default async function ({ app, shell, BrowserWindow, ipcMain }) {
|
|||
id,
|
||||
percentCompleted,
|
||||
// Get the number of bytes received so far
|
||||
bytesReceived: item.getReceivedBytes(),
|
||||
});
|
||||
bytesReceived: item.getReceivedBytes()
|
||||
})
|
||||
},
|
||||
onDownloadCompleted: async ({ id, item }) => {
|
||||
console.log(item)
|
||||
},
|
||||
onDownloadCancelled: 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)
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -8,8 +8,8 @@ File({ app, shell, BrowserWindow, ipcMain })
|
|||
function createWindow() {
|
||||
// Create the browser window.
|
||||
const mainWindow = new BrowserWindow({
|
||||
width: 888,
|
||||
height: 520,
|
||||
width: 1050,
|
||||
height: 650,
|
||||
show: false,
|
||||
frame: false,
|
||||
autoHideMenuBar: true,
|
||||
|
|
|
@ -8,3 +8,18 @@ export const getSmarttalkPage = (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
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 2794390 */
|
||||
src: url('iconfont.woff2?t=1720953486579') format('woff2'),
|
||||
url('iconfont.woff?t=1720953486579') format('woff'),
|
||||
url('iconfont.ttf?t=1720953486579') format('truetype'),
|
||||
url('iconfont.svg?t=1720953486579#iconfont') format('svg');
|
||||
src: url('iconfont.woff2?t=1721179711733') format('woff2'),
|
||||
url('iconfont.woff?t=1721179711733') format('woff'),
|
||||
url('iconfont.ttf?t=1721179711733') format('truetype'),
|
||||
url('iconfont.svg?t=1721179711733#iconfont') format('svg');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
|
@ -14,6 +14,10 @@
|
|||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-yidongdaozu:before {
|
||||
content: "\e67d";
|
||||
}
|
||||
|
||||
.icon-shanchu:before {
|
||||
content: "\e852";
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -5,6 +5,13 @@
|
|||
"css_prefix_text": "icon-",
|
||||
"description": "",
|
||||
"glyphs": [
|
||||
{
|
||||
"icon_id": "1207918",
|
||||
"name": "移动到组",
|
||||
"font_class": "yidongdaozu",
|
||||
"unicode": "e67d",
|
||||
"unicode_decimal": 59005
|
||||
},
|
||||
{
|
||||
"icon_id": "8288874",
|
||||
"name": "删除",
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
/>
|
||||
<missing-glyph />
|
||||
|
||||
<glyph glyph-name="yidongdaozu" unicode="" 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="" 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="" 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 |
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -32,3 +32,27 @@ html,body{
|
|||
fill: currentColor;
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -1,16 +1,20 @@
|
|||
<template>
|
||||
<div class="book-wrap">
|
||||
<div class="book-name flex" @click="dialogVisible = true">
|
||||
<span>{{ curBookName }}</span>
|
||||
<i class="iconfont icon-xiangyou"></i>
|
||||
</div>
|
||||
<div class="book-list">
|
||||
<el-tree :data="treeData" :props="defaultProps" highlight-current @node-click="handleNodeClick">
|
||||
<template #default="{ node }">
|
||||
<span :title="node.label" class="tree-label">{{ node.label }}</span>
|
||||
</template>
|
||||
</el-tree>
|
||||
</div>
|
||||
<el-scrollbar height="100%">
|
||||
<div class="book-name flex" @click="dialogVisible = true">
|
||||
<span>{{ curBookName }}</span>
|
||||
<i class="iconfont icon-xiangyou"></i>
|
||||
</div>
|
||||
<div class="book-list">
|
||||
<el-tree ref="refTree" :data="treeData" :props="defaultProps" node-key="id"
|
||||
:default-expanded-keys="defaultExpandedKeys" :current-node-key="currentNode" 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>
|
||||
<!--弹窗 选择教材-->
|
||||
<el-dialog v-model="dialogVisible" append-to-body :show-close="false" width="550"
|
||||
|
@ -35,12 +39,12 @@
|
|||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { onMounted, ref, nextTick, toRaw } from 'vue';
|
||||
import useUserStore from '@/store/modules/user'
|
||||
import { listEvaluation } from '@/api/subject'
|
||||
|
||||
// 定义要发送的emit事件
|
||||
const emit = defineEmits(['nodeClick'])
|
||||
const emit = defineEmits(['nodeClick', 'changeBook'])
|
||||
// store
|
||||
const userStore = useUserStore()
|
||||
const { edustage, edusubject, userId } = userStore.user
|
||||
|
@ -63,6 +67,14 @@ const curBookName = ref('')
|
|||
const volumeOne = 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,
|
||||
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()
|
||||
//上册
|
||||
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()
|
||||
}
|
||||
|
@ -93,7 +116,6 @@ const changeBook = ({ id, itemtitle }) => {
|
|||
setTimeout(() => {
|
||||
dialogVisible.value = false
|
||||
}, 100);
|
||||
|
||||
}
|
||||
|
||||
const getTreeData = () => {
|
||||
|
@ -101,8 +123,76 @@ 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(() => {
|
||||
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) => {
|
||||
let ary = []
|
||||
|
@ -133,8 +223,16 @@ const transData = (data) => {
|
|||
|
||||
//获取教材
|
||||
const getSubject = async () => {
|
||||
const { rows } = await listEvaluation({ itemkey: "version", pageSize: 500 })
|
||||
subjectList.value = rows.filter(item => item.edustage == edustage && item.edusubject == edusubject && isHaveUnit(item.id))
|
||||
if (localStorage.getItem('subjectList')) {
|
||||
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
|
||||
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) => {
|
||||
emit('nodeClick', data)
|
||||
const handleNodeClick = (data, node) => {
|
||||
/**
|
||||
* 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(() => {
|
||||
|
@ -161,7 +279,6 @@ onMounted(() => {
|
|||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
.book-wrap {
|
||||
width: 300px;
|
||||
height: 100%;
|
||||
|
@ -170,10 +287,17 @@ onMounted(() => {
|
|||
box-shadow: 0px 0px 20px 0px rgba(99, 99, 99, 0.06);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
|
||||
.book-name {
|
||||
background-color: #ffffff;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 45px;
|
||||
padding: 0 15px;
|
||||
z-index: 1;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
color: #3b3b3b;
|
||||
|
@ -184,7 +308,7 @@ onMounted(() => {
|
|||
}
|
||||
|
||||
.book-list {
|
||||
padding-left: 10px;
|
||||
padding: 45px 10px 0 10px;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
@ -240,12 +364,12 @@ onMounted(() => {
|
|||
:deep(.el-tree-node) {
|
||||
.el-tree-node__content {
|
||||
height: 40px;
|
||||
border-radius: 10px;
|
||||
|
||||
&:hover {
|
||||
background-color: #eaf3ff;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.tree-label {
|
||||
|
@ -254,9 +378,8 @@ onMounted(() => {
|
|||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
:deep(.el-tree--highlight-current .el-tree-node.is-current>.el-tree-node__content){
|
||||
background-color: #d9e8fe !important;
|
||||
:deep(.el-tree--highlight-current .el-tree-node.is-current>.el-tree-node__content) {
|
||||
background-color: #eaf3ff !important;
|
||||
color: #409EFF
|
||||
}
|
||||
|
||||
</style>
|
|
@ -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>
|
|
@ -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>
|
|
@ -1,10 +1,11 @@
|
|||
<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">
|
||||
<el-form>
|
||||
<el-form-item label="文件">
|
||||
<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-upload>
|
||||
<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-name">
|
||||
<span class="name">标题:</span>
|
||||
<svg class="icon icon-ppt" aria-hidden="true">
|
||||
<use :xlink:href="getFileTypeIcon(item.name)"></use>
|
||||
</svg>
|
||||
<span class="text">{{ item.name }}</span>
|
||||
<FileImage :fileName="item.name" size="50"/>
|
||||
<el-input class="file-input" v-model="item.fileData.name" placeholder="请输入文件名" />
|
||||
<span>.{{ getFileSuffix(item.name) }}</span>
|
||||
</div>
|
||||
<div class="flex-type flex">
|
||||
<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-select>
|
||||
|
||||
|
@ -46,7 +46,11 @@
|
|||
|
||||
<script setup>
|
||||
import { ref, defineProps, defineEmits, watch } from 'vue'
|
||||
import FileImage from '@/components/file-image/index.vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { resourceType } from '@/utils/resourceDict'
|
||||
import { getFileSuffix, getFileName } from '@/utils/ruoyi'
|
||||
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
|
@ -60,30 +64,14 @@ const emit = defineEmits(['update:modelValue', 'submitFile'])
|
|||
// 文件列表
|
||||
const fileList = ref([])
|
||||
|
||||
const fileType = ref(1)
|
||||
// 资源类型
|
||||
const resourceType = ref([
|
||||
{
|
||||
label: '课件',
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
label: '教案',
|
||||
value: 2
|
||||
},
|
||||
{
|
||||
label: '素材',
|
||||
value: 3
|
||||
}
|
||||
])
|
||||
|
||||
|
||||
watch(() => props.modelValue, (newVal) => {
|
||||
dialogValue.value = newVal
|
||||
})
|
||||
const hanleFileBefore = (rawFile) => {
|
||||
|
||||
// 音频 类型
|
||||
const hanleFileChange = (file) => {
|
||||
console.log(file)
|
||||
|
||||
//音频 类型
|
||||
const audioTypes = ['audio/mpeg', 'audio/wav', 'audio/ogg', 'audio/aac']
|
||||
// 视频 类型
|
||||
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']
|
||||
// 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
|
||||
if (!(audioTypes.includes(fileType) || videoTypes.includes(fileType) || wordTypes.includes(fileType) || pptTypes.includes(fileType) || pdfTypes.includes(fileType))) {
|
||||
ElMessage.error('文件格式错误! 请上传音频、视频、word、ppt、pdf文件!')
|
||||
const fileType = file.raw.type
|
||||
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、txt、zip文件!')
|
||||
return false
|
||||
}
|
||||
// 验证文件大小
|
||||
const fileSize = rawFile.size / 1024 / 1024 > 100
|
||||
const fileSize = file.raw.size / 1024 / 1024 > 100
|
||||
if (fileSize) {
|
||||
ElMessage.error('文件大小错误! 请上传小于100M的文件!')
|
||||
return false
|
||||
}
|
||||
console.log(rawFile)
|
||||
rawFile.fileType = 1
|
||||
fileList.value.push(rawFile)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
const getFileTypeIcon = (fileName)=>{
|
||||
const name = fileName.substr(fileName.lastIndexOf('.') + 1);
|
||||
console.log(name)
|
||||
const iconObj = {
|
||||
pdf: 'icon-pdf',
|
||||
ppt: 'icon-ppt',
|
||||
doc: 'icon-word',
|
||||
docx: 'icon-word',
|
||||
mp4: 'icon-video'
|
||||
if (file.status === 'ready') {
|
||||
|
||||
// 给一个默认的fileData
|
||||
file.fileData = {
|
||||
fileFlag: '课件',
|
||||
name: getFileName(file.name),
|
||||
}
|
||||
fileList.value.push(file)
|
||||
}
|
||||
return '#' + iconObj[name]
|
||||
}
|
||||
|
||||
// 删除
|
||||
|
@ -143,11 +128,16 @@ const closeDialog = () => {
|
|||
}
|
||||
// 确定
|
||||
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>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
@ -172,15 +162,18 @@ const submitFile = () => {
|
|||
width: 100%;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 15px;
|
||||
|
||||
.file-name {
|
||||
width: 50%;
|
||||
width: 55%;
|
||||
margin-right: 20px;
|
||||
display: flex;
|
||||
align-items: center
|
||||
}
|
||||
.icon{
|
||||
|
||||
.icon {
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
.flex-type {
|
||||
align-items: center;
|
||||
margin-right: 20px;
|
||||
|
@ -192,6 +185,7 @@ const submitFile = () => {
|
|||
.name {
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
|
||||
&::after {
|
||||
content: '*';
|
||||
position: absolute;
|
||||
|
@ -210,4 +204,24 @@ const submitFile = () => {
|
|||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
.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>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<el-popover placement="left-end" width="300px" trigger="click">
|
||||
<el-popover placement="left-end" width="300px" title="文件上传" trigger="click">
|
||||
<template #default>
|
||||
<el-upload
|
||||
ref="talk_uploader_core"
|
||||
|
@ -22,7 +22,7 @@
|
|||
<div class="talk-uploader-body">
|
||||
<div v-for="(item, index) in uploaderStore.uploadList" :key="index">
|
||||
<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">
|
||||
<svg
|
||||
class="icon"
|
||||
|
@ -35,11 +35,11 @@
|
|||
</svg>
|
||||
</div>
|
||||
<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>1.6MB</div>
|
||||
|
|
||||
<div>古诗词诵读 > 静女</div>
|
||||
<div>{{formatFileSize(item.raw.size)}}</div>
|
||||
<!-- | -->
|
||||
<!-- <div>古诗词诵读 > 静女</div>-->
|
||||
</div>
|
||||
</div>
|
||||
<div class="prepare-body-main-item-tool" @click="removeUploadFile(item.uid)">
|
||||
|
@ -99,6 +99,23 @@ export default {
|
|||
}, 1000)
|
||||
},
|
||||
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) {
|
||||
this.removeUploadFile(file.uid)
|
||||
file.callback(res)
|
||||
|
@ -119,7 +136,7 @@ export default {
|
|||
this.uploadDatas = this.uploadNow.fileData
|
||||
this.getFileMD5(this.uploadNow.raw).then((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()
|
||||
})
|
||||
}
|
||||
|
@ -158,10 +175,15 @@ export default {
|
|||
.talk-uploader-body {
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
overflow: auto;
|
||||
}
|
||||
.prepare-body-main-item {
|
||||
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%;
|
||||
position: absolute;
|
||||
background-color: #83bb67;
|
||||
|
@ -171,10 +193,7 @@ export default {
|
|||
&:hover {
|
||||
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 {
|
||||
width: 80px;
|
||||
}
|
||||
|
|
|
@ -84,6 +84,7 @@ const useUserStore = defineStore(
|
|||
this.token = ''
|
||||
this.roles = []
|
||||
this.permissions = []
|
||||
localStorage.clear()
|
||||
removeToken()
|
||||
resolve()
|
||||
}).catch(error => {
|
||||
|
|
|
@ -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 - 是否使用简单的格式,默认为false,如:23/06/25、06/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
|
||||
}
|
|
@ -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: '教案'
|
||||
}
|
||||
]
|
|
@ -245,3 +245,15 @@ export function getNormalPath(p) {
|
|||
export function blobValidate(data) {
|
||||
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(/\.[^/.]+$/, "");
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="page-resource flex">
|
||||
<ChooseTextbook @node-click="nodeClick" />
|
||||
<div class="page-resource flex" v-loading="isLoading">
|
||||
<ChooseTextbook @changeBook="changeBook" @node-click="nodeClick" />
|
||||
<div class="page-right">
|
||||
<div class="prepare-body-header">
|
||||
<div>
|
||||
|
@ -8,62 +8,84 @@
|
|||
<el-popover placement="top-start" :width="250" trigger="hover">
|
||||
<template #default>
|
||||
<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>
|
||||
</template>
|
||||
<template #reference>
|
||||
<el-button size="small" text
|
||||
><el-icon><Refresh /></el-icon>云同步</el-button
|
||||
>
|
||||
<el-icon>
|
||||
<Refresh />
|
||||
</el-icon>
|
||||
云同步
|
||||
</el-button
|
||||
>
|
||||
</template>
|
||||
</el-popover>
|
||||
</div>
|
||||
<div style="display: flex">
|
||||
<el-upload
|
||||
ref="choosefile"
|
||||
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>
|
||||
<el-button @click="isDialogOpen=true">上传资料</el-button>
|
||||
<el-button type="primary" style="margin-left: 10px">新建课件</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<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">
|
||||
<svg class="icon" aria-hidden="true" font-size="50px" color="red" style="margin: auto">
|
||||
<use xlink:href="#icon-ppt"></use>
|
||||
</svg>
|
||||
</div>
|
||||
<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>
|
||||
<div style="width: 80px">
|
||||
<el-icon
|
||||
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>1.6MB</div>
|
||||
|
|
||||
<div>2024-07-10</div>
|
||||
|
|
||||
<div>古诗词诵读 > 静女</div>
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
<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 #reference>
|
||||
<span class="iconfont icon-shenglvehao" style="cursor: pointer"></span>
|
||||
|
@ -73,35 +95,47 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<uploadDialog v-model="isDialogOpen" @submitFile="submitFile" />
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { Check } from '@element-plus/icons-vue'
|
||||
import ChooseTextbook from '@/components/choose-textbook/index.vue'
|
||||
import { deleteSmarttalk } from '@/api/file'
|
||||
</script>
|
||||
<script>
|
||||
import FileUpload from '@/components/file-upload/index.vue'
|
||||
import ChooseTextbook from '@/components/choose-textbook/index.vue'
|
||||
import ResoureList from '@/views/resource/container/resoure-list.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 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'
|
||||
const { ipcRenderer } = window.electron || {}
|
||||
export default {
|
||||
name: 'Prepare',
|
||||
components: { ResoureSearch, ResoureList, ChooseTextbook, FileUpload, Refresh },
|
||||
components: { ResoureSearch, ResoureList, ChooseTextbook, FileUpload, Refresh, uploadDialog },
|
||||
data() {
|
||||
return {
|
||||
fileList:[],
|
||||
fileUrl:
|
||||
'https://wzyzoss.eos-chongqing-3.cmecloud.cn/2024/7/10/117cdf208c6b4e58bf2b73369eaf3cb5.pptx',
|
||||
filePath: 'C:/Users/zhuhao/Desktop/工作文档/0901高一【数学(人教A版)】集合的概念-PPT课件.pptx',
|
||||
isLoading: false,
|
||||
isDialogOpen: false,
|
||||
showToolVisible: false,
|
||||
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: {
|
||||
textbookId: '123',
|
||||
levelFirstId: '123',
|
||||
levelSecondId: '123',
|
||||
fileSource: '平台',
|
||||
fileFlag: '课件'
|
||||
textbookId: null,
|
||||
levelFirstId: 39103,
|
||||
levelSecondId: null,
|
||||
fileSource: '个人',
|
||||
fileRoot: '备课'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -112,6 +146,8 @@ export default {
|
|||
})
|
||||
},
|
||||
mounted() {
|
||||
//查询所有选中目录下的所有文件
|
||||
|
||||
// const destination = '0901高一【数学(人教A版)】集合的概念-PPT课件.pptx'
|
||||
// ipcRenderer.send('open-path-app',this.filePath)
|
||||
// const source = 'D:\\edufile\\0901高一【数学(人教A版)】集合的概念-PPT课件.pptx'
|
||||
|
@ -122,6 +158,67 @@ export default {
|
|||
// })
|
||||
},
|
||||
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 }) {
|
||||
if (error) {
|
||||
console.error('An error occurred:', error)
|
||||
|
@ -129,27 +226,36 @@ export default {
|
|||
}
|
||||
console.log('File copied to:', filePath)
|
||||
},
|
||||
changeBook(data) {
|
||||
this.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) {
|
||||
file.fileData = {
|
||||
textbookId: '123',
|
||||
levelFirstId: '123',
|
||||
levelSecondId: '123',
|
||||
fileSource: '平台',
|
||||
fileFlag: '课件'
|
||||
parseCataByNode(node) {
|
||||
if (node.parentNode) {
|
||||
let arr = this.parseCataByNode(node.parentNode)
|
||||
arr.push(node.id)
|
||||
return arr
|
||||
} else {
|
||||
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;
|
||||
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 scoped lang="scss">
|
||||
.page-resource {
|
||||
|
@ -167,6 +287,7 @@ export default {
|
|||
height: 100%;
|
||||
|
||||
.page-right {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
margin-left: 20px;
|
||||
height: 100%;
|
||||
|
@ -175,6 +296,7 @@ export default {
|
|||
box-shadow: 0px 0px 20px 0px rgba(99, 99, 99, 0.06);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.prepare-body-header {
|
||||
height: 60px;
|
||||
width: 100%;
|
||||
|
@ -184,44 +306,54 @@ export default {
|
|||
justify-content: space-between;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.prepare-body-main {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
padding: 0 30px;
|
||||
|
||||
.prepare-body-main-item {
|
||||
&:hover{
|
||||
&:hover {
|
||||
background-color: rgba(144, 147, 153, 0.2);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid rgba(131, 131, 127, 0.17);
|
||||
padding: 10px 0;
|
||||
|
||||
.prepare-body-main-item-icon {
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.prepare-body-main-item-tool {
|
||||
font-size: 18px !important;
|
||||
font-weight: bold;
|
||||
flex: 1;
|
||||
text-align: right;
|
||||
padding-right: 30px;
|
||||
}
|
||||
|
||||
.prepare-body-main-item-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
|
||||
.prepare-item-info-title {
|
||||
text-align: left;
|
||||
font-size: 16px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.prepare-item-info-message {
|
||||
font-size: 12px;
|
||||
line-height: 23px;
|
||||
color: #909399;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
|
@ -1,43 +1,46 @@
|
|||
<template>
|
||||
<div class="resource-list">
|
||||
<div class="resource-list" v-loading="sourceStore.loading">
|
||||
<el-scrollbar height="400px">
|
||||
<el-empty description="暂无数据" v-if="!sourceStore.result.list.length" />
|
||||
<ul>
|
||||
<li class="list-item">
|
||||
|
||||
<li class="list-item" v-for="item in sourceStore.result.list" :key="item.id">
|
||||
<div class="item-left flex">
|
||||
<svg class="icon icon-ppt" aria-hidden="true">
|
||||
<use xlink:href="#icon-ppt"></use>
|
||||
</svg>
|
||||
<FileImage :fileName="item.fileName" :size="50" />
|
||||
<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">
|
||||
<el-tag type="info" class="mr-10">教案</el-tag>
|
||||
<el-tag type="info" class="mr-10">PPT</el-tag>
|
||||
<span class="gray-text mr-10">2024-07-13上传</span>
|
||||
<span class="line mr-10"></span>
|
||||
<span class="gray-text mr-10">下载3次</span>
|
||||
<el-tag type="info" class="mr-10">{{ item.fileFlag }}</el-tag>
|
||||
<el-tag type="info" class="mr-10">{{ getFileSuffix(item.fileName) }}</el-tag>
|
||||
<span class="gray-text mr-10">{{ item.uploadTime }}上传</span>
|
||||
<!-- <span class="line mr-10"></span>
|
||||
<span class="gray-text mr-10">下载3次</span> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
<el-button link type="primary"> <i class="iconfont icon-shenglvehao"></i></el-button>
|
||||
</template>
|
||||
<template #default>
|
||||
<div class="item-popover">
|
||||
<div class="item-popover-item">
|
||||
<div class="item-popover-item" @click="editRow(item)">
|
||||
<i class="iconfont icon-bianji"></i>
|
||||
<span>编辑</span>
|
||||
</div>
|
||||
<div class="item-popover-item">
|
||||
<div class="item-popover-item" @click="delRow(item)">
|
||||
<i class="iconfont icon-shanchu"></i>
|
||||
<span>删除</span>
|
||||
</div>
|
||||
<div class="item-popover-item">
|
||||
<div class="item-popover-item" @click="downloadFile(item)">
|
||||
<i class="iconfont icon-xiazai"></i>
|
||||
<span>下载</span>
|
||||
</div>
|
||||
<div class="item-popover-item" @click="moveFile(item)">
|
||||
<i class="iconfont icon-xiazai"></i>
|
||||
<span>移动至</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-popover>
|
||||
|
@ -48,24 +51,73 @@
|
|||
</div>
|
||||
</li>
|
||||
</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>
|
||||
<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>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
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 pageSize = ref(100)
|
||||
|
||||
const { ipcRenderer } = window.electron || {}
|
||||
const sourceStore = useResoureStore()
|
||||
const handleSizeChange = () => { }
|
||||
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>
|
||||
|
||||
|
@ -82,6 +134,7 @@ const handleCurrentChange = () => { }
|
|||
justify-content: center;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
|
||||
.iconfont {
|
||||
margin-right: 5px;
|
||||
color: #a2a2a2;
|
||||
|
@ -97,6 +150,7 @@ const handleCurrentChange = () => { }
|
|||
|
||||
.resource-list {
|
||||
.list-item {
|
||||
flex: 1;
|
||||
padding: 10px 20px;
|
||||
border-bottom: solid #f1f1f1 1px;
|
||||
display: flex;
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
<el-row justify="space-between">
|
||||
<el-col :span="12" class="tab-btns flex">
|
||||
<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-col>
|
||||
<el-col :span="12" class="search-box flex">
|
||||
|
@ -12,46 +13,30 @@
|
|||
</el-row>
|
||||
<el-row class="resoure-btns">
|
||||
<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">
|
||||
<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" />
|
||||
</el-select>
|
||||
<div class="line"></div>
|
||||
<el-button size="small" v-for="item in sourceStore.typeList" :key="item.id"
|
||||
:type="sourceStore.curType == item.id ? 'primary' : ''" round @click="sourceStore.changeType(item.id)">{{
|
||||
item.text }}</el-button>
|
||||
<el-button v-for="item in sourceStore.resourceTypeList" :key="item.id"
|
||||
:type="sourceStore.query.fileFlag == item.value ? 'primary' : ''" round
|
||||
@click="sourceStore.changeType(item.value)">{{
|
||||
item.label }}</el-button>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
<el-button type="primary" round size="small" @click="openDialog">
|
||||
<i class="iconfont icon-jiahao"></i>
|
||||
新建资源</el-button>
|
||||
<slot name="add" />
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<uploadDialog v-model="isDialogOpen" @submitFile="submitFile"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, toRaw } from 'vue'
|
||||
import useResoureStore from '../store'
|
||||
import uploadDialog from '@/components/upload-dialog/index.vue'
|
||||
import uploaderState from '@/store/modules/uploader'
|
||||
|
||||
|
||||
const sourceStore = useResoureStore()
|
||||
const isDialogOpen = ref(false)
|
||||
|
||||
const openDialog = ()=>{
|
||||
isDialogOpen.value = true
|
||||
}
|
||||
|
||||
const submitFile = (fileList)=>{
|
||||
console.log(toRaw(fileList))
|
||||
uploaderState().pushFile(toRaw(fileList))
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.resoure-search {
|
||||
|
@ -74,6 +59,9 @@ const submitFile = (fileList)=>{
|
|||
|
||||
.query-row {
|
||||
justify-content: space-between;
|
||||
.row-left{
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.line {
|
||||
|
@ -83,12 +71,12 @@ const submitFile = (fileList)=>{
|
|||
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>
|
|
@ -1,33 +1,94 @@
|
|||
<template>
|
||||
<div class="page-resource flex">
|
||||
<ChooseTextbook @nodeClick="nodeClick" />
|
||||
<!--左侧 教材 目录-->
|
||||
<ChooseTextbook @changeBook="changeBook" @nodeClick="nodeClick" />
|
||||
|
||||
<div class="page-right">
|
||||
<template v-if="!sourceStore.isCreate">
|
||||
<ResoureSearch />
|
||||
<ResoureList />
|
||||
</template>
|
||||
<template v-else>
|
||||
<CreateResoure/>
|
||||
</template>
|
||||
<!-- 搜索 -->
|
||||
<ResoureSearch #add>
|
||||
<el-button type="primary" round @click="openDialog" class="create-btn">
|
||||
<i class="iconfont icon-jiahao"></i>
|
||||
新建资源</el-button>
|
||||
</ResoureSearch>
|
||||
<!-- 列表 -->
|
||||
<ResoureList />
|
||||
</div>
|
||||
</div>
|
||||
<!-- 上传弹窗 -->
|
||||
<uploadDialog v-model="isDialogOpen" @submitFile="submitFile" />
|
||||
<!-- <MoveFile v-model="isDialogOpen" @onSubmit="onSubmit" /> -->
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { toRaw } from 'vue'
|
||||
import { ref, toRaw } from 'vue'
|
||||
import useResoureStore from './store'
|
||||
import ChooseTextbook from '@/components/choose-textbook/index.vue'
|
||||
import ResoureSearch from './container/resoure-search.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 isDialogOpen = ref(false)
|
||||
const { ipcRenderer } = window.electron || {}
|
||||
// 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) => {
|
||||
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>
|
||||
|
@ -38,6 +99,7 @@ const nodeClick = (data) => {
|
|||
height: 100%;
|
||||
|
||||
.page-right {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
margin-left: 20px;
|
||||
height: 100%;
|
||||
|
@ -45,5 +107,14 @@ const nodeClick = (data) => {
|
|||
border-radius: 10px;
|
||||
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>
|
||||
|
|
|
@ -1,76 +1,82 @@
|
|||
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 = [
|
||||
{
|
||||
type: '',
|
||||
text: '平台资源',
|
||||
id: 1
|
||||
},
|
||||
{
|
||||
type: '',
|
||||
text: '校本资源',
|
||||
id: 2
|
||||
}
|
||||
]
|
||||
const userStore = useUserStore()
|
||||
|
||||
const typeList = [
|
||||
const resourceTypeList = [
|
||||
{
|
||||
type: '',
|
||||
text: '全部',
|
||||
id: 1
|
||||
label: '全部',
|
||||
value: ''
|
||||
},
|
||||
{
|
||||
type: '',
|
||||
text: '素材',
|
||||
id: 2
|
||||
},
|
||||
|
||||
{
|
||||
type: '',
|
||||
text: '课件',
|
||||
id: 4
|
||||
},
|
||||
|
||||
{
|
||||
type: '',
|
||||
text: '教案',
|
||||
id: 6
|
||||
},
|
||||
|
||||
...resourceType
|
||||
]
|
||||
// 资源格式
|
||||
const formatList = [
|
||||
const resourceFormatList = [
|
||||
{
|
||||
label: '资源格式',
|
||||
value: -1
|
||||
value: ''
|
||||
},
|
||||
{
|
||||
label: 'word',
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
label: 'ppt',
|
||||
value: 2
|
||||
}
|
||||
...resourceFormat
|
||||
]
|
||||
|
||||
// 校本资源为学校ID
|
||||
tabs.forEach(item =>{
|
||||
if( item.label == "校本资源"){
|
||||
item.value = userStore.user.deptId
|
||||
}
|
||||
})
|
||||
|
||||
const structQuery = {
|
||||
pageNum: 1,
|
||||
pageSize: 10
|
||||
}
|
||||
|
||||
export default defineStore('resource', {
|
||||
state: () => ({
|
||||
tabs,
|
||||
typeList,
|
||||
formatList,
|
||||
curTab: 1,
|
||||
curType: 1,
|
||||
resourceTypeList,
|
||||
resourceFormatList,
|
||||
|
||||
curFormat: -1,
|
||||
searchKey: '',
|
||||
// 新建资源
|
||||
isCreate: false,
|
||||
loading: false,
|
||||
//查询条件
|
||||
query: {
|
||||
fileSource: '平台',
|
||||
fileSuffix: '',
|
||||
fileFlag: '',
|
||||
fileRoot: '资源',
|
||||
orderByColumn: 'uploadTime',
|
||||
isAsc: 'desc',
|
||||
...structQuery
|
||||
},
|
||||
result: {
|
||||
list: [],
|
||||
total: 0
|
||||
}
|
||||
}),
|
||||
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) {
|
||||
this.curTab = val
|
||||
this.query.fileSource = val
|
||||
this.handleQuery()
|
||||
},
|
||||
changeType(val) {
|
||||
this.curType = val
|
||||
this.query.fileFlag = val
|
||||
this.handleQuery()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue