feat:移除无用依赖

This commit is contained in:
zyronon
2025-08-03 22:02:00 +08:00
parent 6d9fbf234f
commit c372a18ca0
50 changed files with 540 additions and 1334 deletions

10
auto-imports.d.ts vendored
View File

@@ -1,10 +0,0 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// Generated by unplugin-auto-import
export {}
declare global {
const ElMessage: typeof import('element-plus/es')['ElMessage']
const ElMessageBox: typeof import('element-plus/es')['ElMessageBox']
}

43
components.d.ts vendored
View File

@@ -1,43 +0,0 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399
export {}
declare module 'vue' {
export interface GlobalComponents {
BackIcon: typeof import('./src/components/icon/BackIcon.vue')['default']
BaseButton: typeof import('./src/components/BaseButton.vue')['default']
BaseIcon: typeof import('./src/components/BaseIcon.vue')['default']
Close: typeof import('./src/components/icon/Close.vue')['default']
DeleteIcon: typeof import('./src/components/icon/DeleteIcon.vue')['default']
ElButton: typeof import('element-plus/es')['ElButton']
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
ElForm: typeof import('element-plus/es')['ElForm']
ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElInput: typeof import('element-plus/es')['ElInput']
ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
ElOption: typeof import('element-plus/es')['ElOption']
ElPagination: typeof import('element-plus/es')['ElPagination']
ElPopover: typeof import('element-plus/es')['ElPopover']
ElProgress: typeof import('element-plus/es')['ElProgress']
ElRadio: typeof import('element-plus/es')['ElRadio']
ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
ElSelect: typeof import('element-plus/es')['ElSelect']
ElSlider: typeof import('element-plus/es')['ElSlider']
ElSwitch: typeof import('element-plus/es')['ElSwitch']
ElTableV2: typeof import('element-plus/es')['ElTableV2']
ElUpload: typeof import('element-plus/es')['ElUpload']
Empty: typeof import('./src/components/Empty.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
SlideHorizontal: typeof import('./src/components/slide/SlideHorizontal.vue')['default']
SlideItem: typeof import('./src/components/slide/SlideItem.vue')['default']
VolumeIcon: typeof import('./src/components/icon/VolumeIcon.vue')['default']
}
export interface ComponentCustomProperties {
vLoading: typeof import('element-plus/es')['ElLoadingDirective']
}
}

View File

@@ -26,16 +26,15 @@
"git-last-commit": "^1.0.1",
"libarchive-wasm": "^1.2.0",
"localforage": "^1.10.0",
"lodash-es": "^4.17.21",
"mitt": "^3.0.1",
"nanoid": "^5.1.5",
"pinia": "^3.0.3",
"sentence-splitter": "^4.4.1",
"string-comparison": "^1.3.0",
"tesseract.js": "^4.1.4",
"unplugin-element-plus": "^0.10.0",
"vue": "^3.5.17",
"vue-activity-calendar": "^1.2.2",
"vue-i18n": "^9.14.4",
"vue-router": "^4.5.1",
"vue-virtual-scroller": "2.0.0-beta.8"
},
@@ -57,8 +56,6 @@
"tslib": "^2.8.1",
"typescript": "^5.8.3",
"unocss": "^66.4.0",
"unplugin-auto-import": "^0.16.7",
"unplugin-vue-components": "^0.25.2",
"unplugin-vue-macros": "^2.14.5",
"vite": "^7.0.3",
"vite-plugin-cdn-import": "^1.0.1",

241
pnpm-lock.yaml generated
View File

@@ -44,9 +44,6 @@ importers:
localforage:
specifier: ^1.10.0
version: 1.10.0
lodash-es:
specifier: ^4.17.21
version: 4.17.21
mitt:
specifier: ^3.0.1
version: 3.0.1
@@ -65,15 +62,15 @@ importers:
tesseract.js:
specifier: ^4.1.4
version: 4.1.4
unplugin-element-plus:
specifier: ^0.10.0
version: 0.10.0
vue:
specifier: ^3.5.17
version: 3.5.17(typescript@5.8.3)
vue-activity-calendar:
specifier: ^1.2.2
version: 1.2.2
vue-i18n:
specifier: ^9.14.4
version: 9.14.4(vue@3.5.17(typescript@5.8.3))
vue-router:
specifier: ^4.5.1
version: 4.5.1(vue@3.5.17(typescript@5.8.3))
@@ -108,9 +105,6 @@ importers:
cz-conventional-changelog:
specifier: ^3.3.0
version: 3.3.0(@types/node@24.0.11)(typescript@5.8.3)
daisyui:
specifier: ^5.0.50
version: 5.0.50
esm:
specifier: ^3.2.25
version: 3.2.25
@@ -136,11 +130,11 @@ importers:
specifier: ^66.4.0
version: 66.4.0(postcss@8.5.6)(vite@7.0.3(@types/node@24.0.11)(jiti@2.4.2)(sass@1.89.2))
unplugin-auto-import:
specifier: ^0.16.7
version: 0.16.7(@vueuse/core@9.13.0(vue@3.5.17(typescript@5.8.3)))(rollup@4.44.2)
specifier: '^19.3.0 '
version: 19.3.0(@vueuse/core@9.13.0(vue@3.5.17(typescript@5.8.3)))
unplugin-vue-components:
specifier: ^0.25.2
version: 0.25.2(@babel/parser@7.28.0)(rollup@4.44.2)(vue@3.5.17(typescript@5.8.3))
specifier: ^28.8.0
version: 28.8.0(@babel/parser@7.28.0)(vue@3.5.17(typescript@5.8.3))
unplugin-vue-macros:
specifier: ^2.14.5
version: 2.14.5(@vueuse/core@9.13.0(vue@3.5.17(typescript@5.8.3)))(esbuild@0.25.6)(rollup@4.44.2)(typescript@5.8.3)(vite@7.0.3(@types/node@24.0.11)(jiti@2.4.2)(sass@1.89.2))(vue-tsc@3.0.1(typescript@5.8.3))(vue@3.5.17(typescript@5.8.3))
@@ -166,9 +160,6 @@ packages:
'@antfu/install-pkg@1.1.0':
resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==}
'@antfu/utils@0.7.10':
resolution: {integrity: sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==}
'@antfu/utils@8.1.1':
resolution: {integrity: sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ==}
@@ -509,18 +500,6 @@ packages:
'@imengyu/vue3-context-menu@1.5.1':
resolution: {integrity: sha512-Y3M/PVOj0Fz7lu3aviIu6NKFYjqMP1tZSffSiYy55JdAfcm/bD06dRT9RL5AccOqTSJdvcAImhiYvmBnPKtYEg==}
'@intlify/core-base@9.14.4':
resolution: {integrity: sha512-vtZCt7NqWhKEtHa3SD/322DlgP5uR9MqWxnE0y8Q0tjDs9H5Lxhss+b5wv8rmuXRoHKLESNgw9d+EN9ybBbj9g==}
engines: {node: '>= 16'}
'@intlify/message-compiler@9.14.4':
resolution: {integrity: sha512-vcyCLiVRN628U38c3PbahrhbbXrckrM9zpy0KZVlDk2Z0OnGwv8uQNNXP3twwGtfLsCf4gu3ci6FMIZnPaqZsw==}
engines: {node: '>= 16'}
'@intlify/shared@9.14.4':
resolution: {integrity: sha512-P9zv6i1WvMc9qDBWvIgKkymjY2ptIiQ065PjDv7z7fDqH3J/HBRBN5IoiR46r/ujRcU7hCuSIZWvCAFCyuOYZA==}
engines: {node: '>= 16'}
'@isaacs/balanced-match@4.0.1':
resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==}
engines: {node: 20 || >=22}
@@ -545,18 +524,6 @@ packages:
'@napi-rs/wasm-runtime@0.2.11':
resolution: {integrity: sha512-9DPkXtvHydrcOsopiYpUgPHpmj0HWZKMUnL2dZqpvC42lsratuBG06V5ipyno0fUek5VlFsNQ+AcFATSrJXgMA==}
'@nodelib/fs.scandir@2.1.5':
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
engines: {node: '>= 8'}
'@nodelib/fs.stat@2.0.5':
resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
engines: {node: '>= 8'}
'@nodelib/fs.walk@1.2.8':
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
engines: {node: '>= 8'}
'@opentranslate/baidu@1.4.2':
resolution: {integrity: sha512-j8V7P+OCzEIAa+Zh4P6tbgWizVuVfKJOXDvk6M865J4QOE1lUQIN/Hb5SdodePkFinwNbQK2mAb7qF9wO/nQbA==}
@@ -1708,9 +1675,6 @@ packages:
resolution: {integrity: sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==}
engines: {node: '>=0.12'}
daisyui@5.0.50:
resolution: {integrity: sha512-c1PweK5RI1C76q58FKvbS4jzgyNJSP6CGTQ+KkZYzADdJoERnOxFoeLfDHmQgxLpjEzlYhFMXCeodQNLCC9bow==}
dayjs@1.11.13:
resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==}
@@ -1864,6 +1828,9 @@ packages:
es-module-lexer@0.4.1:
resolution: {integrity: sha512-ooYciCUtfw6/d2w56UVeqHPcoCFAiJdz5XOkYpv/Txl1HMUozpXjz/2RIQgqwKdXNDPSF1W7mJCFse3G+HDyAA==}
es-module-lexer@1.7.0:
resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==}
es-object-atoms@1.1.1:
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
engines: {node: '>= 0.4'}
@@ -1963,19 +1930,12 @@ packages:
fast-deep-equal@3.1.3:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
fast-glob@3.3.3:
resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==}
engines: {node: '>=8.6.0'}
fast-levenshtein@1.1.4:
resolution: {integrity: sha512-Ia0sQNrMPXXkqVFt6w6M1n1oKo3NfKs+mvaV811Jwir7vAk9a6PVV9VPYf6X3BU97QiLEmuW3uXH9u87zDFfdw==}
fast-uri@3.0.6:
resolution: {integrity: sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==}
fastq@1.19.1:
resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==}
fdir@6.4.6:
resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==}
peerDependencies:
@@ -2545,14 +2505,6 @@ packages:
resolution: {integrity: sha512-cy7ZdNRXdablkXYNI049pthVeXFurRyb9+hA/dZzerZ0pGTx42z+y+ssxBaVV2l70t1muq5IdKhn4UtcoGUY9A==}
engines: {node: '>=0.10.0'}
local-pkg@0.4.3:
resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==}
engines: {node: '>=14'}
local-pkg@0.5.1:
resolution: {integrity: sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==}
engines: {node: '>=14'}
local-pkg@1.1.1:
resolution: {integrity: sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg==}
engines: {node: '>=14'}
@@ -2641,10 +2593,6 @@ packages:
memoize-one@6.0.0:
resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==}
merge2@1.4.1:
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
engines: {node: '>= 8'}
merge@2.1.1:
resolution: {integrity: sha512-jz+Cfrg9GWOZbQAnDQ4hlVnQky+341Yk5ru8bZSe6sIDTCIg8n9i/u7hSQGSVOF3C7lH6mGtqjkiT9G4wFLL0w==}
@@ -2985,9 +2933,6 @@ packages:
quansync@0.2.10:
resolution: {integrity: sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==}
queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
read-pkg-up@1.0.1:
resolution: {integrity: sha512-WD9MTlNtI55IwYUS27iHh9tK3YoIVhxis8yKhLpTqWtml739uXc9NWTpxoHkfZf3+DkCCsXox94/VWZniuZm6A==}
engines: {node: '>=0.10.0'}
@@ -3097,10 +3042,6 @@ packages:
resolution: {integrity: sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==}
engines: {node: '>=0.12'}
reusify@1.1.0:
resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
rfdc@1.4.1:
resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==}
@@ -3131,9 +3072,6 @@ packages:
resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==}
engines: {node: '>=0.12.0'}
run-parallel@1.2.0:
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
rxjs@7.8.2:
resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==}
@@ -3321,8 +3259,8 @@ packages:
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
engines: {node: '>=8'}
strip-literal@2.1.1:
resolution: {integrity: sha512-631UJ6O00eNGfMiWG78ck80dfBab8X6IVFB51jZK5Icd7XAs60Z5y7QdSd/wGIklnWvRbUNloVzhOKKmutxQ6Q==}
strip-literal@3.0.0:
resolution: {integrity: sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==}
structured-source@4.0.0:
resolution: {integrity: sha512-qGzRFNJDjFieQkl/sVOI2dUjHKRyL9dAJi2gCPGJLbJHBIkyOHxjuocpIEfbLioX+qSJpvbYdT49/YCdMznKxA==}
@@ -3462,8 +3400,9 @@ packages:
undici-types@7.8.0:
resolution: {integrity: sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==}
unimport@3.14.6:
resolution: {integrity: sha512-CYvbDaTT04Rh8bmD8jz3WPmHYZRG/NnvYVzwD6V1YAlvvKROlAeNDUBhkBGzNav2RKaeuXvlWYaa1V4Lfi/O0g==}
unimport@4.2.0:
resolution: {integrity: sha512-mYVtA0nmzrysnYnyb3ALMbByJ+Maosee2+WyE0puXl+Xm2bUwPorPaaeZt0ETfuroPOtG8jj1g/qeFZ6buFnag==}
engines: {node: '>=18.12.0'}
union-value@1.0.1:
resolution: {integrity: sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==}
@@ -3488,8 +3427,8 @@ packages:
vite:
optional: true
unplugin-auto-import@0.16.7:
resolution: {integrity: sha512-w7XmnRlchq6YUFJVFGSvG1T/6j8GrdYN6Em9Wf0Ye+HXgD/22kont+WnuCAA0UaUoxtuvRR1u/mXKy63g/hfqQ==}
unplugin-auto-import@19.3.0:
resolution: {integrity: sha512-iIi0u4Gq2uGkAOGqlPJOAMI8vocvjh1clGTfSK4SOrJKrt+tirrixo/FjgBwXQNNdS7ofcr7OxzmOb/RjWxeEQ==}
engines: {node: '>=14'}
peerDependencies:
'@nuxt/kit': ^3.2.2
@@ -3527,16 +3466,20 @@ packages:
webpack:
optional: true
unplugin-element-plus@0.10.0:
resolution: {integrity: sha512-oRSW0x6U58xBOWKy8TcoVZNA8ElIpfp3TUJRLQI6ey/E9PpjHl9/deeTAZNt8D57Li4OA4pCJtM6p2cb4Ff4ZA==}
engines: {node: '>=18.12.0'}
unplugin-utils@0.2.4:
resolution: {integrity: sha512-8U/MtpkPkkk3Atewj1+RcKIjb5WBimZ/WSLhhR3w6SsIj8XJuKTacSP8g+2JhfSGw0Cb125Y+2zA/IzJZDVbhA==}
engines: {node: '>=18.12.0'}
unplugin-vue-components@0.25.2:
resolution: {integrity: sha512-OVmLFqILH6w+eM8fyt/d/eoJT9A6WO51NZLf1vC5c1FZ4rmq2bbGxTy8WP2Jm7xwFdukaIdv819+UI7RClPyCA==}
unplugin-vue-components@28.8.0:
resolution: {integrity: sha512-2Q6ZongpoQzuXDK0ZsVzMoshH0MWZQ1pzVL538G7oIDKRTVzHjppBDS8aB99SADGHN3lpGU7frraCG6yWNoL5Q==}
engines: {node: '>=14'}
peerDependencies:
'@babel/parser': ^7.15.8
'@nuxt/kit': ^3.2.2
'@nuxt/kit': ^3.2.2 || ^4.0.0
vue: 2 || 3
peerDependenciesMeta:
'@babel/parser':
@@ -3558,6 +3501,10 @@ packages:
resolution: {integrity: sha512-4/u/j4FrCKdi17jaxuJA0jClGxB1AvU2hw/IuayPc4ay1XGaJs/rbb4v5WKwAjNifjmXK9PIFyuPiaK8azyR9w==}
engines: {node: '>=14.0.0'}
unplugin@2.3.5:
resolution: {integrity: sha512-RyWSb5AHmGtjjNQ6gIlA67sHOsWpsbWpwDokLwTcejVdOjEkJZh7QKu14J00gDDVSh8kGH4KYC/TNBceXFZhtw==}
engines: {node: '>=18.12.0'}
unset-value@1.0.0:
resolution: {integrity: sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==}
engines: {node: '>=0.10.0'}
@@ -3675,12 +3622,6 @@ packages:
vue-flow-layout@0.2.0:
resolution: {integrity: sha512-zKgsWWkXq0xrus7H4Mc+uFs1ESrmdTXlO0YNbR6wMdPaFvosL3fMB8N7uTV308UhGy9UvTrGhIY7mVz9eN+L0Q==}
vue-i18n@9.14.4:
resolution: {integrity: sha512-B934C8yUyWLT0EMud3DySrwSUJI7ZNiWYsEEz2gknTthqKiG4dzWE/WSa8AzCuSQzwBEv4HtG1jZDhgzPfWSKQ==}
engines: {node: '>= 16'}
peerDependencies:
vue: ^3.0.0
vue-observe-visibility@2.0.0-alpha.1:
resolution: {integrity: sha512-flFbp/gs9pZniXR6fans8smv1kDScJ8RS7rEpMjhVabiKeq7Qz3D9+eGsypncjfIyyU84saU88XZ0zjbD6Gq/g==}
peerDependencies:
@@ -3808,8 +3749,6 @@ snapshots:
package-manager-detector: 1.3.0
tinyexec: 1.0.1
'@antfu/utils@0.7.10': {}
'@antfu/utils@8.1.1': {}
'@babel/code-frame@7.27.1':
@@ -4158,18 +4097,6 @@ snapshots:
dependencies:
'@imengyu/vue-scroll-rect': 0.1.4
'@intlify/core-base@9.14.4':
dependencies:
'@intlify/message-compiler': 9.14.4
'@intlify/shared': 9.14.4
'@intlify/message-compiler@9.14.4':
dependencies:
'@intlify/shared': 9.14.4
source-map-js: 1.2.1
'@intlify/shared@9.14.4': {}
'@isaacs/balanced-match@4.0.1': {}
'@isaacs/brace-expansion@5.0.0':
@@ -4197,18 +4124,6 @@ snapshots:
'@tybys/wasm-util': 0.9.0
optional: true
'@nodelib/fs.scandir@2.1.5':
dependencies:
'@nodelib/fs.stat': 2.0.5
run-parallel: 1.2.0
'@nodelib/fs.stat@2.0.5': {}
'@nodelib/fs.walk@1.2.8':
dependencies:
'@nodelib/fs.scandir': 2.1.5
fastq: 1.19.1
'@opentranslate/baidu@1.4.2':
dependencies:
'@opentranslate/translator': 1.4.2
@@ -5526,8 +5441,6 @@ snapshots:
es5-ext: 0.10.64
type: 2.7.3
daisyui@5.0.50: {}
dayjs@1.11.13: {}
de-indent@1.0.2: {}
@@ -5666,6 +5579,8 @@ snapshots:
es-module-lexer@0.4.1: {}
es-module-lexer@1.7.0: {}
es-object-atoms@1.1.1:
dependencies:
es-errors: 1.3.0
@@ -5821,23 +5736,11 @@ snapshots:
fast-deep-equal@3.1.3:
optional: true
fast-glob@3.3.3:
dependencies:
'@nodelib/fs.stat': 2.0.5
'@nodelib/fs.walk': 1.2.8
glob-parent: 5.1.2
merge2: 1.4.1
micromatch: 4.0.8
fast-levenshtein@1.1.4: {}
fast-uri@3.0.6:
optional: true
fastq@1.19.1:
dependencies:
reusify: 1.1.0
fdir@6.4.6(picomatch@4.0.2):
optionalDependencies:
picomatch: 4.0.2
@@ -6436,13 +6339,6 @@ snapshots:
pinkie-promise: 2.0.1
strip-bom: 2.0.0
local-pkg@0.4.3: {}
local-pkg@0.5.1:
dependencies:
mlly: 1.7.4
pkg-types: 1.3.1
local-pkg@1.1.1:
dependencies:
mlly: 1.7.4
@@ -6533,8 +6429,6 @@ snapshots:
memoize-one@6.0.0: {}
merge2@1.4.1: {}
merge@2.1.1: {}
micromatch@3.1.10:
@@ -6894,8 +6788,6 @@ snapshots:
quansync@0.2.10: {}
queue-microtask@1.2.3: {}
read-pkg-up@1.0.1:
dependencies:
find-up: 1.1.2
@@ -7010,8 +6902,6 @@ snapshots:
ret@0.1.15: {}
reusify@1.1.0: {}
rfdc@1.4.1: {}
rollup-plugin-external-globals@0.10.0(rollup@4.44.2):
@@ -7059,10 +6949,6 @@ snapshots:
run-async@2.4.1: {}
run-parallel@1.2.0:
dependencies:
queue-microtask: 1.2.3
rxjs@7.8.2:
dependencies:
tslib: 2.8.1
@@ -7270,7 +7156,7 @@ snapshots:
strip-json-comments@3.1.1: {}
strip-literal@2.1.1:
strip-literal@3.0.0:
dependencies:
js-tokens: 9.0.1
@@ -7427,24 +7313,22 @@ snapshots:
undici-types@7.8.0:
optional: true
unimport@3.14.6(rollup@4.44.2):
unimport@4.2.0:
dependencies:
'@rollup/pluginutils': 5.2.0(rollup@4.44.2)
acorn: 8.15.0
escape-string-regexp: 5.0.0
estree-walker: 3.0.3
fast-glob: 3.3.3
local-pkg: 1.1.1
magic-string: 0.30.17
mlly: 1.7.4
pathe: 2.0.3
picomatch: 4.0.2
pkg-types: 1.3.1
pkg-types: 2.2.0
scule: 1.3.0
strip-literal: 2.1.1
unplugin: 1.16.1
transitivePeerDependencies:
- rollup
strip-literal: 3.0.0
tinyglobby: 0.2.14
unplugin: 2.3.5
unplugin-utils: 0.2.4
union-value@1.0.1:
dependencies:
@@ -7487,20 +7371,16 @@ snapshots:
- postcss
- supports-color
unplugin-auto-import@0.16.7(@vueuse/core@9.13.0(vue@3.5.17(typescript@5.8.3)))(rollup@4.44.2):
unplugin-auto-import@19.3.0(@vueuse/core@9.13.0(vue@3.5.17(typescript@5.8.3))):
dependencies:
'@antfu/utils': 0.7.10
'@rollup/pluginutils': 5.2.0(rollup@4.44.2)
fast-glob: 3.3.3
local-pkg: 0.5.1
local-pkg: 1.1.1
magic-string: 0.30.17
minimatch: 9.0.5
unimport: 3.14.6(rollup@4.44.2)
unplugin: 1.16.1
picomatch: 4.0.2
unimport: 4.2.0
unplugin: 2.3.5
unplugin-utils: 0.2.4
optionalDependencies:
'@vueuse/core': 9.13.0(vue@3.5.17(typescript@5.8.3))
transitivePeerDependencies:
- rollup
unplugin-combine@1.2.1(esbuild@0.25.6)(rollup@4.44.2)(unplugin@1.16.1)(vite@7.0.3(@types/node@24.0.11)(jiti@2.4.2)(sass@1.89.2)):
optionalDependencies:
@@ -7509,28 +7389,32 @@ snapshots:
unplugin: 1.16.1
vite: 7.0.3(@types/node@24.0.11)(jiti@2.4.2)(sass@1.89.2)
unplugin-element-plus@0.10.0:
dependencies:
es-module-lexer: 1.7.0
magic-string: 0.30.17
unplugin: 2.3.5
unplugin-utils: 0.2.4
unplugin-utils@0.2.4:
dependencies:
pathe: 2.0.3
picomatch: 4.0.2
unplugin-vue-components@0.25.2(@babel/parser@7.28.0)(rollup@4.44.2)(vue@3.5.17(typescript@5.8.3)):
unplugin-vue-components@28.8.0(@babel/parser@7.28.0)(vue@3.5.17(typescript@5.8.3)):
dependencies:
'@antfu/utils': 0.7.10
'@rollup/pluginutils': 5.2.0(rollup@4.44.2)
chokidar: 3.6.0
debug: 4.4.1
fast-glob: 3.3.3
local-pkg: 0.4.3
local-pkg: 1.1.1
magic-string: 0.30.17
minimatch: 9.0.5
resolve: 1.22.10
unplugin: 1.16.1
mlly: 1.7.4
tinyglobby: 0.2.14
unplugin: 2.3.5
unplugin-utils: 0.2.4
vue: 3.5.17(typescript@5.8.3)
optionalDependencies:
'@babel/parser': 7.28.0
transitivePeerDependencies:
- rollup
- supports-color
unplugin-vue-define-options@1.5.5(vue@3.5.17(typescript@5.8.3)):
@@ -7592,6 +7476,12 @@ snapshots:
acorn: 8.15.0
webpack-virtual-modules: 0.6.2
unplugin@2.3.5:
dependencies:
acorn: 8.15.0
picomatch: 4.0.2
webpack-virtual-modules: 0.6.2
unset-value@1.0.0:
dependencies:
has-value: 0.3.1
@@ -7701,13 +7591,6 @@ snapshots:
vue-flow-layout@0.2.0: {}
vue-i18n@9.14.4(vue@3.5.17(typescript@5.8.3)):
dependencies:
'@intlify/core-base': 9.14.4
'@intlify/shared': 9.14.4
'@vue/devtools-api': 6.6.4
vue: 3.5.17(typescript@5.8.3)
vue-observe-visibility@2.0.0-alpha.1(vue@3.5.17(typescript@5.8.3)):
dependencies:
vue: 3.5.17(typescript@5.8.3)

View File

@@ -1,11 +1,6 @@
//@import '/node_modules/element-plus/dist/index.css';
@use "anim" as *;
@use 'element-plus/theme-chalk/dark/css-vars' as *;
:root {
//修改element-ui的进度条底色
--el-border-color-lighter: #d1d1d1 !important;
--color-item-bg: rgb(228, 230, 232);
--color-item-hover: white;
//--color-item-active: rgb(75, 110, 175);
@@ -23,12 +18,12 @@
--practice-wrapper-translateX: 1px;
--article-width: 50vw;
--toolbar-width: 50rem;
--toolbar-height: 3.2rem;
--panel-width: 24rem;
--space: 1rem;
--stat-gap: 2rem;
--shadow: rgba(0, 0, 0, 0.08) 0px 4px 12px;
--panel-margin-left: calc(50% + var(--toolbar-width) / 2 + 1rem);
--article-panel-margin-left: calc(50% + var(--article-width) / 2 + 3.5rem);
--article-panel-margin-left: calc(50% + var(--article-width) / 2 + 1rem);
--anim-time: 0.3s;
--color-input-bg: white;
@@ -46,7 +41,7 @@
--zh-article-family: "Songti SC", "SimSun", "Noto Serif CJK SC", serif;
--btn-primary: rgb(75, 85, 99);
--btn-info: #909399;
--btn-info: white;
--color-primary: #E6E8EB;
--color-second: rgb(247, 247, 247);
@@ -54,7 +49,7 @@
--color-card-active: #FED7AA;
--color-list-item-active: rgb(253, 246, 236);
--color-icon-hightlight: #E6A23C;
--color-icon-hightlight: rgb(12, 140, 233);
//--color-icon-hightlight: rgb(12, 140, 233);
--color-sub-text: gray;
--color-main-text: rgb(91, 91, 91);
@@ -62,15 +57,26 @@
--color-select-text: white;
--color-notice-bg: rgb(247, 247, 247);
//修改element-ui的进度条底色
--el-border-color-lighter: #e2e5ed !important;
}
.footer {
&.hide {
--el-border-color-lighter: #dbdbdb !important;
}
}
html.dark {
--color-primary: #0E1217;
--color-second: rgb(30, 31, 34);
--color-third: rgb(43, 45, 48);
--color-card-active: rgb(84, 84, 84);
--color-list-item-active: rgb(84, 84, 84);
--color-icon-hightlight: #E6A23C;
--color-icon-hightlight: rgb(147, 173, 227);
--color-sub-text: #b8b8b8;
--color-main-text: rgba(249, 250, 251, 0.8);
--color-select-bg: rgb(147, 173, 227);
@@ -88,65 +94,51 @@ html.dark {
--color-font-3: rgba(255, 255, 255, 0.3);
--color-sub-gray: #383737;
--color-scrollbar: rgb(92, 93, 94);
--btn-info: transparent;
--color-input-bg: rgba(14, 18, 23, 1);
--color-input-icon: #383737;
--color-textarea-bg: rgb(43, 45, 48);
--color-article: white;
--el-border-color-lighter: var(--color-third) !important;
.footer {
&.hide {
--el-border-color-lighter: var(--color-third) !important;
}
}
}
@media (max-width: 1680px) {
:root {
--practice-wrapper-translateX: -12vw;
--toolbar-width: 40vw;
--article-width: 60vw;
--panel-width: 38rem;
--toolbar-height: 4.8rem;
--panel-margin-left: calc(50vw + var(--practice-wrapper-translateX) + var(--toolbar-width) / 2 + 5vw);
--article-panel-margin-left: calc(50% + var(--practice-wrapper-translateX) + var(--article-width) / 2 + 48rem);
--toolbar-width: 50vw;
--article-width: 50vw;
--panel-width: 20vw;
--space: 0.5rem;
}
.footer {
.bottom {
padding: 1.5rem 1rem 1rem 1rem !important;
}
.stat {
margin-top: 0.4rem !important;
.row {
gap: 0.5rem !important;
}
padding: .5rem !important;
}
}
}
@media (max-width: 1366px) {
:root {
--space: 1rem;
--practice-wrapper-translateX: -22vw;
--article-width: 53vw;
--panel-width: 30vw;
--panel-width: 20vw;
--article-width: 50vw;
--toolbar-width: 50vw;
--toolbar-height: 40rem;
--panel-margin-left: calc(50vw + var(--practice-wrapper-translateX) + var(--toolbar-width) / 2 + 14vw);
--article-panel-margin-left: calc(50% + var(--practice-wrapper-translateX) + var(--article-width) / 2 + 12vw);
--stat-gap: 0.5rem;
--space: 0.3rem;
}
.footer {
.bottom {
padding: 1.5rem 0.5rem 0.5rem 0.5rem !important;
}
.stat {
margin-top: 0.4rem !important;
.row {
gap: 0.5rem !important;
}
padding: .5rem !important;
}
}
}
@@ -397,7 +389,6 @@ footer {
}
}
.item-title {
display: flex;
align-items: center;

View File

@@ -30,7 +30,6 @@ defineEmits(['click'])
size,
type,
(disabled||loading) && 'disabled',
!disabled && 'hvr-grow'
]">
<span :style="{opacity:loading?0:1}"><slot></slot></span>
<Icon v-if="loading"
@@ -51,19 +50,25 @@ defineEmits(['click'])
.base-button {
cursor: pointer;
border-radius: .4rem;
padding: 0 1rem;
display: flex;
box-sizing: border-box;
display: inline-flex;
align-items: center;
justify-content: center;
transition: all .3s;
//background: #999;
//background: rgb(60, 63, 65);
//background: var(--color-second);
height: 2.5rem;
line-height: 1;
position: relative;
word-break: keep-all;
outline: none;
text-align: center;
transition: .1s;
user-select: none;
vertical-align: middle;
white-space: nowrap;
border-radius: .3rem;
padding: 0 0.9rem;
font-size: .9rem;
height: 2rem;
color: white;
& + .base-button {
margin-left: var(--space);
}
.loading {
position: absolute;
@@ -76,27 +81,21 @@ defineEmits(['click'])
}
&.small {
height: 2.4rem;
& > span {
font-size: .8rem;
}
border-radius: 0.2rem;
padding: 0 0.8rem;
height: 1.6rem;
font-size: .8rem;
}
&.large {
height: 3rem;
font-size: 1.1rem;
padding: 0 1.4rem;
& > span {
font-size: 1.1rem;
}
padding: 0 1.3rem;
height: 2.4rem;
font-size: 0.9rem;
}
& > span {
font-size: 1rem;
color: white;
line-height: 1;
transform: translateY(-5%);
:deep(a) {
color: white;
@@ -104,10 +103,9 @@ defineEmits(['click'])
}
&:hover {
opacity: .7;
opacity: .8;
}
&.primary {
background: var(--btn-primary);
}
@@ -117,12 +115,14 @@ defineEmits(['click'])
border-bottom: 2px solid transparent;
&:hover {
border-bottom: 2px solid var(--color-font-1);
border-bottom: 2px solid var(--color-font-2);
}
}
&.info {
background: var(--btn-info);
border: 1px solid var(--color-main-text);
color: var(--color-main-text);
}
&.active {

View File

@@ -43,7 +43,7 @@ $w: 1.4rem;
transition: all .3s;
&:hover:not(.disabled) {
background: var(--color-primary);
background: var(--color-icon-hightlight);
color: white;
}

2
src/global.d.ts vendored
View File

@@ -1,4 +1,4 @@
import {cloneDeep} from "lodash-es"
import {cloneDeep} from "@/utils"
export {}

View File

@@ -1,5 +1,5 @@
import {Article, ArticleWord, DictType, getDefaultArticleWord, Sentence} from "@/types.ts";
import {cloneDeep} from "lodash-es";
import {cloneDeep} from "@/utils";
import nlp from "compromise/one";
import {usePlayWordAudio} from "@/hooks/sound.ts";
import {getSentenceAllText, getSentenceAllTranslateText} from "@/hooks/translate.ts";

View File

@@ -1,7 +1,5 @@
import {Article, Dict, Word} from "@/types.ts";
import {Article, Word} from "@/types.ts";
import {useBaseStore} from "@/stores/base.ts";
import {cloneDeep,} from "lodash-es";
import {isArticle} from "@/hooks/article.ts";
export function useWordOptions() {

View File

@@ -1,6 +1,5 @@
import {Article, Sentence, TranslateEngine} from "@/types.ts";
import Baidu from "@opentranslate/baidu";
import {axiosInstance} from "@/utils/http.ts";
import {Translator} from "@opentranslate/translator/src/translator.ts";
export function getSentenceAllTranslateText(article: Article) {
@@ -27,7 +26,6 @@ export async function getNetworkTranslate(
let translator: Translator
if (translateEngine === TranslateEngine.Baidu) {
translator = new Baidu({
axios: axiosInstance as any,
config: {
appid: "20230910001811857",
key: "Xxe_yftQR3K3Ue43NQMC"

View File

@@ -3,28 +3,17 @@ import './assets/css/style.scss'
import 'virtual:uno.css';
import App from './App.vue'
import {createPinia} from "pinia"
import ZH from "@/locales/zh-CN.ts";
import {createI18n} from 'vue-i18n'
import router from "@/router.ts";
import VueVirtualScroller from 'vue-virtual-scroller'
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
import './global.d.ts'
const i18n = createI18n({
locale: 'zh-CN',
fallbackLocale: 'zh-CN',
messages: {
'zh-CN': ZH
},
})
const pinia = createPinia()
const app = createApp(App)
app.use(VueVirtualScroller)
// app.use(ElementPlus)
app.use(pinia)
app.use(i18n)
app.use(router)
app.directive('opacity', (el, binding) => {

View File

@@ -4,7 +4,7 @@ import {ref, watch} from "vue";
import {useSettingStore} from "@/stores/setting.ts";
import {getAudioFileUrl, useChangeAllSound, usePlayAudio, useWatchAllSound} from "@/hooks/sound.ts";
import {getShortcutKey, useDisableEventListener, useEventListener} from "@/hooks/event.ts";
import {cloneDeep} from "lodash-es";
import {cloneDeep} from "@/utils";
import {DefaultShortcutKeyMap, ShortcutKey} from "@/types.ts";
import BaseButton from "@/components/BaseButton.vue";
import {APP_NAME, EXPORT_DATA_KEY, SAVE_DICT_KEY, SAVE_SETTING_KEY, SoundFileOptions} from "@/utils/const.ts";
@@ -15,7 +15,7 @@ import {checkAndUpgradeSaveDict, checkAndUpgradeSaveSetting, shakeCommonDict} fr
import {GITHUB} from "@/config/ENV.ts";
import dayjs from "dayjs";
import BasePage from "@/pages/pc/components/BasePage.vue";
import {ElSwitch, ElSelect, ElOption, ElSlider, ElRadioGroup, ElRadio, ElInputNumber} from 'element-plus'
const emit = defineEmits<{
toggleDisabledDialogEscKey: [val: boolean]
@@ -162,11 +162,11 @@ function importData(e) {
<div class="row">
<label class="main-title">所有音效</label>
<div class="wrapper">
<el-switch v-model="settingStore.allSound"
@change="useChangeAllSound"
inline-prompt
active-text=""
inactive-text=""
<ElSwitch v-model="settingStore.allSound"
@change="useChangeAllSound"
inline-prompt
active-text=""
inactive-text=""
/>
</div>
</div>
@@ -174,35 +174,35 @@ function importData(e) {
<div class="row">
<label class="item-title">单词/句子自动发音</label>
<div class="wrapper">
<el-switch v-model="settingStore.wordSound"
inline-prompt
active-text=""
inactive-text=""
<ElSwitch v-model="settingStore.wordSound"
inline-prompt
active-text=""
inactive-text=""
/>
</div>
</div>
<div class="row">
<label class="sub-title">单词/句子发音口音</label>
<div class="wrapper">
<el-select v-model="settingStore.wordSoundType"
placeholder="请选择"
<ElSelect v-model="settingStore.wordSoundType"
placeholder="请选择"
>
<el-option label="美音" value="us"/>
<el-option label="英音" value="uk"/>
</el-select>
<ElOption label="美音" value="us"/>
<ElOption label="英音" value="uk"/>
</ElSelect>
</div>
</div>
<div class="row">
<label class="sub-title">音量</label>
<div class="wrapper">
<el-slider v-model="settingStore.wordSoundVolume"/>
<ElSlider v-model="settingStore.wordSoundVolume"/>
<span>{{ settingStore.wordSoundVolume }}%</span>
</div>
</div>
<div class="row">
<label class="sub-title">倍速</label>
<div class="wrapper">
<el-slider v-model="settingStore.wordSoundSpeed" :step="0.1" :min="0.5" :max="3"/>
<ElSlider v-model="settingStore.wordSoundSpeed" :step="0.1" :min="0.5" :max="3"/>
<span>{{ settingStore.wordSoundSpeed }}</span>
</div>
</div>
@@ -210,20 +210,20 @@ function importData(e) {
<div class="row">
<label class="item-title">按键音</label>
<div class="wrapper">
<el-switch v-model="settingStore.keyboardSound"
inline-prompt
active-text=""
inactive-text=""
<ElSwitch v-model="settingStore.keyboardSound"
inline-prompt
active-text=""
inactive-text=""
/>
</div>
</div>
<div class="row">
<label class="item-title">按键音效</label>
<div class="wrapper">
<el-select v-model="settingStore.keyboardSoundFile"
placeholder="请选择"
<ElSelect v-model="settingStore.keyboardSoundFile"
placeholder="请选择"
>
<el-option
<ElOption
v-for="item in SoundFileOptions"
:key="item.value"
:label="item.label"
@@ -235,14 +235,14 @@ function importData(e) {
:time="100"
@click="usePlayAudio(getAudioFileUrl(item.value)[0])"/>
</div>
</el-option>
</el-select>
</ElOption>
</ElSelect>
</div>
</div>
<div class="row">
<label class="sub-title">音量</label>
<div class="wrapper">
<el-slider v-model="settingStore.keyboardSoundVolume"/>
<ElSlider v-model="settingStore.keyboardSoundVolume"/>
<span>{{ settingStore.keyboardSoundVolume }}%</span>
</div>
</div>
@@ -250,17 +250,17 @@ function importData(e) {
<div class="row">
<label class="item-title">效果音输入错误完成时的音效</label>
<div class="wrapper">
<el-switch v-model="settingStore.effectSound"
inline-prompt
active-text=""
inactive-text=""
<ElSwitch v-model="settingStore.effectSound"
inline-prompt
active-text=""
inactive-text=""
/>
</div>
</div>
<div class="row">
<label class="sub-title">音量</label>
<div class="wrapper">
<el-slider v-model="settingStore.effectSoundVolume"/>
<ElSlider v-model="settingStore.effectSoundVolume"/>
<span>{{ settingStore.effectSoundVolume }}%</span>
</div>
</div>
@@ -269,19 +269,19 @@ function importData(e) {
<div class="row">
<label class="item-title">单词循环设置</label>
<div class="wrapper">
<el-radio-group v-model="settingStore.repeatCount">
<el-radio :value="1" size="default">1</el-radio>
<el-radio :value="2" size="default">2</el-radio>
<el-radio :value="3" size="default">3</el-radio>
<el-radio :value="5" size="default">5</el-radio>
<el-radio :value="100" size="default">自定义</el-radio>
</el-radio-group>
<ElRadioGroup v-model="settingStore.repeatCount">
<ElRadio :value="1" size="default">1</ElRadio>
<ElRadio :value="2" size="default">2</ElRadio>
<ElRadio :value="3" size="default">3</ElRadio>
<ElRadio :value="5" size="default">5</ElRadio>
<ElRadio :value="100" size="default">自定义</ElRadio>
</ElRadioGroup>
<div class="mini-row" v-if="settingStore.repeatCount === 100">
<label class="item-title">循环次数</label>
<el-input-number v-model="settingStore.repeatCustomCount"
:min="6"
:max="15"
type="number"
<ElInputNumber v-model="settingStore.repeatCustomCount"
:min="6"
:max="15"
type="number"
/>
</div>
</div>
@@ -289,10 +289,10 @@ function importData(e) {
<div class="row">
<label class="item-title">显示上一个/下一个单词</label>
<div class="wrapper">
<el-switch v-model="settingStore.showNearWord"
inline-prompt
active-text=""
inactive-text=""
<ElSwitch v-model="settingStore.showNearWord"
inline-prompt
active-text=""
inactive-text=""
/>
</div>
</div>
@@ -303,10 +303,10 @@ function importData(e) {
<div class="row">
<label class="item-title">忽略大小写</label>
<div class="wrapper">
<el-switch v-model="settingStore.ignoreCase"
inline-prompt
active-text=""
inactive-text=""
<ElSwitch v-model="settingStore.ignoreCase"
inline-prompt
active-text=""
inactive-text=""
/>
</div>
</div>
@@ -317,10 +317,10 @@ function importData(e) {
<div class="row">
<label class="item-title">允许默写模式下显示提示</label>
<div class="wrapper">
<el-switch v-model="settingStore.allowWordTip"
inline-prompt
active-text=""
inactive-text=""
<ElSwitch v-model="settingStore.allowWordTip"
inline-prompt
active-text=""
inactive-text=""
/>
</div>
</div>
@@ -334,7 +334,7 @@ function importData(e) {
<div class="row">
<label class="sub-title">外语字体</label>
<div class="wrapper">
<el-slider
<ElSlider
:min="10"
:max="100"
v-model="settingStore.fontSize.wordForeignFontSize"/>
@@ -344,7 +344,7 @@ function importData(e) {
<div class="row">
<label class="sub-title">中文字体</label>
<div class="wrapper">
<el-slider
<ElSlider
:min="10"
:max="100"
v-model="settingStore.fontSize.wordTranslateFontSize"/>
@@ -359,10 +359,10 @@ function importData(e) {
<div class="row">
<label class="sub-title">切换下一个单词时间</label>
<div class="wrapper">
<el-input-number v-model="settingStore.waitTimeForChangeWord"
:min="6"
:max="100"
type="number"
<ElInputNumber v-model="settingStore.waitTimeForChangeWord"
:min="6"
:max="100"
type="number"
/>
<span>毫秒</span>
</div>

View File

@@ -7,7 +7,7 @@ import {enArticle} from "@/assets/dictionary.ts";
import BasePage from "@/pages/pc/components/BasePage.vue";
import {useNav} from "@/utils";
import {Dict, DictResource, getDefaultDict} from "@/types.ts";
import {cloneDeep} from "lodash-es";
import {cloneDeep} from "@/utils";
import {useRuntimeStore} from "@/stores/runtime.ts";
import {getArticleBookDataByUrl} from "@/utils/article.ts";
import BaseIcon from "@/components/BaseIcon.vue";
@@ -15,6 +15,7 @@ import Dialog from "@/pages/pc/components/dialog/Dialog.vue";
import Input from "@/pages/pc/components/Input.vue";
import {computed} from "vue";
import Book from "@/pages/pc/components/Book.vue";
import {ElProgress} from 'element-plus';
const {nav} = useNav()
const base = useBaseStore()
@@ -71,7 +72,7 @@ function startStudy() {
<BasePage>
<div class="card ">
<div class="flex justify-between items-center">
<div class="bg-slate-200 p-3 gap-4 rounded-md cursor-pointer flex items-center">
<div class="bg-third p-3 gap-4 rounded-md cursor-pointer flex items-center">
<span class="text-lg font-bold"
@click="getBookDetail2(base.currentBook)">{{
base.currentBook.name || '请选择书籍开始学习'
@@ -86,7 +87,7 @@ function startStudy() {
</div>
</div>
<div class="mt-5 text-sm">已学习{{ base.currentBook.lastLearnIndex }}篇文章</div>
<el-progress class="mt-1" :percentage="base.currentBookProgress" :show-text="false"></el-progress>
<ElProgress class="mt-1" :percentage="base.currentBookProgress" :show-text="false"></ElProgress>
</div>
<div class="card flex flex-col">

View File

@@ -2,7 +2,7 @@
import {onMounted, onUnmounted} from "vue";
import {Article, getDefaultArticle} from "@/types.ts";
import BaseButton from "@/components/BaseButton.vue";
import {cloneDeep} from "lodash-es";
import {cloneDeep} from "@/utils";
import {useBaseStore} from "@/stores/base.ts";
import List from "@/pages/pc/components/list/List.vue";

View File

@@ -11,7 +11,6 @@ import BaseButton from "@/components/BaseButton.vue";
import {useRoute, useRouter} from "vue-router";
import EditBook from "@/pages/pc/article/components/EditBook.vue";
import {computed, onMounted} from "vue";
import {cloneDeep} from "lodash-es";
const runtimeStore = useRuntimeStore()
const base = useBaseStore()
@@ -73,7 +72,7 @@ function formClose() {
<div class="flex justify-between items-center relative">
<BackIcon class="z-2" @click="$router.back"/>
<div class="absolute text-2xl text-align-center w-full">{{ runtimeStore.editDict.name }}</div>
<div class="flex gap-2">
<div class="flex">
<BaseButton type="info" @click="isEdit = true">编辑</BaseButton>
<BaseButton type="info" @click="router.push('batch-edit-article')">文章管理</BaseButton>
<BaseButton @click="addMyStudyList">学习</BaseButton>

View File

@@ -6,11 +6,10 @@ import EditAbleText from "@/pages/pc/components/EditAbleText.vue";
import {Icon} from "@iconify/vue";
import {getNetworkTranslate, getSentenceAllText, getSentenceAllTranslateText} from "@/hooks/translate.ts";
import {genArticleSectionData, splitCNArticle2, splitEnArticle2, usePlaySentenceAudio} from "@/hooks/article.ts";
import {cloneDeep, last} from "lodash-es";
import {_nextTick, _parseLRC, cloneDeep, last} from "@/utils";
import {watch} from "vue";
import Empty from "@/components/Empty.vue";
import {UploadProps} from "element-plus";
import {_nextTick, _parseLRC} from "@/utils";
import {ElInputNumber, ElOption, ElPopover, ElSelect, ElUpload, UploadProps} from "element-plus";
import * as Comparison from "string-comparison"
import BaseIcon from "@/components/BaseIcon.vue";
import Dialog from "@/pages/pc/components/dialog/Dialog.vue";
@@ -286,13 +285,13 @@ function setStartTime(val: Sentence, i: number, j: number) {
>
</textarea>
<div class="justify-end items-center flex">
<el-popover
<ElPopover
class="box-item"
title="使用方法"
placement="top"
:width="400"
>
<ol class="py-0 pl-5 my-0 text-base color-black/60">
<ol class="py-0 pl-5 my-0 text-base color-main">
<li>复制原文然后分句</li>
<li>点击 <span class="color-red font-bold">分句</span> 按钮进行自动分句<span
class="color-red font-bold"> </span> 手动编辑分句
@@ -304,9 +303,9 @@ function setStartTime(val: Sentence, i: number, j: number) {
<template #reference>
<Icon icon="ri:question-line" class="mr-3" width="20"/>
</template>
</el-popover>
<el-button type="primary" @click="splitText">分句</el-button>
<el-button type="primary" @click="() => apply()">应用</el-button>
</ElPopover>
<BaseButton @click="splitText">分句</BaseButton>
<BaseButton @click="apply()">应用</BaseButton>
</div>
</div>
<div class="row flex flex-col gap-2">
@@ -334,27 +333,23 @@ function setStartTime(val: Sentence, i: number, j: number) {
>
</textarea>
<div class="justify-between items-center flex">
<div class="flex gap-2 items-center ">
<el-button
type="primary"
@click="startNetworkTranslate"
:loading="progress!==0 && progress !== 100"
>翻译
</el-button>
<el-select v-model="networkTranslateEngine"
class="w-20"
<div class="flex gap-space items-center w-50 ">
<BaseButton @click="startNetworkTranslate"
:loading="progress!==0 && progress !== 100">翻译
</BaseButton>
<ElSelect v-model="networkTranslateEngine"
>
<el-option
<ElOption
v-for="item in TranslateEngineOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</ElSelect>
{{ progress }}%
</div>
<div class="flex items-center">
<el-popover
<ElPopover
class="box-item"
title="使用方法"
placement="top"
@@ -372,9 +367,9 @@ function setStartTime(val: Sentence, i: number, j: number) {
<template #reference>
<Icon icon="ri:question-line" class="mr-3" width="20"/>
</template>
</el-popover>
<el-button type="primary" @click="splitTranslateText">分句</el-button>
<el-button type="primary" @click="apply(true)">应用</el-button>
</ElPopover>
<BaseButton @click="splitTranslateText">分句</BaseButton>
<BaseButton @click="apply(true)">应用</BaseButton>
</div>
</div>
</div>
@@ -383,14 +378,14 @@ function setStartTime(val: Sentence, i: number, j: number) {
<div class="center">正文译文与结果均可编辑编辑后点击应用按钮会自动同步</div>
<div class="flex gap-2">
<BaseButton>添加音频</BaseButton>
<el-upload
<ElUpload
class="upload-demo"
:limit="1"
:on-change="handleChange"
:auto-upload="false"
>
<el-button type="primary">添加音频LRC文件</el-button>
</el-upload>
<BaseButton>添加音频LRC文件</BaseButton>
</ElUpload>
<audio ref="audioRef" :src="editArticle.audioSrc" controls></audio>
</div>
<template v-if="editArticle.sections.length">
@@ -498,11 +493,11 @@ function setStartTime(val: Sentence, i: number, j: number) {
<div>开始时间</div>
<div class="flex justify-between flex-1">
<div class="flex items-center gap-2">
<el-input-number v-model="editSentence.audioPosition[0]" :precision="2" :step="0.1">
<ElInputNumber v-model="editSentence.audioPosition[0]" :precision="2" :step="0.1">
<template #suffix>
<span>s</span>
</template>
</el-input-number>
</ElInputNumber>
<BaseIcon
@click="jumpAudio(editSentence.audioPosition[0])"
title="跳转"
@@ -521,11 +516,11 @@ function setStartTime(val: Sentence, i: number, j: number) {
<div>结束时间</div>
<div class="flex justify-between flex-1">
<div class="flex items-center gap-2">
<el-input-number v-model="editSentence.audioPosition[1]" :precision="2" :step="0.1">
<ElInputNumber v-model="editSentence.audioPosition[1]" :precision="2" :step="0.1">
<template #suffix>
<span>s</span>
</template>
</el-input-number>
</ElInputNumber>
<span></span>
<BaseButton size="small" @click="editSentence.audioPosition[1] = -1">结束</BaseButton>
</div>

View File

@@ -2,7 +2,7 @@
import {onMounted, onUnmounted} from "vue";
import {Article, getDefaultArticle} from "@/types.ts";
import BaseButton from "@/components/BaseButton.vue";
import {cloneDeep} from "lodash-es";
import {cloneDeep} from "@/utils";
import {useBaseStore} from "@/stores/base.ts";
import List from "@/pages/pc/components/list/List.vue";

View File

@@ -1,12 +1,13 @@
<script setup lang="ts">
import {Dict, DictType, getDefaultDict} from "@/types.ts";
import {cloneDeep} from "lodash-es";
import {cloneDeep} from "@/utils";
import {FormInstance, FormRules} from "element-plus";
import {ElForm,ElFormItem,ElInput,ElSelect,ElOption, FormInstance, FormRules} from "element-plus";
import {onMounted, reactive} from "vue";
import {useRuntimeStore} from "@/stores/runtime.ts";
import {useBaseStore} from "@/stores/base.ts";
import BaseButton from "@/components/BaseButton.vue";
const props = defineProps<{
isAdd: boolean,
@@ -85,38 +86,38 @@ onMounted(() => {
<template>
<div class="w-120 mt-4">
<el-form
<ElForm
ref="dictFormRef"
:rules="dictRules"
:model="dictForm"
label-width="8rem">
<el-form-item label="名称" prop="name">
<el-input v-model="dictForm.name"/>
</el-form-item>
<el-form-item label="描述">
<el-input v-model="dictForm.description" type="textarea"/>
</el-form-item>
<el-form-item label="原文语言">
<el-select v-model="dictForm.language" placeholder="请选择选项">
<el-option label="英语" value="en"/>
<el-option label="德语" value="de"/>
<el-option label="日语" value="ja"/>
<el-option label="代码" value="code"/>
</el-select>
</el-form-item>
<el-form-item label="译文语言">
<el-select v-model="dictForm.translateLanguage" placeholder="请选择选项">
<el-option label="中文" value="zh-CN"/>
<el-option label="英语" value="en"/>
<el-option label="德语" value="de"/>
<el-option label="日语" value="ja"/>
</el-select>
</el-form-item>
<ElFormItem label="名称" prop="name">
<ElInput v-model="dictForm.name"/>
</ElFormItem>
<ElFormItem label="描述">
<ElInput v-model="dictForm.description" type="textarea"/>
</ElFormItem>
<ElFormItem label="原文语言">
<ElSelect v-model="dictForm.language" placeholder="请选择选项">
<ElOption label="英语" value="en"/>
<ElOption label="德语" value="de"/>
<ElOption label="日语" value="ja"/>
<ElOption label="代码" value="code"/>
</ElSelect>
</ElFormItem>
<ElFormItem label="译文语言">
<ElSelect v-model="dictForm.translateLanguage" placeholder="请选择选项">
<ElOption label="中文" value="zh-CN"/>
<ElOption label="英语" value="en"/>
<ElOption label="德语" value="de"/>
<ElOption label="日语" value="ja"/>
</ElSelect>
</ElFormItem>
<div class="center">
<el-button @click="emit('close')">关闭</el-button>
<el-button type="primary" @click="onSubmit">确定</el-button>
<base-button type="info" @click="emit('close')">关闭</base-button>
<base-button type="primary" @click="onSubmit">确定</base-button>
</div>
</el-form>
</ElForm>
</div>
</template>

View File

@@ -33,8 +33,8 @@
</template>
<script setup>
import {ref, computed, watch, onMounted, nextTick} from 'vue'
import {shuffle} from "lodash-es";
import {computed, nextTick, onMounted, ref, watch} from 'vue'
import {shuffle} from "@/utils";
const props = defineProps({
stem: String,

View File

@@ -1,7 +1,7 @@
<script setup lang="ts">
import TypingArticle from "./TypingArticle.vue";
import {Article, ArticleItem, ArticleWord, DisplayStatistics, getDefaultArticle, ShortcutKey, Word} from "@/types.ts";
import {cloneDeep} from "lodash-es";
import {cloneDeep} from "@/utils";
import Panel from "../../components/Panel.vue";
import {onMounted, onUnmounted} from "vue";
import {useBaseStore} from "@/stores/base.ts";
@@ -19,6 +19,7 @@ import ArticleList from "@/pages/pc/components/list/ArticleList.vue";
import {useOnKeyboardEventListener} from "@/hooks/event.ts";
import TranslateSetting from "@/pages/pc/components/toolbar/TranslateSetting.vue";
import {genArticleSectionData, usePlaySentenceAudio} from "@/hooks/article.ts";
import {ElProgress} from 'element-plus';
const store = useBaseStore()
const statisticsStore = usePracticeStore()
@@ -286,56 +287,52 @@ const {playSentenceAudio} = usePlaySentenceAudio()
:article="articleData.article"
/>
<Teleport to="body">
<div class="panel-wrapper">
<Panel v-if="tabIndex === 0">
<template v-slot="{active}">
<div class="panel-page-item">
<div class="list-header">
<div class="left">
<BaseIcon title="切换词典"
icon="carbon:change-catalog"/>
<div class="title">
{{ store.currentBook.name }}
</div>
<Tooltip
:title="`下一章(${settingStore.shortcutKeyMap[ShortcutKey.NextChapter]})`"
v-if="store.currentBook.lastLearnIndex < articleData.articles.length - 1">
<IconWrapper>
<Icon @click="emitter.emit(EventKey.continueStudy)" icon="octicon:arrow-right-24"/>
</IconWrapper>
</Tooltip>
</div>
<div class="right">
{{ articleData.articles.length }}篇文章
<div class="panel-wrapper">
<Panel>
<template v-slot="{active}">
<div class="panel-page-item pl-4">
<div class="list-header">
<div class="left">
<div class="title">
{{ store.currentBook.name }}
</div>
<Tooltip
:title="`下一篇(${settingStore.shortcutKeyMap[ShortcutKey.NextChapter]})`"
v-if="store.currentBook.lastLearnIndex < articleData.articles.length - 1">
<IconWrapper>
<Icon @click="emitter.emit(EventKey.continueStudy)" icon="octicon:arrow-right-24"/>
</IconWrapper>
</Tooltip>
</div>
<div class="right">
{{ articleData.articles.length }}篇文章
</div>
<ArticleList
:isActive="active"
:static="false"
:show-translate="settingStore.translate"
@click="handleChangeChapterIndex"
:active-id="articleData.article.id"
:list="articleData.articles ">
<template v-slot:suffix="{item,index}">
<BaseIcon
v-if="!isArticleCollect(item)"
class="collect"
@click="toggleArticleCollect(item)"
title="收藏" icon="ph:star"/>
<BaseIcon
v-else
class="fill"
@click="toggleArticleCollect(item)"
title="取消收藏" icon="ph:star-fill"/>
</template>
</ArticleList>
</div>
</template>
</Panel>
</div>
</Teleport>
<ArticleList
:isActive="active"
:static="false"
:show-translate="settingStore.translate"
@click="handleChangeChapterIndex"
:active-id="articleData.article.id"
:list="articleData.articles ">
<template v-slot:suffix="{item,index}">
<BaseIcon
v-if="!isArticleCollect(item)"
class="collect"
@click="toggleArticleCollect(item)"
title="收藏" icon="ph:star"/>
<BaseIcon
v-else
class="fill"
@click="toggleArticleCollect(item)"
title="取消收藏" icon="ph:star-fill"/>
</template>
</ArticleList>
</div>
</template>
</Panel>
</div>
<EditSingleArticleModal
v-model="showEditArticle"
@@ -345,34 +342,32 @@ const {playSentenceAudio} = usePlaySentenceAudio()
</div>
<div class="footer " :class="!settingStore.showToolbar && 'hide'">
<div class="bottom">
<div class="flex justify-between">
<div>
<el-progress
class="flex-1"
:percentage="progress"
:stroke-width="8"
:show-text="false"/>
<div class="stat">
<div class="row">
<div class="num">{{ speedMinute }}分钟</div>
<div class="line"></div>
<div class="name">时间</div>
</div>
<div class="row">
<div class="num">{{ statisticsStore.total }}</div>
<div class="line"></div>
<div class="name">单词总数</div>
</div>
<div class="row">
<div class="num">{{ format(statisticsStore.inputWordNumber, '', 0) }}</div>
<div class="line"></div>
<div class="name">输入数</div>
</div>
<div class="row">
<div class="num">{{ format(statisticsStore.wrong, '', 0) }}</div>
<div class="line"></div>
<div class="name">错误数</div>
</div>
<ElProgress
class="flex-1"
:percentage="progress"
:stroke-width="8"
:show-text="false"/>
<div class="flex justify-between items-center">
<div class="stat">
<div class="row">
<div class="num">{{ speedMinute }}分钟</div>
<div class="line"></div>
<div class="name">时间</div>
</div>
<div class="row">
<div class="num">{{ statisticsStore.total }}</div>
<div class="line"></div>
<div class="name">单词总数</div>
</div>
<div class="row">
<div class="num">{{ format(statisticsStore.inputWordNumber, '', 0) }}</div>
<div class="line"></div>
<div class="name">输入数</div>
</div>
<div class="row">
<div class="num">{{ format(statisticsStore.wrong, '', 0) }}</div>
<div class="line"></div>
<div class="name">错误数</div>
</div>
</div>
<div class="flex flex-col items-center justify-center gap-1">
@@ -417,7 +412,7 @@ const {playSentenceAudio} = usePlaySentenceAudio()
</div>
</div>
<div class="progress">
<el-progress :percentage="progress"
<ElProgress :percentage="progress"
:stroke-width="8"
:show-text="false"/>
</div>
@@ -471,12 +466,12 @@ const {playSentenceAudio} = usePlaySentenceAudio()
}
.panel-wrapper {
position: fixed;
left: 0;
top: .6rem;
position: absolute;
left: var(--article-panel-margin-left);
//left: 0;
top: .8rem;
z-index: 1;
margin-left: var(--article-panel-margin-left);
height: calc(100% - 1.2rem);
height: calc(100% - 1.5rem);
}
.footer {
@@ -501,7 +496,7 @@ const {playSentenceAudio} = usePlaySentenceAudio()
box-sizing: border-box;
border-radius: .6rem;
background: var(--color-second);
padding: .2rem var(--space) .4rem var(--space);
padding: .5rem var(--space);
z-index: 2;
border: 1px solid var(--color-item-border);
box-shadow: var(--shadow);
@@ -510,7 +505,7 @@ const {playSentenceAudio} = usePlaySentenceAudio()
margin-top: .5rem;
display: flex;
justify-content: space-around;
gap: 2rem;
gap: var(--stat-gap);
.row {
display: flex;
@@ -538,7 +533,7 @@ const {playSentenceAudio} = usePlaySentenceAudio()
bottom: 0;
}
:deep(.el-progress-bar__inner) {
:deep(.ElProgress-bar__inner) {
background: var(--color-scrollbar);
}

View File

@@ -5,11 +5,12 @@ import {Sort} from "@/types.ts";
import MiniDialog from "@/pages/pc/components/dialog/MiniDialog.vue";
import BaseIcon from "@/components/BaseIcon.vue";
import BaseButton from "@/components/BaseButton.vue";
import {cloneDeep, debounce, reverse, shuffle} from "lodash-es";
import {cloneDeep, debounce, reverse, shuffle} from "@/utils";
import Input from "@/pages/pc/components/Input.vue";
import PopConfirm from "@/pages/pc/components/PopConfirm.vue";
import Empty from "@/components/Empty.vue";
import {Icon} from "@iconify/vue";
import {ElCheckbox, ElPagination} from 'element-plus'
let list = defineModel('list')
@@ -117,7 +118,7 @@ const s = useSlots()
defineRender(
() => {
const d = (item) => <el-checkbox
const d = (item) => <ElCheckbox
modelValue={selectIds.includes(item.id)}
onChange={() => toggleSelect(item)}
size="large"/>
@@ -133,14 +134,14 @@ defineRender(
<Input
modelValue={searchKey}
onUpdate:modelValue=
{debounce(e => searchKey = e)}
{debounce(e => searchKey = e)}
class="flex-1"/>
<BaseButton onClick={() => (showSearchInput = false, searchKey = '')}>取消</BaseButton>
</div>
) : (
<div class="flex justify-between " v-else>
<div class="flex gap-2 items-center">
<el-checkbox
<ElCheckbox
disabled={!currentList.length}
onClick={() => toggleSelectAll()}
modelValue={selectAll}
@@ -219,14 +220,14 @@ defineRender(
})}
</div>
<div class="flex justify-end">
<el-pagination background
currentPage={pageNo}
onUpdate:current-page={handlePageNo}
pageSize={pageSize}
onUpdate:page-size={(e) => pageSize = e}
pageSizes={[20, 50, 100, 200]}
layout="prev, pager, next"
total={list.value.length}/>
<ElPagination background
currentPage={pageNo}
onUpdate:current-page={handlePageNo}
pageSize={pageSize}
onUpdate:page-size={(e) => pageSize = e}
pageSizes={[20, 50, 100, 200]}
layout="prev, pager, next"
total={list.value.length}/>
</div>
</>
) : <Empty/>

View File

@@ -1,6 +1,7 @@
<script setup lang="ts">
import {Dict} from "@/types.ts";
import {Icon} from "@iconify/vue";
import {ElProgress,ElCheckbox} from 'element-plus';
const props = defineProps<{
item?: Dict
@@ -36,11 +37,11 @@ const studyProgress = $computed(() => {
<div>{{ studyProgress }}{{ item?.length }}{{ quantifier }}</div>
</div>
<div class="absolute bottom-2 left-4 right-4">
<el-progress v-if="item?.lastLearnIndex || item.complete" class="mt-1"
<ElProgress v-if="item?.lastLearnIndex || item.complete" class="mt-1"
:percentage="progress"
:show-text="false"></el-progress>
:show-text="false"></ElProgress>
</div>
<el-checkbox v-if="showCheckbox"
<ElCheckbox v-if="showCheckbox"
:model-value="checked"
@click.stop="$emit('check')"
class="absolute left-3 bottom-2"/>

View File

@@ -1,6 +1,7 @@
<script setup lang="ts">
import BaseButton from "@/components/BaseButton.vue";
import {ElInput} from "element-plus";
import {watchEffect} from "vue";
@@ -37,7 +38,7 @@ function toggle() {
<div
v-if="edit"
class="edit-text">
<el-input
<ElInput
v-model="editVal"
ref="inputRef"
autosize

View File

@@ -20,7 +20,7 @@ $w: 1.4rem;
transition: all .3s;
&:hover {
background: var(--color-primary);
background: var(--color-icon-hightlight);
color: white;
}

View File

@@ -1,7 +1,7 @@
<script setup lang="ts">
import {useBaseStore} from "@/stores/base.ts"
import {computed, onMounted, onUnmounted, provide, watch} from "vue"
import {computed, provide, watch} from "vue"
import {Dict, DictType, ShortcutKey} from "@/types.ts"
import PopConfirm from "@/pages/pc/components/PopConfirm.vue"
import BaseButton from "@/components/BaseButton.vue";
@@ -9,18 +9,14 @@ import {useSettingStore} from "@/stores/setting.ts";
import Close from "@/components/icon/Close.vue";
import Empty from "@/components/Empty.vue";
import {useArticleOptions, useWordOptions} from "@/hooks/dict.ts";
import {Icon} from "@iconify/vue";
import Tooltip from "@/pages/pc/components/Tooltip.vue";
import IconWrapper from "@/pages/pc/components/IconWrapper.vue";
import BaseIcon from "@/components/BaseIcon.vue";
import {emitter, EventKey, useEvent} from "@/utils/eventBus.ts";
import {useRouter} from "vue-router";
import {useRuntimeStore} from "@/stores/runtime.ts";
import {cloneDeep} from "lodash-es";
import {useNav} from "@/utils";
import WordList from "@/pages/pc/components/list/WordList.vue";
import ArticleList from "@/pages/pc/components/list/ArticleList.vue";
import Slide from "@/pages/pc/components/Slide.vue";
import {useNav} from "@/utils";
const props = withDefaults(defineProps<{
type?: DictType

View File

@@ -1,644 +0,0 @@
<script setup lang="ts">
import {Icon} from '@iconify/vue';
import {ref, watch} from "vue";
import {useSettingStore} from "@/stores/setting.ts";
import {getAudioFileUrl, useChangeAllSound, usePlayAudio, useWatchAllSound} from "@/hooks/sound.ts";
import {getShortcutKey, useDisableEventListener, useEventListener} from "@/hooks/event.ts";
import {cloneDeep} from "lodash-es";
import {DefaultShortcutKeyMap, ShortcutKey} from "@/types.ts";
import BaseButton from "@/components/BaseButton.vue";
import {APP_NAME, EXPORT_DATA_KEY, SAVE_DICT_KEY, SAVE_SETTING_KEY, SoundFileOptions} from "@/utils/const.ts";
import VolumeIcon from "@/components/icon/VolumeIcon.vue";
import {useBaseStore} from "@/stores/base.ts";
import {saveAs} from "file-saver";
import {checkAndUpgradeSaveDict, checkAndUpgradeSaveSetting, shakeCommonDict} from "@/utils";
import {GITHUB} from "@/config/ENV.ts";
import dayjs from "dayjs";
const emit = defineEmits<{
toggleDisabledDialogEscKey: [val: boolean]
}>()
const tabIndex = $ref(0)
const settingStore = useSettingStore()
const store = useBaseStore()
//@ts-ignore
const gitLastCommitHash = ref(LATEST_COMMIT_HASH);
useDisableEventListener(() => undefined)
useWatchAllSound()
let editShortcutKey = $ref('')
const disabledDefaultKeyboardEvent = $computed(() => {
return editShortcutKey && tabIndex === 2
})
watch(() => disabledDefaultKeyboardEvent, v => {
emit('toggleDisabledDialogEscKey', !!v)
})
useEventListener('keydown', (e: KeyboardEvent) => {
if (!disabledDefaultKeyboardEvent) return
e.preventDefault()
let shortcutKey = getShortcutKey(e)
// console.log('e', e, e.keyCode, e.ctrlKey, e.altKey, e.shiftKey)
// console.log('key', shortcutKey)
// if (shortcutKey[shortcutKey.length-1] === '+') {
// settingStore.shortcutKeyMap[editShortcutKey] = DefaultShortcutKeyMap[editShortcutKey]
// return ElMessage.warning('设备失败!')
// }
if (editShortcutKey) {
if (shortcutKey === 'Delete') {
settingStore.shortcutKeyMap[editShortcutKey] = ''
} else {
for (const [k, v] of Object.entries(settingStore.shortcutKeyMap)) {
if (v === shortcutKey && k !== editShortcutKey) {
settingStore.shortcutKeyMap[editShortcutKey] = DefaultShortcutKeyMap[editShortcutKey]
return ElMessage.warning('快捷键重复!')
}
}
settingStore.shortcutKeyMap[editShortcutKey] = shortcutKey
}
}
})
function resetShortcutKeyMap() {
editShortcutKey = ''
settingStore.shortcutKeyMap = cloneDeep(DefaultShortcutKeyMap)
ElMessage.success('恢复成功')
}
function exportData() {
let data = {
version: EXPORT_DATA_KEY.version,
val: {
setting: {
version: SAVE_SETTING_KEY.version,
val: settingStore.$state
},
dict: {
version: SAVE_DICT_KEY.version,
val: shakeCommonDict(store.$state)
}
}
}
let blob = new Blob([JSON.stringify(data)], {type: "text/plain;charset=utf-8"});
saveAs(blob, `${APP_NAME}-User-Data-${dayjs().format('YYYY-MM-DD HH-mm-ss')}.json`);
ElMessage.success('导出成功!')
}
function importData(e) {
let file = e.target.files[0]
if (!file) return
// no()
let reader = new FileReader();
reader.onload = function (v) {
let str: any = v.target.result;
if (str) {
let obj = {
version: -1,
val: {
setting: {},
dict: {},
}
}
try {
obj = JSON.parse(str)
} catch (err) {
ElMessage.error('导入失败!')
}
if (obj.version === EXPORT_DATA_KEY.version) {
} else {
//TODO
}
let data = obj.val
let settingState = checkAndUpgradeSaveSetting(data.setting)
settingStore.setState(settingState)
let dictState = checkAndUpgradeSaveDict(data.dict)
store.init(dictState)
ElMessage.success('导入成功!')
}
}
reader.readAsText(file);
}
</script>
<template>
<div class="setting">
<div class="left">
<div class="tabs">
<div class="tab" :class="tabIndex === 0 && 'active'" @click="tabIndex = 0">
<Icon icon="bx:headphone" width="20" color="#0C8CE9"/>
<span>音效设置</span>
</div>
<div class="tab" :class="tabIndex === 2 && 'active'" @click="tabIndex = 2">
<Icon icon="material-symbols:keyboard-outline" width="20" color="#0C8CE9"/>
<span>快捷键设置</span>
</div>
<div class="tab" :class="tabIndex === 1 && 'active'" @click="tabIndex = 1">
<Icon icon="icon-park-outline:setting-config" width="20" color="#0C8CE9"/>
<span>其他设置</span>
</div>
<div class="tab" :class="tabIndex === 3 && 'active'" @click="tabIndex = 3">
<Icon icon="mdi:database-cog-outline" width="20" color="#0C8CE9"/>
<span>数据管理</span>
</div>
<div class="tab" :class="tabIndex === 4 && 'active'" @click="tabIndex = 4">
<Icon icon="mingcute:service-fill" width="20" color="#0C8CE9"/>
<span>反馈</span>
</div>
<div class="tab" :class="tabIndex === 5 && 'active'" @click="tabIndex = 5">
<Icon icon="mdi:about-circle-outline" width="20" color="#0C8CE9"/>
<span>关于</span>
</div>
</div>
<div class="git-log">
Build {{ gitLastCommitHash }}
</div>
</div>
<div class="content">
<div v-if="tabIndex === 0">
<div class="row">
<label class="main-title">所有音效</label>
<div class="wrapper">
<el-switch v-model="settingStore.allSound"
@change="useChangeAllSound"
inline-prompt
active-text=""
inactive-text=""
/>
</div>
</div>
<div class="line"></div>
<div class="row">
<label class="item-title">单词/句子自动发音</label>
<div class="wrapper">
<el-switch v-model="settingStore.wordSound"
inline-prompt
active-text=""
inactive-text=""
/>
</div>
</div>
<div class="row">
<label class="sub-title">单词/句子发音口音</label>
<div class="wrapper">
<el-select v-model="settingStore.wordSoundType"
placeholder="请选择"
>
<el-option label="美音" value="us"/>
<el-option label="英音" value="uk"/>
</el-select>
</div>
</div>
<div class="row">
<label class="sub-title">音量</label>
<div class="wrapper">
<el-slider v-model="settingStore.wordSoundVolume"/>
<span>{{ settingStore.wordSoundVolume }}%</span>
</div>
</div>
<div class="row">
<label class="sub-title">倍速</label>
<div class="wrapper">
<el-slider v-model="settingStore.wordSoundSpeed" :step="0.1" :min="0.5" :max="3"/>
<span>{{ settingStore.wordSoundSpeed }}</span>
</div>
</div>
<div class="line"></div>
<div class="row">
<label class="item-title">按键音</label>
<div class="wrapper">
<el-switch v-model="settingStore.keyboardSound"
inline-prompt
active-text=""
inactive-text=""
/>
</div>
</div>
<div class="row">
<label class="item-title">按键音效</label>
<div class="wrapper">
<el-select v-model="settingStore.keyboardSoundFile"
placeholder="请选择"
>
<el-option
v-for="item in SoundFileOptions"
:key="item.value"
:label="item.label"
:value="item.value"
>
<div class="el-option-row">
<span>{{ item.label }}</span>
<VolumeIcon
:time="100"
@click="usePlayAudio(getAudioFileUrl(item.value)[0])"/>
</div>
</el-option>
</el-select>
</div>
</div>
<div class="row">
<label class="sub-title">音量</label>
<div class="wrapper">
<el-slider v-model="settingStore.keyboardSoundVolume"/>
<span>{{ settingStore.keyboardSoundVolume }}%</span>
</div>
</div>
<div class="line"></div>
<div class="row">
<label class="item-title">效果音输入错误完成时的音效</label>
<div class="wrapper">
<el-switch v-model="settingStore.effectSound"
inline-prompt
active-text=""
inactive-text=""
/>
</div>
</div>
<div class="row">
<label class="sub-title">音量</label>
<div class="wrapper">
<el-slider v-model="settingStore.effectSoundVolume"/>
<span>{{ settingStore.effectSoundVolume }}%</span>
</div>
</div>
</div>
<div v-if="tabIndex === 1">
<div class="row">
<label class="item-title">显示上一个/下一个单词</label>
<div class="wrapper">
<el-switch v-model="settingStore.showNearWord"
inline-prompt
active-text=""
inactive-text=""
/>
</div>
</div>
<div class="desc">
开启后练习中会在上方显示上一个/下一个单词
</div>
<div class="line"></div>
<div class="row">
<label class="item-title">忽略大小写</label>
<div class="wrapper">
<el-switch v-model="settingStore.ignoreCase"
inline-prompt
active-text=""
inactive-text=""
/>
</div>
</div>
<div class="desc">
开启后输入时不区分大小写如输入helloHello都会被认为是正确的
</div>
<div class="line"></div>
<div class="row">
<label class="item-title">允许默写模式下显示提示</label>
<div class="wrapper">
<el-switch v-model="settingStore.allowWordTip"
inline-prompt
active-text=""
inactive-text=""
/>
</div>
</div>
<div class="desc">
开启后可以通过鼠标 hover 单词或者按 {{ settingStore.shortcutKeyMap[ShortcutKey.ShowWord] }} 显示正确答案
</div>
<div class="line"></div>
<div class="row">
<label class="item-title">字体设置(仅可调整单词练习)</label>
</div>
<div class="row">
<label class="sub-title">外语字体</label>
<div class="wrapper">
<el-slider
:min="10"
:max="100"
v-model="settingStore.fontSize.wordForeignFontSize"/>
<span>{{ settingStore.fontSize.wordForeignFontSize }}</span>
</div>
</div>
<div class="row">
<label class="sub-title">中文字体</label>
<div class="wrapper">
<el-slider
:min="10"
:max="100"
v-model="settingStore.fontSize.wordTranslateFontSize"/>
<span>{{ settingStore.fontSize.wordTranslateFontSize }}</span>
</div>
</div>
<div class="line"></div>
<div class="row">
<label class="item-title">其他设置</label>
</div>
<div class="row">
<label class="sub-title">切换下一个单词时间</label>
<div class="wrapper">
<el-input-number v-model="settingStore.waitTimeForChangeWord"
:min="6"
:max="100"
type="number"
/>
<span>毫秒</span>
</div>
</div>
</div>
<div class="body" v-if="tabIndex === 2">
<div class="row">
<label class="main-title">功能</label>
<div class="wrapper">快捷键(点击可修改)</div>
</div>
<div class="scroll">
<div class="row" v-for="item of Object.entries(settingStore.shortcutKeyMap)">
<label class="item-title">{{ $t(item[0]) }}</label>
<div class="wrapper" @click="editShortcutKey = item[0]">
<div class="set-key" v-if="editShortcutKey === item[0]">
<input :value="item[1]?item[1]:'未设置快捷键'" readonly type="text" @blur="editShortcutKey = ''">
<span @click.stop="editShortcutKey = ''">直接按键盘进行设置</span>
</div>
<div v-else>
<div v-if="item[1]">{{ item[1] }}</div>
<span v-else>未设置快捷键</span>
</div>
</div>
</div>
</div>
<div class="row footer">
<label class="item-title"></label>
<div class="wrapper">
<BaseButton @click="resetShortcutKeyMap">恢复默认</BaseButton>
</div>
</div>
</div>
<div v-if="tabIndex === 3">
<div class="row">
<div class="main-title">数据导出</div>
</div>
<div class="row">
<label class="sub-title">
目前用户的所有数据(自定义设置自定义词典练习进度等)
<b>仅保存在本地</b>
如果您需要在不同的设备浏览器或者其他非官方部署上使用 {{ APP_NAME }} 您需要手动进行数据同步和保存
</label>
</div>
<div class="row">
<BaseButton @click="exportData">数据导出</BaseButton>
</div>
<div class="row">
<div class="main-title">数据导入</div>
</div>
<div class="row">
<label class="sub-title">
请注意导入数据将
<b style="color: red"> 完全覆盖 </b>
当前数据请谨慎操作
</label>
</div>
<div class="row">
<div class="import hvr-grow">
<BaseButton>数据导入</BaseButton>
<input type="file"
accept="application/json"
@change="importData">
</div>
</div>
</div>
<div v-if="tabIndex === 4" class="feedback-modal">
<div>
给我发Email<a href="mailto:zyronon@163.com">zyronon@163.com</a>
</div>
<p>or</p>
<div class="github">
<span><a :href="GITHUB" target="_blank">Github</a>上给我提一个
<a :href="`${GITHUB}/issues`" target="_blank">Issue</a>
</span>
<div class="options">
<BaseButton>
<a :href="`${GITHUB}/issues/new?assignees=&labels=&projects=&template=%E5%8D%95%E8%AF%8D%E9%94%99%E8%AF%AF---word-error.md&title=%E5%8D%95%E8%AF%8D%E9%94%99%E8%AF%AF+%7C+Word+error`"
target="_blank">词典错误</a>
</BaseButton>
<BaseButton>
<a :href="`${GITHUB}/issues/new?assignees=&labels=&projects=&template=问题报告---bug-report-.md&title=问题报告+%7C+Bug+report+`"
target="_blank">反馈BUG</a>
</BaseButton>
<BaseButton>
<a :href="`${GITHUB}/issues/new?assignees=&labels=&projects=&template=功能请求---feature-request.md&title=功能请求+%7C+Feature+request`"
target="_blank">功能请求</a>
</BaseButton>
</div>
</div>
</div>
<div v-if="tabIndex === 5" class="about">
<h1>Type Words</h1>
<p>
本项目完全开源好用请大家多多点Star
</p>
<p>
GitHub地址<a href="https://github.com/zyronon/typing-word">https://github.com/zyronon/typing-word</a>
</p>
<p>
反馈<a href="https://github.com/zyronon/typing-word/issues">https://github.com/zyronon/typing-word/issues</a>
</p>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
.setting {
width: 40vw;
height: 70vh;
display: flex;
color: var(--color-font-1);
.left {
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
.tabs {
padding: .6rem 1.6rem;
display: flex;
flex-direction: column;
//align-items: center;
//justify-content: center;
gap: .6rem;
.tab {
cursor: pointer;
padding: .6rem .9rem;
border-radius: .5rem;
display: flex;
align-items: center;
gap: .6rem;
&.active {
background: var(--color-item-bg);
}
}
}
.git-log {
font-size: .6rem;
color: gray;
margin-bottom: .3rem;
}
}
.content {
flex: 1;
height: 100%;
overflow: auto;
padding: 0 var(--space);
.row {
min-height: 2.6rem;
display: flex;
justify-content: space-between;
align-items: center;
gap: calc(var(--space) * 5);
.wrapper {
height: 2rem;
flex: 1;
display: flex;
justify-content: flex-end;
gap: var(--space);
span {
text-align: right;
//width: 30rem;
font-size: .7rem;
color: gray;
}
.set-key {
align-items: center;
input {
width: 9rem;
box-sizing: border-box;
margin-right: .6rem;
height: 1.8rem;
outline: none;
font-size: 1rem;
border: 1px solid gray;
border-radius: .2rem;
padding: 0 .3rem;
background: var(--color-second);
color: var(--color-font-1);
}
}
}
.main-title {
font-size: 1.1rem;
font-weight: bold;
}
.item-title {
font-size: 1rem;
}
.sub-title {
font-size: .9rem;
}
}
.body {
height: 100%;
overflow: hidden;
display: flex;
flex-direction: column;
}
.scroll {
flex: 1;
padding-right: .6rem;
overflow: auto;
}
.footer {
margin-bottom: 1.3rem;
}
.desc {
margin-bottom: .6rem;
font-size: .8rem;
}
.line {
border-bottom: 1px solid #c4c3c3;
}
}
}
.el-option-row {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
.icon-wrapper {
transform: translateX(10rem);
}
}
.import {
display: inline-flex;
position: relative;
input {
position: absolute;
height: 100%;
width: 100%;
opacity: 0;
}
}
.feedback-modal {
//height: 80vh;
display: flex;
flex-direction: column;
align-items: center;
padding: var(--space);
//justify-content: center;
color: var(--color-font-1);
p {
font-size: 2.4rem;
}
.github {
display: flex;
align-items: center;
gap: var(--space);
.options {
display: flex;
flex-direction: column;
gap: .6rem;
}
}
}
.about {
text-align: center;
}
</style>

View File

@@ -3,6 +3,7 @@
import {Word} from "@/types.ts";
import VolumeIcon from "@/components/icon/VolumeIcon.vue";
import {usePlayWordAudio} from "@/hooks/sound.ts";
import {ElPopover} from 'element-plus'
const props = withDefaults(defineProps<{
item: Word,
@@ -35,7 +36,7 @@ const playWordAudio = usePlayWordAudio()
</div>
<div class="item-sub-title flex flex-col gap-2" v-if="item.trans.length && showTranslate">
<div v-for="v in item.trans">
<el-popover
<ElPopover
v-if="v.cn.length > 30 && showTransPop"
width="300"
:content="v.pos + ' ' + v.cn"
@@ -44,7 +45,7 @@ const playWordAudio = usePlayWordAudio()
<template #reference>
<span>{{ v.pos + ' ' + v.cn.slice(0, 30) + '...' }}</span>
</template>
</el-popover>
</ElPopover>
<span v-else>{{ v.pos + ' ' + v.cn }}</span>
</div>
</div>

View File

@@ -2,7 +2,7 @@
import BaseIcon from "@/components/BaseIcon.vue";
import Input from "@/pages/pc/components/Input.vue";
import {cloneDeep, throttle} from "lodash-es";
import {cloneDeep, throttle} from "@/utils";
import {Article} from "@/types.ts";
interface IProps {

View File

@@ -4,6 +4,7 @@ import {Word} from "@/types.ts";
import VolumeIcon from "@/components/icon/VolumeIcon.vue";
import BaseList from "@/pages/pc/components/list/BaseList.vue";
import {usePlayWordAudio} from "@/hooks/sound.ts";
import {ElPopover} from 'element-plus'
const props = withDefaults(defineProps<{
list: Word[],
@@ -53,7 +54,7 @@ defineExpose({scrollToBottom, scrollToItem})
</div>
<div class="item-sub-title flex flex-col gap-2" v-if="item.trans.length && showTranslate">
<div v-for="v in item.trans">
<el-popover
<ElPopover
v-if="v.cn.length > 30"
width="300"
:content="v.pos + ' ' + v.cn"
@@ -62,7 +63,7 @@ defineExpose({scrollToBottom, scrollToItem})
<template #reference>
<span>{{ v.pos + ' ' + v.cn.slice(0, 30) + '...' }}</span>
</template>
</el-popover>
</ElPopover>
<span v-else>{{ v.pos + ' ' + v.cn }}</span>
</div>
</div>

View File

@@ -6,11 +6,10 @@ import IconWrapper from "@/pages/pc/components/IconWrapper.vue";
import Tooltip from "@/pages/pc/components/Tooltip.vue";
import {useBaseStore} from "@/stores/base.ts";
import {useWindowClick} from "@/hooks/event.ts";
import {emitter, EventKey} from "@/utils/eventBus.ts";
import BaseButton from "@/components/BaseButton.vue";
import Dialog from "@/pages/pc/components/dialog/Dialog.vue";
import {useSettingStore} from "@/stores/setting.ts";
import {ShortcutKey} from "@/types.ts";
import {ElSwitch, ElRadioGroup,ElRadioButton,ElSelect,ElOption} from 'element-plus'
const store = useBaseStore()
const settingStore = useSettingStore()
@@ -59,19 +58,19 @@ function save() {
<div class="mini-row">
<label class="item-title">显示翻译</label>
<div class="wrapper">
<el-switch v-model="settingStore.translate"
inline-prompt
active-text=""
inactive-text=""
<ElSwitch v-model="settingStore.translate"
inline-prompt
active-text=""
inactive-text=""
/>
</div>
</div>
<div class="mini-row">
<label class="item-title">翻译类型</label>
<el-radio-group v-model="translateType" size="small">
<el-radio-button :value="1">本地翻译</el-radio-button>
<el-radio-button :value="0">网络翻译</el-radio-button>
</el-radio-group>
<ElRadioGroup v-model="translateType" size="small">
<ElRadioButton :value="1">本地翻译</ElRadioButton>
<ElRadioButton :value="0">网络翻译</ElRadioButton>
</ElRadioGroup>
</div>
<div class="mini-row" v-if="translateType">
<label class="item-title">本地翻译</label>
@@ -88,14 +87,14 @@ function save() {
<div class="mini-row" v-else>
<label class="item-title">网络翻译</label>
<div class="wrapper">
<el-select v-model="networkTranslateEngine" class="m-2" placeholder="Select" size="small">
<el-option
<ElSelect v-model="networkTranslateEngine" class="m-2" placeholder="Select" size="small">
<ElOption
v-for="item in TranslateEngine"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</ElSelect>
</div>
</div>
<div class="footer">

View File

@@ -1,6 +1,7 @@
<script setup lang="ts">
import {splitEnArticle2} from "@/hooks/article.ts";
import test from '../../test/test.vue'
import BaseButton from "@/components/BaseButton.vue";
function test1() {
splitEnArticle2(
@@ -19,8 +20,8 @@ function test2() {
<template>
<div class="word flex center h-screen ">
<El-Button @click="test1">test1</El-Button>
<El-Button @click="test2">test2</El-Button>
<base-button @click="test1">test1</base-button>
<base-button @click="test2">test2</base-button>
<test/>
</div>
</template>

View File

@@ -99,7 +99,7 @@ const {toggleTheme} = useTheme()
flex-shrink: 0;
&:hover {
background: var(--color-primary);
background: var(--color-select-bg);
color: white;
}

View File

@@ -5,7 +5,7 @@ import {DictId, getDefaultDict} from "@/types";
import BasePage from "@/pages/pc/components/BasePage.vue";
import {computed, onMounted, reactive} from "vue";
import {useRuntimeStore} from "@/stores/runtime.ts";
import {assign, cloneDeep} from "lodash-es";
import {assign, cloneDeep} from "@/utils";
import {nanoid} from "nanoid";
import BaseIcon from "@/components/BaseIcon.vue";
import BaseTable from "@/pages/pc/components/BaseTable.vue";
@@ -18,6 +18,7 @@ import {useRoute, useRouter} from "vue-router";
import {useBaseStore} from "@/stores/base.ts";
import EditBook from "@/pages/pc/article/components/EditBook.vue";
import {_getDictDataByUrl, _nextTick, convertToWord} from "@/utils";
import {ElForm, ElFormItem, ElInput, ElMessage} from "element-plus";
const runtimeStore = useRuntimeStore()
const base = useBaseStore()
@@ -261,85 +262,86 @@ defineRender(() => {
<div class="common-title">
{wordForm.id ? '修改' : '添加'}单词
</div>
<el-form
<ElForm
class="flex-1 overflow-auto pr-2"
ref={e => wordFormRef = e}
rules={wordRules}
model={wordForm}
label-width="7rem">
<el-form-item label="单词" prop="word">
<el-input
<ElFormItem label="单词" prop="word">
<ElInput
modelValue={wordForm.word}
onUpdate:modelValue={e => wordForm.word = e}
/>
</el-form-item>
<el-form-item label="英音音标">
<el-input
</ElFormItem>
<ElFormItem label="英音音标">
<ElInput
modelValue={wordForm.phonetic0}
onUpdate:modelValue={e => wordForm.phonetic0 = e}
/>
</el-form-item>
<el-form-item label="美音音标">
<el-input
</ElFormItem>
<ElFormItem label="美音音标">
<ElInput
modelValue={wordForm.phonetic1}
onUpdate:modelValue={e => wordForm.phonetic1 = e}/>
</el-form-item>
<el-form-item label="翻译">
<el-input
</ElFormItem>
<ElFormItem label="翻译">
<ElInput
modelValue={wordForm.trans}
onUpdate:modelValue={e => wordForm.trans = e}
placeholder="一行一个翻译前面词性后面内容如n.取消);多个翻译请换行"
autosize={{minRows: 6, maxRows: 10}}
type="textarea"/>
</el-form-item>
<el-form-item label="例句">
<el-input
</ElFormItem>
<ElFormItem label="例句">
<ElInput
modelValue={wordForm.sentences}
onUpdate:modelValue={e => wordForm.sentences = e}
placeholder="一行原文,一行译文;多个请换两行"
autosize={{minRows: 6, maxRows: 10}}
type="textarea"/>
</el-form-item>
<el-form-item label="短语">
<el-input
</ElFormItem>
<ElFormItem label="短语">
<ElInput
modelValue={wordForm.phrases}
onUpdate:modelValue={e => wordForm.phrases = e}
placeholder="一行原文,一行译文;多个请换两行"
autosize={{minRows: 6, maxRows: 10}}
type="textarea"/>
</el-form-item>
<el-form-item label="同义词">
<el-input
</ElFormItem>
<ElFormItem label="同义词">
<ElInput
modelValue={wordForm.synos}
onUpdate:modelValue={e => wordForm.synos = e}
placeholder="请参考已有单词格式"
autosize={{minRows: 6, maxRows: 20}}
type="textarea"/>
</el-form-item>
<el-form-item label="同根词">
<el-input
</ElFormItem>
<ElFormItem label="同根词">
<ElInput
modelValue={wordForm.relWords}
onUpdate:modelValue={e => wordForm.relWords = e}
placeholder="请参考已有单词格式"
autosize={{minRows: 6, maxRows: 20}}
type="textarea"/>
</el-form-item>
<el-form-item label="词源">
<el-input
</ElFormItem>
<ElFormItem label="词源">
<ElInput
modelValue={wordForm.etymology}
onUpdate:modelValue={e => wordForm.etymology = e}
placeholder="请参考已有单词格式"
autosize={{minRows: 6, maxRows: 10}}
type="textarea"/>
</el-form-item>
</el-form>
</ElFormItem>
</ElForm>
<div class="center">
<el-button
<base-button
type="info"
onClick={closeWordForm}>关闭
</el-button>
<el-button type="primary"
onClick={onSubmitWord}>保存
</el-button>
</base-button>
<base-button type="primary"
onClick={onSubmitWord}>保存
</base-button>
</div>
</div>
) : null

View File

@@ -13,7 +13,7 @@ import BackIcon from "@/pages/pc/components/BackIcon.vue";
import DictGroup from "@/pages/pc/components/list/DictGroup.vue";
import {useBaseStore} from "@/stores/base.ts";
import {useRouter} from "vue-router";
import {groupBy} from "lodash-es";
import {groupBy} from "@/utils";
import {dictionaryResources} from "@/assets/dictionary.ts";
import {computed} from "vue";

View File

@@ -10,7 +10,7 @@ import {getDefaultWord, ShortcutKey, StudyData, Word} from "@/types.ts";
import {useOnKeyboardEventListener, useStartKeyboardEventListener} from "@/hooks/event.ts";
import useTheme from "@/hooks/theme.ts";
import {getCurrentStudyWord, useWordOptions} from "@/hooks/dict.ts";
import {cloneDeep, shuffle} from "lodash-es";
import {_getDictDataByUrl, cloneDeep, shuffle} from "@/utils";
import {useRoute, useRouter} from "vue-router";
import {Icon} from "@iconify/vue";
import Footer from "@/pages/pc/word/components/Footer.vue";
@@ -23,7 +23,6 @@ import Empty from "@/components/Empty.vue";
import {useBaseStore} from "@/stores/base.ts";
import {usePracticeStore} from "@/stores/practice.ts";
import {dictionaryResources} from "@/assets/dictionary.ts";
import {_getDictDataByUrl} from "@/utils";
interface IProps {
new: Word[],

View File

@@ -14,7 +14,8 @@ import {getCurrentStudyWord} from "@/hooks/dict.ts";
import {useRuntimeStore} from "@/stores/runtime.ts";
import Book from "@/pages/pc/components/Book.vue";
import PopConfirm from "@/pages/pc/components/PopConfirm.vue";
import {ElMessage} from 'element-plus';
import {ElMessage, ElProgress, ElSlider} from 'element-plus';
import BaseButton from "@/components/BaseButton.vue";
const store = useBaseStore()
const router = useRouter()
@@ -184,7 +185,7 @@ const progressTextRight = $computed(() => {
<span>{{ progressTextLeft }}</span>
<span>{{ progressTextRight }} / {{ store.sdict.words.length }}</span>
</div>
<el-progress class="mt-1" :percentage="store.currentStudyProgress" :show-text="false"></el-progress>
<ElProgress class="mt-1" :percentage="store.currentStudyProgress" :show-text="false"></ElProgress>
</div>
<div class="text-sm text-align-end">
预计完成日期{{ _getAccomplishDate(store.sdict.words.length, store.sdict.perDayStudyNumber) }}
@@ -219,12 +220,12 @@ const progressTextRight = $computed(() => {
</div>
个单词 <span class="color-blue cursor-pointer" @click="setPerDayStudyNumber">更改</span>
</div>
<div class="btn">开始学习</div>
<div class="rounded-xl bg-slate-800 flex items-center gap-2 py-3 px-5 text-white cursor-pointer"
:class="store.sdict.name || 'opacity-70 cursor-not-allowed'" @click="startStudy">
<span>开始学习</span>
<Icon icon="icons8:right-round" class="text-2xl"/>
</div>
<BaseButton :disabled="!store.sdict.name" @click="startStudy">
<div class="flex items-center gap-2">
<span>开始学习</span>
<Icon icon="icons8:right-round" class="text-2xl"/>
</div>
</BaseButton>
</div>
</div>
@@ -278,8 +279,8 @@ const progressTextRight = $computed(() => {
<div class="center text-sm" :style="{ opacity: tempPerDayStudyNumber === 20 ? 1 : 0 }">
推荐
</div>
<el-slider :min="10" :step="10" show-stops :marks="{ 10: '10', 200: '200' }" size="small" class="my-6"
:max="200" v-model="tempPerDayStudyNumber"/>
<ElSlider :min="10" :step="10" show-stops :marks="{ 10: '10', 200: '200' }" size="small" class="my-6"
:max="200" v-model="tempPerDayStudyNumber"/>
<div class="flex gap-2 mb-2 mt-10 items-center">
<div>预计</div>
<span class="text-2xl" style="color:rgb(176,116,211)">{{

View File

@@ -9,6 +9,7 @@ import {Icon} from "@iconify/vue";
import IconWrapper from "@/pages/pc/components/IconWrapper.vue";
import Tooltip from "@/pages/pc/components/Tooltip.vue";
import TranslateSetting from "@/pages/pc/components/toolbar/TranslateSetting.vue";
import {ElProgress} from 'element-plus';
const statisticsStore = usePracticeStore()
const settingStore = useSettingStore()
@@ -85,7 +86,7 @@ const progress = $computed(() => {
</Tooltip>
<div class="bottom">
<el-progress
<ElProgress
:percentage="progress"
:stroke-width="8"
:show-text="false"/>
@@ -171,7 +172,7 @@ const progress = $computed(() => {
</div>
</div>
<div class="progress">
<el-progress :percentage="progress"
<ElProgress :percentage="progress"
:stroke-width="8"
:show-text="false"/>
</div>

View File

@@ -42,6 +42,6 @@ function escapeRegExp(string: string): string {
<style scoped lang="scss">
:deep(.highlight-word) {
color: var(--color-primary);
color: var(--color-icon-hightlight);
}
</style>

View File

@@ -323,7 +323,7 @@ function mouseleave() {
cursor: pointer;
&.active {
border-bottom: 2px solid var(--color-font-1);
border-bottom: 2px solid var(--color-font-2);
}
}
}

View File

@@ -1,10 +1,11 @@
<script setup lang="ts">
// import origin from './data.json'
import BaseButton from "@/components/BaseButton.vue";
import {checkAndUpgradeSaveDict, shakeCommonDict} from "@/utils";
import {checkAndUpgradeSaveDict} from "@/utils";
import localforage from "localforage";
import {SAVE_DICT_KEY} from "@/utils/const.ts";
import str from './data.json'
import {ElTableV2} from 'element-plus'
let data = {}
let origin = {}
@@ -73,7 +74,7 @@ const data1 = generateData(columns, 1000)
<BaseButton @click="set">设置data.json的数据到localforage</BaseButton>
<BaseButton @click="check">检测升级逻辑</BaseButton>
</div>
<el-table-v2
<ElTableV2
:columns="columns"
:data="data1"
:width="700"

View File

@@ -52,7 +52,8 @@ export const routes: RouteRecordRaw[] = [
]
const router = VueRouter.createRouter({
history: VueRouter.createWebHistory(),
// history: VueRouter.createWebHistory(),
history: VueRouter.createWebHashHistory(),
routes,
scrollBehavior(to, from, savedPosition) {
// console.log('savedPosition', savedPosition)

View File

@@ -1,6 +1,6 @@
import {defineStore} from 'pinia'
import {Dict, DictId, getDefaultDict, Word} from "../types.ts"
import {cloneDeep, merge} from "lodash-es";
import {cloneDeep} from "@/utils";
import * as localforage from "localforage";
import {nanoid} from "nanoid";
import {SAVE_DICT_KEY} from "@/utils/const.ts";

View File

@@ -1,8 +1,7 @@
import {defineStore} from "pinia"
import {cloneDeep, merge} from "lodash-es";
import {checkAndUpgradeSaveSetting, cloneDeep} from "@/utils";
import {DefaultShortcutKeyMap} from "@/types.ts";
import {SAVE_SETTING_KEY} from "@/utils/const.ts";
import {checkAndUpgradeSaveDict, checkAndUpgradeSaveSetting} from "@/utils";
export interface SettingState {
showToolbar: boolean,

View File

@@ -1,6 +1,6 @@
import {Dict, DictResource, getDefaultDict} from "@/types.ts";
import {getDictFile} from "@/utils/index.ts";
import {cloneDeep} from "lodash-es";
import {cloneDeep} from "@/utils";
import {nanoid} from "nanoid";
export async function getArticleBookDataByUrl(val: DictResource) {

View File

@@ -1,27 +1,27 @@
import axios, {AxiosInstance} from 'axios'
// import globalMethods from './global-methods'
// import Config from '../config/index'
// import CONSTANT from './const_var'
// import store from '../store'
// import Storage from './storage'
export const axiosInstance: AxiosInstance = axios.create({
// baseURL: process.env.NODE_ENV === 'production' ? Config.PRODUCT_API_URL : Config.API_URL,
// baseURL: 'http://testtestgp.com',
timeout: 15000,
})
// request 拦截器
axiosInstance.interceptors.request.use(
(config) => {
// console.log('config', config)
if (config.url === 'https://api.fanyi.baidu.com/api/trans/vip/translate') {
config.url = '/baidu'
}
return config
},
error => Promise.reject(error),
)
// import axios, {AxiosInstance} from 'axios'
// // import globalMethods from './global-methods'
// // import Config from '../config/index'
// // import CONSTANT from './const_var'
// // import store from '../store'
// // import Storage from './storage'
//
// export const axiosInstance: AxiosInstance = axios.create({
// // baseURL: process.env.NODE_ENV === 'production' ? Config.PRODUCT_API_URL : Config.API_URL,
// // baseURL: 'http://testtestgp.com',
// timeout: 15000,
// })
//
// // request 拦截器
// axiosInstance.interceptors.request.use(
// (config) => {
// // console.log('config', config)
// if (config.url === 'https://api.fanyi.baidu.com/api/trans/vip/translate') {
// config.url = '/baidu'
// }
// return config
// },
// error => Promise.reject(error),
// )
// respone 拦截器
// instance.interceptors.response.use(
@@ -111,4 +111,4 @@ axiosInstance.interceptors.request.use(
// })
// }
// export default request
// export default request

View File

@@ -1,9 +1,7 @@
import {SAVE_DICT_KEY, SAVE_SETTING_KEY} from "@/utils/const.ts";
import {BaseState, DefaultBaseState} from "@/stores/base.ts";
import {getDefaultSettingState, SettingState} from "@/stores/setting.ts";
import {cloneDeep} from "lodash-es";
import {getDefaultSettingState} from "@/stores/setting.ts";
import {Dict, DictResource, DictType, getDefaultArticle, getDefaultDict, getDefaultWord} from "@/types.ts";
import {ArchiveReader, libarchiveWasm} from "libarchive-wasm";
import {useRouter} from "vue-router";
import {useRuntimeStore} from "@/stores/runtime.ts";
import {nanoid} from "nanoid";
@@ -514,3 +512,57 @@ export function convertToWord(raw: any) {
custom: true
});
}
export function cloneDeep<T>(val: T) {
return JSON.parse(JSON.stringify(val))
}
export function shuffle<T>(array: T[]): T[] {
const result = array.slice(); // 复制数组,避免修改原数组
for (let i = result.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1)); // 生成 0 ~ i 的随机索引
[result[i], result[j]] = [result[j], result[i]]; // 交换元素
}
return result;
}
export function last<T>(array: T[]): T | undefined {
return array.length > 0 ? array[array.length - 1] : undefined;
}
export function debounce<T extends (...args: any[]) => void>(func: T, wait: number) {
let timer: ReturnType<typeof setTimeout> | null = null;
return function (this: ThisParameterType<T>, ...args: Parameters<T>) {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
func.apply(this, args);
}, wait);
};
}
export function throttle<T extends (...args: any[]) => void>(func: T, wait: number) {
let lastTime = 0;
return function (this: ThisParameterType<T>, ...args: Parameters<T>) {
const now = Date.now();
if (now - lastTime >= wait) {
func.apply(this, args);
lastTime = now;
}
};
}
export function reverse<T>(array: T[]): T[] {
return array.slice().reverse();
}
export function assign<T extends object, U extends object>(target: T, ...sources: U[]): T & U {
return Object.assign(target, ...sources);
}
export function groupBy<T extends Record<string, any>>(array: T[], key: string) {
return array.reduce<Record<string, T[]>>((result, item) => {
const groupKey = String(item[key]);
(result[groupKey] ||= []).push(item);
return result;
}, {});
}

View File

@@ -8,6 +8,7 @@ export default defineConfig({
'bg-third': 'bg-[var(--color-third)]',
'bg-card-active': 'bg-[var(--color-card-active)]',
'color-main': 'color-[var(--color-main-text)]',
'gap-space': 'gap-[var(--space)]',
},
presets: [
presetWind3(),

View File

@@ -1,16 +1,14 @@
import {defineConfig} from 'vite'
import {defineConfig, UserConfig} from 'vite'
import Vue from '@vitejs/plugin-vue'
import VueJsx from "@vitejs/plugin-vue-jsx";
import {resolve} from 'path'
import {visualizer} from "rollup-plugin-visualizer";
import SlidePlugin from './src/components/slide/data.js';
import {ElementPlusResolver} from "unplugin-vue-components/resolvers";
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import {getLastCommit} from "git-last-commit";
import UnoCSS from 'unocss/vite'
import VueMacros from 'unplugin-vue-macros/vite'
import {Plugin as importToCDN} from 'vite-plugin-cdn-import'
import ElementPlus from 'unplugin-element-plus/vite'
function pathResolve(dir: string) {
return resolve(__dirname, ".", dir)
@@ -18,7 +16,7 @@ function pathResolve(dir: string) {
const lifecycle = process.env.npm_lifecycle_event;
async function getConfig() {
async function getConfig(): Promise<Partial<UserConfig>> {
const latestCommitHash = await new Promise<string>((resolve) => {
return getLastCommit((err, commit) => (err ? 'unknown' : resolve(commit.shortHash)))
})
@@ -31,12 +29,7 @@ async function getConfig() {
},
}),
UnoCSS(),
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver()],
}),
ElementPlus(),
lifecycle === 'report' ?
visualizer({
gzipSize: true,
@@ -58,11 +51,11 @@ async function getConfig() {
var: 'VueRouter',
path: `https://cdn.jsdelivr.net/npm/vue-router@4.5.1/dist/vue-router.global.prod.min.js`
},
// {
// name: 'axios',
// var: 'axios',
// path: 'https://cdn.jsdelivr.net/npm/axios@1.9.0/dist/axios.min.js'
// },
{
name: 'axios',
var: 'axios',
path: 'https://cdn.jsdelivr.net/npm/axios@1.9.0/dist/axios.min.js'
},
]
})
],
@@ -77,6 +70,11 @@ async function getConfig() {
},
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue']
},
// build: {
// rollupOptions: {
// external: ['axios'],// 使用全局的 axios。因为百度翻译库内部用了0.19版本的axios会被打包到代码里面
// }
// },
css: {
preprocessorOptions: {
scss: {