ppt 优化
This commit is contained in:
parent
0d68e6efa7
commit
482cab5cd3
|
@ -27,6 +27,7 @@ import msgUtils from '@/plugins/modal' // 消息工具
|
||||||
import * as API_entpcoursefile from '@/api/education/entpcoursefile' // 相关api
|
import * as API_entpcoursefile from '@/api/education/entpcoursefile' // 相关api
|
||||||
import { PPTApi } from './api'
|
import { PPTApi } from './api'
|
||||||
import { sessionStore } from '@/utils/store' // electron-store 状态管理
|
import { sessionStore } from '@/utils/store' // electron-store 状态管理
|
||||||
|
import './api/watcher' // 监听
|
||||||
|
|
||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
const _isPC = isPC()
|
const _isPC = isPC()
|
||||||
|
@ -69,25 +70,20 @@ interface Result {
|
||||||
}
|
}
|
||||||
// 获取参数
|
// 获取参数
|
||||||
const initLoad: Function = () => {
|
const initLoad: Function = () => {
|
||||||
// const urlSearch = location.href.split('?')[1]
|
// 获取缓存的ppt 资源数据
|
||||||
// const query = Object.fromEntries(new URLSearchParams(urlSearch))
|
|
||||||
// const id: String = query.id
|
|
||||||
// // 如果存在就获取pptx幻灯片内容
|
|
||||||
// if (!!id) return PPTApi.getSlideList(id)
|
|
||||||
// 缓存当前资源信息
|
|
||||||
const resource = sessionStore.get('curr.resource')
|
const resource = sessionStore.get('curr.resource')
|
||||||
if (!!resource) { // 有ppt 资源数据缓存
|
if (!!resource) { // 有ppt 资源数据缓存
|
||||||
slidesStore.setTitle(resource.title)
|
slidesStore.setTitle(resource.title)
|
||||||
|
if (!!resource.parentContent) { // 有全局配置项
|
||||||
|
const opt = JSON.parse(resource.parentContent)
|
||||||
|
!!(opt.width??null) && slidesStore.setViewportSize(opt.width) // 有宽度配置项
|
||||||
|
!!(opt.ratio??null) && slidesStore.setViewportRatio(opt.ratio)// 有比例配置项
|
||||||
|
}
|
||||||
return PPTApi.getSlideList(resource.id)
|
return PPTApi.getSlideList(resource.id)
|
||||||
}
|
}
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 监听幻灯片内容变化
|
|
||||||
watch(() => slidesStore.slides, (newVal, oldVal) => {
|
|
||||||
// 更新幻灯片内容
|
|
||||||
PPTApi.updateSlides(newVal, oldVal)
|
|
||||||
},{ deep: true })
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|
|
@ -51,7 +51,7 @@ export class PPTApi {
|
||||||
// 获取所有幻灯片列表
|
// 获取所有幻灯片列表
|
||||||
static getSlideList(parentid: (Number | String)): Promise<Boolean> {
|
static getSlideList(parentid: (Number | String)): Promise<Boolean> {
|
||||||
return new Promise(async (resolve, reject) => {
|
return new Promise(async (resolve, reject) => {
|
||||||
const params: object = { parentid, orderByColumn: 'fileidx', isAsc: 'asc' }
|
const params: object = { parentid, orderByColumn: 'fileidx', isAsc: 'asc', pageSize: 9999 }
|
||||||
const res: Result = await API_entpcoursefile.listEntpcoursefileNew(params)
|
const res: Result = await API_entpcoursefile.listEntpcoursefileNew(params)
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
const slides = (res.rows || []).map(o => {
|
const slides = (res.rows || []).map(o => {
|
||||||
|
|
|
@ -9,7 +9,6 @@ export default class {
|
||||||
// 删除幻灯片
|
// 删除幻灯片
|
||||||
static delSlide(id: string): Promise<Boolean> {
|
static delSlide(id: string): Promise<Boolean> {
|
||||||
return new Promise(async (resolve, reject) => {
|
return new Promise(async (resolve, reject) => {
|
||||||
console.log('delSlide', id)
|
|
||||||
const res: Result = await API_entpcoursefile.delEntpcoursefile(id)
|
const res: Result = await API_entpcoursefile.delEntpcoursefile(id)
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
resolve(true)
|
resolve(true)
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
/**
|
||||||
|
* @description 公共监听器
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { watch } from 'vue'
|
||||||
|
import { PPTApi } from './index'
|
||||||
|
import * as store from '../store'
|
||||||
|
import { sessionStore } from '@/utils/store' // electron-store 状态管理
|
||||||
|
const slidesStore = store.useSlidesStore()
|
||||||
|
const resource = sessionStore.get('curr.resource')
|
||||||
|
/**
|
||||||
|
* @description 监听器
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 监听幻灯片内容变化
|
||||||
|
watch(() => slidesStore.slides, (newVal, oldVal) => {
|
||||||
|
PPTApi.updateSlides(newVal, oldVal) // 更新幻灯片内容
|
||||||
|
},{ deep: true })
|
||||||
|
|
||||||
|
// 监听标题变化
|
||||||
|
watch(() => slidesStore.title, (newVal, oldVal) => {
|
||||||
|
if (oldVal == '未命名演示文稿') return // 初始加载,不需要更新数据
|
||||||
|
updatePPT({title: newVal})
|
||||||
|
})
|
||||||
|
|
||||||
|
const updatePPT = async (data) => {
|
||||||
|
if (!resource) return
|
||||||
|
data.id = resource.id
|
||||||
|
await PPTApi.updateSlide(data) // 更新ppt内容
|
||||||
|
sessionStore.set('curr.resource.title', data.title)
|
||||||
|
}
|
|
@ -57,9 +57,12 @@
|
||||||
<div class="menu-item" v-tooltip="'导出'" @click="setDialogForExport('pptx')">
|
<div class="menu-item" v-tooltip="'导出'" @click="setDialogForExport('pptx')">
|
||||||
<IconDownload class="icon" />
|
<IconDownload class="icon" />
|
||||||
</div>
|
</div>
|
||||||
<a class="github-link" v-tooltip="'Copyright © 2020-PRESENT pipipi-pikachu'" href="https://github.com/pipipi-pikachu/PPTist" target="_blank">
|
<div class="menu-item" v-tooltip="`${userStore.user.parentDeptName}-${userStore.user.nickName}`">
|
||||||
|
<el-avatar size="small" :src="avatar" />
|
||||||
|
</div>
|
||||||
|
<!-- <a class="github-link" v-tooltip="'Copyright © 2020-PRESENT pipipi-pikachu'" href="https://github.com/pipipi-pikachu/PPTist" target="_blank">
|
||||||
<div class="menu-item"><IconGithub class="icon" /></div>
|
<div class="menu-item"><IconGithub class="icon" /></div>
|
||||||
</a>
|
</a> -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Drawer
|
<Drawer
|
||||||
|
@ -92,6 +95,9 @@ import Input from '../../../components/Input.vue'
|
||||||
import Popover from '../../../components/Popover.vue'
|
import Popover from '../../../components/Popover.vue'
|
||||||
import PopoverMenuItem from '../../../components/PopoverMenuItem.vue'
|
import PopoverMenuItem from '../../../components/PopoverMenuItem.vue'
|
||||||
|
|
||||||
|
import useUserStore from '@/store/modules/user' // 外部-用户信息
|
||||||
|
|
||||||
|
const userStore = useUserStore() // 外部-用户信息
|
||||||
const mainStore = useMainStore()
|
const mainStore = useMainStore()
|
||||||
const slidesStore = useSlidesStore()
|
const slidesStore = useSlidesStore()
|
||||||
const { title } = storeToRefs(slidesStore)
|
const { title } = storeToRefs(slidesStore)
|
||||||
|
@ -104,7 +110,7 @@ const hotkeyDrawerVisible = ref(false)
|
||||||
const editingTitle = ref(false)
|
const editingTitle = ref(false)
|
||||||
const titleInputRef = ref<InstanceType<typeof Input>>()
|
const titleInputRef = ref<InstanceType<typeof Input>>()
|
||||||
const titleValue = ref('')
|
const titleValue = ref('')
|
||||||
|
const avatar = ref(import.meta.env.VITE_APP_BASE_API+userStore.avatar) // 用户头像
|
||||||
const startEditTitle = () => {
|
const startEditTitle = () => {
|
||||||
titleValue.value = title.value
|
titleValue.value = title.value
|
||||||
editingTitle.value = true
|
editingTitle.value = true
|
||||||
|
|
|
@ -118,6 +118,14 @@ export function delEntpcoursefile(id) {
|
||||||
method: 'delete'
|
method: 'delete'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
// 删除entpcoursefile - new
|
||||||
|
export function delEntpcoursefileNew(id) {
|
||||||
|
return request({
|
||||||
|
url: '/education/entpcoursefile/delete',
|
||||||
|
method: 'get',
|
||||||
|
params: {id}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 保存base64图片,返回url
|
// 保存base64图片,返回url
|
||||||
export function saveEntpCourseBase64File(data) {
|
export function saveEntpCourseBase64File(data) {
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
// import tab from './tab'
|
// import tab from './tab'
|
||||||
// import auth from './auth'
|
// import auth from './auth'
|
||||||
// import cache from './cache'
|
// import cache from './cache'
|
||||||
import modal from './modal'
|
|
||||||
// import download from './download'
|
// import download from './download'
|
||||||
|
import modal from './modal'
|
||||||
|
// import './vue3-menus'
|
||||||
|
import vue3Menus from './vue3-menus'
|
||||||
|
// console.log('vue3Menus', defineComponent)
|
||||||
export default function installPlugins(app){
|
export default function installPlugins(app){
|
||||||
// 页签操作
|
// 页签操作
|
||||||
// app.config.globalProperties.$tab = tab
|
// app.config.globalProperties.$tab = tab
|
||||||
|
@ -15,4 +17,6 @@ export default function installPlugins(app){
|
||||||
app.config.globalProperties.$modal = modal
|
app.config.globalProperties.$modal = modal
|
||||||
// 下载文件
|
// 下载文件
|
||||||
// app.config.globalProperties.$download = download
|
// app.config.globalProperties.$download = download
|
||||||
|
// 右键菜单 支持组件|指令|函数 三种方式使用
|
||||||
|
vue3Menus(app)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,437 @@
|
||||||
|
import { defineComponent, getCurrentInstance, ref, computed, watch, nextTick, createVNode, Teleport, Transition, render } from 'vue';
|
||||||
|
|
||||||
|
function styleInject(css, ref) {
|
||||||
|
if ( ref === void 0 ) ref = {};
|
||||||
|
var insertAt = ref.insertAt;
|
||||||
|
|
||||||
|
if (!css || typeof document === 'undefined') { return; }
|
||||||
|
|
||||||
|
var head = document.head || document.getElementsByTagName('head')[0];
|
||||||
|
var style = document.createElement('style');
|
||||||
|
style.type = 'text/css';
|
||||||
|
|
||||||
|
if (insertAt === 'top') {
|
||||||
|
if (head.firstChild) {
|
||||||
|
head.insertBefore(style, head.firstChild);
|
||||||
|
} else {
|
||||||
|
head.appendChild(style);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
head.appendChild(style);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (style.styleSheet) {
|
||||||
|
style.styleSheet.cssText = css;
|
||||||
|
} else {
|
||||||
|
style.appendChild(document.createTextNode(css));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var css_248z = ".menus-fade-enter-active,\n.menus-fade-leave-active {\n transition: opacity 0.2s ease-in-out;\n}\n.menus-fade-enter-from,\n.menus-fade-leave-to {\n opacity: 0;\n}\n\n.v3-menus {\n position: fixed;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04);\n background: #fff;\n border-radius: 4px;\n padding: 8px 0;\n user-select: none;\n box-sizing: border-box;\n}\n\n.v3-menus-body {\n display: block;\n}\n\n.v3-menus-item {\n display: flex;\n line-height: 2rem;\n padding: 0 1rem;\n margin: 0;\n font-size: 0.8rem;\n outline: 0;\n align-items: center;\n transition: 0.2s;\n box-sizing: border-box;\n list-style: none;\n border-bottom: 1px solid #00000000;\n}\n\n.v3-menus-divided {\n border-bottom-color: #ebeef5;\n}\n\n.v3-menus-icon {\n display: flex;\n margin-right: 0.6rem;\n width: 1rem;\n}\n\n.v3-menus-label {\n flex: 1;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.v3-menus-suffix {\n margin-left: 1.5rem;\n font-size: 0.39rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.v3-menus-available {\n color: #606266;\n cursor: pointer;\n}\n\n.v3-menus-available:hover {\n background: #ecf5ff;\n color: #409eff;\n}\n\n.v3-menus-disabled {\n color: #c0c4cc;\n cursor: not-allowed;\n}\n\n.v3-menus-active {\n background: #ecf5ff;\n color: #409eff;\n}\n\n.v3-menus-tip {\n font-size: 9px;\n color: #999;\n}\n";
|
||||||
|
styleInject(css_248z);
|
||||||
|
|
||||||
|
const props = {
|
||||||
|
menus: {
|
||||||
|
type: Array,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
menusClass: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
itemClass: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
event: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
minWidth: {
|
||||||
|
type: [Number, String],
|
||||||
|
default: 'none'
|
||||||
|
},
|
||||||
|
maxWidth: {
|
||||||
|
type: [Number, String],
|
||||||
|
default: 'none'
|
||||||
|
},
|
||||||
|
zIndex: {
|
||||||
|
type: Number,
|
||||||
|
default: 3
|
||||||
|
},
|
||||||
|
direction: {
|
||||||
|
type: String,
|
||||||
|
default: 'right'
|
||||||
|
},
|
||||||
|
open: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
args: {
|
||||||
|
type: [Object, Function, Array, Boolean, String],
|
||||||
|
default: {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const vue3MenusComponent = defineComponent({
|
||||||
|
name: 'vue3-menus',
|
||||||
|
inheritAttrs: false,
|
||||||
|
props,
|
||||||
|
|
||||||
|
setup(props, {
|
||||||
|
slots,
|
||||||
|
attrs
|
||||||
|
}) {
|
||||||
|
const windowWidth = globalThis.document.documentElement.clientWidth;
|
||||||
|
const windowHeight = globalThis.document.documentElement.clientHeight;
|
||||||
|
const {
|
||||||
|
proxy
|
||||||
|
} = getCurrentInstance();
|
||||||
|
const show = ref(props.open);
|
||||||
|
const self = {};
|
||||||
|
const menusRef = ref(null);
|
||||||
|
const activeIndex = ref(-1);
|
||||||
|
const left = ref(0);
|
||||||
|
const top = ref(0);
|
||||||
|
let direction = props.direction;
|
||||||
|
const hasIcon = computed(() => {
|
||||||
|
for (let index = 0; index < props.menus.length; index++) {
|
||||||
|
const menu = props.menus[index];
|
||||||
|
|
||||||
|
if (menu.icon !== undefined) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const position = computed(() => {
|
||||||
|
return {
|
||||||
|
x: props.event.clientX,
|
||||||
|
y: props.event.clientY,
|
||||||
|
width: props.event.width || 0,
|
||||||
|
height: props.event.height || 0
|
||||||
|
};
|
||||||
|
});
|
||||||
|
const style = computed(() => {
|
||||||
|
return {
|
||||||
|
left: `${left.value}px`,
|
||||||
|
top: `${top.value}px`,
|
||||||
|
minWidth: `${props.minWidth}px`,
|
||||||
|
maxWidth: props.maxWidth == 'none' ? props.maxWidth : `${props.maxWidth}px`,
|
||||||
|
zIndex: props.zIndex
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
function leftOpen(menusWidth) {
|
||||||
|
left.value = position.value.x - menusWidth;
|
||||||
|
direction = 'left';
|
||||||
|
|
||||||
|
if (left.value < 0) {
|
||||||
|
direction = 'right';
|
||||||
|
|
||||||
|
if (position.value.width === 0 || position.value.width === undefined) {
|
||||||
|
left.value = 0;
|
||||||
|
} else {
|
||||||
|
left.value = position.value.x + position.value.width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function rightOpen(windowWidth, menusWidth) {
|
||||||
|
left.value = position.value.x + position.value.width;
|
||||||
|
direction = 'right';
|
||||||
|
|
||||||
|
if (left.value + menusWidth > windowWidth) {
|
||||||
|
direction = 'left';
|
||||||
|
|
||||||
|
if (position.value.width === 0 || position.value.width === undefined) {
|
||||||
|
left.value = windowWidth - menusWidth;
|
||||||
|
} else {
|
||||||
|
left.value = position.value.x - menusWidth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeEvent() {
|
||||||
|
activeIndex.value = -1;
|
||||||
|
show.value = false;
|
||||||
|
|
||||||
|
if (self && self.instance) {
|
||||||
|
self.instance.close.bind(self.instance)();
|
||||||
|
self.instance = null;
|
||||||
|
self.index = null; // @ts-ignore
|
||||||
|
|
||||||
|
if (proxy.closeAll) {
|
||||||
|
// @ts-ignore
|
||||||
|
proxy.closeAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(() => props.open, newVal => show.value = newVal);
|
||||||
|
watch(show, newVal => {
|
||||||
|
if (newVal) {
|
||||||
|
nextTick(() => {
|
||||||
|
const menusWidth = menusRef.value.offsetWidth;
|
||||||
|
const menusHeight = menusRef.value.offsetHeight;
|
||||||
|
|
||||||
|
if (direction === 'left') {
|
||||||
|
leftOpen(menusWidth);
|
||||||
|
} else {
|
||||||
|
rightOpen(windowWidth, menusWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
top.value = position.value.y;
|
||||||
|
|
||||||
|
if (position.value.y + menusHeight > windowHeight) {
|
||||||
|
if (position.value.height === 0 || position.value.height === undefined) {
|
||||||
|
top.value = position.value.y - menusHeight;
|
||||||
|
} else {
|
||||||
|
top.value = windowHeight - menusHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
globalThis.document.addEventListener('click', closeEvent);
|
||||||
|
globalThis.document.addEventListener('contextmenu', closeEvent);
|
||||||
|
globalThis.document.addEventListener('wheel', closeEvent);
|
||||||
|
}, 0);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
activeIndex.value = -1;
|
||||||
|
globalThis.document.removeEventListener('click', closeEvent);
|
||||||
|
globalThis.document.removeEventListener('contextmenu', closeEvent);
|
||||||
|
globalThis.document.removeEventListener('wheel', closeEvent);
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
immediate: true
|
||||||
|
});
|
||||||
|
|
||||||
|
function mouseEnter(event, menu, index) {
|
||||||
|
event.preventDefault();
|
||||||
|
activeIndex.value = index;
|
||||||
|
|
||||||
|
if (!menu || menu.disabled || menu.hidden) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.instance) {
|
||||||
|
if (self.index === index) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.instance.close.bind(self.instance)();
|
||||||
|
self.instance = null;
|
||||||
|
self.index = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!menu.children) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const enter = menu.enter && typeof menu.enter === 'function' ? menu.enter : null;
|
||||||
|
|
||||||
|
if (enter) {
|
||||||
|
const val = enter(menu, props.args);
|
||||||
|
|
||||||
|
if (val === false || val === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const menuItemClientRect = event.target.getBoundingClientRect();
|
||||||
|
const vm = createVNode(vue3MenusComponent, { ...props,
|
||||||
|
menus: menu.children,
|
||||||
|
direction: direction,
|
||||||
|
event: {
|
||||||
|
clientX: menuItemClientRect.x + 3,
|
||||||
|
clientY: menuItemClientRect.y - 8,
|
||||||
|
width: menuItemClientRect.width - 2 * 3,
|
||||||
|
height: menuItemClientRect.width
|
||||||
|
},
|
||||||
|
open: false
|
||||||
|
}, slots);
|
||||||
|
const container = globalThis.document.createElement('div');
|
||||||
|
render(vm, container);
|
||||||
|
vm.component.props.open = true; // @ts-ignore
|
||||||
|
|
||||||
|
vm.component.proxy.close = close;
|
||||||
|
self.instance = vm.component.proxy;
|
||||||
|
self.instance.container = container;
|
||||||
|
self.instance.props = vm.component.props;
|
||||||
|
self.index = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mouseClick(event, menu) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
if (!menu || menu.disabled) {
|
||||||
|
event.stopPropagation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const click = menu.click && typeof menu.click === 'function' ? menu.click : null;
|
||||||
|
|
||||||
|
if (click) {
|
||||||
|
const val = click(menu, props.args);
|
||||||
|
|
||||||
|
if (val === false || val === null) {
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (menu.children) {
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function close() {
|
||||||
|
this.show = false;
|
||||||
|
|
||||||
|
if (this.self && this.self.instance) {
|
||||||
|
this.self.instance.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
nextTick(() => {
|
||||||
|
render(null, this.container);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
default: $default,
|
||||||
|
label,
|
||||||
|
icon,
|
||||||
|
suffix
|
||||||
|
} = slots;
|
||||||
|
const $class = ['v3-menus', attrs.class, props.menusClass];
|
||||||
|
return () => createVNode(Teleport, {
|
||||||
|
"to": 'body'
|
||||||
|
}, {
|
||||||
|
default: () => [createVNode(Transition, {
|
||||||
|
"name": 'menus-fade'
|
||||||
|
}, {
|
||||||
|
default: () => [!show.value ? null : createVNode("div", {
|
||||||
|
"ref": menusRef,
|
||||||
|
"class": $class,
|
||||||
|
"style": style.value,
|
||||||
|
"onWheel": e => e.preventDefault(),
|
||||||
|
"onContextmenu": e => e.preventDefault()
|
||||||
|
}, [createVNode("div", {
|
||||||
|
"class": 'v3-menus-body'
|
||||||
|
}, [props.menus.map((menu, index) => {
|
||||||
|
if (menu.hidden) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($default) {
|
||||||
|
return createVNode("div", {
|
||||||
|
"onContextmenu": $event => mouseClick($event, menu),
|
||||||
|
"onClick": $event => mouseClick($event, menu),
|
||||||
|
"onMouseenter": $event => mouseEnter($event, menu, index)
|
||||||
|
}, [$default({
|
||||||
|
menu,
|
||||||
|
activeIndex: activeIndex.value,
|
||||||
|
index
|
||||||
|
})]);
|
||||||
|
} else {
|
||||||
|
let $class = [props.itemClass, 'v3-menus-item', menu.disabled ? 'v3-menus-disabled' : 'v3-menus-available'];
|
||||||
|
$class = $class.concat([menu.divided ? 'v3-menus-divided' : null, !menu.disabled && activeIndex.value === index ? 'v3-menus-active' : null]);
|
||||||
|
return createVNode("div", {
|
||||||
|
"style": menu.style,
|
||||||
|
"class": $class.join(' '),
|
||||||
|
"onClick": $event => mouseClick($event, menu),
|
||||||
|
"onMouseenter": $event => mouseEnter($event, menu, index),
|
||||||
|
"onContextmenu": $event => mouseClick($event, menu)
|
||||||
|
}, [hasIcon.value ? createVNode("div", {
|
||||||
|
"class": 'v3-menus-icon '
|
||||||
|
}, [icon ? icon({
|
||||||
|
menu,
|
||||||
|
activeIndex: activeIndex.value,
|
||||||
|
index
|
||||||
|
}) : createVNode("span", {
|
||||||
|
"innerHTML": menu.icon
|
||||||
|
}, null)]) : null, label ? createVNode("span", {
|
||||||
|
"class": 'v3-menus-label'
|
||||||
|
}, [label({
|
||||||
|
menu,
|
||||||
|
activeIndex: activeIndex.value,
|
||||||
|
index
|
||||||
|
})]) : createVNode("span", {
|
||||||
|
"class": 'v3-menus-label'
|
||||||
|
}, [menu.label]), menu.children || menu.tip ? createVNode("div", {
|
||||||
|
"class": 'v3-menus-suffix'
|
||||||
|
}, [suffix ? suffix({
|
||||||
|
menu,
|
||||||
|
activeIndex: activeIndex.value,
|
||||||
|
index
|
||||||
|
}) : menu.children ? '▶' : menu.tip ? createVNode("span", {
|
||||||
|
"class": 'v3-menus-tip'
|
||||||
|
}, [menu.tip]) : null]) : null]);
|
||||||
|
}
|
||||||
|
})])])]
|
||||||
|
})]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
function mouseEvent(menus, args, event) {
|
||||||
|
let props = {};
|
||||||
|
if (Array.isArray(menus)) {
|
||||||
|
props = {
|
||||||
|
menus,
|
||||||
|
event,
|
||||||
|
args,
|
||||||
|
open: false,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
props = {
|
||||||
|
...menus,
|
||||||
|
args,
|
||||||
|
event,
|
||||||
|
open: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const vNode = createVNode(vue3MenusComponent, props);
|
||||||
|
const container = globalThis.document.createElement('div');
|
||||||
|
render(vNode, container);
|
||||||
|
vNode.component.props.open = true;
|
||||||
|
vNode.component.proxy.closeAll = () => {
|
||||||
|
nextTick(() => {
|
||||||
|
render(null, container);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
if (props.prevent == undefined || props.prevent) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const directive = {
|
||||||
|
mounted(el, { value, arg }) {
|
||||||
|
const vnode = el.__vnode || {};
|
||||||
|
if (arg === undefined || arg === 'right') {
|
||||||
|
el.addEventListener("contextmenu", mouseEvent.bind(el, value, vnode.props || {}));
|
||||||
|
} else if (arg === 'left') {
|
||||||
|
el.addEventListener("click", mouseEvent.bind(el, value, vnode.props || {}));
|
||||||
|
} else if (arg === 'all') {
|
||||||
|
el.addEventListener("contextmenu", mouseEvent.bind(el, value, vnode.props || {}));
|
||||||
|
el.addEventListener("click", mouseEvent.bind(el, value, vnode.props || {}));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
unmounted(el) {
|
||||||
|
el.removeEventListener("contextmenu", mouseEvent);
|
||||||
|
el.removeEventListener("click", mouseEvent);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const install = function (app, options = {}) {
|
||||||
|
app.component(options.name || vue3MenusComponent.name, vue3MenusComponent);
|
||||||
|
app.directive('menus', directive);
|
||||||
|
app.config.globalProperties.$menusEvent = (event, menus, args) => mouseEvent(menus, args || {}, event);
|
||||||
|
};
|
||||||
|
|
||||||
|
const menusEvent = (event, menus, args) => mouseEvent(menus, args || {}, event);
|
||||||
|
|
||||||
|
function index (app) {
|
||||||
|
app.use(install);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { vue3MenusComponent as Vue3Menus, index as default, directive, menusEvent };
|
|
@ -12,6 +12,7 @@
|
||||||
<el-button type="info" @click="onchange('/model/design')">教学框架设计</el-button>
|
<el-button type="info" @click="onchange('/model/design')">教学框架设计</el-button>
|
||||||
<el-button type="success" @click="openPPTist">打开PPTist</el-button>
|
<el-button type="success" @click="openPPTist">打开PPTist</el-button>
|
||||||
<el-button type="info" @click="onchange('/model/examination')">考试分析</el-button>
|
<el-button type="info" @click="onchange('/model/examination')">考试分析</el-button>
|
||||||
|
<el-button type="primary" v-menus="dt.menus">测试</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -55,6 +56,7 @@ import * as API_entpcourse from '@/api/education/entpcourse' // 相关api
|
||||||
import * as API_entpcoursefile from '@/api/education/entpcoursefile' // 相关api
|
import * as API_entpcoursefile from '@/api/education/entpcoursefile' // 相关api
|
||||||
// 组件引入
|
// 组件引入
|
||||||
import ChooseTextbook from '@/components/choose-textbook/index.vue'
|
import ChooseTextbook from '@/components/choose-textbook/index.vue'
|
||||||
|
import { menusEvent } from '@/plugins/vue3-menus' // 右键菜单
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const userStore = useUserStore() // 用户信息
|
const userStore = useUserStore() // 用户信息
|
||||||
|
@ -70,6 +72,10 @@ const courseObj = reactive({
|
||||||
})
|
})
|
||||||
const dt = reactive({
|
const dt = reactive({
|
||||||
curRow: null, // 当前行数据
|
curRow: null, // 当前行数据
|
||||||
|
menus: [ // 右键菜单
|
||||||
|
{ label: '打开', click: (_, args) => handleAll('open', args) },
|
||||||
|
{ label: '删除', click: (_, args) => handleAll('delete', args) },
|
||||||
|
],
|
||||||
})
|
})
|
||||||
// ref定义
|
// ref定义
|
||||||
const resourRef = ref() // 资源ref
|
const resourRef = ref() // 资源ref
|
||||||
|
@ -91,12 +97,19 @@ const sourceOpt = reactive({
|
||||||
noPage: true, // 不显示分页
|
noPage: true, // 不显示分页
|
||||||
isMain: false, // 主界面
|
isMain: false, // 主界面
|
||||||
highlightCurrentRow: true, // 高亮当前行
|
highlightCurrentRow: true, // 高亮当前行
|
||||||
rowClick: (r, c, e) => { // 行点击事件
|
rowClick: (r, c, e) => { // 行点击事件-处理高亮(再次点击取消选中)
|
||||||
if (dt.curRow == r) { // 重复点击-取消选中
|
if (dt.curRow == r) { // 重复点击-取消选中
|
||||||
resourRef.value.$refs.table.setCurrentRow()
|
resourRef.value.$refs.table.setCurrentRow()
|
||||||
dt.curRow = null
|
dt.curRow = null
|
||||||
} else dt.curRow = r
|
} else dt.curRow = r
|
||||||
}
|
},
|
||||||
|
rowContextmenu: (r, c, e) => { // 行—右键菜单事件
|
||||||
|
dt.menus.forEach(item => {
|
||||||
|
if(item.label == '打开') item.icon = getIcon(r, 'svg')
|
||||||
|
else if(item.label == '删除') item.icon = getIcon('icon-shanchu', 'class')
|
||||||
|
})
|
||||||
|
menusEvent(e, dt.menus, r)
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
// 页面加载
|
// 页面加载
|
||||||
|
@ -220,7 +233,7 @@ const HTTP_SERVER_API = (type, params = {}) => {
|
||||||
}
|
}
|
||||||
// 事件回调
|
// 事件回调
|
||||||
const handleAll = async(type, row) =>{
|
const handleAll = async(type, row) =>{
|
||||||
console.log(type)
|
// console.log(type)
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'refresh': // 刷新
|
case 'refresh': // 刷新
|
||||||
getResourceList()
|
getResourceList()
|
||||||
|
@ -255,21 +268,46 @@ const handleAll = async(type, row) =>{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'open': { // 打开资源-pptist
|
case 'open': { // 打开资源-pptist
|
||||||
if (row.filetype != 'aptist') return msgUtils.msgWarning('暂不支持该类型文件!')
|
if (row.filetype != 'aptist') return msgUtils.msgWarning('暂不支持该类型文件操作!')
|
||||||
// 缓存当前资源信息
|
sessionStore.set('curr.resource', row) // 缓存当前资源信息
|
||||||
sessionStore.set('curr.resource', row)
|
|
||||||
createWindow('open-win', {
|
createWindow('open-win', {
|
||||||
url: '/pptist',
|
url: '/pptist', // 窗口关闭时,清除缓存
|
||||||
// 窗口关闭时,清除缓存
|
close: () => {
|
||||||
close: () => {sessionStore.set('curr.resource', null)}
|
sessionStore.set('curr.resource', null) // 清除缓存
|
||||||
|
getResourceList() // 刷新资源列表
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'delete':{ // 删除资源
|
||||||
|
if (!(row && row.id)) return msgUtils.msgWarning('请选择要删除的资源!')
|
||||||
|
await msgUtils.confirm(`是否确认删除【${row.title}】课程课件?`)
|
||||||
|
await API_entpcoursefile.delEntpcoursefileNew(row.id)
|
||||||
|
msgUtils.msgSuccess('删除成功!')
|
||||||
|
// 刷新资源列表
|
||||||
|
await getResourceList()
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// icons 处理
|
// icons 处理 type 代表传递svg
|
||||||
const getIcon = o => {
|
const getIcon = (o, type) => {
|
||||||
let icon = o.filetype
|
let icon = typeof o == 'string' ? o : o?.filetype
|
||||||
if (['aptist','PPTX','pptList'].includes(o.filetype)) icon = 'pptx'
|
if (['aptist'].includes(o?.filetype)) icon = 'pptx'
|
||||||
|
if (!!type) { // 其他格式icon
|
||||||
|
switch(type) {
|
||||||
|
case 'svg': // 返回svg格式
|
||||||
|
return `<svg class="icon svg-icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#icon-${icon}"></use>
|
||||||
|
</svg>`
|
||||||
|
case 'class': // class
|
||||||
|
return `<span class="icon iconfont ${icon}"></span>`
|
||||||
|
case 'unicode': // unicode
|
||||||
|
return `<span class="icon iconfont">${icon}</span>`
|
||||||
|
default: // 返回icon-class
|
||||||
|
return `icon-${icon}`
|
||||||
|
}
|
||||||
|
}
|
||||||
return icon
|
return icon
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -68,6 +68,7 @@ import {PPTXFileToJson} from '@/AixPPTist/src/hooks/useImport' // ppt转json
|
||||||
import * as API_entpcourse from '@/api/education/entpcourse' // 相关api
|
import * as API_entpcourse from '@/api/education/entpcourse' // 相关api
|
||||||
import * as API_entpcoursefile from '@/api/education/entpcoursefile' // 相关api
|
import * as API_entpcoursefile from '@/api/education/entpcoursefile' // 相关api
|
||||||
import * as Api_server from '@/api/apiService' // 相关api
|
import * as Api_server from '@/api/apiService' // 相关api
|
||||||
|
import msgUtils from '@/plugins/modal' // 消息工具
|
||||||
|
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
const pptDialog = ref(false)
|
const pptDialog = ref(false)
|
||||||
|
@ -126,7 +127,7 @@ const addAiPPT = async(res) => {
|
||||||
.then(async buffer => {
|
.then(async buffer => {
|
||||||
const resPptJson = await PPTXFileToJson(buffer)
|
const resPptJson = await PPTXFileToJson(buffer)
|
||||||
const { def, slides, ...content } = resPptJson
|
const { def, slides, ...content } = resPptJson
|
||||||
console.log(slides)
|
// 转换图片|音频|视频 为线上地址
|
||||||
for( let o of slides ) {
|
for( let o of slides ) {
|
||||||
await toRousrceUrl(o)
|
await toRousrceUrl(o)
|
||||||
}
|
}
|
||||||
|
@ -136,10 +137,14 @@ const addAiPPT = async(res) => {
|
||||||
const parentid = await HTTP_SERVER_API('addEntpcoursefile', p_params)
|
const parentid = await HTTP_SERVER_API('addEntpcoursefile', p_params)
|
||||||
if (!!parentid??null) { // 生成内容幻灯片
|
if (!!parentid??null) { // 生成内容幻灯片
|
||||||
if (slides.length > 0) {
|
if (slides.length > 0) {
|
||||||
const resSlides = slides.map(({id, ...slide}) => slide)
|
const resSlides = slides.map(({id, ...slide}) => JSON.stringify(slide))
|
||||||
const params = {parentid, filetype: 'slide', title: '', slides: resSlides }
|
const params = {parentid, filetype: 'slide', title: '', slides: resSlides }
|
||||||
const res_3 = await HTTP_SERVER_API('batchAddNew', params)
|
const res_3 = await HTTP_SERVER_API('batchAddNew', params)
|
||||||
console.log('xxxx', res_3)
|
if (res_3 && res_3.code == 200) {
|
||||||
|
msgUtils.msgSuccess('生成PPT课件成功')
|
||||||
|
} else {
|
||||||
|
msgUtils.msgWarning('生成PPT课件失败')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue