Merge remote-tracking branch 'origin/main'
This commit is contained in:
commit
222b0f54f8
|
@ -125,6 +125,7 @@ import {
|
|||
User,
|
||||
Switch,
|
||||
More,
|
||||
Material
|
||||
} from '@icon-park/vue-next'
|
||||
|
||||
export interface Icons {
|
||||
|
@ -255,6 +256,7 @@ export const icons: Icons = {
|
|||
IconUser: User,
|
||||
IconSwitch: Switch,
|
||||
IconMore: More,
|
||||
IconMaterial: Material
|
||||
}
|
||||
|
||||
export default {
|
||||
|
|
|
@ -0,0 +1,173 @@
|
|||
<template>
|
||||
<header class="flex material-header">
|
||||
<span>选择素材</span>
|
||||
<i class="iconfont icon-guanbi" @click="onClose"></i>
|
||||
</header>
|
||||
<div class="flex material-list" v-loading="loading">
|
||||
<div class="flex material-item" v-for="item in list" :key="item.id" >
|
||||
<div class="flex">
|
||||
<el-image :src="fileUrl(item)" class="img" />
|
||||
<el-text truncated>{{ item.fileShowName }}</el-text>
|
||||
</div>
|
||||
<el-button type="primary" @click="onInsert(item)">插入</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, computed } from 'vue';
|
||||
import { sessionStore } from '@/utils/store'
|
||||
import { getSmarttalkPage } from '@/api/file'
|
||||
import { getFileSuffix, urlToBase64 } from '@/utils/ruoyi.js'
|
||||
|
||||
const emit = defineEmits(['insertMaterial', 'close'])
|
||||
|
||||
const curNode = reactive({})
|
||||
let params = {
|
||||
textbookId: '',
|
||||
levelFirstId: '',
|
||||
levelSecondId: null,
|
||||
fileSource: '个人',
|
||||
fileRoot: '备课',
|
||||
orderByColumn: 'uploadTime',
|
||||
isAsc: 'desc',
|
||||
pageSize: 500
|
||||
}
|
||||
|
||||
const suffixAry = [ 'jpeg','jpg','png','gif','mp3','mp4','avi','mov']
|
||||
const videoSuffix = ['mp3','mp4','avi','mov']
|
||||
const list = ref([])
|
||||
const loading = ref(false)
|
||||
const init = () => {
|
||||
loading.value = true
|
||||
getSmarttalkPage(params).then(res => {
|
||||
loading.value = false
|
||||
if(res.rows && res.rows.length){
|
||||
// 过滤出图片和视频
|
||||
list.value = res.rows.filter( item => suffixAry.indexOf(getFileSuffix(item.fileShowName)) != -1)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const fileUrl = computed(() => (item) =>{
|
||||
if(videoSuffix.indexOf(getFileSuffix(item.fileShowName)) != -1){
|
||||
return item.coverPic
|
||||
}
|
||||
else{
|
||||
return item.fileFullPath
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
// 插入
|
||||
const onInsert = async (item) =>{
|
||||
if(videoSuffix.indexOf(getFileSuffix(item.fileShowName)) != -1){
|
||||
emit('insertMaterial',{ type: 'video', data: item.fileFullPath })
|
||||
}
|
||||
else{
|
||||
const base64 = await urlToBase64(item.fileFullPath)
|
||||
emit('insertMaterial',{ type: 'img', data: base64 })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
const GetUrlParameters = (parameters) => {
|
||||
let resData = "";
|
||||
|
||||
let url = document.location.toString();
|
||||
let arrUrl = url.split("?");
|
||||
// 判断是否有参数
|
||||
if (arrUrl.length > 1) {
|
||||
// 拆分参数字符串
|
||||
let parametersArr = arrUrl[1].split("&");
|
||||
// 循环查找参数
|
||||
for (let i = 0; i <= parametersArr.length; i++) {
|
||||
if (parametersArr[i]) {
|
||||
// 拆分参数的键和值
|
||||
let parameterStr = parametersArr[i].split("=");
|
||||
if (parameters == parameterStr[0]) {
|
||||
resData = parameterStr[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return resData;
|
||||
}
|
||||
|
||||
|
||||
const proxyToBase64 = (url)=> {
|
||||
const dourl = GetUrlParameters(url)
|
||||
console.log(dourl,'dourl')
|
||||
return
|
||||
axios({
|
||||
url: "/api/logo.png",
|
||||
method: "get",
|
||||
responseType: "blob",
|
||||
}).then((res) => {
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(res.data);
|
||||
reader.onload = () => {
|
||||
console.log(reader.result);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// 关闭
|
||||
const onClose = () =>{
|
||||
emit('close')
|
||||
}
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
let data = sessionStore.get('subject.curNode')
|
||||
Object.assign(curNode, data);
|
||||
params.textbookId = curNode.rootid
|
||||
if (curNode.parentNode) {
|
||||
params.levelFirstId = curNode.parentNode.id
|
||||
params.levelSecondId = curNode.id
|
||||
}
|
||||
else {
|
||||
params.levelFirstId = curNode.id
|
||||
}
|
||||
init()
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.material-header {
|
||||
font-size: 17px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 20px;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
.icon-guanbi{
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.material-list{
|
||||
flex-direction: column;
|
||||
min-height: 150px;
|
||||
max-height: 500px;
|
||||
overflow-y: auto
|
||||
}
|
||||
|
||||
.material-item {
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
font-size: 14px;
|
||||
justify-content: space-between;
|
||||
.img{
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -82,6 +82,7 @@
|
|||
<IconVideoTwo class="handler-item" v-tooltip="'插入音视频'" />
|
||||
</Popover>
|
||||
<IconPreviewOpen class="handler-item" v-tooltip="'插入试题'" @click="classWorkTaskVisible = true" />
|
||||
<IconMaterial class="handler-item" v-tooltip="'插入素材'" @click="materiaVisible = true"/>
|
||||
</div>
|
||||
|
||||
<div class="right-handler">
|
||||
|
@ -112,16 +113,21 @@
|
|||
/>
|
||||
</Modal>
|
||||
|
||||
<Modal
|
||||
v-model:visible="classWorkTaskVisible"
|
||||
:width="880"
|
||||
>
|
||||
|
||||
<el-dialog v-model="classWorkTaskVisible" append-to-body :show-close="false" width="70%">
|
||||
<QuestToPPTist
|
||||
class="class-work-task-modal"
|
||||
@close="classWorkTaskVisible = false"
|
||||
@update="data => { onhtml2canvas(data); classWorkTaskVisible = false }"
|
||||
/>
|
||||
</el-dialog>
|
||||
|
||||
<Modal
|
||||
v-model:visible="materiaVisible"
|
||||
:width="880">
|
||||
<MaterialDialog @close="materiaVisible = false" @insertMaterial="insertMaterial"/>
|
||||
</Modal>
|
||||
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
@ -149,6 +155,7 @@ import Divider from '../../../components/Divider.vue'
|
|||
import Popover from '../../../components/Popover.vue'
|
||||
import PopoverMenuItem from '../../../components/PopoverMenuItem.vue'
|
||||
import QuestToPPTist from '@/views/classTask/newClassTaskAssign/questToPPTist/index.vue'
|
||||
import MaterialDialog from './MaterialDialog.vue'
|
||||
|
||||
const mainStore = useMainStore()
|
||||
const { creatingElement, creatingCustomShape, showSelectPanel, showSearchPanel, showNotesPanel } = storeToRefs(mainStore)
|
||||
|
@ -200,6 +207,7 @@ const classWorkTaskVisible = ref(false)
|
|||
const textTypeSelectVisible = ref(false)
|
||||
const shapeMenuVisible = ref(false)
|
||||
const moreVisible = ref(false)
|
||||
const materiaVisible = ref(false)
|
||||
|
||||
// 绘制文字范围
|
||||
const drawText = (vertical = false) => {
|
||||
|
@ -246,6 +254,23 @@ const toggleSraechPanel = () => {
|
|||
const toggleNotesPanel = () => {
|
||||
mainStore.setNotesPanelState(!showNotesPanel.value)
|
||||
}
|
||||
|
||||
// 插入素材
|
||||
interface MaterialParams {
|
||||
type: string,
|
||||
data: string
|
||||
}
|
||||
const insertMaterial = (item: MaterialParams) =>{
|
||||
const { type, data } = item
|
||||
if(type == 'video'){
|
||||
createVideoElement(data)
|
||||
}
|
||||
else{
|
||||
createImageElement(data)
|
||||
}
|
||||
materiaVisible.value = false
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
@ -363,7 +388,7 @@ const toggleNotesPanel = () => {
|
|||
}
|
||||
}
|
||||
.class-work-task-modal{
|
||||
height: 80vh;
|
||||
height: 70vh;
|
||||
}
|
||||
|
||||
@media screen and (width <= 1200px) {
|
||||
|
|
|
@ -160,7 +160,7 @@ const topImgPositionStyle = computed(() => {
|
|||
return {
|
||||
left: -left * (100 / width) + '%',
|
||||
top: -top * (100 / height) + '%',
|
||||
width: bottomWidth / width * 100 + '%',
|
||||
width: bottomWidth / width * 100 + '%' ,
|
||||
height: bottomHeight / height * 100 + '%',
|
||||
}
|
||||
})
|
||||
|
@ -228,6 +228,7 @@ const updateRange = () => {
|
|||
width: parseInt(topImgPositionStyle.value.width),
|
||||
height: parseInt(topImgPositionStyle.value.height),
|
||||
}
|
||||
console.log('retPosition', retPosition)
|
||||
|
||||
const widthScale = 100 / retPosition.width
|
||||
const heightScale = 100 / retPosition.height
|
||||
|
@ -475,7 +476,7 @@ const scaleClipRange = (e: MouseEvent, type: OperateResizeHandlers) => {
|
|||
isMouseDown = false
|
||||
document.onmousemove = null
|
||||
document.onmouseup = null
|
||||
|
||||
console.log('----------------------------------')
|
||||
updateRange()
|
||||
|
||||
setTimeout(() => isSettingClipRange.value = false, 0)
|
||||
|
@ -537,6 +538,7 @@ const edgePoints = [
|
|||
|
||||
img {
|
||||
position: absolute;
|
||||
max-width: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -183,6 +183,7 @@ const handleClip = (data: ImageClipedEmitData | null) => {
|
|||
}
|
||||
img {
|
||||
position: absolute;
|
||||
max-width: none !important; // 防止图片被压缩
|
||||
}
|
||||
}
|
||||
.color-mask {
|
||||
|
|
|
@ -258,16 +258,45 @@ export const getFileName = (filename) => {
|
|||
return filename.replace(/\.[^/.]+$/, "");
|
||||
}
|
||||
|
||||
// 清除当前选中的教材 章节 相关信息
|
||||
export const clearBookInfo = () =>{
|
||||
//当前选中的教材
|
||||
localStorage.removeItem('curBook')
|
||||
// 当前选中的节点
|
||||
localStorage.removeItem('curNode')
|
||||
// 所有章节单元数据
|
||||
localStorage.removeItem('unitList')
|
||||
// 所有教材数据
|
||||
localStorage.removeItem('subjectList')
|
||||
// 展开的节点
|
||||
localStorage.removeItem('defaultExpandedKeys')
|
||||
|
||||
/**
|
||||
* 根据图片的url转换对应的base64值
|
||||
* @param { String } url 如:http://xxxx/xxx.png
|
||||
* @returns base64取值
|
||||
*/
|
||||
export const urlToBase64 = (url) => {
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!url) {
|
||||
reject('请传入url内容')
|
||||
}
|
||||
if (/\.(png|jpe?g|gif|svg)(\?.*)?$/.test(url)) {
|
||||
// 图片地址
|
||||
let image = new Image()
|
||||
// 设置跨域问题
|
||||
image.setAttribute('crossOrigin', 'anonymous')
|
||||
// 图片地址
|
||||
image.src = url +"?v=" + Math.random(); // 处理缓存,fix缓存bug,有缓存,浏览器会报错;
|
||||
image.onload = () => {
|
||||
let canvas = document.createElement('canvas')
|
||||
const ctx = canvas.getContext('2d')
|
||||
canvas.width = image.width
|
||||
canvas.height = image.height
|
||||
ctx.drawImage(image, 0, 0, image.width, image.height)
|
||||
|
||||
// 获取图片后缀
|
||||
const ext = url.substring(url.lastIndexOf('.') + 1).toLowerCase()
|
||||
// 转base64
|
||||
const dataUrl = canvas.toDataURL(`image/${ext}`)
|
||||
resolve(dataUrl || '')
|
||||
canvas = null // 清除canvas元素
|
||||
image = null // 清除img元素
|
||||
}
|
||||
} else {
|
||||
// 非图片地址
|
||||
reject('非(png/jpe?g/gif/svg等)图片地址');
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
}
|
|
@ -68,7 +68,7 @@ defineExpose({
|
|||
|
||||
.page-resource {
|
||||
user-select: none;
|
||||
height: calc(100% - 55px);
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
|
|
Loading…
Reference in New Issue