zhuhao_dev #33

Merged
zhuhao merged 8 commits from zhuhao_dev into main 2024-07-17 21:25:06 +08:00
9 changed files with 251 additions and 244 deletions
Showing only changes of commit 2a31a70a49 - Show all commits

View File

@ -29,6 +29,13 @@ const getFileTypeIcon = () => {
docx: 'icon-word', docx: 'icon-word',
mp4: 'icon-video', mp4: 'icon-video',
mov: 'icon-mov', 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] return '#' + iconObj[name]

View File

@ -1,5 +1,5 @@
<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="文件">
@ -16,8 +16,9 @@
<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>
<FileImage :fileName="item.name" /> <FileImage :fileName="item.name" size="50"/>
<span class="text">{{ item.name }}</span> <el-input class="file-input" v-model="item.fileData.name" placeholder="请输入文件名" />
<span>.{{ getFileSuffix(item.name) }}</span>
</div> </div>
<div class="flex-type flex"> <div class="flex-type flex">
<span class="name">类别</span> <span class="name">类别</span>
@ -47,6 +48,9 @@
import { ref, defineProps, defineEmits, watch } from 'vue' import { ref, defineProps, defineEmits, watch } from 'vue'
import FileImage from '@/components/file-image/index.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,34 +64,12 @@ const emit = defineEmits(['update:modelValue', 'submitFile'])
// //
const fileList = ref([]) const fileList = ref([])
const fileType = ref(1)
//
const resourceType = ref([
{
text: '素材',
value: '素材'
},
{
text: '课件',
value: '课件'
},
{
text: '教案',
value: '教案'
}
])
watch(() => props.modelValue, (newVal) => { watch(() => props.modelValue, (newVal) => {
dialogValue.value = newVal dialogValue.value = newVal
}) })
const hanleFileChange = (file) => { 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']
@ -99,10 +81,16 @@ const hanleFileChange = (file) => {
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 = file.raw.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、text、zip文件!')
return false return false
} }
// //
@ -112,28 +100,17 @@ const hanleFileChange = (file) => {
return false return false
} }
if (file.status === 'ready') { if (file.status === 'ready') {
// fileData // fileData
file.fileData = { file.fileData = {
fileFlag: '课件' fileFlag: '课件',
name: getFileName(file.name),
} }
fileList.value.push(file) fileList.value.push(file)
} }
} }
const getFileTypeIcon = (fileName) => {
const name = fileName.substr(fileName.lastIndexOf('.') + 1);
const iconObj = {
pdf: 'icon-pdf',
ppt: 'icon-ppt',
doc: 'icon-word',
docx: 'icon-word',
mp4: 'icon-video'
}
return '#' + iconObj[name]
}
// //
const delFile = (index) => { const delFile = (index) => {
fileList.value.splice(index, 1) fileList.value.splice(index, 1)
@ -152,12 +129,16 @@ const closeDialog = () => {
} }
// //
const submitFile = () => { const submitFile = () => {
//
fileList.value.forEach((item) => {
let suffix = getFileSuffix(item.name)
item.fileData.fileShowName = item.fileData.name + '.' + suffix
delete item.fileData.name
})
emit('submitFile',fileList.value) emit('submitFile',fileList.value)
closeDialog() closeDialog()
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -184,7 +165,7 @@ const submitFile = () => {
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
@ -224,4 +205,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

@ -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,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

@ -7,13 +7,13 @@
<div class="item-left flex"> <div class="item-left flex">
<FileImage :fileName="item.fileName" :size="50" /> <FileImage :fileName="item.fileName" :size="50" />
<div class="flex item-left-content"> <div class="flex item-left-content">
<div class="name flex">{{ item.fileName }}</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">{{ item.uploadTime }}上传</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>
@ -21,12 +21,11 @@
<el-popover placement="bottom-end" trigger="hover" popper-class="custom-popover" <el-popover placement="bottom-end" trigger="hover" popper-class="custom-popover"
:visible="item.showPopover"> :visible="item.showPopover">
<template #reference> <template #reference>
<el-button link type="primary" > <i <el-button link type="primary"> <i class="iconfont icon-shenglvehao"></i></el-button>
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>
@ -34,7 +33,7 @@
<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>
@ -59,15 +58,44 @@
</template> </template>
<script setup> <script setup>
import { deleteSmarttalk } from '@/api/file' import { ElMessage, ElMessageBox } from 'element-plus'
import useResoureStore from '../store'
import FileImage from '@/components/file-image/index.vue' import FileImage from '@/components/file-image/index.vue'
import { ElMessage } from 'element-plus' import { deleteSmarttalk, updateSmarttalk } from '@/api/file'
import { getFileSuffix } from '@/utils/ruoyi'
import useResoureStore from '../store'
const { ipcRenderer } = window.electron || {}
const sourceStore = useResoureStore() 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) => { const delRow = (item) => {
sourceStore.loading = true sourceStore.loading = true
@ -79,7 +107,6 @@ const delRow = (item) => {
} finally { } finally {
sourceStore.loading = false sourceStore.loading = false
} }
} }
</script> </script>

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.query.fileSource == item.value ? 'primary' : ''" @click="sourceStore.changeTab(item.value)">{{ 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,63 +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.query.fileSuffix" 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.query.fileFlag == item.value ? 'primary' : ''" round @click="sourceStore.changeType(item.value)">{{ :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 = (data)=>{
let fileList = toRaw(data)
const { textBookId, levelFirstId, levelSecondId, fileSource, fileRoot} = sourceStore.query
console.log(textBookId)
let fileData = { textBookId, levelFirstId, levelSecondId, fileSource, fileRoot }
fileList.forEach(item => {
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>
<style lang="scss" scoped> <style lang="scss" scoped>
.resoure-search { .resoure-search {
@ -91,6 +59,9 @@ const fileCallBack = (res)=>{
.query-row { .query-row {
justify-content: space-between; justify-content: space-between;
.row-left{
align-items: center;
}
} }
.line { .line {
@ -100,12 +71,12 @@ const fileCallBack = (res)=>{
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,31 +1,42 @@
<template> <template>
<div class="page-resource flex"> <div class="page-resource flex">
<!--左侧 教材 目录-->
<ChooseTextbook @changeBook="changeBook" @nodeClick="nodeClick" /> <ChooseTextbook @changeBook="changeBook" @nodeClick="nodeClick" />
<div class="page-right"> <div class="page-right">
<template v-if="!sourceStore.isCreate"> <!-- 搜索 -->
<ResoureSearch /> <ResoureSearch #add>
<el-button type="primary" round @click="openDialog" class="create-btn">
<i class="iconfont icon-jiahao"></i>
新建资源</el-button>
</ResoureSearch>
<!-- 列表 -->
<ResoureList /> <ResoureList />
</template>
<template v-else>
<CreateResoure/>
</template>
</div> </div>
</div> </div>
<!-- 上传弹窗 -->
<uploadDialog v-model="isDialogOpen" @submitFile="submitFile" />
</template> </template>
<script setup> <script setup>
import { onMounted } 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 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 changeBook = (data) => { const changeBook = (data) => {
getData(data) getData(data)
@ -50,8 +61,30 @@ const getData = (data) => {
sourceStore.handleQuery() 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>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -68,5 +101,14 @@ const getData = (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,61 +1,24 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { getSmarttalkPage } from '@/api/file/index' import { getSmarttalkPage } from '@/api/file/index'
import { tabs, resourceType, resourceFormat } from '@/utils/resourceDict'
const tabs = [ const resourceTypeList = [
{ {
text: '平台资源', label: '全部',
value: '平台'
},
{
text: '校本资源',
value: '校本'
}
]
const typeList = [
{
text: '全部',
value: '' value: ''
}, },
{ ...resourceType
text: '素材',
value: '素材'
},
{
text: '课件',
value: '课件'
},
{
text: '教案',
value: '教案'
}
] ]
// 资源格式 const resourceFormatList = [
const formatList = [
{ {
label: '资源格式', label: '资源格式',
value: '' value: ''
}, },
{ ...resourceFormat
label: 'word',
value: 'word'
},
{
label: 'ppt',
value: 'ppt'
},
{
label: 'mp3',
value: 'mp3'
},
{
label: 'mp4',
value: 'mp4'
}
] ]
const structQuery = { const structQuery = {
pageNum: 1, pageNum: 1,
pageSize: 10 pageSize: 10
@ -64,8 +27,8 @@ const structQuery = {
export default defineStore('resource', { export default defineStore('resource', {
state: () => ({ state: () => ({
tabs, tabs,
typeList, resourceTypeList,
formatList, resourceFormatList,
curFormat: -1, curFormat: -1,
searchKey: '', searchKey: '',
@ -94,7 +57,6 @@ export default defineStore('resource', {
getSmarttalkPage(this.query).then((res) => { getSmarttalkPage(this.query).then((res) => {
this.result.total = res.total this.result.total = res.total
this.result.list = res.rows this.result.list = res.rows
}) })
} finally { } finally {
this.loading = false this.loading = false