52
package-lock.json
generated
52
package-lock.json
generated
@@ -10,6 +10,7 @@
|
||||
"dependencies": {
|
||||
"@imengyu/vue3-context-menu": "^1.5.1",
|
||||
"@vueuse/core": "14.0.0-alpha.0",
|
||||
"@zumer/snapdom": "^2.0.0",
|
||||
"axios": "^1.10.0",
|
||||
"compromise": "^14.14.4",
|
||||
"copy-to-clipboard": "^3.3.3",
|
||||
@@ -28,12 +29,14 @@
|
||||
"devDependencies": {
|
||||
"@alicloud/pop-core": "^1.8.0",
|
||||
"@iconify-json/bx": "^1.2.2",
|
||||
"@iconify-json/dinkie-icons": "^1.2.0",
|
||||
"@iconify-json/eos-icons": "^1.2.4",
|
||||
"@iconify-json/fluent": "^1.2.28",
|
||||
"@iconify-json/icon-park-outline": "^1.2.4",
|
||||
"@iconify-json/icon-park-solid": "^1.2.4",
|
||||
"@iconify-json/ix": "^1.2.10",
|
||||
"@iconify-json/material-symbols": "^1.2.33",
|
||||
"@iconify-json/mdi": "^1.2.3",
|
||||
"@iconify-json/oui": "^1.2.6",
|
||||
"@iconify-json/ph": "^1.2.2",
|
||||
"@iconify-json/qlementine-icons": "^1.2.11",
|
||||
@@ -1095,6 +1098,16 @@
|
||||
"@iconify/types": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@iconify-json/dinkie-icons": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@iconify-json/dinkie-icons/-/dinkie-icons-1.2.0.tgz",
|
||||
"integrity": "sha512-/RSAjK6twyEOKfW9FddZDP+EPMeSIocb4Y4Zq2Y2IbqUxYmNHqXtxidVioBzpv+a3JeiQ61ARlRKtCQ0BqSGpA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@iconify/types": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@iconify-json/eos-icons": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@iconify-json/eos-icons/-/eos-icons-1.2.4.tgz",
|
||||
@@ -1150,6 +1163,16 @@
|
||||
"@iconify/types": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@iconify-json/mdi": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@iconify-json/mdi/-/mdi-1.2.3.tgz",
|
||||
"integrity": "sha512-O3cLwbDOK7NNDf2ihaQOH5F9JglnulNDFV7WprU2dSoZu3h3cWH//h74uQAB87brHmvFVxIOkuBX2sZSzYhScg==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@iconify/types": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@iconify-json/oui": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@iconify-json/oui/-/oui-1.2.6.tgz",
|
||||
@@ -3934,6 +3957,12 @@
|
||||
"vue": "^3.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@zumer/snapdom": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@zumer/snapdom/-/snapdom-2.0.0.tgz",
|
||||
"integrity": "sha512-e/fkm5wCUd+9CssUIyH09xTeR4DvRTmZLGVOlnXLhr4HeI7sdc6ed8cLPiZKFtiQDRiwD3EKx4RIUrpQOJQY7A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.15.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||
@@ -13543,6 +13572,15 @@
|
||||
"@iconify/types": "*"
|
||||
}
|
||||
},
|
||||
"@iconify-json/dinkie-icons": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@iconify-json/dinkie-icons/-/dinkie-icons-1.2.0.tgz",
|
||||
"integrity": "sha512-/RSAjK6twyEOKfW9FddZDP+EPMeSIocb4Y4Zq2Y2IbqUxYmNHqXtxidVioBzpv+a3JeiQ61ARlRKtCQ0BqSGpA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@iconify/types": "*"
|
||||
}
|
||||
},
|
||||
"@iconify-json/eos-icons": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@iconify-json/eos-icons/-/eos-icons-1.2.4.tgz",
|
||||
@@ -13597,6 +13635,15 @@
|
||||
"@iconify/types": "*"
|
||||
}
|
||||
},
|
||||
"@iconify-json/mdi": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@iconify-json/mdi/-/mdi-1.2.3.tgz",
|
||||
"integrity": "sha512-O3cLwbDOK7NNDf2ihaQOH5F9JglnulNDFV7WprU2dSoZu3h3cWH//h74uQAB87brHmvFVxIOkuBX2sZSzYhScg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@iconify/types": "*"
|
||||
}
|
||||
},
|
||||
"@iconify-json/oui": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@iconify-json/oui/-/oui-1.2.6.tgz",
|
||||
@@ -15554,6 +15601,11 @@
|
||||
"integrity": "sha512-J8cmTJkB0hAwPBXRR5U3N9FJkhPBrhyYiQm21kZ3j/o8W69Pg6JlPSxLOJtkg+AwC/r5x7Gpq2Vglv84vHotwA==",
|
||||
"requires": {}
|
||||
},
|
||||
"@zumer/snapdom": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@zumer/snapdom/-/snapdom-2.0.0.tgz",
|
||||
"integrity": "sha512-e/fkm5wCUd+9CssUIyH09xTeR4DvRTmZLGVOlnXLhr4HeI7sdc6ed8cLPiZKFtiQDRiwD3EKx4RIUrpQOJQY7A=="
|
||||
},
|
||||
"acorn": {
|
||||
"version": "8.15.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||
|
||||
@@ -256,6 +256,17 @@ async function addMyStudyList() {
|
||||
startPractice()
|
||||
}
|
||||
|
||||
async function startTest() {
|
||||
if (!runtimeStore.editDict.words.length) {
|
||||
loading = true
|
||||
let r = await _getDictDataByUrl(runtimeStore.editDict)
|
||||
runtimeStore.editDict = r
|
||||
loading = false
|
||||
}
|
||||
await base.changeDict(runtimeStore.editDict)
|
||||
nav('word-test/' + store.sdict.id, {})
|
||||
}
|
||||
|
||||
let exportLoading = $ref(false)
|
||||
let importLoading = $ref(false)
|
||||
let tableRef = ref()
|
||||
@@ -388,6 +399,7 @@ defineRender(() => {
|
||||
<BaseButton loading={studyLoading || loading} type="info"
|
||||
onClick={() => isEdit = true}>编辑</BaseButton>
|
||||
<BaseButton loading={studyLoading || loading} onClick={addMyStudyList}>学习</BaseButton>
|
||||
<BaseButton loading={studyLoading || loading} onClick={startTest}>测试</BaseButton>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-lg ">介绍:{runtimeStore.editDict.description}</div>
|
||||
|
||||
248
src/pages/word/WordTest.vue
Normal file
248
src/pages/word/WordTest.vue
Normal file
@@ -0,0 +1,248 @@
|
||||
<script setup lang="ts">
|
||||
import {onMounted, ref} from 'vue'
|
||||
import BasePage from '@/components/BasePage.vue'
|
||||
import BaseButton from '@/components/BaseButton.vue'
|
||||
import VolumeIcon from '@/components/icon/VolumeIcon.vue'
|
||||
import {useRoute, useRouter} from 'vue-router'
|
||||
import {useBaseStore} from '@/stores/base.ts'
|
||||
import {Dict, Word} from '@/types/types.ts'
|
||||
import {_getDictDataByUrl, shuffle} from '@/utils'
|
||||
import {useRuntimeStore} from '@/stores/runtime.ts'
|
||||
import {usePlayBeep, usePlayCorrect, usePlayWordAudio} from '@/hooks/sound.ts'
|
||||
import Toast from '@/components/base/toast/Toast.ts'
|
||||
|
||||
type Candidate = { word: string, wordObj?: Word }
|
||||
type Question = {
|
||||
stem: Word,
|
||||
candidates: Candidate[],
|
||||
optionTexts: string[],
|
||||
correctIndex: number,
|
||||
selectedIndex: number,
|
||||
submitted: boolean
|
||||
}
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const base = useBaseStore()
|
||||
const runtimeStore = useRuntimeStore()
|
||||
const playBeep = usePlayBeep()
|
||||
const playCorrect = usePlayCorrect()
|
||||
const playWordAudio = usePlayWordAudio()
|
||||
|
||||
let loading = $ref(false)
|
||||
let dict = $ref<Dict>()
|
||||
let questions = $ref<Question[]>([])
|
||||
let index = $ref(0)
|
||||
|
||||
function getWordByText(val: string, list: Word[]): Word | undefined {
|
||||
let r = list.find(v => v.word.toLowerCase() === val.toLowerCase())
|
||||
return r
|
||||
}
|
||||
|
||||
function pickRelVariant(w: Word, list: Word[]): Candidate | null {
|
||||
let rels = w.relWords?.rels || []
|
||||
for (let i = 0; i < rels.length; i++) {
|
||||
for (let j = 0; j < rels[i].words.length; j++) {
|
||||
let c = rels[i].words[j].c
|
||||
let r = getWordByText(c, list)
|
||||
if (r && r.word.toLowerCase() !== w.word.toLowerCase()) {
|
||||
return {word: r.word, wordObj: r}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function pickSynonym(w: Word, list: Word[]): Candidate | null {
|
||||
let synos = w.synos || []
|
||||
for (let i = 0; i < synos.length; i++) {
|
||||
for (let j = 0; j < synos[i].ws.length; j++) {
|
||||
let c = synos[i].ws[j]
|
||||
let r = getWordByText(c, list)
|
||||
if (r && r.word.toLowerCase() !== w.word.toLowerCase()) {
|
||||
return {word: r.word, wordObj: r}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function pickSamePos(w: Word, list: Word[]): Candidate | null {
|
||||
let pos = (w.trans?.[0]?.pos || '').trim()
|
||||
let samePos = list.filter(v => v.word.toLowerCase() !== w.word.toLowerCase() && v.trans?.some(t => t.pos === pos))
|
||||
if (samePos.length) {
|
||||
let r = samePos[Math.floor(Math.random() * samePos.length)]
|
||||
return {word: r.word, wordObj: r}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function buildQuestion(w: Word, list: Word[]): Question {
|
||||
let candidates: Candidate[] = []
|
||||
candidates.push({word: w.word, wordObj: w})
|
||||
let c1 = pickRelVariant(w, list) || pickSynonym(w, list) || pickSamePos(w, list)
|
||||
let c2 = null as Candidate | null
|
||||
let tried = new Set<string>([w.word.toLowerCase()])
|
||||
if (c1) tried.add(c1.word.toLowerCase())
|
||||
let attempts = 0
|
||||
while (!c2 && attempts < 5) {
|
||||
c2 = pickSynonym(w, list) || pickSamePos(w, list) || pickRelVariant(w, list)
|
||||
if (c2 && tried.has(c2.word.toLowerCase())) c2 = null
|
||||
attempts++
|
||||
}
|
||||
if (!c1) {
|
||||
let rand = list.filter(v => v.word.toLowerCase() !== w.word.toLowerCase())
|
||||
if (rand.length) c1 = {word: rand[Math.floor(Math.random() * rand.length)].word, wordObj: getWordByText(rand[Math.floor(Math.random() * rand.length)].word, list)}
|
||||
}
|
||||
if (!c2) {
|
||||
let rand = list.filter(v => v.word.toLowerCase() !== w.word.toLowerCase() && v.word.toLowerCase() !== c1?.word.toLowerCase())
|
||||
if (rand.length) c2 = {word: rand[Math.floor(Math.random() * rand.length)].word, wordObj: getWordByText(rand[Math.floor(Math.random() * rand.length)].word, list)}
|
||||
}
|
||||
if (c1) candidates.push(c1)
|
||||
if (c2) candidates.push(c2)
|
||||
const labels = candidates.map(v => formatCandidateText(v))
|
||||
const order = shuffle([0,1,2])
|
||||
const optionTexts = order.map(i => labels[i])
|
||||
const correctIndex = order.indexOf(0)
|
||||
return {
|
||||
stem: w,
|
||||
candidates,
|
||||
optionTexts,
|
||||
correctIndex,
|
||||
selectedIndex: -1,
|
||||
submitted: false
|
||||
}
|
||||
}
|
||||
|
||||
function formatCandidateText(c: Candidate): string {
|
||||
const w = c.wordObj
|
||||
if (!w || !w.trans || !w.trans.length) return '当前词典未收录释义'
|
||||
|
||||
const cleanCn = (cn: string, head: string) => {
|
||||
let t = cn || ''
|
||||
// 去掉含英文的括号片段(避免出现人名或英文拼写)
|
||||
t = t.replace(/([^)]*[A-Za-z][^)]*)/g, '')
|
||||
// 去掉“时态/过去式/复数”等形态说明
|
||||
t = t.replace(/(时\s*态|过去式|过去分词|现在分词|复数|第三人称|比较级|最高级)[::].*/g, '')
|
||||
// 去掉直接出现的英文词头
|
||||
const headEsc = head.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
||||
t = t.replace(new RegExp(headEsc, 'gi'), '')
|
||||
// 统一分隔符为中文分号
|
||||
t = t.replace(/[;;]\s*/g, ';')
|
||||
// 收尾空白
|
||||
t = t.trim()
|
||||
return t
|
||||
}
|
||||
|
||||
const parts = w.trans
|
||||
.map(v => {
|
||||
const pos = (v.pos || '').trim()
|
||||
const cn = cleanCn(v.cn || '', w.word)
|
||||
if (/^\s*【名】/.test(v.cn || '')) return ''
|
||||
if (!cn) return ''
|
||||
return `${pos ? '- ' + pos + ' ' : '- '}${cn}`
|
||||
})
|
||||
.filter(Boolean)
|
||||
|
||||
return parts.length ? parts.join(';') : '当前词典未收录释义'
|
||||
}
|
||||
|
||||
async function init() {
|
||||
let dictId: any = route.params.id
|
||||
let d = base.word.bookList.find(v => v.id === dictId)
|
||||
if (!d) d = base.sdict
|
||||
if (!d?.id) return router.push('/words')
|
||||
if (!d.words.length && runtimeStore.editDict?.id === d.id) {
|
||||
loading = true
|
||||
let r = await _getDictDataByUrl(runtimeStore.editDict)
|
||||
d = r
|
||||
loading = false
|
||||
}
|
||||
dict = d
|
||||
if (!dict.words.length) {
|
||||
return Toast.warning('没有单词可测试!')
|
||||
}
|
||||
const wordList = shuffle(dict.words)
|
||||
questions = wordList.map(w => buildQuestion(w, dict.words))
|
||||
index = 0
|
||||
}
|
||||
|
||||
function select(i: number) {
|
||||
let q = questions[index]
|
||||
if (!q || q.submitted) return
|
||||
q.selectedIndex = i
|
||||
q.submitted = true
|
||||
if (i === q.correctIndex) {
|
||||
playCorrect()
|
||||
} else {
|
||||
playBeep()
|
||||
let temp = q.stem.word.toLowerCase()
|
||||
if (!base.wrong.words.find((v: Word) => v.word.toLowerCase() === temp)) {
|
||||
base.wrong.words.push(q.stem)
|
||||
base.wrong.length = base.wrong.words.length
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function next() {
|
||||
if (index < questions.length - 1) index++
|
||||
}
|
||||
|
||||
function end() {
|
||||
router.back()
|
||||
}
|
||||
|
||||
onMounted(init)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BasePage>
|
||||
<div class="card flex flex-col">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="page-title">测试:{{ dict?.name }}</div>
|
||||
<div class="text-base">{{ index + 1 }} / {{ questions.length }}</div>
|
||||
</div>
|
||||
<div class="line my-2"></div>
|
||||
|
||||
<div v-if="questions.length" class="flex flex-col gap-4">
|
||||
<div class="text-2xl en-article-family flex items-center gap-2">
|
||||
<span>题目:{{ questions[index].stem.word }}</span>
|
||||
<VolumeIcon :simple="true" :title="'发音'" :cb="() => playWordAudio(questions[index].stem.word)"/>
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<div
|
||||
v-for="(opt,i) in questions[index].optionTexts"
|
||||
:key="i"
|
||||
class="option border rounded p-2 cursor-pointer"
|
||||
:class="{
|
||||
'text-green-600': questions[index].submitted && i === questions[index].correctIndex,
|
||||
'text-red-600': questions[index].submitted && i === questions[index].selectedIndex && i !== questions[index].correctIndex
|
||||
}"
|
||||
@click="select(i)"
|
||||
>
|
||||
<span>(<span class="italic">{{ ['A','B','C'][i] }}</span>) {{ opt }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="questions[index].submitted" class="mt-4">
|
||||
<div class="mb-2 text-base">选项解析:</div>
|
||||
<div class="grid gap-2 grid-cols-1 md:grid-cols-3">
|
||||
<div v-for="(c,i) in questions[index].candidates" :key="i" class="p-2 rounded bg-[--bg-card-secend]">
|
||||
<div class="en-article-family text-lg">{{ c.word }}</div>
|
||||
<div class="mt-1 text-sm">{{ c.wordObj?.trans?.map(v => v.cn).join(';') || '当前词典未收录释义' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-6 flex gap-3">
|
||||
<BaseButton type="primary" @click="next">继续测试</BaseButton>
|
||||
<BaseButton type="info" @click="end">结束</BaseButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</BasePage>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.option:hover { background: var(--color-second); }
|
||||
</style>
|
||||
@@ -6,6 +6,7 @@ import ArticlesPage from "@/pages/article/ArticlesPage.vue";
|
||||
import PracticeArticles from "@/pages/article/PracticeArticles.vue";
|
||||
import DictDetail from "@/pages/word/DictDetail.vue";
|
||||
import PracticeWords from "@/pages/word/PracticeWords.vue";
|
||||
import WordTest from "@/pages/word/WordTest.vue";
|
||||
import BookDetail from "@/pages/article/BookDetail.vue";
|
||||
import DictList from "@/pages/word/DictList.vue";
|
||||
import BookList from "@/pages/article/BookList.vue";
|
||||
@@ -24,6 +25,7 @@ export const routes: RouteRecordRaw[] = [
|
||||
{path: 'words', component: WordsPage},
|
||||
{path: 'word', redirect: '/words'},
|
||||
{path: 'practice-words/:id', component: PracticeWords},
|
||||
{path: 'word-test/:id', component: WordTest},
|
||||
{path: 'study-word', redirect: '/words'},
|
||||
{path: 'dict-list', component: DictList},
|
||||
{path: 'dict-detail', component: DictDetail},
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
"moduleResolution": "bundler",
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
"include": ["vite.config.mts"]
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ const lifecycle = process.env.npm_lifecycle_event;
|
||||
let isCdnBuild = ['build-oss', 'report-oss'].includes(lifecycle)
|
||||
let isAnalyseBuild = ['report-oss', 'report'].includes(lifecycle)
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig(() => {
|
||||
return new Promise(resolve => {
|
||||
let latestCommitHash = ''
|
||||
@@ -29,22 +28,20 @@ export default defineConfig(() => {
|
||||
resolve({
|
||||
plugins: [
|
||||
Icons({
|
||||
//自动安装@iconify-json/xx
|
||||
autoInstall: true,
|
||||
compiler: 'vue3',
|
||||
}),
|
||||
Components({
|
||||
resolvers: [
|
||||
// 自动解析 <IconMdiHome /> 这种组件名
|
||||
IconsResolver({
|
||||
prefix: 'Icon', // 默认前缀
|
||||
prefix: 'Icon',
|
||||
}),
|
||||
],
|
||||
}),
|
||||
VueMacros({
|
||||
plugins: {
|
||||
vue: Vue(),
|
||||
vueJsx: VueJsx(), // 如果需要
|
||||
vueJsx: VueJsx(),
|
||||
},
|
||||
}),
|
||||
UnoCSS(),
|
||||
@@ -53,12 +50,11 @@ export default defineConfig(() => {
|
||||
gzipSize: true,
|
||||
brotliSize: true,
|
||||
emitFile: false,
|
||||
filename: "report.html", //分析图生成的文件名
|
||||
open: true //如果存在本地服务端口,将在打包后自动展示
|
||||
filename: "report.html",
|
||||
open: true
|
||||
}) : null,
|
||||
SlidePlugin(),
|
||||
isCdnBuild ? [
|
||||
//这里不要用vite-plugin-cdn-import,他里面使用了rollup-plugin-external-globals插件,会导致自动加载components.d.ts里面的组件全部没引入,也不报错
|
||||
{
|
||||
name: 'inject-cdn-head',
|
||||
enforce: 'pre',
|
||||
@@ -80,8 +76,6 @@ export default defineConfig(() => {
|
||||
],
|
||||
build: {
|
||||
rollupOptions: {
|
||||
// 因为已经把包复制过来了,里面的axios实例用的项目的,所以这行代码可以不要了
|
||||
// external: isCdnBuild ? ['axios'] : [],// 使用全局的 axios。因为百度翻译库内部用了0.19版本的axios,会被打包到代码里面
|
||||
output: {
|
||||
manualChunks(id) {
|
||||
if (id.includes('node_modules/@iconify') || id.includes('~icons')) {
|
||||
@@ -89,13 +83,10 @@ export default defineConfig(() => {
|
||||
}
|
||||
if (id.includes('utils')
|
||||
|| id.includes('hooks')
|
||||
// || id.includes('types')
|
||||
// || id.includes('libs')
|
||||
) {
|
||||
return 'utils'
|
||||
}
|
||||
if (!isCdnBuild) return
|
||||
//不知为何不引入cdn之后,这里分包会报错
|
||||
if (id.includes('dialog')) {
|
||||
return 'dialog'
|
||||
}
|
||||
@@ -106,7 +97,6 @@ export default defineConfig(() => {
|
||||
define: {
|
||||
LATEST_COMMIT_HASH: JSON.stringify(latestCommitHash + (process.env.NODE_ENV === 'production' ? '' : ' (dev)')),
|
||||
},
|
||||
//默认是'',导致只能在一级域名下使用。
|
||||
base: './',
|
||||
resolve: {
|
||||
alias: {
|
||||
@@ -117,8 +107,7 @@ export default defineConfig(() => {
|
||||
css: {
|
||||
preprocessorOptions: {
|
||||
scss: {
|
||||
//解决 sass 控制台出现 Deprecation Warning: The legacy JS API is deprecated and will be removed in Dart Sass 2.0.0 的问题
|
||||
api: "modern-compiler" // or 'modern'
|
||||
api: "modern-compiler"
|
||||
}
|
||||
}
|
||||
},
|
||||
Reference in New Issue
Block a user