index.tsx 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701
  1. import { Transition, defineComponent, onMounted, ref, reactive,onUnmounted } from 'vue';
  2. import LayoutSilder from './layoutSilder';
  3. import LayoutTop from './layoutTop';
  4. import styles from './index.module.less';
  5. import { NButton, NImage, NModal, NPopover, NSpace, useDialog } from 'naive-ui';
  6. import Moveable from 'moveable';
  7. import toolStartClass from './images/toolStartClass.png';
  8. import toolbox from './images/toolbox.png';
  9. import setTimeIcon from './images/setTimeIcon.png';
  10. import beatIcon from './images/beatIcon.png';
  11. import toneIcon from './images/toneIcon.png';
  12. import beatImage from './images/beatImage.png';
  13. import toneImage from './images/toneImage.png';
  14. import setTimeImage from './images/setTimeImage.png';
  15. import dragingBoxIcon from './images/dragingBoxIcon.png';
  16. import TimerMeter from '../timerMeter';
  17. import { useRoute, useRouter } from 'vue-router';
  18. import { vaildUrl } from '/src/utils/urlUtils';
  19. import ChioseModal from '/src/views/home/modals/chioseModal';
  20. import { px2vw, px2vwH } from '@/utils/index';
  21. import PlaceholderTone from './modals/placeholderTone';
  22. import { state } from '/src/state';
  23. import PreviewWindow from '/src/views/preview-window';
  24. import {fscreen} from '@/utils/index'
  25. export default defineComponent({
  26. name: 'layoutView',
  27. setup() {
  28. const router = useRouter();
  29. const previewModal = ref(false);
  30. const previewItem = ref({});
  31. const directionType = ref('left');
  32. const showClass = ref(false);
  33. const showModalBeat = ref(false);
  34. const showModalTone = ref(false);
  35. const showModalTime = ref(false);
  36. const showBoxConent = ref(false);
  37. const dialog = useDialog();
  38. const boxBoundaryInfo = reactive({
  39. isBoundary: true,
  40. isBoundaryType: 'right' as any,
  41. mainWidth: '' as any,
  42. mainHeight: '' as any,
  43. subWidth: '' as any,
  44. subHeight: '' as any
  45. });
  46. const classBoundaryInfo = reactive({
  47. isBoundary: true,
  48. isBoundaryType: 'right' as any,
  49. mainWidth: '' as any,
  50. mainHeight: '' as any,
  51. subWidth: '' as any,
  52. subHeight: '' as any
  53. });
  54. const route = useRoute();
  55. const isDragIng = ref(false);
  56. const NPopoverRef = ref()
  57. const initMoveable = async () => {
  58. if (document.querySelector('.wrap')) {
  59. const moveable = new Moveable(document.querySelector('.wrap') as any, {
  60. target: document.querySelector('#moveNPopover') as any,
  61. // If the container is null, the position is fixed. (default: parentElement(document.body))
  62. container: document.querySelector('.wrap') as any,
  63. // snappable: true,
  64. // bounds: {"left":100,"top":100,"right":100,"bottom":100},
  65. draggable: true,
  66. resizable: false,
  67. scalable: false,
  68. rotatable: false,
  69. warpable: false,
  70. pinchable: false, // ["resizable", "scalable", "rotatable"]
  71. origin: false,
  72. keepRatio: false,
  73. // Resize, Scale Events at edges.
  74. edge: false,
  75. throttleDrag: 0,
  76. throttleResize: 0,
  77. throttleScale: 0,
  78. throttleRotate: 0
  79. });
  80. moveable
  81. // .on('dragStart', ({ target, clientX, clientY }) => {
  82. // console.log('dragStart');
  83. // })
  84. .on(
  85. 'drag',
  86. ({
  87. target,
  88. // transform,
  89. left,
  90. top,
  91. right,
  92. bottom
  93. // beforeDelta,
  94. // beforeDist,
  95. // delta,
  96. // dist,
  97. // clientX,
  98. // clientY
  99. }) => {
  100. isDragIng.value = true;
  101. if(NPopoverRef.value){
  102. NPopoverRef.value.setShow(false)
  103. }
  104. const subdEl = document.getElementById(
  105. `moveNPopover`
  106. ) as HTMLDivElement;
  107. // console.log(subdEl, "subdEl", "drag");
  108. const subdElStyle = getComputedStyle(subdEl, null);
  109. const RectInfo = {
  110. left: Number(subdElStyle.left.replace('px', '')),
  111. top: Number(subdElStyle.top.replace('px', '')),
  112. width: Number(subdElStyle.width.replace('px', '')),
  113. height: Number(subdElStyle.height.replace('px', ''))
  114. };
  115. // target.style.transition = ''
  116. const mainWidth =
  117. parseInt(
  118. window.getComputedStyle(
  119. document.querySelector('.wrap') as Element
  120. ).width
  121. ) - RectInfo.width;
  122. const mainHeight =
  123. parseInt(
  124. window.getComputedStyle(
  125. document.querySelector('.wrap') as Element
  126. ).height
  127. ) - RectInfo.height;
  128. subdEl.style.transition = '';
  129. boxBoundaryInfo.isBoundary = false;
  130. boxBoundaryInfo.isBoundaryType = '';
  131. boxBoundaryInfo.mainHeight = mainHeight;
  132. boxBoundaryInfo.mainWidth = mainWidth;
  133. boxBoundaryInfo.subWidth = RectInfo.width;
  134. boxBoundaryInfo.subHeight = RectInfo.height;
  135. if (left < 0) {
  136. left = 2;
  137. boxBoundaryInfo.isBoundary = true;
  138. boxBoundaryInfo.isBoundaryType = 'left';
  139. }
  140. if (top < 0) {
  141. top = 2;
  142. boxBoundaryInfo.isBoundary = true;
  143. boxBoundaryInfo.isBoundaryType = 'top';
  144. }
  145. if (right < 0) {
  146. right = 2;
  147. }
  148. if (bottom < 0) {
  149. bottom = 2;
  150. }
  151. if (left > mainWidth - 2) {
  152. left = mainWidth - 2;
  153. // top = 2;
  154. boxBoundaryInfo.isBoundary = true;
  155. boxBoundaryInfo.isBoundaryType = 'right';
  156. }
  157. if (top > mainHeight - 2) {
  158. top = mainHeight - 2;
  159. boxBoundaryInfo.isBoundary = true;
  160. boxBoundaryInfo.isBoundaryType = 'bottom';
  161. }
  162. target!.style.left = `${left}px`;
  163. target!.style.top = `${top}px`;
  164. }
  165. )
  166. .on(
  167. 'dragEnd',
  168. async ({
  169. target,
  170. // isDrag,
  171. clientX
  172. // clientY
  173. }) => {
  174. if (document.body.clientWidth / 2 - clientX > 0) {
  175. // 往左出
  176. directionType.value = 'right';
  177. } else {
  178. // 往又出
  179. directionType.value = 'left';
  180. }
  181. isDragIng.value = false;
  182. // 在这里进行动画
  183. if (boxBoundaryInfo.isBoundary) {
  184. // 这里说明贴边了
  185. target.style.transition = '.3s';
  186. actionEnd(target, boxBoundaryInfo.isBoundaryType);
  187. }
  188. }
  189. );
  190. }
  191. };
  192. const initMoveableClass = async () => {
  193. if (document.querySelector('.wrap')) {
  194. const moveable = new Moveable(document.querySelector('.wrap') as any, {
  195. target: document.querySelector('#moveNPopover2') as any,
  196. // If the container is null, the position is fixed. (default: parentElement(document.body))
  197. container: document.querySelector('.wrap') as any,
  198. // snappable: true,
  199. // bounds: {"left":100,"top":100,"right":100,"bottom":100},
  200. draggable: true,
  201. resizable: false,
  202. scalable: false,
  203. rotatable: false,
  204. warpable: false,
  205. pinchable: false, // ["resizable", "scalable", "rotatable"]
  206. origin: false,
  207. keepRatio: false,
  208. // Resize, Scale Events at edges.
  209. edge: false,
  210. throttleDrag: 0,
  211. throttleResize: 0,
  212. throttleScale: 0,
  213. throttleRotate: 0
  214. });
  215. moveable
  216. .on(
  217. 'drag',
  218. ({
  219. target,
  220. // transform,
  221. left,
  222. top,
  223. right,
  224. bottom
  225. }) => {
  226. isDragIng.value = true;
  227. const subdEl = document.getElementById(
  228. `moveNPopover2`
  229. ) as HTMLDivElement;
  230. // console.log(subdEl, "subdEl", "drag");
  231. const subdElStyle = getComputedStyle(subdEl, null);
  232. const RectInfo = {
  233. left: Number(subdElStyle.left.replace('px', '')),
  234. top: Number(subdElStyle.top.replace('px', '')),
  235. width: Number(subdElStyle.width.replace('px', '')),
  236. height: Number(subdElStyle.height.replace('px', ''))
  237. };
  238. const mainWidth =
  239. parseInt(
  240. window.getComputedStyle(
  241. document.querySelector('.wrap') as Element
  242. ).width
  243. ) - RectInfo.width;
  244. const mainHeight =
  245. parseInt(
  246. window.getComputedStyle(
  247. document.querySelector('.wrap') as Element
  248. ).height
  249. ) - RectInfo.height;
  250. subdEl.style.transition = '';
  251. classBoundaryInfo.isBoundary = false;
  252. classBoundaryInfo.isBoundaryType = '';
  253. classBoundaryInfo.mainHeight = mainHeight;
  254. classBoundaryInfo.mainWidth = mainWidth;
  255. classBoundaryInfo.subWidth = RectInfo.width;
  256. classBoundaryInfo.subHeight = RectInfo.height;
  257. if (left < 0) {
  258. left = 2;
  259. classBoundaryInfo.isBoundary = true;
  260. classBoundaryInfo.isBoundaryType = 'left';
  261. }
  262. if (top < 0) {
  263. top = 2;
  264. classBoundaryInfo.isBoundary = true;
  265. classBoundaryInfo.isBoundaryType = 'top';
  266. }
  267. if (right < 0) {
  268. right = 2;
  269. }
  270. if (bottom < 0) {
  271. bottom = 2;
  272. }
  273. if (left > mainWidth - 2) {
  274. left = mainWidth - 2;
  275. // top = 2;
  276. classBoundaryInfo.isBoundary = true;
  277. classBoundaryInfo.isBoundaryType = 'right';
  278. }
  279. if (top > mainHeight - 2) {
  280. top = mainHeight - 2;
  281. classBoundaryInfo.isBoundary = true;
  282. classBoundaryInfo.isBoundaryType = 'bottom';
  283. }
  284. target!.style.left = `${left}px`;
  285. target!.style.top = `${top}px`;
  286. }
  287. )
  288. .on(
  289. 'dragEnd',
  290. async ({
  291. target,
  292. // isDrag,
  293. clientX
  294. // clientY
  295. }) => {
  296. if (document.body.clientWidth / 2 - clientX > 0) {
  297. // 往左出
  298. directionType.value = 'right';
  299. } else {
  300. // 往又出
  301. directionType.value = 'left';
  302. }
  303. if (classBoundaryInfo.isBoundary) {
  304. // 这里说明贴边了
  305. target.style.transition = '.3s';
  306. actionEnd(target, classBoundaryInfo.isBoundaryType);
  307. }
  308. isDragIng.value = false;
  309. }
  310. )
  311. .on('click', () => {
  312. showClass.value = true;
  313. });
  314. }
  315. };
  316. onMounted(() => {
  317. initMoveable();
  318. // // initMoveableClass();
  319. const subdEl = document.getElementById(`moveNPopover`) as HTMLDivElement;
  320. // // const classEl = document.getElementById(
  321. // // `moveNPopover2`
  322. // // ) as HTMLDivElement;
  323. initBoxRectInfo(subdEl, boxBoundaryInfo);
  324. // // initBoxRectInfo(classEl, classBoundaryInfo);
  325. initBoundaryWrap(subdEl, boxBoundaryInfo);
  326. // // initBoundaryWrap(classEl, classBoundaryInfo);
  327. window.addEventListener("resize", resetSize);
  328. });
  329. const resetSize = ()=>{
  330. const subdEl = document.getElementById(`moveNPopover`) as HTMLDivElement;
  331. subdEl.style.display = 'none'
  332. // const boxBoundaryInfo = reactive({
  333. // isBoundary: true,
  334. // isBoundaryType: 'right' as any,
  335. // mainWidth: '' as any,
  336. // mainHeight: '' as any,
  337. // subWidth: '' as any,
  338. // subHeight: '' as any
  339. // });
  340. // boxBoundaryInfo.isBoundary = true;
  341. // boxBoundaryInfo.isBoundaryType= 'right'
  342. if(NPopoverRef.value){
  343. NPopoverRef.value.setShow(false)
  344. }
  345. setTimeout(()=>{
  346. subdEl.style.transition = ''
  347. initBoxRectInfo(subdEl, boxBoundaryInfo);
  348. initBoundaryWrap(subdEl, boxBoundaryInfo);
  349. console.log('resize')
  350. subdEl.style.display = 'block'
  351. },100)
  352. }
  353. onUnmounted(()=>{
  354. window.removeEventListener("resize", resetSize);
  355. })
  356. const initBoundaryWrap = (target: any, wrapInfo: any) => {
  357. target.addEventListener('mouseenter', () => {
  358. if (wrapInfo.isBoundary) {
  359. // 如果在边框 就得还原 元素位置 还原完毕后 去除transition
  360. if (wrapInfo.isBoundaryType == 'left') {
  361. target.style.left = '2px';
  362. } else if (wrapInfo.isBoundaryType == 'right') {
  363. target.style.left = `${wrapInfo.mainWidth - 2}px`;
  364. } else if (wrapInfo.isBoundaryType == 'top') {
  365. target.style.top = `${2}px`;
  366. } else if (wrapInfo.isBoundaryType == 'bottom') {
  367. target.style.top = `${wrapInfo.mainHeight - 2}px`;
  368. }
  369. }
  370. rate(target, 0);
  371. });
  372. target.addEventListener('mouseleave', () => {
  373. if (wrapInfo.isBoundary) {
  374. // 如果在边框 就得还原 元素位置 还原完毕后 去除transition
  375. if (wrapInfo.isBoundaryType == 'left') {
  376. actionEnd(target, 'left');
  377. } else if (wrapInfo.isBoundaryType == 'right') {
  378. actionEnd(target, 'right');
  379. } else if (wrapInfo.isBoundaryType == 'top') {
  380. actionEnd(target, 'top');
  381. } else if (wrapInfo.isBoundaryType == 'bottom') {
  382. actionEnd(target, 'bottom');
  383. }
  384. }
  385. // rate(target, 0)
  386. });
  387. target.addEventListener('contextmenu', (event: any) => {
  388. event.preventDefault();
  389. dialog.warning({
  390. title: '提示',
  391. content: '是否收入托盘',
  392. positiveText: '确定',
  393. negativeText: '取消',
  394. onPositiveClick: () => {
  395. console.log('确定')
  396. },
  397. onNegativeClick: () => {
  398. console.log('取消')
  399. }
  400. })
  401. });
  402. actionEnd(target, 'right');
  403. };
  404. const startShowModal = (val: 'setTimeIcon' | 'beatIcon' | 'toneIcon') => {
  405. if (val == 'setTimeIcon') {
  406. showModalTime.value = true;
  407. }
  408. if (val == 'beatIcon') {
  409. showModalBeat.value = true;
  410. }
  411. if (val == 'toneIcon') {
  412. showModalTone.value = true;
  413. }
  414. };
  415. // const moveTargetBoundary = (target: any, type: any) => {
  416. // console.log('moveTargetBoundary', target, type);
  417. // };
  418. // 这里是旋转
  419. const rate = (target: any, rate: any) => {
  420. target.style.transform = ' rotate(' + rate + ')';
  421. };
  422. // 这里是选装的方式
  423. const actionEnd = (target: any, type: any) => {
  424. switch (type) {
  425. case 'left':
  426. rate(target, '90deg');
  427. target!.style.left = `${2 - boxBoundaryInfo.subWidth / 2}px`;
  428. target!.style.top = `${top}px`;
  429. break;
  430. case 'right':
  431. rate(target, '-90deg');
  432. target!.style.left = `${
  433. boxBoundaryInfo.mainWidth - 2 + boxBoundaryInfo.subWidth / 2
  434. }px`;
  435. target!.style.top = `${top}px`;
  436. break;
  437. case 'top':
  438. target!.style.top = `${2 - boxBoundaryInfo.subHeight / 2}px`;
  439. rate(target, '-180deg');
  440. break;
  441. case 'bottom':
  442. target!.style.top = `${
  443. boxBoundaryInfo.mainHeight - 2 + boxBoundaryInfo.subHeight / 2
  444. }px`;
  445. break;
  446. default:
  447. rate(target, '-0');
  448. break;
  449. }
  450. };
  451. const initBoxRectInfo = (target: any, wrapInfo: any) => {
  452. // const subdEl = document.getElementById(`moveNPopover`) as HTMLDivElement;
  453. // console.log(subdEl, "subdEl", "drag");
  454. const subdElStyle = getComputedStyle(target, null);
  455. const RectInfo = {
  456. left: Number(subdElStyle.left.replace('px', '')),
  457. top: Number(subdElStyle.top.replace('px', '')),
  458. width: Number(subdElStyle.width.replace('px', '')),
  459. height: Number(subdElStyle.height.replace('px', ''))
  460. };
  461. // target.style.transition = ''
  462. const mainWidth =
  463. parseInt(
  464. window.getComputedStyle(document.querySelector('.wrap') as Element)
  465. .width
  466. ) - RectInfo.width;
  467. const mainHeight =
  468. parseInt(
  469. window.getComputedStyle(document.querySelector('.wrap') as Element)
  470. .height
  471. ) - RectInfo.height;
  472. // boxBoundaryInfo.isBoundary = false;
  473. // boxBoundaryInfo.isBoundaryType = '';
  474. wrapInfo.mainHeight = mainHeight;
  475. wrapInfo.mainWidth = mainWidth;
  476. wrapInfo.subWidth = RectInfo.width;
  477. wrapInfo.subHeight = RectInfo.height;
  478. target.style.transition = '.3s';
  479. };
  480. return () => (
  481. <div class={[styles.wrap, 'wrap']}>
  482. <div>
  483. <LayoutSilder></LayoutSilder>
  484. </div>
  485. <div class={styles.Wrapcore}>
  486. <LayoutTop></LayoutTop>
  487. <div class={styles.WrapcoreView}>
  488. {/* <div class={styles.WrapcoreViewInfo}> */}
  489. <router-view>
  490. {(obj: any) => (
  491. <Transition name="fade-slide" mode="out-in">
  492. <obj.Component />
  493. </Transition>
  494. )}
  495. </router-view>
  496. {/* </div> */}
  497. </div>
  498. </div>
  499. {/* <img
  500. src={toolStartClass}
  501. id="moveNPopover2"
  502. style={{
  503. display: ['/', '/home', '/classList', '/prepare-lessons'].includes(
  504. route.path
  505. )
  506. ? 'none'
  507. : 'block'
  508. }}
  509. class={[
  510. styles.toolClassImg,
  511. 'moveNPopover2',
  512. isDragIng.value ? styles.isDragIng : ''
  513. ]}
  514. alt=""
  515. /> */}
  516. <NPopover
  517. raw
  518. trigger="click"
  519. ref={NPopoverRef}
  520. show-arrow={false}
  521. placement={directionType.value as 'left' | 'right'}
  522. v-slots={{
  523. trigger: () => (
  524. // 首页不显示工具箱
  525. <img
  526. // src={isDragIng.value ? dragingBoxIcon : toolbox}
  527. src={toolbox}
  528. id="moveNPopover"
  529. style={{
  530. display: ['/', '/home'].includes(route.path)
  531. ? 'none'
  532. : 'block'
  533. }}
  534. class={[
  535. styles.toolboxImg,
  536. 'moveNPopover',
  537. isDragIng.value ? styles.isDragIng : ''
  538. ]}
  539. alt=""
  540. />
  541. )
  542. }}>
  543. <div class={styles.booxToolWrap}>
  544. <div
  545. class={styles.booxToolItem}
  546. onClick={() => startShowModal('beatIcon')}>
  547. <img src={beatIcon} alt="" />
  548. 节拍器
  549. </div>
  550. <div
  551. class={styles.booxToolItem}
  552. onClick={() => startShowModal('toneIcon')}>
  553. <img src={toneIcon} alt="" />
  554. 调音器
  555. </div>
  556. <div
  557. class={styles.booxToolItem}
  558. onClick={() => startShowModal('setTimeIcon')}>
  559. <img src={setTimeIcon} alt="" />
  560. 计时器
  561. </div>
  562. <div class={styles.booxToolItem} onClick={()=>{
  563. showClass.value = true
  564. }} style={{
  565. display: [
  566. '/',
  567. '/home',
  568. '/classList',
  569. '/prepare-lessons'
  570. ].includes(route.path)
  571. ? 'none'
  572. : 'block'
  573. }}>
  574. <img
  575. src={toolStartClass}
  576. style={{
  577. display: [
  578. '/',
  579. '/home',
  580. '/classList',
  581. '/prepare-lessons'
  582. ].includes(route.path)
  583. ? 'none'
  584. : 'block'
  585. }}
  586. class={[
  587. styles.toolClassImg,
  588. ]}
  589. alt=""
  590. />
  591. 开始上课
  592. </div>
  593. </div>
  594. </NPopover>
  595. <NModal
  596. class={['modalTitle background']}
  597. title={'节拍器'}
  598. preset="card"
  599. v-model:show={showModalBeat.value}
  600. style={{ width: '687px' }}>
  601. <div class={styles.modeWrap}>
  602. <iframe
  603. src={`${vaildUrl()}/metronome/?id=${new Date().getTime()}`}
  604. scrolling="no"
  605. frameborder="0"
  606. width="100%"
  607. height={'650px'}></iframe>
  608. </div>
  609. </NModal>
  610. <NModal v-model:show={showModalTone.value} class={['background']}>
  611. {/* <div
  612. onClick={() => {
  613. showModalTone.value = false;
  614. }}>
  615. <NImage
  616. src={toneImage}
  617. previewDisabled
  618. class={styles.beatImage}></NImage>
  619. </div> */}
  620. <div>
  621. <PlaceholderTone
  622. onClose={() => {
  623. showModalTone.value = false;
  624. }}></PlaceholderTone>
  625. </div>
  626. </NModal>
  627. <NModal
  628. v-model:show={showModalTime.value}
  629. class={['modalTitle background']}
  630. title={'计时器'}
  631. preset="card"
  632. style={{ width: px2vw(772) }}>
  633. <div>
  634. <TimerMeter></TimerMeter>
  635. </div>
  636. </NModal>
  637. <NModal
  638. v-model:show={showClass.value}
  639. class={['modalTitle background', styles.showClass]}
  640. preset="card"
  641. title={'开始上课'}>
  642. <ChioseModal
  643. onClose={() => (showClass.value = false)}
  644. onPreview={(item: any) => {
  645. if (window.matchMedia('(display-mode: standalone)').matches) {
  646. previewModal.value = true;
  647. previewItem.value = {
  648. ...item
  649. };
  650. state.application = window.matchMedia('(display-mode: standalone)').matches
  651. fscreen()
  652. } else {
  653. const { href } = router.resolve({
  654. path: '/attend-class',
  655. query: {
  656. ...item
  657. }
  658. });
  659. window.open(href, +new Date() + '');
  660. }
  661. }}
  662. />
  663. </NModal>
  664. {/* 弹窗查看 */}
  665. <PreviewWindow
  666. v-model:show={previewModal.value}
  667. type="attend"
  668. params={previewItem.value}
  669. />
  670. </div>
  671. );
  672. }
  673. });