123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398 |
- import {
- computed,
- defineComponent,
- nextTick,
- reactive,
- ref,
- Teleport,
- toRef
- } from 'vue';
- import styles from './index.module.less';
- import {
- ImageRenderToolbarProps,
- NCollapse,
- NCollapseItem,
- NImage,
- NImageGroup,
- NSpace,
- NSpin
- } from 'naive-ui';
- import arrow from './images/arrow.png';
- import TheSearch from '../../TheSearch';
- import {
- api_sysTeacherManual_detail,
- api_sysTeacherManualPage
- } from '/src/api/user';
- import TheEmpty from '../../TheEmpty';
- import VideoModal from '../../card-preview/video-modal';
- import useDrag from './guide-drag';
- import { onBeforeRouteUpdate, useRoute } from 'vue-router';
- /** 功能引导 */
- export default defineComponent({
- name: 'guide-section',
- setup(props, { expose }) {
- const route = useRoute();
- // 操作记录
- const opInfo = reactive({
- showGuide: false, // 是否显示
- routePath: route.path === '/' ? '/Home' : route.path,
- searchValue: '',
- collapseId: [], // 默认展开哪个
- dataList: [] as any[], // 手册列表
- manualDetail: {} as any, // 详情
- detailLoading: false // 加载详情
- });
- const previewShow = ref(false);
- const previewUrl = ref('');
- const previewImgList = ref<string[]>([]);
- const guideBoxClass = 'guideBoxClass_drag';
- const { styleDrag, windowInfo, onScreen, onResize, onReset } = useDrag(
- [`${guideBoxClass} .guideTitle`],
- guideBoxClass,
- toRef(opInfo, 'showGuide')
- );
- const videoBoxClass = 'videoBoxClass_drag';
- const videoBoxData = useDrag(
- [`${videoBoxClass} .guideTitleVideo`],
- videoBoxClass,
- previewShow,
- {
- resizeDirection: [true, true, true, true, true, true, true, true],
- minHeight: 391.5,
- minWidth: 600,
- defaultPosition: 'center',
- width: 864,
- height: 540
- }
- );
- const titleName = computed(() => {
- let name = '操作手册';
- if (
- windowInfo.showType === 'CONTENT' &&
- windowInfo.windowType === 'SMALL'
- ) {
- name = opInfo.manualDetail?.name || '返回';
- }
- return name;
- });
- /** 操作窗口状态 */
- const onToggle = () => {
- opInfo.showGuide = !opInfo.showGuide;
- if (!opInfo.showGuide) {
- previewShow.value = false;
- videoBoxData.onReset();
- }
- };
- /** 操作视频窗口状态 */
- const onToggleVideo = () => {
- previewShow.value = !previewShow.value;
- if (!previewShow.value) videoBoxData.onReset();
- };
- const onClickItem = async (item: any) => {
- opInfo.detailLoading = true;
- try {
- windowInfo.showType = 'CONTENT';
- await getTeacherManualDetail(item.id);
- } catch {
- //
- }
- opInfo.detailLoading = false;
- };
- // 点击菜单
- const onClickMenu = () => {
- if (windowInfo.windowType === 'LARGE') return;
- windowInfo.showType = 'MENU';
- };
- /** 获取操作手册 */
- const getTeacherManual = async () => {
- try {
- const { data } = await api_sysTeacherManualPage({
- keyword: opInfo.searchValue,
- permission: opInfo.routePath
- });
- opInfo.dataList = data || [];
- opInfo.manualDetail = {};
- const id: any = opInfo.dataList[0]?.id;
- if (id) {
- opInfo.collapseId = id;
- const firstChildId = opInfo.dataList[0]?.children[0]?.id;
- getTeacherManualDetail(firstChildId);
- }
- } catch {
- //
- }
- };
- /** 获取详情 */
- const getTeacherManualDetail = async (id: string) => {
- try {
- if (id === opInfo.manualDetail?.id) return;
- const { data } = await api_sysTeacherManual_detail(id);
- let opFlow = data.opFlow || '';
- opFlow = opFlow.replace(
- /<img/gi,
- '<img class="manualImg" onClick="onLookImg(this)"'
- );
- opFlow = opFlow.replace(
- /<video/gi,
- '<div class="videoSection"><video class="manualVideo" onClick="onLookVideo(this)" '
- );
- opFlow = opFlow.replace(/<\/video>/gi, '</video></div>');
- opFlow = opFlow.replace(/controls/gi, 'c');
- data.opFlow = opFlow;
- opInfo.manualDetail = data || {};
- nextTick(() => {
- // const manualImg = document.querySelector('.manualImg')
- const domList = document.querySelectorAll('.manualImg');
- const imgList: any[] = [];
- domList.forEach((item: any) => {
- imgList.push(item.src);
- });
- previewImgList.value = imgList;
- });
- } catch {
- //
- }
- };
- getTeacherManual();
- onBeforeRouteUpdate((route: any) => {
- if (route.path !== opInfo.routePath) {
- opInfo.showGuide = false;
- previewShow.value = false;
- onReset();
- opInfo.routePath = route.path === '/' ? '/Home' : route.path;
- windowInfo.showType = 'MENU';
- opInfo.searchValue = '';
- getTeacherManual();
- }
- });
- expose({
- onToggle
- });
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- window.onLookImg = (target: any) => {
- const index = previewImgList.value.findIndex(
- (src: string) => target.src === src
- );
- const nImage = document.querySelectorAll('.rightGuide .n-image')[index];
- const img: any = nImage.querySelector('img');
- img.click();
- // 默认关闭视频
- previewShow.value = false;
- videoBoxData.onReset();
- };
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- window.onLookVideo = (target: any) => {
- const sourceElement = target.querySelector('source');
- const videoSrc = sourceElement ? sourceElement.src : null;
- previewUrl.value = videoSrc;
- previewShow.value = true;
- };
- return () => (
- <Teleport to={'body'}>
- <div
- class={[
- styles['drag-wrapper-draggable'],
- !opInfo.showGuide ? styles.draggleClose : ''
- ]}>
- <div
- class={[styles.guideSection, guideBoxClass]}
- style={{
- ...styleDrag.value,
- borderRadius: windowInfo.windowType === 'LARGE' ? '16px' : ''
- }}>
- <div class={styles.guideCenter}>
- <div class={[styles.guideTitle, 'guideTitle']}>
- <div class={styles.name} onClick={onClickMenu}>
- {windowInfo.showType === 'CONTENT' &&
- windowInfo.windowType === 'SMALL' && (
- <i class={styles.back}></i>
- )}
- {titleName.value}
- </div>
- <div class={styles.operation}>
- <i
- class={[
- styles.screen,
- windowInfo.windowType === 'LARGE'
- ? styles.screenSmall
- : ''
- ]}
- onClick={onScreen}></i>
- <i
- class={[
- styles.resize,
- windowInfo.currentType === 'SMALL'
- ? styles.resizeLarge
- : ''
- ]}
- onClick={onResize}></i>
- <i class={styles.close} onClick={onToggle}></i>
- </div>
- </div>
- <div
- class={[
- styles.container,
- windowInfo.windowType === 'LARGE'
- ? styles.windowContainer
- : ''
- ]}>
- <div
- class={styles.leftGuide}
- style={{
- display:
- windowInfo.showType === 'MENU' ||
- windowInfo.windowType === 'LARGE'
- ? 'block'
- : 'none'
- }}>
- <div
- style={{
- height: opInfo.dataList.length <= 0 ? '100%' : 'auto'
- }}>
- <div class={styles.searchContainer}>
- <TheSearch
- round={false}
- v-model:value={opInfo.searchValue}
- onSearch={async (val: string) => {
- opInfo.searchValue = val;
- await getTeacherManual();
- if (windowInfo.windowType === 'LARGE') {
- const id = opInfo.dataList[0]?.children[0]?.id;
- id && getTeacherManualDetail(id);
- }
- }}
- />
- </div>
- <NCollapse
- v-model:expandedNames={opInfo.collapseId}
- accordion>
- {{
- arrow: () => <img class={styles.arrow} src={arrow} />,
- default: () =>
- opInfo.dataList.map((item: any) => (
- <NCollapseItem title={item.name} name={item.id}>
- <div class={styles.childList}>
- {item.children &&
- item.children.map((child: any) => (
- <div
- class={[
- styles.childItem,
- windowInfo.windowType === 'LARGE' &&
- child.id === opInfo.manualDetail?.id
- ? styles.active
- : ''
- ]}
- onClick={() => onClickItem(child)}>
- {child.name}
- </div>
- ))}
- </div>
- </NCollapseItem>
- ))
- }}
- </NCollapse>
- {opInfo.dataList.length <= 0 && (
- <div class={styles.emptyDiv}>
- <TheEmpty />
- </div>
- )}
- </div>
- </div>
- <div
- class={[styles.rightGuide, 'rightGuide']}
- style={{
- display:
- windowInfo.showType === 'CONTENT' ||
- windowInfo.windowType === 'LARGE'
- ? 'block'
- : 'none'
- }}>
- {previewImgList.value.length > 0 && (
- <NImageGroup
- renderToolbar={({ nodes }: ImageRenderToolbarProps) => {
- return [
- nodes.prev,
- nodes.next,
- nodes.rotateCounterclockwise,
- nodes.rotateClockwise,
- nodes.resizeToOriginalSize,
- nodes.zoomOut,
- nodes.zoomIn,
- nodes.close
- ];
- }}>
- <NSpace>
- {previewImgList.value.map((src: string) => (
- <NImage width="0" src={src}></NImage>
- ))}
- </NSpace>
- </NImageGroup>
- )}
- <NSpin show={opInfo.detailLoading}>
- <div
- v-html={opInfo.manualDetail.opFlow}
- class-="html-to-dom"></div>
- </NSpin>
- </div>
- </div>
- </div>
- </div>
- <div
- class={[styles.videoModal, videoBoxClass]}
- style={{
- ...videoBoxData.styleDrag.value,
- display: previewShow.value ? 'flex' : 'none'
- }}>
- <div class={[styles.guideTitle, 'guideTitleVideo']}>
- <div class={styles.name}>预览</div>
- <div class={styles.operation}>
- <i class={styles.close} onClick={onToggleVideo}></i>
- </div>
- </div>
- {previewShow.value ? (
- <VideoModal
- key={previewUrl.value}
- title="预览"
- src={previewUrl.value}
- isDownload={false}
- fullscreen
- />
- ) : (
- ''
- )}
- </div>
- </div>
- </Teleport>
- );
- }
- });
|