feat: fix Slider component display bug and add IE browser detection prompt
add remove infrequently used js libraries
This commit is contained in:
41
index.html
41
index.html
@@ -78,6 +78,47 @@
|
||||
<div>你需要启用 JavaScript 来运行 Type Words.</div>
|
||||
</noscript>
|
||||
<div id="app"></div>
|
||||
<script>
|
||||
(function(){
|
||||
var ua = navigator.userAgent || ''
|
||||
var isIE = !!document.documentMode || /MSIE|Trident/i.test(ua)
|
||||
if (!isIE) return
|
||||
var style = document.createElement('style')
|
||||
style.type = 'text/css'
|
||||
style.appendChild(document.createTextNode(
|
||||
'.ie-mask{position:fixed;left:0;top:0;right:0;bottom:0;background:rgba(0,0,0,.35);z-index:9998}'+
|
||||
'.ie-dialog{position:fixed;left:50%;top:50%;transform:translate(-50%,-50%);width:28rem;max-width:90vw;background:#fff;color:#111;border-radius:.6rem;box-shadow:0 10px 30px rgba(0,0,0,.15);z-index:9999;padding:1.2rem}'+
|
||||
'.ie-dialog .title{font-size:1.2rem;font-weight:700;margin-bottom:.6rem}'+
|
||||
'.ie-dialog .desc{font-size:.95rem;line-height:1.6;color:#555}'+
|
||||
'.ie-dialog .actions{display:flex;justify-content:flex-end;margin-top:1rem}'+
|
||||
'.ie-dialog .actions > * + *{margin-left:.6rem}'+
|
||||
'.ie-dialog .btn{display:inline-flex;align-items:center;justify-content:center;height:2.2rem;padding:0 1rem;border-radius:.4rem;background:#0C8CE9;color:#fff;text-decoration:none}'+
|
||||
'.ie-dialog .btn-secondary{display:inline-flex;align-items:center;justify-content:center;height:2.2rem;padding:0 .9rem;border-radius:.4rem;background:#eee;color:#333;border:1px solid #ddd}'+
|
||||
'@media (prefers-color-scheme: dark){.ie-dialog{background:#1e1f22;color:#e6e6e6}.ie-dialog .desc{color:#c6c6c6}.ie-dialog .btn-secondary{background:#2a2b2f;color:#e6e6e6;border-color:#3a3b3f}}'
|
||||
))
|
||||
document.head.appendChild(style)
|
||||
var mask = document.createElement('div')
|
||||
mask.className = 'ie-mask'
|
||||
var dialog = document.createElement('div')
|
||||
dialog.className = 'ie-dialog'
|
||||
dialog.innerHTML = '<div class="title">不支持 IE 浏览器</div>'+
|
||||
'<div class="desc">Type Words 使用现代技术构建,请使用 Chrome、Edge、Firefox 或 Safari 等现代浏览器访问。</div>'+
|
||||
'<div class="actions">'+
|
||||
'<a class="btn" href="https://www.google.cn/chrome/" target="_blank" rel="noreferrer">下载 Chrome</a>'+
|
||||
'<button class="btn-secondary" type="button">我知道了</button>'+
|
||||
'</div>'
|
||||
function close(){
|
||||
try{document.body.removeChild(mask)}catch(e){}
|
||||
try{document.body.removeChild(dialog)}catch(e){}
|
||||
}
|
||||
mask.addEventListener('click', close)
|
||||
var btn = null
|
||||
try{btn = dialog.querySelector('.btn-secondary')}catch(e){}
|
||||
if (btn) btn.addEventListener('click', close)
|
||||
document.body.appendChild(mask)
|
||||
document.body.appendChild(dialog)
|
||||
})()
|
||||
</script>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
"@floating-ui/dom": "^1.7.4",
|
||||
"@imengyu/vue3-context-menu": "^1.5.1",
|
||||
"@vueuse/core": "14.0.0-alpha.0",
|
||||
"@zumer/snapdom": "^2.0.0",
|
||||
"axios": "^1.12.0",
|
||||
"compromise": "^14.14.4",
|
||||
"copy-to-clipboard": "^3.3.3",
|
||||
@@ -32,7 +31,6 @@
|
||||
"mitt": "^3.0.1",
|
||||
"nanoid": "^5.1.5",
|
||||
"pinia": "^3.0.3",
|
||||
"shepherd.js": "^14.5.1",
|
||||
"string-comparison": "^1.3.0",
|
||||
"vue": "^3.5.17",
|
||||
"vue-router": "^4.5.1",
|
||||
|
||||
32
pnpm-lock.yaml
generated
32
pnpm-lock.yaml
generated
@@ -17,9 +17,6 @@ importers:
|
||||
'@vueuse/core':
|
||||
specifier: 14.0.0-alpha.0
|
||||
version: 14.0.0-alpha.0(vue@3.5.18(typescript@5.9.2))
|
||||
'@zumer/snapdom':
|
||||
specifier: ^2.0.0
|
||||
version: 2.0.0
|
||||
axios:
|
||||
specifier: ^1.12.0
|
||||
version: 1.13.2
|
||||
@@ -50,9 +47,6 @@ importers:
|
||||
pinia:
|
||||
specifier: ^3.0.3
|
||||
version: 3.0.3(typescript@5.9.2)(vue@3.5.18(typescript@5.9.2))
|
||||
shepherd.js:
|
||||
specifier: ^14.5.1
|
||||
version: 14.5.1
|
||||
string-comparison:
|
||||
specifier: ^1.3.0
|
||||
version: 1.3.0
|
||||
@@ -866,9 +860,6 @@ packages:
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@scarf/scarf@1.4.0':
|
||||
resolution: {integrity: sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==}
|
||||
|
||||
'@tybys/wasm-util@0.10.0':
|
||||
resolution: {integrity: sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ==}
|
||||
|
||||
@@ -1272,9 +1263,6 @@ packages:
|
||||
peerDependencies:
|
||||
vue: ^3.5.0
|
||||
|
||||
'@zumer/snapdom@2.0.0':
|
||||
resolution: {integrity: sha512-e/fkm5wCUd+9CssUIyH09xTeR4DvRTmZLGVOlnXLhr4HeI7sdc6ed8cLPiZKFtiQDRiwD3EKx4RIUrpQOJQY7A==}
|
||||
|
||||
acorn@8.15.0:
|
||||
resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==}
|
||||
engines: {node: '>=0.4.0'}
|
||||
@@ -1807,10 +1795,6 @@ packages:
|
||||
dedent@0.7.0:
|
||||
resolution: {integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==}
|
||||
|
||||
deepmerge-ts@7.1.5:
|
||||
resolution: {integrity: sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==}
|
||||
engines: {node: '>=16.0.0'}
|
||||
|
||||
default-compare@1.0.0:
|
||||
resolution: {integrity: sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -3224,10 +3208,6 @@ packages:
|
||||
resolution: {integrity: sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
shepherd.js@14.5.1:
|
||||
resolution: {integrity: sha512-VuvPvLG1QjNOLP7AIm2HGyfmxEIz8QdskvWOHwUcxLDibYWjLRBmCWd8LSL5FlwhBW7D/GU+3gNVC/ASxAWdxg==}
|
||||
engines: {node: 18.* || >= 20}
|
||||
|
||||
side-channel-list@1.0.0:
|
||||
resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -4492,8 +4472,6 @@ snapshots:
|
||||
'@rollup/rollup-win32-x64-msvc@4.46.2':
|
||||
optional: true
|
||||
|
||||
'@scarf/scarf@1.4.0': {}
|
||||
|
||||
'@tybys/wasm-util@0.10.0':
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
@@ -5095,8 +5073,6 @@ snapshots:
|
||||
dependencies:
|
||||
vue: 3.5.18(typescript@5.9.2)
|
||||
|
||||
'@zumer/snapdom@2.0.0': {}
|
||||
|
||||
acorn@8.15.0: {}
|
||||
|
||||
address@1.2.2: {}
|
||||
@@ -5683,8 +5659,6 @@ snapshots:
|
||||
|
||||
dedent@0.7.0: {}
|
||||
|
||||
deepmerge-ts@7.1.5: {}
|
||||
|
||||
default-compare@1.0.0:
|
||||
dependencies:
|
||||
kind-of: 5.1.0
|
||||
@@ -7224,12 +7198,6 @@ snapshots:
|
||||
is-plain-object: 2.0.4
|
||||
split-string: 3.1.0
|
||||
|
||||
shepherd.js@14.5.1:
|
||||
dependencies:
|
||||
'@floating-ui/dom': 1.7.4
|
||||
'@scarf/scarf': 1.4.0
|
||||
deepmerge-ts: 7.1.5
|
||||
|
||||
side-channel-list@1.0.0:
|
||||
dependencies:
|
||||
es-errors: 1.3.0
|
||||
|
||||
10
src/assets/css/shepherd.css
Normal file
10
src/assets/css/shepherd.css
Normal file
@@ -0,0 +1,10 @@
|
||||
.shepherd-button{background:#3288e6;border:0;border-radius:3px;color:hsla(0,0%,100%,.75);cursor:pointer;margin-right:.5rem;padding:.5rem 1.5rem;transition:all .5s ease}.shepherd-button:not(:disabled):hover{background:#196fcc;color:hsla(0,0%,100%,.75)}.shepherd-button.shepherd-button-secondary{background:#f1f2f3;color:rgba(0,0,0,.75)}.shepherd-button.shepherd-button-secondary:not(:disabled):hover{background:#d6d9db;color:rgba(0,0,0,.75)}.shepherd-button:disabled{cursor:not-allowed}
|
||||
.shepherd-footer{border-bottom-left-radius:5px;border-bottom-right-radius:5px;display:flex;justify-content:flex-end;padding:0 .75rem .75rem}.shepherd-footer .shepherd-button:last-child{margin-right:0}
|
||||
.shepherd-cancel-icon{background:transparent;border:none;color:hsla(0,0%,50%,.75);cursor:pointer;font-size:2em;font-weight:400;margin:0;padding:0;transition:color .5s ease}.shepherd-cancel-icon:hover{color:rgba(0,0,0,.75)}.shepherd-has-title .shepherd-content .shepherd-cancel-icon{color:hsla(0,0%,50%,.75)}.shepherd-has-title .shepherd-content .shepherd-cancel-icon:hover{color:rgba(0,0,0,.75)}
|
||||
.shepherd-title{color:rgba(0,0,0,.75);display:flex;flex:1 0 auto;font-size:1rem;font-weight:400;margin:0;padding:0}
|
||||
.shepherd-header{align-items:center;border-top-left-radius:5px;border-top-right-radius:5px;display:flex;justify-content:flex-end;line-height:2em;padding:.75rem .75rem 0}.shepherd-has-title .shepherd-content .shepherd-header{background:#e6e6e6;padding:1em}
|
||||
.shepherd-text{color:rgba(0,0,0,.75);font-size:1rem;line-height:1.3em;padding:.75em}.shepherd-text p{margin-top:0}.shepherd-text p:last-child{margin-bottom:0}
|
||||
.shepherd-content{border-radius:5px;outline:none;padding:0}
|
||||
.shepherd-element{background:#fff;border:none;border-radius:5px;box-shadow:0 1px 4px rgba(0,0,0,.2);margin:0;max-width:400px;opacity:0;outline:none;padding:0;transition:opacity .3s,visibility .3s;visibility:hidden;width:100%;z-index:9999}.shepherd-enabled.shepherd-element{opacity:1;visibility:visible}.shepherd-element[data-popper-reference-hidden]:not(.shepherd-centered){opacity:0;pointer-events:none;visibility:hidden}.shepherd-element,.shepherd-element *,.shepherd-element :after,.shepherd-element :before{box-sizing:border-box}.shepherd-arrow,.shepherd-arrow:before{height:16px;position:absolute;width:16px;z-index:-1}.shepherd-arrow:before{background:#fff;content:"";transform:rotate(45deg)}.shepherd-element[data-popper-placement^=top]>.shepherd-arrow{bottom:-8px}.shepherd-element[data-popper-placement^=bottom]>.shepherd-arrow{top:-8px}.shepherd-element[data-popper-placement^=left]>.shepherd-arrow{right:-8px}.shepherd-element[data-popper-placement^=right]>.shepherd-arrow{left:-8px}.shepherd-element.shepherd-centered>.shepherd-arrow{opacity:0}.shepherd-element.shepherd-has-title[data-popper-placement^=bottom]>.shepherd-arrow:before{background-color:#e6e6e6}.shepherd-target-click-disabled.shepherd-enabled.shepherd-target,.shepherd-target-click-disabled.shepherd-enabled.shepherd-target *{pointer-events:none}
|
||||
.shepherd-modal-overlay-container{height:0;left:0;opacity:0;overflow:hidden;pointer-events:none;position:fixed;top:0;transition:all .3s ease-out,height 0s .3s,opacity .3s 0s;width:100vw;z-index:9997}.shepherd-modal-overlay-container.shepherd-modal-is-visible{height:100vh;opacity:.5;transform:translateZ(0);transition:all .3s ease-out,height 0s 0s,opacity .3s 0s}.shepherd-modal-overlay-container.shepherd-modal-is-visible path{pointer-events:all}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@use "anim" as *;
|
||||
@use 'shepherd.js/dist/css/shepherd.css';
|
||||
@use 'shepherd.css';
|
||||
|
||||
:root {
|
||||
--color-reverse-white: white;
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import { APP_NAME, GITHUB, Origin } from "@/config/env.ts";
|
||||
import { APP_NAME, GITHUB, LIB_JS_URL, Origin } from "@/config/env.ts";
|
||||
import BaseIcon from "@/components/BaseIcon.vue";
|
||||
import { defineAsyncComponent, watch } from "vue";
|
||||
import { usePracticeStore } from "@/stores/practice.ts";
|
||||
import { useBaseStore } from "@/stores/base.ts";
|
||||
import { msToHourMinute } from "@/utils";
|
||||
import { loadJsLib, msToHourMinute } from "@/utils";
|
||||
import dayjs from "dayjs";
|
||||
import Toast from "@/components/base/toast/Toast.ts";
|
||||
import { useUserStore } from "@/stores/user.ts";
|
||||
import Progress from "@/components/base/Progress.vue";
|
||||
import { snapdom } from "@zumer/snapdom";
|
||||
|
||||
const Dialog = defineAsyncComponent(() => import('@/components/dialog/Dialog.vue'))
|
||||
|
||||
@@ -21,6 +20,8 @@ let showWechatDialog = $ref(false)
|
||||
let showXhsDialog = $ref(false)
|
||||
let showQQDialog = $ref(false)
|
||||
let showShareDialog = $ref(false)
|
||||
let loading1 = $ref(false)
|
||||
let loading2 = $ref(false)
|
||||
let posterEl = $ref<HTMLDivElement | null>(null)
|
||||
|
||||
// 计算学习统计数据
|
||||
@@ -47,6 +48,8 @@ watch(() => showShareDialog, (newVal) => {
|
||||
// 复制图片到剪贴板
|
||||
async function copyImageToClipboard() {
|
||||
try {
|
||||
loading1 = true
|
||||
const snapdom = await loadJsLib('snapdom',LIB_JS_URL.SNAPDOM);
|
||||
const blob = await snapdom.toBlob(posterEl, {scale: 2, type: 'png'})
|
||||
if (!blob) throw new Error('capture failed')
|
||||
|
||||
@@ -59,12 +62,17 @@ async function copyImageToClipboard() {
|
||||
} catch (error) {
|
||||
Toast.error('复制失败!')
|
||||
await downloadImage()
|
||||
} finally {
|
||||
loading1 = false
|
||||
}
|
||||
}
|
||||
|
||||
// 下载图片
|
||||
async function downloadImage() {
|
||||
loading2 = true
|
||||
const snapdom = await loadJsLib('snapdom',LIB_JS_URL.SNAPDOM);
|
||||
snapdom.download(posterEl, {scale: 2})
|
||||
loading2 = false
|
||||
}
|
||||
|
||||
let imgIndex = $ref(Math.floor(Math.random() * 10))
|
||||
@@ -127,7 +135,8 @@ const sentence = $computed(() => {
|
||||
<IconFluentShare20Regular class="text-blue-500 hover:text-blue-600"/>
|
||||
</BaseIcon>
|
||||
|
||||
<a :href="GITHUB" target="_blank" rel="noreferrer" aria-label="GITHUB 项目地址" class="color-[--color-reverse-black]">
|
||||
<a :href="GITHUB" target="_blank" rel="noreferrer" aria-label="GITHUB 项目地址"
|
||||
class="color-[--color-reverse-black]">
|
||||
<BaseIcon>
|
||||
<IconSimpleIconsGithub/>
|
||||
</BaseIcon>
|
||||
@@ -264,13 +273,15 @@ const sentence = $computed(() => {
|
||||
<!-- 分享战绩 -->
|
||||
<div @click="copyImageToClipboard"
|
||||
class="flex items-center justify-start gap-space px-6 py-3 bg-gradient-to-r from-green-500 to-green-600 text-white cp rounded-lg hover:from-green-600 hover:to-green-700 transition-all duration-200">
|
||||
<IconFluentCopy20Regular class="w-5 h-5"/>
|
||||
<IconEosIconsLoading class="text-xl" v-if="loading1"/>
|
||||
<IconFluentCopy20Regular class="w-5 h-5" v-else/>
|
||||
<span class="font-medium">复制到剪贴板</span>
|
||||
</div>
|
||||
|
||||
<div @click="downloadImage"
|
||||
class="flex items-center justify-start gap-space px-6 py-3 bg-gradient-to-r from-purple-500 to-purple-600 text-white cp rounded-lg hover:from-purple-600 hover:to-purple-700 transition-all duration-200">
|
||||
<IconFluentArrowDownload20Regular class="w-5 h-5"/>
|
||||
<IconEosIconsLoading class="text-xl" v-if="loading2"/>
|
||||
<IconFluentArrowDownload20Regular class="w-5 h-5" v-else/>
|
||||
<span class="font-medium">保存高清海报</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -84,8 +84,15 @@ export const TourConfig = {
|
||||
modalOverlayOpeningPadding: 10,
|
||||
modalOverlayOpeningRadius: 6,
|
||||
floatingUIOptions: {
|
||||
middleware: [offset({mainAxis:30})]
|
||||
middleware: [offset({mainAxis: 30})]
|
||||
},
|
||||
},
|
||||
total: 7
|
||||
}
|
||||
|
||||
export const LIB_JS_URL = {
|
||||
SHEPHERD: import.meta.env.MODE === 'development' ?
|
||||
'https://cdn.jsdelivr.net/npm/shepherd.js@14.5.1/dist/esm/shepherd.mjs'
|
||||
: Origin + '/libs/Shepherd.14.5.1.mjs',
|
||||
SNAPDOM: `${Origin}/libs/snapdom.min.js`
|
||||
}
|
||||
@@ -1,27 +1,35 @@
|
||||
<script setup lang="ts">
|
||||
import {useBaseStore} from "@/stores/base.ts";
|
||||
import {useRouter} from "vue-router";
|
||||
import { useBaseStore } from "@/stores/base.ts";
|
||||
import { useRouter } from "vue-router";
|
||||
import BasePage from "@/components/BasePage.vue";
|
||||
import {_getDictDataByUrl, _nextTick, isMobile, msToHourMinute, resourceWrap, total, useNav} from "@/utils";
|
||||
import {DictResource, DictType} from "@/types/types.ts";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
import {
|
||||
_getDictDataByUrl,
|
||||
_nextTick,
|
||||
isMobile,
|
||||
loadJsLib,
|
||||
msToHourMinute,
|
||||
resourceWrap,
|
||||
total,
|
||||
useNav
|
||||
} from "@/utils";
|
||||
import { DictResource, DictType } from "@/types/types.ts";
|
||||
import { useRuntimeStore } from "@/stores/runtime.ts";
|
||||
import BaseIcon from "@/components/BaseIcon.vue";
|
||||
import Book from "@/components/Book.vue";
|
||||
import Progress from '@/components/base/Progress.vue';
|
||||
import Toast from '@/components/base/toast/Toast.ts'
|
||||
import BaseButton from "@/components/BaseButton.vue";
|
||||
import PopConfirm from "@/components/PopConfirm.vue";
|
||||
import {watch} from "vue";
|
||||
import {getDefaultDict} from "@/types/func.ts";
|
||||
import { watch } from "vue";
|
||||
import { getDefaultDict } from "@/types/func.ts";
|
||||
import DeleteIcon from "@/components/icon/DeleteIcon.vue";
|
||||
import dayjs from "dayjs";
|
||||
import isBetween from "dayjs/plugin/isBetween";
|
||||
import isoWeek from 'dayjs/plugin/isoWeek'
|
||||
import {useFetch} from "@vueuse/core";
|
||||
import {AppEnv, DICT_LIST, Host, PracticeSaveArticleKey, TourConfig} from "@/config/env.ts";
|
||||
import {myDictList} from "@/apis";
|
||||
import Shepherd from "shepherd.js";
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
import { useFetch } from "@vueuse/core";
|
||||
import { AppEnv, DICT_LIST, Host, LIB_JS_URL, PracticeSaveArticleKey, TourConfig } from "@/config/env.ts";
|
||||
import { myDictList } from "@/apis";
|
||||
import { useSettingStore } from "@/stores/setting.ts";
|
||||
|
||||
dayjs.extend(isoWeek)
|
||||
dayjs.extend(isBetween);
|
||||
@@ -73,7 +81,8 @@ async function init() {
|
||||
watch(() => store?.sbook?.id, (n) => {
|
||||
console.log('n', n)
|
||||
if (!n) {
|
||||
_nextTick(() => {
|
||||
_nextTick(async () => {
|
||||
const Shepherd = await loadJsLib('Shepherd', LIB_JS_URL.SHEPHERD);
|
||||
const tour = new Shepherd.Tour(TourConfig);
|
||||
tour.on('cancel', () => {
|
||||
localStorage.setItem('tour-guide', '1');
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
import { useDisableEventListener, useOnKeyboardEventListener, useStartKeyboardEventListener } from "@/hooks/event.ts";
|
||||
import useTheme from "@/hooks/theme.ts";
|
||||
import Toast from '@/components/base/toast/Toast.ts'
|
||||
import {_getDictDataByUrl, _nextTick, cloneDeep, isMobile, msToMinute, resourceWrap, total} from "@/utils";
|
||||
import { _getDictDataByUrl, _nextTick, cloneDeep, isMobile, loadJsLib, msToMinute, resourceWrap, total } from "@/utils";
|
||||
import { usePracticeStore } from "@/stores/practice.ts";
|
||||
import { useArticleOptions } from "@/hooks/dict.ts";
|
||||
import { genArticleSectionData, usePlaySentenceAudio } from "@/hooks/article.ts";
|
||||
@@ -34,10 +34,9 @@ import { useRoute, useRouter } from "vue-router";
|
||||
import PracticeLayout from "@/components/PracticeLayout.vue";
|
||||
import ArticleAudio from "@/pages/article/components/ArticleAudio.vue";
|
||||
import VolumeSetting from "@/pages/article/components/VolumeSetting.vue";
|
||||
import { AppEnv, DICT_LIST, PracticeSaveArticleKey, TourConfig } from "@/config/env.ts";
|
||||
import { AppEnv, DICT_LIST, LIB_JS_URL, PracticeSaveArticleKey, TourConfig } from "@/config/env.ts";
|
||||
import { addStat, setDictProp } from "@/apis";
|
||||
import { useRuntimeStore } from "@/stores/runtime.ts";
|
||||
import Shepherd from "shepherd.js";
|
||||
|
||||
const store = useBaseStore()
|
||||
const runtimeStore = useRuntimeStore()
|
||||
@@ -160,7 +159,8 @@ watch([() => store.load, () => loading], ([a, b]) => {
|
||||
|
||||
watch(() => articleData?.article?.id, id => {
|
||||
if (id) {
|
||||
_nextTick(() => {
|
||||
_nextTick(async () => {
|
||||
const Shepherd = await loadJsLib('Shepherd', LIB_JS_URL.SHEPHERD);
|
||||
const tour = new Shepherd.Tour(TourConfig);
|
||||
tour.on('cancel', () => {
|
||||
localStorage.setItem('tour-guide', '1');
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,7 @@ import { DictId } from "@/types/types.ts";
|
||||
import BasePage from "@/components/BasePage.vue";
|
||||
import { computed, onMounted, reactive, ref, shallowReactive, watch } from "vue";
|
||||
import { useRuntimeStore } from "@/stores/runtime.ts";
|
||||
import { _getDictDataByUrl, _nextTick, convertToWord, isMobile, loadJsLib, sleep, useNav } from "@/utils";
|
||||
import { _getDictDataByUrl, _nextTick, convertToWord, isMobile, loadJsLib, useNav } from "@/utils";
|
||||
import { nanoid } from "nanoid";
|
||||
import BaseIcon from "@/components/BaseIcon.vue";
|
||||
import BaseTable from "@/components/BaseTable.vue";
|
||||
@@ -26,9 +26,8 @@ import { getCurrentStudyWord } from "@/hooks/dict.ts";
|
||||
import PracticeSettingDialog from "@/pages/word/components/PracticeSettingDialog.vue";
|
||||
import { useSettingStore } from "@/stores/setting.ts";
|
||||
import { MessageBox } from "@/utils/MessageBox.tsx";
|
||||
import { AppEnv, Origin, PracticeSaveWordKey, TourConfig } from "@/config/env.ts";
|
||||
import { AppEnv, LIB_JS_URL, Origin, PracticeSaveWordKey, TourConfig } from "@/config/env.ts";
|
||||
import { detail } from "@/apis";
|
||||
import Shepherd from "shepherd.js";
|
||||
|
||||
const runtimeStore = useRuntimeStore()
|
||||
const base = useBaseStore()
|
||||
@@ -388,7 +387,8 @@ function searchWord() {
|
||||
|
||||
watch(() => loading, (val) => {
|
||||
if (!val) return
|
||||
_nextTick(() => {
|
||||
_nextTick(async () => {
|
||||
const Shepherd = await loadJsLib('Shepherd', LIB_JS_URL.SHEPHERD);
|
||||
const tour = new Shepherd.Tour(TourConfig);
|
||||
tour.on('cancel', () => {
|
||||
localStorage.setItem('tour-guide', '1');
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import {_nextTick, groupBy, isMobile, resourceWrap, useNav} from "@/utils";
|
||||
import { _nextTick, groupBy, isMobile, loadJsLib, resourceWrap, useNav } from "@/utils";
|
||||
import BasePage from "@/components/BasePage.vue";
|
||||
import { DictResource } from "@/types/types.ts";
|
||||
import { useRuntimeStore } from "@/stores/runtime.ts";
|
||||
@@ -11,12 +11,11 @@ import BackIcon from "@/components/BackIcon.vue";
|
||||
import DictGroup from "@/components/list/DictGroup.vue";
|
||||
import { useBaseStore } from "@/stores/base.ts";
|
||||
import { useRouter } from "vue-router";
|
||||
import { computed, onMounted, watch } from "vue";
|
||||
import { computed, watch } from "vue";
|
||||
import { getDefaultDict } from "@/types/func.ts";
|
||||
import { useFetch } from "@vueuse/core";
|
||||
import { DICT_LIST, TourConfig } from "@/config/env.ts";
|
||||
import { DICT_LIST, LIB_JS_URL, TourConfig } from "@/config/env.ts";
|
||||
import BaseInput from "@/components/base/BaseInput.vue";
|
||||
import Shepherd from "shepherd.js";
|
||||
import { useSettingStore } from "@/stores/setting.ts";
|
||||
|
||||
const {nav} = useNav()
|
||||
@@ -84,7 +83,8 @@ watch(dict_list, (val) => {
|
||||
if (!val.length) return
|
||||
let cet4 = val.find(v => v.id === 'cet4')
|
||||
if (!cet4) return
|
||||
_nextTick(() => {
|
||||
_nextTick(async () => {
|
||||
const Shepherd = await loadJsLib('Shepherd', LIB_JS_URL.SHEPHERD);
|
||||
const tour = new Shepherd.Tour(TourConfig);
|
||||
tour.on('cancel', () => {
|
||||
localStorage.setItem('tour-guide', '1');
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, provide, ref, toRef, watch } from "vue";
|
||||
import { onMounted, provide, ref, watch } from "vue";
|
||||
|
||||
import Statistics from "@/pages/word/Statistics.vue";
|
||||
import { emitter, EventKey, useEvents } from "@/utils/eventBus.ts";
|
||||
import { useSettingStore } from "@/stores/setting.ts";
|
||||
import { useRuntimeStore } from "@/stores/runtime.ts";
|
||||
import { Dict, PracticeData, WordPracticeType, ShortcutKey, TaskWords, Word, WordPracticeMode } from "@/types/types.ts";
|
||||
import { Dict, PracticeData, ShortcutKey, TaskWords, Word, WordPracticeMode, WordPracticeType } from "@/types/types.ts";
|
||||
import { useDisableEventListener, useOnKeyboardEventListener, useStartKeyboardEventListener } from "@/hooks/event.ts";
|
||||
import useTheme from "@/hooks/theme.ts";
|
||||
import { getCurrentStudyWord, useWordOptions } from "@/hooks/dict.ts";
|
||||
import {_getDictDataByUrl, _nextTick, cloneDeep, isMobile, resourceWrap, shuffle} from "@/utils";
|
||||
import { _getDictDataByUrl, _nextTick, cloneDeep, isMobile, loadJsLib, resourceWrap, shuffle } from "@/utils";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import Footer from "@/pages/word/components/Footer.vue";
|
||||
import Panel from "@/components/Panel.vue";
|
||||
@@ -25,11 +25,9 @@ import { getDefaultDict, getDefaultWord } from "@/types/func.ts";
|
||||
import ConflictNotice from "@/components/ConflictNotice.vue";
|
||||
import PracticeLayout from "@/components/PracticeLayout.vue";
|
||||
|
||||
import { DICT_LIST, PracticeSaveWordKey, TourConfig } from "@/config/env.ts";
|
||||
import { DICT_LIST, LIB_JS_URL, PracticeSaveWordKey, TourConfig } from "@/config/env.ts";
|
||||
import { ToastInstance } from "@/components/base/toast/type.ts";
|
||||
import { watchOnce } from "@vueuse/core";
|
||||
import Shepherd from "shepherd.js";
|
||||
import { offset } from '@floating-ui/dom';
|
||||
|
||||
const {
|
||||
isWordCollect,
|
||||
@@ -117,7 +115,8 @@ onMounted(() => {
|
||||
watchOnce(() => data.words.length, (newVal, oldVal) => {
|
||||
//如果是从无值变有值,代表是开始
|
||||
if (!oldVal && newVal) {
|
||||
_nextTick(() => {
|
||||
_nextTick(async () => {
|
||||
const Shepherd = await loadJsLib('Shepherd', LIB_JS_URL.SHEPHERD);
|
||||
const tour = new Shepherd.Tour(TourConfig);
|
||||
tour.on('cancel', () => {
|
||||
localStorage.setItem('tour-guide', '1');
|
||||
|
||||
@@ -2,10 +2,19 @@
|
||||
import { useBaseStore } from "@/stores/base.ts";
|
||||
import { useRouter } from "vue-router";
|
||||
import BaseIcon from "@/components/BaseIcon.vue";
|
||||
import {_getAccomplishDate, _getDictDataByUrl, _nextTick, isMobile, resourceWrap, shuffle, useNav} from "@/utils";
|
||||
import {
|
||||
_getAccomplishDate,
|
||||
_getDictDataByUrl,
|
||||
_nextTick,
|
||||
isMobile,
|
||||
loadJsLib,
|
||||
resourceWrap,
|
||||
shuffle,
|
||||
useNav
|
||||
} from "@/utils";
|
||||
import BasePage from "@/components/BasePage.vue";
|
||||
import { DictResource, WordPracticeMode } from "@/types/types.ts";
|
||||
import { onMounted, watch } from "vue";
|
||||
import { watch } from "vue";
|
||||
import { getCurrentStudyWord } from "@/hooks/dict.ts";
|
||||
import { useRuntimeStore } from "@/stores/runtime.ts";
|
||||
import Book from "@/components/Book.vue";
|
||||
@@ -19,11 +28,10 @@ import PracticeSettingDialog from "@/pages/word/components/PracticeSettingDialog
|
||||
import ChangeLastPracticeIndexDialog from "@/pages/word/components/ChangeLastPracticeIndexDialog.vue";
|
||||
import { useSettingStore } from "@/stores/setting.ts";
|
||||
import { useFetch } from "@vueuse/core";
|
||||
import { AppEnv, DICT_LIST, Host, PracticeSaveWordKey, TourConfig } from "@/config/env.ts";
|
||||
import { AppEnv, DICT_LIST, Host, LIB_JS_URL, PracticeSaveWordKey, TourConfig } from "@/config/env.ts";
|
||||
import { myDictList } from "@/apis";
|
||||
import PracticeWordListDialog from "@/pages/word/components/PracticeWordListDialog.vue";
|
||||
import ShufflePracticeSettingDialog from "@/pages/word/components/ShufflePracticeSettingDialog.vue";
|
||||
import Shepherd from "shepherd.js";
|
||||
import SettingDialog from "@/pages/word/components/SettingDialog.vue";
|
||||
|
||||
|
||||
@@ -45,7 +53,8 @@ let currentStudy = $ref({
|
||||
watch(() => store.load, n => {
|
||||
if (n) {
|
||||
init()
|
||||
_nextTick(() => {
|
||||
_nextTick(async () => {
|
||||
const Shepherd = await loadJsLib('Shepherd', LIB_JS_URL.SHEPHERD);
|
||||
const tour = new Shepherd.Tour(TourConfig);
|
||||
tour.on('cancel', () => {
|
||||
localStorage.setItem('tour-guide', '1');
|
||||
@@ -237,7 +246,7 @@ let isNewHost = $ref(window.location.host === Host)
|
||||
2study.top 域名将在不久后停止使用
|
||||
</div>
|
||||
|
||||
<SettingDialog/>
|
||||
<!-- <SettingDialog/>-->
|
||||
|
||||
<div class="card flex flex-col md:flex-row gap-8">
|
||||
<div class="flex-1 w-full flex flex-col justify-between">
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import {BaseState, getDefaultBaseState, useBaseStore} from "@/stores/base.ts";
|
||||
import {getDefaultSettingState, SettingState} from "@/stores/setting.ts";
|
||||
import {Dict, DictId, DictResource, DictType} from "@/types/types.ts";
|
||||
import {useRouter} from "vue-router";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
import { BaseState, getDefaultBaseState, useBaseStore } from "@/stores/base.ts";
|
||||
import { getDefaultSettingState, SettingState } from "@/stores/setting.ts";
|
||||
import { Dict, DictId, DictResource, DictType } from "@/types/types.ts";
|
||||
import { useRouter } from "vue-router";
|
||||
import { useRuntimeStore } from "@/stores/runtime.ts";
|
||||
import dayjs from 'dayjs'
|
||||
import {AppEnv, RESOURCE_PATH, SAVE_DICT_KEY, SAVE_SETTING_KEY} from "@/config/env.ts";
|
||||
import {nextTick} from "vue";
|
||||
import { AppEnv, RESOURCE_PATH, SAVE_DICT_KEY, SAVE_SETTING_KEY } from "@/config/env.ts";
|
||||
import { nextTick } from "vue";
|
||||
import Toast from '@/components/base/toast/Toast.ts'
|
||||
import {getDefaultDict, getDefaultWord} from "@/types/func.ts";
|
||||
import { getDefaultDict, getDefaultWord } from "@/types/func.ts";
|
||||
import duration from "dayjs/plugin/duration";
|
||||
|
||||
dayjs.extend(duration);
|
||||
@@ -424,8 +424,42 @@ export async function loadJsLib(key: string, url: string) {
|
||||
if (window[key]) return window[key];
|
||||
return new Promise((resolve, reject) => {
|
||||
const script = document.createElement("script");
|
||||
// 判断是否是 .mjs 文件,如果是,则使用 type="module"
|
||||
if (url.endsWith(".mjs")) {
|
||||
script.type = "module"; // 需要加上 type="module"
|
||||
script.src = url;
|
||||
script.onload = async () => {
|
||||
try {
|
||||
// 使用动态 import 加载模块
|
||||
const module = await import(url); // 动态导入 .mjs 模块
|
||||
window[key] = module.default || module; // 将模块挂到 window 对象
|
||||
resolve(window[key]);
|
||||
} catch (err) {
|
||||
reject(`${key} 加载失败: ${err.message}`);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
// 如果是非 .mjs 文件,直接按原方式加载
|
||||
script.src = url;
|
||||
script.onload = () => resolve(window[key]);
|
||||
}
|
||||
script.onerror = () => reject(key + " 加载失败");
|
||||
document.head.appendChild(script);
|
||||
});
|
||||
}
|
||||
|
||||
export async function loadJsLib2(key: string, url: string, module: boolean = false) {
|
||||
if (window[key]) return window[key];
|
||||
return new Promise((resolve, reject) => {
|
||||
const script = document.createElement("script");
|
||||
if (module) {
|
||||
script.type = 'module'
|
||||
}
|
||||
script.src = url;
|
||||
script.onload = () => resolve(window[key]);
|
||||
script.onload = () => {
|
||||
console.log('key', key)
|
||||
resolve(window[key])
|
||||
};
|
||||
script.onerror = () => reject(key + ' 加载失败')
|
||||
document.head.appendChild(script);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user