├── .backup └── .github │ └── workflows.zip ├── .editorconfig ├── .eslintignore ├── .eslintrc.cjs ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── config.yml │ └── feature_request.md └── renovate.json ├── .gitignore ├── .idea ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── deployment.xml ├── inspectionProfiles │ └── Project_Default.xml ├── jsLibraryMappings.xml ├── jsLinters │ └── eslint.xml ├── jsonSchemas.xml ├── modules.xml ├── prettier.xml ├── vcs.xml ├── vite-electron-builder.iml └── webResources.xml ├── .npmrc ├── .prettierrc ├── .vscode ├── extensions.json ├── launch.json └── settings.json ├── Notes.md ├── README-CN.md ├── README-quasar.md ├── README.md ├── Todos.md ├── builder-nsis.nsh ├── docs ├── 00.png ├── 01.png ├── 02.png ├── 1.png └── doc_home.png ├── index.html ├── package.json ├── postcss.config.cjs ├── public ├── css │ └── font-awesome-6.4.2-pro.css ├── favicon.ico ├── icon.png ├── icons │ ├── aaa.ico │ ├── aaa.png │ ├── bbb.ico │ ├── bbb.png │ ├── favicon-128x128.png │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon-96x96.png │ └── icon.ico └── webfonts │ ├── KFOmCnqEu92Fr1Mu4mxK.woff2 │ ├── fa-brands-400.ttf │ ├── fa-brands-400.woff2 │ ├── fa-duotone-900.ttf │ ├── fa-duotone-900.woff2 │ ├── fa-light-300.ttf │ ├── fa-light-300.woff2 │ ├── fa-regular-400.ttf │ ├── fa-regular-400.woff2 │ ├── fa-sharp-light-300.ttf │ ├── fa-sharp-light-300.woff2 │ ├── fa-sharp-regular-400.ttf │ ├── fa-sharp-regular-400.woff2 │ ├── fa-sharp-solid-900.ttf │ ├── fa-sharp-solid-900.woff2 │ ├── fa-solid-900.ttf │ ├── fa-solid-900.woff2 │ ├── fa-thin-100.ttf │ ├── fa-thin-100.woff2 │ ├── fa-v4compatibility.ttf │ └── fa-v4compatibility.woff2 ├── quasar.config.ts ├── src-electron ├── AsyncReadFilePath.ts ├── IPC-main.ts ├── default-data.ts ├── electron-flag.d.ts ├── electron-main.ts ├── electron-preload.ts ├── icons │ ├── electron-env.d.ts │ ├── icon.icns │ ├── icon.ico │ └── icon.png └── traverseFolder.ts ├── src-old └── old-components │ ├── SetComponent.vue │ └── WaterFall.vue ├── src ├── App.vue ├── assets │ ├── 20240128.png │ ├── edge-avatar.jpg │ ├── error.png │ ├── favicon.ico │ ├── icon.png │ ├── loading.png │ └── quasar-logo-vertical.svg ├── boot │ ├── .gitkeep │ └── i18n.ts ├── components │ ├── DropdownReader.vue │ ├── EssentialLink.vue │ ├── ExampleComponent.vue │ ├── UploadComponent.vue │ ├── WaterFall_biggerpicture.vue │ ├── WaterFall_photoswipe.vue │ ├── WaterFall_viewerjs.vue │ ├── models.ts │ ├── settings │ │ ├── BaseSetting.vue │ │ ├── LanguageChoose.vue │ │ ├── ViewerSelection.vue │ │ └── initLanguage.vue │ ├── testc │ │ ├── TEST.vue │ │ ├── WaterFall.vue │ │ └── waterfall.ts │ ├── w-viewer │ │ ├── BPControllers.vue │ │ └── WPagination.vue │ └── waterfall.ts ├── css │ ├── app.scss │ └── quasar.variables.scss ├── env.d.ts ├── i18n │ ├── en-US │ │ └── index.ts │ ├── index.ts │ └── zh-CN │ │ └── index.ts ├── layouts │ ├── MainLayout.vue │ ├── SettingsLayout.vue │ ├── TestLayout.vue │ └── WViewerLayout.vue ├── pages │ ├── AboutPage.vue │ ├── ErrorNotFound.vue │ ├── IndexPage.vue │ └── test │ │ ├── APage.vue │ │ ├── IndexPage.vue │ │ ├── WIndexPage.vue │ │ ├── initLanguage.vue │ │ └── normalize-path.ts ├── quasar.d.ts ├── router │ ├── index.ts │ └── routes.ts ├── shims-vue.d.ts ├── stores │ ├── example-store.ts │ ├── index.ts │ ├── store-flag.d.ts │ ├── viewerSet-store.ts │ └── wViewerState-store.ts └── window.d.ts ├── tailwind.config.js ├── tmp └── a.ts ├── tsconfig.json ├── tsconfig.vue-tsc.json └── yarn.lock /.backup/.github/workflows.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/.backup/.github/workflows.zip -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /dist 2 | /src-capacitor 3 | /src-cordova 4 | /.quasar 5 | /node_modules 6 | .eslintrc.cjs 7 | .eslintrc.js 8 | /src-ssr 9 | /quasar.config.*.temporary.compiled* -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // https://eslint.org/docs/user-guide/configuring#configuration-cascading-and-hierarchy 3 | // This option interrupts the configuration hierarchy at this file 4 | // Remove this if you have an higher level ESLint config file (it usually happens into a monorepos) 5 | root: true, 6 | 7 | // https://eslint.vuejs.org/user-guide/#how-to-use-a-custom-parser 8 | // Must use parserOptions instead of "parser" to allow vue-eslint-parser to keep working 9 | // `parser: 'vue-eslint-parser'` is already included with any 'plugin:vue/**' config and should be omitted 10 | parserOptions: { 11 | parser: require.resolve('@typescript-eslint/parser'), 12 | extraFileExtensions: [ '.vue' ] 13 | }, 14 | 15 | env: { 16 | browser: true, 17 | es2021: true, 18 | node: true, 19 | 'vue/setup-compiler-macros': true 20 | }, 21 | 22 | // Rules order is important, please avoid shuffling them 23 | extends: [ 24 | // Base ESLint recommended rules 25 | // 'eslint:recommended', 26 | 27 | // https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin#usage 28 | // ESLint typescript rules 29 | 'plugin:@typescript-eslint/recommended', 30 | 31 | // Uncomment any of the lines below to choose desired strictness, 32 | // but leave only one uncommented! 33 | // See https://eslint.vuejs.org/rules/#available-rules 34 | 'plugin:vue/vue3-essential', // Priority A: Essential (Error Prevention) 35 | // 'plugin:vue/vue3-strongly-recommended', // Priority B: Strongly Recommended (Improving Readability) 36 | // 'plugin:vue/vue3-recommended', // Priority C: Recommended (Minimizing Arbitrary Choices and Cognitive Overhead) 37 | 38 | // https://github.com/prettier/eslint-config-prettier#installation 39 | // usage with Prettier, provided by 'eslint-config-prettier'. 40 | 'prettier' 41 | ], 42 | 43 | plugins: [ 44 | // required to apply rules which need type information 45 | '@typescript-eslint', 46 | 47 | // https://eslint.vuejs.org/user-guide/#why-doesn-t-it-work-on-vue-files 48 | // required to lint *.vue files 49 | 'vue' 50 | 51 | // https://github.com/typescript-eslint/typescript-eslint/issues/389#issuecomment-509292674 52 | // Prettier has not been included as plugin to avoid performance impact 53 | // add it as an extension for your IDE 54 | 55 | ], 56 | 57 | globals: { 58 | ga: 'readonly', // Google Analytics 59 | cordova: 'readonly', 60 | __statics: 'readonly', 61 | __QUASAR_SSR__: 'readonly', 62 | __QUASAR_SSR_SERVER__: 'readonly', 63 | __QUASAR_SSR_CLIENT__: 'readonly', 64 | __QUASAR_SSR_PWA__: 'readonly', 65 | process: 'readonly', 66 | Capacitor: 'readonly', 67 | chrome: 'readonly' 68 | }, 69 | 70 | // add your custom rules here 71 | rules: { 72 | 'reportUnusedDisableDirectives': 0, 73 | 74 | quotes: ['warn', 'single', { avoidEscape: true }], 75 | 76 | // this rule, if on, would require explicit return type on the `render` function 77 | '@typescript-eslint/explicit-function-return-type': 'off', 78 | 79 | // in plain CommonJS modules, you can't use `import foo = require('foo')` to pass this rule, so it has to be disabled 80 | '@typescript-eslint/no-var-requires': 'off', 81 | 82 | // The core 'no-unused-vars' rules (in the eslint:recommended ruleset) 83 | // does not work with type definitions 84 | 'no-unused-vars': 'off', 85 | 86 | // allow debugger during development only 87 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off' 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | custom: [] 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: cawa-93 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 16 | 1. Go to '...' 17 | 2. Click on '....' 18 | 3. Scroll down to '....' 19 | 4. See error 20 | 21 | **Expected behavior** 22 | A clear and concise description of what you expected to happen. 23 | 24 | **Screenshots** 25 | If applicable, add screenshots to help explain your problem. 26 | 27 | **Additional context** 28 | Add any other context about the problem here. 29 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Questions & Discussions 4 | url: https://github.com/cawa-93/vite-electron-builder/discussions/categories/q-a 5 | about: Use GitHub discussions for message-board style questions and discussions. 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: cawa-93 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base", 4 | ":semanticCommits", 5 | ":semanticCommitTypeAll(deps)", 6 | ":semanticCommitScopeDisabled", 7 | ":automergeAll", 8 | ":automergeBranch", 9 | ":disableDependencyDashboard", 10 | ":pinVersions", 11 | ":onlyNpm", 12 | ":label(dependencies)" 13 | ], 14 | "packageRules": [ 15 | { 16 | "groupName": "Vite packages", 17 | "matchUpdateTypes": "major", 18 | "matchSourceUrlPrefixes": [ 19 | "https://github.com/vitejs/" 20 | ] 21 | } 22 | ], 23 | "gitNoVerify": [ 24 | "commit", 25 | "push" 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .thumbs.db 3 | node_modules 4 | 5 | # Quasar core related directories 6 | .quasar 7 | /dist 8 | /quasar.config.*.temporary.compiled* 9 | 10 | # local .env files 11 | .env.local* 12 | 13 | # Cordova related directories and files 14 | /src-cordova/node_modules 15 | /src-cordova/platforms 16 | /src-cordova/plugins 17 | /src-cordova/www 18 | 19 | # Capacitor related directories and files 20 | /src-capacitor/www 21 | /src-capacitor/node_modules 22 | 23 | # Log files 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # Editor directories and files 29 | .idea 30 | *.suo 31 | *.ntvs* 32 | *.njsproj 33 | *.sln -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /.idea/deployment.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/jsLibraryMappings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/jsLinters/eslint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/jsonSchemas.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/prettier.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/vite-electron-builder.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/webResources.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | # pnpm-related options 2 | shamefully-hoist=true 3 | strict-peer-dependencies=false 4 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "semi": true 4 | } 5 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "dbaeumer.vscode-eslint", 4 | "esbenp.prettier-vscode", 5 | "editorconfig.editorconfig", 6 | "vue.volar", 7 | "wayou.vscode-todo-highlight" 8 | ], 9 | "unwantedRecommendations": [ 10 | "octref.vetur", 11 | "hookyqr.beautify", 12 | "dbaeumer.jshint", 13 | "ms-vscode.vscode-typescript-tslint-plugin" 14 | ] 15 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "node", 6 | "request": "launch", 7 | "name": "Debug Main Process", 8 | "skipFiles": ["/**"], 9 | "program": "${workspaceFolder}\\scripts\\watch.mjs", 10 | "autoAttachChildProcesses": true 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.bracketPairColorization.enabled": true, 3 | "editor.guides.bracketPairs": true, 4 | "editor.formatOnSave": false, 5 | "editor.defaultFormatter": "esbenp.prettier-vscode", 6 | "editor.codeActionsOnSave": ["source.fixAll.eslint"], 7 | "eslint.validate": ["javascript", "javascriptreact", "typescript", "vue"], 8 | "typescript.tsdk": "node_modules/typescript/lib", 9 | "i18n-ally.localesPaths": ["src/i18n"], 10 | "i18n-ally.displayLanguage": "zh-CN", 11 | "i18n-ally.enabledParsers": ["js", "ts"], 12 | "cSpell.words": [ 13 | "autorenew", 14 | "fpath", 15 | "fullfpath", 16 | "lazyload", 17 | "photoswipe", 18 | "pinia", 19 | "pswp", 20 | "thefile", 21 | "thepath", 22 | "todos", 23 | "viewerjs", 24 | "Wviewer" 25 | ], 26 | "[vue]": { 27 | "editor.defaultFormatter": "esbenp.prettier-vscode" 28 | }, 29 | "i18n-ally.keystyle": "nested" 30 | } 31 | -------------------------------------------------------------------------------- /Notes.md: -------------------------------------------------------------------------------- 1 | Now Viewer: 2 | import Wviewer from 'components/WaterFall_full.vue' 3 | 4 | Viewer Component: 5 | 6 | 7 | 使用 -------------------------------------------------------------------------------- /README-CN.md: -------------------------------------------------------------------------------- 1 | # WViewer 瀑布流图片查看器 2 | 3 |  4 | 5 | **瀑布流**图片查看器,可以遍历文件夹和所有子文件夹并读取其中的全部图片,对于每个文件夹下的图片**按照名称排序**展示。 6 | 如果图片过多,会按照 定制数量(默认40张图片)/一页进行拆分,支持页面跳转,翻页。 7 | 8 | ## 适用范围 9 | 10 | 11 | 当看画师图包的时候,很有可能会碰到这种情况: 12 |  13 | 14 | - 非常多的子文件夹,每个子文件夹中图片又不是很多。图片看起来,很不能尽兴。 15 | - 同时,文件浏览器哪怕是**超大图标**,浏览起来仍然让人不很舒服。 16 | - 想要放大查看某张图片,需要调用图片查看器程序,比较麻烦。 17 | - 使用第三方查看软件,需要复制建库,持续添加…… 但是我只是想浏览一下这个文件夹的图片啊? 18 | 19 | **于是,本软件诞生以解决您的所有需求!!!** 20 | 21 | 具体请查看[使用示例](#使用示例) 22 | 23 | 24 | 25 | ## 特性 26 | - 💡 **展示所有**:递归遍历展示选择文件夹**及其所有子文件夹**中的图片,并按照名称排序展示。 27 | - 🔑 **方便**: 直接拖入一个文件夹,即可查看图片,**0配置,0副作用**!!! 28 | - ⚡ **快捷**: 可配置每一页展示的图片数量,以避免图片加载卡顿。 29 | - 🦋 **瀑布流**:美观,大方 30 | - ⌨️ **快捷键支持**:~~翻页快捷键~~,翻图片快捷键(均为 左右键)支持。 31 | - 🛠️ **实用的图片查看器**:([photoswipe](https://github.com/dimsemenov/photoswipe))支持 图片查看的常规操作,放大,旋转,同样支持浏览上一张,下一张图片。 32 | - 🖥️ **可设置**:支持定制 每页显示图片数量(当硬盘一般且单张图片过大时,建议选择较少的数量,10~20张比较不错),可设置图片筛选器(选定图片类型)…… 33 | 34 | ## 警告 35 | 36 | - 加载大量图片时可能会软件卡顿,这是电脑硬盘读取过慢造成的,耐心等待即可。 37 | - 如果图片过大,硬盘性能不好,可能会造成加载浏览卡顿,这时候,建议使用固态硬盘,或者调小每一页展示的图片数量!! 38 | 39 | ## 使用示例 40 | - **[YouTube](https://www.youtube.com/watch?v=MHicKz_QJ1w)** 41 | - **[Telegram](https://t.me/edge_wasteland/6526)** 42 | - **[哔哩哔哩](https://www.bilibili.com/video/BV1tm411Q7Js/)** 43 | 44 | 45 | ### For NSFW Use 46 | **[TG preview link](https://t.me/edge_wasteland/6465)** 47 | 48 | ## 致歉 49 | 50 | 因为本人时间原因(~~高三~~),个人能力有限(~~是个废物~~)。软件可能存在诸多问题,优化上也很烂,请诸位多多包涵,也欢迎大佬贡献代码。 51 | 52 | ## 最后 53 | 54 | 希望大家赏个**Star**,以满足吾辈这小小的虚荣心吧!!! 55 | 56 | -------------------------------------------------------------------------------- /README-quasar.md: -------------------------------------------------------------------------------- 1 | # Quasar App (ETools) 2 | 3 | A Quasar Project 4 | 5 | ## Install the dependencies 6 | ```bash 7 | yarn 8 | # or 9 | npm install 10 | ``` 11 | 12 | ### Start the app in development mode (hot-code reloading, error reporting, etc.) 13 | ```bash 14 | quasar dev 15 | ``` 16 | 17 | 18 | ### Lint the files 19 | ```bash 20 | yarn lint 21 | # or 22 | npm run lint 23 | ``` 24 | 25 | 26 | ### Format the files 27 | ```bash 28 | yarn format 29 | # or 30 | npm run format 31 | ``` 32 | 33 | 34 | 35 | ### Build the app for production 36 | ```bash 37 | quasar build 38 | ``` 39 | 40 | ### Customize the configuration 41 | See [Configuring quasar.config.js](https://v2.quasar.dev/quasar-cli-vite/quasar-config-js). 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WViewer: Waterfall-Style Image Viewer 2 | 3 |  4 | 5 | A **waterfall-style** image viewer that traverses a folder and all its subfolders to read and display all images. Images within each folder are shown **sorted by name**. 6 | If there are too many images, the software splits them into pages based on a configurable number of images per page (default: 40). It supports page navigation and flipping. 7 | 8 | ## Suitable Use Cases 9 | 10 | 11 | When browsing an artist's image pack, you might encounter situations like this: 12 |  13 | 14 | - Many subfolders, each containing only a few images, making it hard to browse them enjoyably. 15 | - Even with **extra-large icons** in file explorer, browsing can feel cumbersome. 16 | - To enlarge and view a specific image, you must open a separate image viewer, which can be inconvenient. 17 | - Third-party viewers often require library setup, continuous additions, etc., but sometimes you just want to browse a folder's images quickly! 18 | 19 | **That's why this software was created—to solve all your needs!!!** 20 | 21 | For details, see [Usage Examples](#usage-examples). 22 | 23 | 24 | 25 | ## Features 26 | - 💡 **Show Everything**: Recursively traverse the selected folder **and all its subfolders**, displaying images sorted by name. 27 | - 🔑 **Convenience**: Simply drag a folder into the program to view its images—**zero configuration, zero side effects**!!! 28 | - ⚡ **Speed**: Configurable images per page to avoid lag during image loading. 29 | - 🦋 **Waterfall Layout**: Aesthetic and elegant. 30 | - ⌨️ **Keyboard Shortcuts**: ~~Page navigation shortcuts~~ and image navigation shortcuts (Left/Right keys supported). 31 | - 🛠️ **Practical Image Viewer**: ([photoswipe](https://github.com/dimsemenov/photoswipe)) Supports standard image viewer operations like zooming, rotating, and browsing previous/next images. 32 | - 🖥️ **Customizable Settings**: Configurable number of images per page (if your hard drive is slow or individual images are large, a smaller number like 10–20 is recommended). Includes a filter for selecting specific image types. 33 | 34 | ## Warnings 35 | - Loading a large number of images may cause lag due to slow hard drive read speeds—please be patient. 36 | - If the images are large and your hard drive performs poorly, browsing may be sluggish. In such cases, consider using an SSD or reducing the number of images displayed per page. 37 | 38 | ## Usage Examples 39 | - **[YouTube](https://www.youtube.com/watch?v=MHicKz_QJ1w)** 40 | - **[Telegram](https://t.me/edge_wasteland/6526)** 41 | - **[Bilibili](https://www.bilibili.com/video/BV1tm411Q7Js/)** 42 | 43 | 44 | ### For NSFW Use 45 | **[TG Preview Link](https://t.me/edge_wasteland/6465)** 46 | 47 | ## Apologies 48 | Due to time constraints (~~final year of high school~~) and limited personal skills (~~just a noob~~), the software may have several issues and suboptimal performance. Please be understanding and feel free to contribute code improvements! 49 | 50 | ## Finally 51 | If you like this project, please consider giving it a **Star**—it would satisfy my tiny bit of vanity!!! 😊 -------------------------------------------------------------------------------- /Todos.md: -------------------------------------------------------------------------------- 1 | - [Part1](#part1) 2 | - [缩略图](#缩略图) 3 | - [当前版本记事](#当前版本记事) 4 | - [Bugs](#bugs) 5 | - [规范化](#规范化) 6 | - [自定义灯箱](#自定义灯箱) 7 | - [TODO](#todo) 8 | - [Note](#note) 9 | - [布局](#布局) 10 | 11 | ## Part1 12 | ### 缩略图 13 | 支援缩略图目录,及同级目录下有 thumbnails 文件夹都支援(及读取图片的时候判断一下同级目录下是否有 thumbnails 文件夹 或者 根目录下 有无 thumbnails 及 thumbnails文件夹内是否有该图片的缩略图) 14 | 该结构受 Steam 支持? 15 | 16 | ### 当前版本记事 17 | - Fix 文件读取少读的Bug 18 | - 19 | 20 | ### Bugs 21 | - [ ] 直接记录时当文件夹中文件个数小于pagenumber时,会发生读少的情况 22 | - [ ] 长文件名(中间有空格和-的文件名)展示错误:"kieed23232 - 1829852267304140916 - Copy (2).gif" 23 | - [ ] 当重载新的文件夹,应当放弃对旧文件夹的读取,防止旧文件夹非常大,浪费过多的时间去读 24 | 25 | 26 | ## 规范化 27 | - 规范后端交互接口,可以让不同的前端给出不同的收集要求,比如photoswipe需要大小,viewerjs不需要。 28 | 29 | 30 | - https://github.com/dimsemenov/photoswipe-video-plugin 31 | 我仍然想知道,是否自己重写灯箱,或者利用好现有灯箱 32 | 33 | 还是自己重写灯箱更好一点,不好不好,重写灯箱要自己处理手势之类的东西,太烦人了,想办法解决Video封面图以及大小 34 | 35 | 将所有逻辑独立出来,toolbox, 比如图片删除逻辑,应该是公用的才对 36 | 37 | 还是研究 Photoswipe 的 API 以及插件的运作方式吧。。。 38 | 39 | ## 自定义灯箱 40 | 新解决方案,用 bigger-picture 重构,目前唯一问题就是 视频尺寸 41 | 42 | https://codesandbox.io/p/sandbox/bigger-picture-video-hua-lang-forked-8gdtlj 43 | 44 | 通过 45 | https://photoswipe.com/data-sources/#custom-html-markup 46 | 实现动态的获取图片/视频的尺寸 47 | 48 | 49 | 灯箱设计: 50 | - Core: 当灯箱打开后,我们要有一个指向当前ID的指针 51 | 这在删除的时候会出现问题,设计滞后 52 | 53 | 我们有一个图片列表 54 | 55 | 图片查看器使用 bigger-picture(X),我需要一个不带灯箱的图片查看器,要不就是 56 | 57 | 58 | - 不要把分页移出去,把查看器移出去,然后把现在的 Wviewer 放到 Layout 里面? 59 | - 把 setting 也放到 Layout 里面 60 | 61 | ## TODO 62 | - [ ] 增加介绍,时间顺序仅针对单文件夹 63 | - [x] 添加增加按照时间排序查看图片吗 64 | - [ ] 自动翻页:滚动到页面底部自动识别继续滚动的话加载下一页,包括动画效果 65 | - [ ] 添加到右键目录菜单 66 | - [ ] 图片打标记功能,和 复制,移动功能分开 67 | - [ ] 判断 用户自定义安装目录 68 | - [ ] 找一个本地数据库调试工具 69 | - [ ] 浏览器开发者插件: https://devtools.vuejs.org/guide/standalone 70 | - [ ] 打包大小分析 71 | - [x] 瀑布流:默认开启的定位图片支持 72 | - [ ] 瀑布流:滚动持续加载支持 73 | - [ ] This PIC is not good, use ThisPicIndex instead 74 | - [ ] 添加视频格式支持!(怎样获取大小数据呢?采用必须生成预览图的设计) 75 | - [x] 更改软件名称为 WViewer 76 | - [x] 优化文件夹获取算法 77 | - [x] 已经加载了文件夹的前提下,能不能再直接拖动别的文件夹进软件边框范围内重新加载新的文件夹 78 | 79 | 80 | 81 | 82 | 通过 https://www.npmjs.com/package/file-type 判断文件类型,通过 前端方案,动态载入(瀑布流变动?) 大小数据。 83 | 84 | - 如果上述方案不可行,用户可选择 图片查看器引擎,从而支持图片+视频查看。 85 | viewer.js v-viewer。 https://videojs.com/city 不好? 86 | 87 | bigger-picture: https://github.com/henrygd/bigger-picture 88 | ``` 89 | // open (will be a child of the target element above) 90 | bp.open({ 91 | items: document.querySelectorAll('#images a'), 92 | }) 93 | ``` 94 | 选择器使用 ID + Class 95 | 所以支持视频, a 标签 或者 标签《?《 (测试别的标签是否可行) 96 | 这些都是参考的库。 97 | 98 | #任务列表 99 | - [ ] LazyLoad: https://github.com/hilongjw/vue-lazyload 100 | - [ ] 增加分页处加载动画 101 | - [ ] 增加图片准备 转圈效果(没啥意义) 102 | 103 | 多语言尝试 i18n 104 | 105 | 支援 图片压缩,缩略图(同样采用async 策略) 106 | 支持策略设置,Async 缩略图,就是 缩略图优先加载,没有就算《 107 | 108 | 重新设计 主页,支持最近打开图库(当然可以开/关) 109 | 110 | 支持记忆阅读到的内容 (分页* 每页个数) 111 | 112 | 目前的规划《 113 | 114 | 115 | https://github.com/lokesh/lightbox2 116 | 117 | 118 | ## Note 119 | 窗口名称 依据 package.json productName 120 | 121 | ## 布局 122 | ├── components/ # 通用组件 123 | │ ├── auth/ 124 | │ │ ├── Login.vue 125 | │ │ └── Register.vue 126 | │ ├── dashboard/ 127 | │ │ ├── Overview.vue 128 | │ │ └── Stats.vue 129 | │ ├── settings/ 130 | | | ├── 查看器.vue 131 | │ │ ├── 瀑布流.vue 132 | │ │ └── 瀑布流分页.vue 133 | │ └── shared/ 134 | │ ├── Header.vue 135 | │ └── Footer.vue 136 | ├── directives/ # 自定义指令 137 | │ └── click-outside.js 138 | ├── layouts/ # 页面布局组件 139 | │ ├── DefaultLayout.vue 140 | │ └── AuthLayout.vue 141 | -------------------------------------------------------------------------------- /builder-nsis.nsh: -------------------------------------------------------------------------------- 1 | !macro preInit 2 | SetRegView 64 3 | WriteRegExpandStr HKLM "${INSTALL_REGISTRY_KEY}" InstallLocation "C:\Program Files\W Veiwer" 4 | WriteRegExpandStr HkCU "${INSTALL_REGISTRY_KEY}" InstallLocation "C:\Program Files\W Veiwer" 5 | SetRegView 32 6 | WriteRegExpandStr HKLM "${INSTALL_REGISTRY_KEY}" InstallLocation "C:\Program Files\W Veiwer" 7 | WriteRegExpandStr HkCU "${INSTALL_REGISTRY_KEY}" InstallLocation "C:\Program Files\W Veiwer" 8 | !macroend 9 | -------------------------------------------------------------------------------- /docs/00.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/docs/00.png -------------------------------------------------------------------------------- /docs/01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/docs/01.png -------------------------------------------------------------------------------- /docs/02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/docs/02.png -------------------------------------------------------------------------------- /docs/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/docs/1.png -------------------------------------------------------------------------------- /docs/doc_home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/docs/doc_home.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <%= productName %> 7 | 8 | 9 | 10 | 11 | 12 | 13 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "waterfall-picture-viewer", 3 | "version": "1.4.0", 4 | "versionKeys": "", 5 | "description": "A simple picture viewer", 6 | "productName": "WViewer", 7 | "author": "边缘坐标 ", 8 | "private": true, 9 | "type": "module", 10 | "scripts": { 11 | "lint": "eslint --ext .js,.ts,.vue ./", 12 | "format": "prettier --write \"**/*.{js,ts,vue,scss,html,md,json}\" --ignore-path .gitignore", 13 | "test": "echo \"No test specified\" && exit 0", 14 | "dev-spa": "quasar dev", 15 | "dev": "quasar dev -m electron", 16 | "build": "quasar build -m electron" 17 | }, 18 | "dependencies": { 19 | "@electron/remote": "^2.1.2", 20 | "@quasar/extras": "^1.16.12", 21 | "bigger-picture": "^1.1.17", 22 | "electron-store": "^8.1.0", 23 | "fs-extra": "^11.2.0", 24 | "image-size": "^1.1.1", 25 | "photoswipe": "^5.4.3", 26 | "pinia": "^2.0.11", 27 | "quasar": "^2.16.6", 28 | "trash": "^7.2.0", 29 | "vue": "^3.0.0", 30 | "vue-i18n": "^9.2.2", 31 | "vue-router": "^4.0.0" 32 | }, 33 | "devDependencies": { 34 | "@intlify/unplugin-vue-i18n": "^4.0.0", 35 | "@quasar/app-vite": "^2.0.0-beta.12", 36 | "@types/node": "^20.10.6", 37 | "@typescript-eslint/eslint-plugin": "^6.18.1", 38 | "@typescript-eslint/parser": "^6.18.1", 39 | "autoprefixer": "^10.4.16", 40 | "daisyui": "^4.4.24", 41 | "electron": "^28.1.0", 42 | "electron-builder": "^24.3.0", 43 | "electron-packager": "^17.1.1", 44 | "eslint": "^8.10.0", 45 | "eslint-config-prettier": "^9.1.0", 46 | "eslint-plugin-vue": "^9.0.0", 47 | "naive-ui": "^2.36.0", 48 | "postcss": "^8.4.32", 49 | "prettier": "^3.2.2", 50 | "tailwindcss": "^3.4.0", 51 | "typescript": "^5.3.3", 52 | "v-viewer": "^3.0.11", 53 | "viewerjs": "^1.11.6", 54 | "vite-plugin-electron-renderer": "^0.14.5", 55 | "vue-waterfall-plugin-next": "^2.3.1" 56 | }, 57 | "engines": { 58 | "node": "^20 || ^18 || ^16 || ^14.19", 59 | "npm": ">= 6.13.4", 60 | "yarn": ">= 1.21.1" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /postcss.config.cjs: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | // https://github.com/michael-ciniawsky/postcss-load-config 3 | 4 | module.exports = { 5 | plugins: { 6 | // https://github.com/postcss/autoprefixer 7 | autoprefixer: { 8 | overrideBrowserslist: [ 9 | 'last 4 Chrome versions', 10 | 'last 4 Firefox versions', 11 | 'last 4 Edge versions', 12 | 'last 4 Safari versions', 13 | 'last 4 Android versions', 14 | 'last 4 ChromeAndroid versions', 15 | 'last 4 FirefoxAndroid versions', 16 | 'last 4 iOS versions', 17 | ], 18 | }, 19 | tailwindcss: {}, 20 | // https://github.com/elchininet/postcss-rtlcss 21 | // If you want to support RTL css, then 22 | // 1. yarn/npm install postcss-rtlcss 23 | // 2. optionally set quasar.config.js > framework > lang to an RTL language 24 | // 3. uncomment the following line: 25 | // require('postcss-rtlcss') 26 | }, 27 | }; 28 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/public/favicon.ico -------------------------------------------------------------------------------- /public/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/public/icon.png -------------------------------------------------------------------------------- /public/icons/aaa.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/public/icons/aaa.ico -------------------------------------------------------------------------------- /public/icons/aaa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/public/icons/aaa.png -------------------------------------------------------------------------------- /public/icons/bbb.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/public/icons/bbb.ico -------------------------------------------------------------------------------- /public/icons/bbb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/public/icons/bbb.png -------------------------------------------------------------------------------- /public/icons/favicon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/public/icons/favicon-128x128.png -------------------------------------------------------------------------------- /public/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/public/icons/favicon-16x16.png -------------------------------------------------------------------------------- /public/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/public/icons/favicon-32x32.png -------------------------------------------------------------------------------- /public/icons/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/public/icons/favicon-96x96.png -------------------------------------------------------------------------------- /public/icons/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/public/icons/icon.ico -------------------------------------------------------------------------------- /public/webfonts/KFOmCnqEu92Fr1Mu4mxK.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/public/webfonts/KFOmCnqEu92Fr1Mu4mxK.woff2 -------------------------------------------------------------------------------- /public/webfonts/fa-brands-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/public/webfonts/fa-brands-400.ttf -------------------------------------------------------------------------------- /public/webfonts/fa-brands-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/public/webfonts/fa-brands-400.woff2 -------------------------------------------------------------------------------- /public/webfonts/fa-duotone-900.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/public/webfonts/fa-duotone-900.ttf -------------------------------------------------------------------------------- /public/webfonts/fa-duotone-900.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/public/webfonts/fa-duotone-900.woff2 -------------------------------------------------------------------------------- /public/webfonts/fa-light-300.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/public/webfonts/fa-light-300.ttf -------------------------------------------------------------------------------- /public/webfonts/fa-light-300.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/public/webfonts/fa-light-300.woff2 -------------------------------------------------------------------------------- /public/webfonts/fa-regular-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/public/webfonts/fa-regular-400.ttf -------------------------------------------------------------------------------- /public/webfonts/fa-regular-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/public/webfonts/fa-regular-400.woff2 -------------------------------------------------------------------------------- /public/webfonts/fa-sharp-light-300.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/public/webfonts/fa-sharp-light-300.ttf -------------------------------------------------------------------------------- /public/webfonts/fa-sharp-light-300.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/public/webfonts/fa-sharp-light-300.woff2 -------------------------------------------------------------------------------- /public/webfonts/fa-sharp-regular-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/public/webfonts/fa-sharp-regular-400.ttf -------------------------------------------------------------------------------- /public/webfonts/fa-sharp-regular-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/public/webfonts/fa-sharp-regular-400.woff2 -------------------------------------------------------------------------------- /public/webfonts/fa-sharp-solid-900.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/public/webfonts/fa-sharp-solid-900.ttf -------------------------------------------------------------------------------- /public/webfonts/fa-sharp-solid-900.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/public/webfonts/fa-sharp-solid-900.woff2 -------------------------------------------------------------------------------- /public/webfonts/fa-solid-900.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/public/webfonts/fa-solid-900.ttf -------------------------------------------------------------------------------- /public/webfonts/fa-solid-900.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/public/webfonts/fa-solid-900.woff2 -------------------------------------------------------------------------------- /public/webfonts/fa-thin-100.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/public/webfonts/fa-thin-100.ttf -------------------------------------------------------------------------------- /public/webfonts/fa-thin-100.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/public/webfonts/fa-thin-100.woff2 -------------------------------------------------------------------------------- /public/webfonts/fa-v4compatibility.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/public/webfonts/fa-v4compatibility.ttf -------------------------------------------------------------------------------- /public/webfonts/fa-v4compatibility.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/public/webfonts/fa-v4compatibility.woff2 -------------------------------------------------------------------------------- /quasar.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 3 | /* 4 | * This file runs in a Node context (it's NOT transpiled by Babel), so use only 5 | * the ES6 features that are supported by your Node version. https://node.green/ 6 | */ 7 | 8 | // Configuration for your app 9 | // https://v2.quasar.dev/quasar-cli-vite/quasar-config-js 10 | 11 | import { configure } from 'quasar/wrappers'; 12 | // import path from 'path'; 13 | import { fileURLToPath } from 'node:url'; 14 | 15 | export default configure((ctx) => { 16 | return { 17 | eslint: { 18 | // fix: true, 19 | // include: [], 20 | // exclude: [], 21 | // rawOptions: {}, 22 | warnings: true, 23 | errors: true, 24 | }, 25 | 26 | // https://v2.quasar.dev/quasar-cli-vite/prefetch-feature 27 | // preFetch: true, 28 | 29 | // app boot file (/src/boot) 30 | // --> boot files are part of "main.js" 31 | // https://v2.quasar.dev/quasar-cli-vite/boot-files 32 | boot: ['i18n'], 33 | 34 | // https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#css 35 | css: ['app.scss'], 36 | 37 | // Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#build 38 | build: { 39 | target: { 40 | browser: ['es2019', 'edge88', 'firefox78', 'chrome87', 'safari13.1'], 41 | node: 'node20', 42 | }, 43 | 44 | env: { 45 | version: JSON.stringify(require('./package.json').version).replace( 46 | /^"|"$/g, 47 | '', 48 | ), 49 | versionKeys: JSON.stringify( 50 | require('./package.json').versionKeys, 51 | ).replace(/^"|"$/g, ''), 52 | }, 53 | 54 | vueRouterMode: 'hash', // available values: 'hash', 'history' 55 | // vueRouterBase, 56 | // vueOptionsAPI: false, 57 | 58 | /** 59 | * Automatically open remote Vue Devtools when running in development mode. 60 | */ 61 | // vueDevtools: true, 62 | /** 63 | * Folder where Quasar CLI should look for .env* files. 64 | * Can be an absolute path or a relative path to project root directory. 65 | * 66 | * @default project root directory 67 | */ 68 | // envFolder: '', 69 | /** 70 | * Additional .env* files to be loaded. 71 | * Each entry can be an absolute path or a relative path to quasar.config > build > envFolder. 72 | * 73 | * @example ['.env.somefile', '../.env.someotherfile'] 74 | */ 75 | // envFiles: [''], 76 | 77 | // publicPath: '/', 78 | // analyze: true, 79 | // env: {}, 80 | // rawDefine: {} 81 | // ignorePublicFolder: true, 82 | // minify: false, 83 | // polyfillModulePreload: true, 84 | // distDir 85 | 86 | // extendViteConf (viteConf) {}, 87 | // viteVuePluginOptions: {}, 88 | 89 | vitePlugins: [ 90 | [ 91 | 'vite-plugin-checker', 92 | { 93 | vueTsc: { 94 | tsconfigPath: 'tsconfig.vue-tsc.json', 95 | }, 96 | eslint: { 97 | lintCommand: 'eslint "./**/*.{js,ts,mjs,cjs,vue}"', 98 | }, 99 | }, 100 | { server: false }, 101 | ], 102 | [ 103 | '@intlify/unplugin-vue-i18n/vite', 104 | { 105 | // if you want to use Vue I18n Legacy API, you need to set `compositionOnly: false` 106 | // compositionOnly: false, 107 | 108 | // if you want to use named tokens in your Vue I18n messages, such as 'Hello {name}', 109 | // you need to set `runtimeOnly: false` 110 | // runtimeOnly: false, 111 | 112 | // you need to set i18n resource including paths ! 113 | include: [fileURLToPath(new URL('./src/i18n', import.meta.url))], 114 | ssr: ctx.modeName === 'ssr', 115 | }, 116 | ], 117 | // [require('vite-plugin-electron-renderer'), {}], 118 | ], 119 | }, 120 | 121 | // Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#devServer 122 | devServer: { 123 | // https: true 124 | open: true, // opens browser window automatically 125 | }, 126 | 127 | /** 128 | * What to import from [@quasar/extras](https://github.com/quasarframework/quasar/tree/dev/extras) package. 129 | * @example ['material-icons', 'roboto-font', 'ionicons-v4'] 130 | */ 131 | extras: [ 132 | // 'ionicons-v4', 133 | 'mdi-v7', 134 | // 'fontawesome-v6', 135 | // 'eva-icons', 136 | // 'themify', 137 | // 'line-awesome', 138 | // 'roboto-font-latin-ext', // this or either 'roboto-font', NEVER both! 139 | 140 | 'roboto-font', // optional, you are not bound to it 141 | 'material-icons', // optional, you are not bound to it 142 | ], 143 | 144 | // https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#framework 145 | framework: { 146 | config: {notify: /* look at QuasarConfOptions from the API card */{}}, 147 | 148 | iconSet: 'mdi-v7', // Quasar icon set 149 | // lang: 'en-US', // Quasar language pack 150 | 151 | // For special cases outside of where the auto-import strategy can have an impact 152 | // (like functional components as one of the examples), 153 | // you can manually specify Quasar components/directives to be available everywhere: 154 | // 155 | // components: [], 156 | // directives: [], 157 | 158 | // Quasar plugins 159 | plugins: [ 160 | 'Notify', 161 | ], 162 | }, 163 | 164 | // animations: 'all', // --- includes all animations 165 | // https://v2.quasar.dev/options/animations 166 | animations: [], 167 | 168 | // https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#sourcefiles 169 | // sourceFiles: { 170 | // rootComponent: 'src/App.vue', 171 | // router: 'src/router/index', 172 | // store: 'src/store/index', 173 | // registerServiceWorker: 'src-pwa/register-service-worker', 174 | // serviceWorker: 'src-pwa/custom-service-worker', 175 | // pwaManifestFile: 'src-pwa/manifest.json', 176 | // electronMain: 'src-electron/electron-main', 177 | // electronPreload: 'src-electron/electron-preload' 178 | // }, 179 | 180 | // https://v2.quasar.dev/quasar-cli-vite/developing-ssr/configuring-ssr 181 | ssr: { 182 | // ssrPwaHtmlFilename: 'offline.html', // do NOT use index.html as name! 183 | // will mess up SSR 184 | 185 | // extendSSRWebserverConf (esbuildConf) {}, 186 | // extendPackageJson (json) {}, 187 | 188 | pwa: false, 189 | 190 | // manualStoreHydration: true, 191 | // manualPostHydrationTrigger: true, 192 | 193 | prodPort: 3000, // The default port that the production server should use 194 | // (gets superseded if process.env.PORT is specified at runtime) 195 | 196 | middlewares: [ 197 | 'render', // keep this as last one 198 | ], 199 | }, 200 | 201 | // https://v2.quasar.dev/quasar-cli-vite/developing-pwa/configuring-pwa 202 | // pwa: { 203 | // workboxMode: 'generateSW', // or 'injectManifest' 204 | // injectPwaMetaTags: true, 205 | // swFilename: 'sw.js', 206 | // manifestFilename: 'manifest.json', 207 | // useCredentialsForManifestTag: false 208 | // // useFilenameHashes: true, 209 | // // extendGenerateSWOptions (cfg) {} 210 | // // extendInjectManifestOptions (cfg) {}, 211 | // // extendManifestJson (json) {} 212 | // // extendPWACustomSWConf (esbuildConf) {} 213 | // }, 214 | 215 | // Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-cordova-apps/configuring-cordova 216 | cordova: { 217 | // noIosLegacyBuildFlag: true, // uncomment only if you know what you are doing 218 | }, 219 | 220 | // Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-capacitor-apps/configuring-capacitor 221 | capacitor: { 222 | hideSplashscreen: true, 223 | }, 224 | 225 | // Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-electron-apps/configuring-electron 226 | electron: { 227 | // Electron preload scripts (if any) from /src-electron, WITHOUT file extension 228 | preloadScripts: ['electron-preload'], 229 | 230 | // extendElectronMainConf (esbuildConf) 231 | // extendElectronPreloadConf (esbuildConf) 232 | 233 | inspectPort: 5858, 234 | 235 | bundler: 'builder', // 'packager' or 'builder' 236 | 237 | packager: { 238 | // https://github.com/electron-userland/electron-packager/blob/master/docs/api.md#options 239 | // OS X / Mac App Store 240 | // appBundleId: '', 241 | // appCategoryType: '', 242 | // osxSign: '', 243 | // protocol: 'myapp://path', 244 | // Windows only 245 | // win32metadata: { ... } 246 | }, 247 | 248 | builder: { 249 | // https://www.electron.build/configuration/configuration 250 | appId: 'waterfall-picture-viewer', 251 | // productName: 'Waterfall Picture Viewer', 252 | productName: 'WViewer', 253 | directories: { 254 | output: 'dist', 255 | buildResources: 'buildResources', 256 | }, 257 | extraResources: ['./public/'], 258 | // extraFiles: [ 259 | // { 260 | // from: './public/', 261 | // to: '/', 262 | // filter: ['**/*'], 263 | // }, 264 | // ], 265 | // extraMetadata: { 266 | // version: '1.0.0', 267 | // }, 268 | // asar: 'false', 269 | win: { 270 | target: 'nsis', 271 | }, 272 | nsis: { 273 | include: './builder-nsis.nsh', 274 | oneClick: false, // 一键安装 275 | perMachine: true, // 是否开启安装时权限限制(此电脑或当前用户) 276 | allowElevation: true, // 允许请求提升。 如果为false,则用户必须使用提升的权限重新启动安装程序。 277 | allowToChangeInstallationDirectory: true, // 允许修改安装目录 278 | installerIcon: './public/icons/icon.ico', // 安装图标 279 | uninstallerIcon: './public/icons/bbb.ico', //卸载图标 280 | installerHeaderIcon: './public/icons/icon.ico', // 安装时头部图标 281 | createDesktopShortcut: true, // 创建桌面图标 282 | createStartMenuShortcut: true, // 创建开始菜单图标 283 | shortcutName: 'WViewer', // 图标名称 284 | // shortcutName: 'Waterfall Picture Viewer', // 图标名称 285 | }, 286 | }, 287 | }, 288 | 289 | // Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-browser-extensions/configuring-bex 290 | bex: { 291 | contentScripts: ['my-content-script'], 292 | 293 | // extendBexScriptsConf (esbuildConf) {} 294 | // extendBexManifestJson (json) {} 295 | }, 296 | }; 297 | }); 298 | -------------------------------------------------------------------------------- /src-electron/AsyncReadFilePath.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs-extra'; 2 | import path from 'path'; 3 | import imageSize from 'image-size'; 4 | import { promisify } from 'util'; 5 | import type { WImage } from './traverseFolder'; 6 | import { mainWindow } from './electron-main'; 7 | import { get } from 'http'; 8 | 9 | const sizeOf = promisify(imageSize); 10 | 11 | export default class AsyncReadFilePath { 12 | picLinks = [] as WImage[]; 13 | /** 有效的图片格式 */ 14 | picFormats = [] as string[]; 15 | /** 有效的视频格式 */ 16 | videoFormats = [] as string[]; 17 | /** 任务名称 */ 18 | taskName; 19 | /** 每页分割的大小 */ 20 | pageSize = 20; 21 | sortMethod = 'name-asc'; 22 | 23 | constructor( 24 | taskName: string, 25 | picFormats: string[], 26 | videoFormats: string[], 27 | pageSize: number, 28 | sortMethod: string, 29 | ) { 30 | this.taskName = taskName; 31 | this.picFormats = picFormats; 32 | this.videoFormats = videoFormats; 33 | this.pageSize = pageSize || 20; 34 | this.sortMethod = sortMethod || 'name-asc'; 35 | } 36 | 37 | async getSortedFilesByDate(files, currentPath) { 38 | 39 | // 获取文件的详细信息 40 | await Promise.all( 41 | files.map(async (file) => { 42 | const filePath = path.join(currentPath, file.name); 43 | const stats = await fs.stat(filePath); // 获取文件的状态信息 44 | file.mtime = stats.mtime; // 添加时间属性 45 | // return { name: file.name, isDir: file.isDirectory(), mtime: stats.mtime }; 46 | }) 47 | ); 48 | 49 | // ! 实际上是逆序的,因为是先进后出,所以要反着排序 50 | if (this.sortMethod === 'time-asc') { 51 | // 按修改时间升序排列 52 | files.sort((a, b) => b.mtime - a.mtime); 53 | } else if (this.sortMethod === 'time-desc') { 54 | // 按修改时间降序排列 55 | files.sort((a, b) => a.mtime - b.mtime); 56 | } 57 | // console.log('fileDetails', files); 58 | return files; 59 | } 60 | 61 | async readDirectory(dir: String | Array) { 62 | const stack: any = (() => { 63 | if (typeof dir === 'string') { 64 | return [dir]; // use this way to avoid judgment of empty strings 65 | } 66 | if (Array.isArray(dir)) { 67 | // Array 68 | dir.sort((a, b) => 69 | b.localeCompare(a, undefined, { sensitivity: 'base' }), 70 | ); 71 | return dir; 72 | } 73 | return ''; 74 | })(); 75 | const pageStack = [] as WImage[]; 76 | 77 | while (stack.length >= 1) { 78 | console.log('stack', stack); 79 | // prioritize in depth 80 | const currentPath = stack.pop(); 81 | console.log('stack after pop', stack); 82 | if (!currentPath) { 83 | continue; 84 | } 85 | let files = await fs.readdir(currentPath, { withFileTypes: true }); 86 | 87 | 88 | console.log('sortMethod', this.sortMethod); 89 | // ! 实际上是逆序的,因为是先进后出,所以要反着排序 90 | if(this.sortMethod == 'name-asc') { 91 | files.sort((a, b) => 92 | b.name.localeCompare(a.name, undefined, { sensitivity: 'base' }), 93 | ); 94 | } else if(this.sortMethod == 'name-desc') { 95 | files.sort((a, b) => 96 | b.name.localeCompare(b.name, undefined, { sensitivity: 'base' }), 97 | ); 98 | } else if(this.sortMethod == 'time-asc') { 99 | files = await this.getSortedFilesByDate(files, currentPath); 100 | } else if(this.sortMethod == 'time-desc') { 101 | files = await this.getSortedFilesByDate(files, currentPath); 102 | } 103 | console.log('files', files); 104 | const result = [] as WImage[]; 105 | for (const file of files) { 106 | const fullPath = path.join(currentPath, file.name); 107 | if (file.isDirectory()) { 108 | stack.push(fullPath); 109 | } else if (file.isFile()) { 110 | const extname = path.extname(fullPath).toLowerCase(); 111 | if (this.picFormats.includes(extname)) { 112 | await sizeOf(fullPath) 113 | .then((dimensions) => { 114 | // console.log('sizeOf', dimensions); 115 | result.push({ 116 | source: fullPath, 117 | src: 'atom://' + fullPath, 118 | srcThumb: 'atom://' + fullPath, 119 | width: dimensions?.width, 120 | height: dimensions?.height, 121 | }); 122 | }) 123 | .catch((err) => { 124 | console.log('sizeOf error', err); 125 | }); 126 | } else if (this.videoFormats.includes(extname)) { 127 | // console.log('video', fullPath) 128 | // [{"src": "example.webm", "type": "video/webm"}] 129 | // result.push({ 130 | // isVideo: true, 131 | // source: fullPath, 132 | // originalSrc: 'atom://' + fullPath, 133 | // src: { 134 | // src: 'atom://' + fullPath, 135 | // type: 'video/' + extname.substring(1), 136 | // }, 137 | // srcThumb: 'atom://' + fullPath, 138 | // width: 1920, 139 | // height: 1080, 140 | // }); 141 | } 142 | } 143 | } 144 | pageStack.push(...result.reverse()); 145 | if (pageStack.length >= this.pageSize) { 146 | this.picLinks.push(...pageStack.splice(0, this.pageSize)); 147 | this.taskReport(); 148 | } 149 | } 150 | 151 | // console.log('pageStack', pageStack); 152 | if (pageStack.length) { 153 | this.picLinks.push(...pageStack.splice(0, pageStack.length)); 154 | this.taskReport(); 155 | } 156 | } 157 | 158 | taskReport() { 159 | if (mainWindow && this.picLinks.length) { 160 | console.log('taskReport', this.picLinks.length); 161 | mainWindow.webContents.send( 162 | 'async:imageLinks-append', 163 | this.taskName, 164 | this.picLinks, 165 | ); 166 | } 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src-electron/IPC-main.ts: -------------------------------------------------------------------------------- 1 | import { BrowserWindow, ipcMain, shell, dialog } from 'electron'; 2 | import Store from 'electron-store'; 3 | import { schema } from './default-data'; 4 | 5 | import fs from 'fs-extra'; 6 | import trash from 'trash'; 7 | 8 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 9 | function isFileSync(path) { 10 | try { 11 | // 使用 fs.existsSync 检查路径是否存在 12 | if (fs.existsSync(path)) { 13 | // 使用 fs.statSync 获取路径的状态 14 | const stats = fs.statSync(path); 15 | // 判断是否是文件 16 | return stats.isFile(); 17 | } 18 | return false; 19 | } catch (err) { 20 | // 处理错误 21 | console.error('Error checking file synchronously:', err); 22 | return false; 23 | } 24 | } 25 | 26 | let store = new Store(); 27 | // 重构判断是否为空并 28 | 29 | import { imageRetrieval, imageRetrievalAsync } from './traverseFolder'; 30 | 31 | export function data_init() { 32 | if (!store.get('itemNum')) store = new Store({ schema }); 33 | console.log('pre inited.'); 34 | } 35 | 36 | // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any 37 | export function ipcMains(value: void): any { 38 | ipcMain.on('read-file', (event) => { 39 | // const fileContent = fs.readFileSync('./file-to-read.txt', { encoding: 'utf-8' }) 40 | const fileContent = 'ddd'; 41 | // Send back an IPC event to the renderer process with the file content. 42 | event.sender.send('read-file-success', fileContent); 43 | }); 44 | 45 | // ANCHOR Store API 46 | ipcMain.handle('store-get', (event, key) => { 47 | // console.log(store.get(key)); 48 | return store.get(key); 49 | }); 50 | 51 | ipcMain.handle('store-set', (event, key, value) => { 52 | return store.set(key, value); 53 | }); 54 | 55 | ipcMain.handle('store-delete', (event, key) => { 56 | // console.log(store.get(key)); 57 | store.delete(key); 58 | return 1; 59 | }); 60 | 61 | // ANCHOR Tool API 62 | ipcMain.handle('tool-traverseFolder', (event, path, pFormats) => { 63 | console.log(path, pFormats); 64 | return imageRetrieval(path, pFormats); 65 | }); 66 | 67 | ipcMain.handle( 68 | 'tool-traverseFolder-async', 69 | (event, path, pFormats, vFormats, pPageNum, sortMethod) => { 70 | return imageRetrievalAsync(path, pFormats, vFormats, pPageNum, sortMethod); 71 | }, 72 | ); 73 | 74 | ipcMain.handle('tool-openLink', (event, link) => { 75 | console.log(link); 76 | return shell.openExternal(link); 77 | }); 78 | 79 | ipcMain.handle('tool-openPath', (event, link) => { 80 | shell.openPath(link); 81 | }); 82 | 83 | ipcMain.handle('tool-delPic', (event, src) => { 84 | // fs.removeSync(src); 85 | if (isFileSync(src)) { 86 | trash(src); 87 | } 88 | console.log('Del: ', src); 89 | }); 90 | ipcMain.handle('tool-selectFolders', async (event) => { 91 | console.log('selectFolders'); 92 | const result = await dialog.showOpenDialog(BrowserWindow.getFocusedWindow()!, { 93 | properties: ['openDirectory', 'multiSelections'], // 允许选择多个文件夹 94 | }); 95 | return result.filePaths; 96 | }); 97 | } 98 | -------------------------------------------------------------------------------- /src-electron/default-data.ts: -------------------------------------------------------------------------------- 1 | // It won't work. 2 | /* eslint-disable @typescript-eslint/no-explicit-any */ 3 | export const schema: any = { 4 | itemNum: { 5 | type: 'number', 6 | maximum: 10000, 7 | minimum: 1, 8 | default: 50 9 | }, 10 | viewer_navbar: { 11 | type: 'number', 12 | maximum: 5, 13 | minimum: 0, 14 | default: 0 15 | }, 16 | foo: { 17 | type: 'number', 18 | maximum: 100, 19 | minimum: 1, 20 | default: 50 21 | }, 22 | bar: { 23 | type: 'string', 24 | format: 'url' 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /src-electron/electron-flag.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | // THIS FEATURE-FLAG FILE IS AUTOGENERATED, 3 | // REMOVAL OR CHANGES WILL CAUSE RELATED TYPES TO STOP WORKING 4 | import "quasar/dist/types/feature-flag"; 5 | 6 | declare module "quasar/dist/types/feature-flag" { 7 | interface QuasarFeatureFlags { 8 | electron: true; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src-electron/electron-main.ts: -------------------------------------------------------------------------------- 1 | import { app, protocol, BrowserWindow } from 'electron'; 2 | import { initialize, enable } from '@electron/remote/main/index.js'; 3 | import path from 'path'; 4 | import os from 'os'; 5 | import { fileURLToPath } from 'node:url'; 6 | 7 | initialize(); 8 | const currentDir = fileURLToPath(new URL('.', import.meta.url)); 9 | // needed in case process is undefined under Linux 10 | const platform = process.platform || os.platform(); 11 | 12 | export let mainWindow: BrowserWindow | undefined; 13 | 14 | function createWindow() { 15 | /** 16 | * Initial window options 17 | */ 18 | mainWindow = new BrowserWindow({ 19 | icon: path.resolve(currentDir, 'icons/icon.png'), // tray icon 20 | width: 1000, 21 | height: 600, 22 | // transparent: true, 23 | // backgroundColor: '##34eb7d', 24 | useContentSize: true, 25 | frame: false, 26 | webPreferences: { 27 | contextIsolation: true, 28 | // nodeIntegration: true, 29 | sandbox: false, 30 | // More info: https://v2.quasar.dev/quasar-cli-vite/developing-electron-apps/electron-preload-script 31 | preload: path.resolve( 32 | currentDir, 33 | path.join( 34 | process.env.QUASAR_ELECTRON_PRELOAD_FOLDER!, 35 | 'electron-preload' + process.env.QUASAR_ELECTRON_PRELOAD_EXTENSION, 36 | ), 37 | ), 38 | }, 39 | }); 40 | 41 | enable(mainWindow.webContents); 42 | 43 | // mainWindow.loadURL(process.env.APP_URL); 44 | 45 | if (process.env.DEV) { 46 | mainWindow.loadURL(process.env.APP_URL); 47 | } else { 48 | mainWindow.loadFile('index.html'); 49 | } 50 | 51 | if (process.env.DEBUGGING) { 52 | // if on DEV or Production with debug enabled 53 | mainWindow.webContents.openDevTools(); 54 | } else { 55 | // we're on production; no access to devtools pls 56 | // mainWindow.webContents.on('devtools-opened', () => { 57 | // mainWindow?.webContents.closeDevTools(); 58 | // }); 59 | } 60 | 61 | mainWindow.on('closed', () => { 62 | mainWindow = undefined; 63 | }); 64 | } 65 | 66 | app.on('window-all-closed', () => { 67 | if (platform !== 'darwin') { 68 | app.quit(); 69 | } 70 | }); 71 | 72 | import { data_init } from './IPC-main'; 73 | 74 | app.whenReady().then(() => { 75 | if (mainWindow === undefined) { 76 | data_init(); 77 | createWindow(); 78 | } 79 | }); 80 | 81 | app.whenReady().then(() => { 82 | // 这个需要在app.ready触发之后使用 83 | // TODO 新方案:https://stackoverflow.com/questions/77340986/what-to-use-now-that-registerfileprotocol-is-deprecated-in-electron 84 | protocol.registerFileProtocol('atom', (request, callback) => { 85 | const url = request.url.substring(7); 86 | callback(decodeURI(path.normalize(url))); 87 | }); 88 | }); 89 | 90 | import { ipcMains } from './IPC-main'; 91 | app.whenReady().then(ipcMains()); 92 | 93 | // app.on('ready', () => setTimeout(onAppReady, 100)); 94 | -------------------------------------------------------------------------------- /src-electron/electron-preload.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /* eslint-disable @typescript-eslint/no-non-null-assertion */ 3 | 4 | /** 5 | * This file is used specifically for security reasons. 6 | * Here you can access Nodejs stuff and inject functionality into 7 | * the renderer thread (accessible there through the "window" object) 8 | * 9 | * WARNING! 10 | * If you import anything from node_modules, then make sure that the package is specified 11 | * in package.json > dependencies and NOT in devDependencies 12 | * 13 | * Example (injects window.myAPI.doAThing() into renderer thread): 14 | * 15 | * import { contextBridge } from 'electron' 16 | * 17 | * contextBridge.exposeInMainWorld('myAPI', { 18 | * doAThing: () => {} 19 | * }) 20 | * 21 | * WARNING! 22 | * If accessing Node functionality (like importing @electron/remote) then in your 23 | * electron-main.ts you will need to set the following when you instantiate BrowserWindow: 24 | * 25 | * mainWindow = new BrowserWindow({ 26 | * // ... 27 | * webPreferences: { 28 | * // ... 29 | * sandbox: false // <-- to be able to import @electron/remote in preload script 30 | * } 31 | * } 32 | */ 33 | 34 | export type storeAPI = { 35 | // Declare a `readFile` function that will return a promise. This promise 36 | // will contain the data of the file read from the main process. 37 | get: (key: any) => any; 38 | set: (key: any, value: any) => any; 39 | delete: (key: any) => any; 40 | initData: () => any; 41 | ini: () => void; 42 | }; 43 | 44 | export type myWindowAPI = { 45 | minimize: () => Promise; 46 | toggleMaximize: () => Promise; 47 | close: () => Promise; 48 | openDevTool: () => any; 49 | isPackaged: () => any; 50 | }; 51 | 52 | export type myToolAPI = { 53 | traverseFolder: (path: string, pFormats: any) => any; 54 | traverseFolderAsync: (path: Array|String, pFormats: string[], vFormats: string[], perPageNum: number, sortMethod: string) => any; 55 | openLink: (link: string) => any; 56 | delPic: (src: string) => any; 57 | openPath: (src: string) => any; 58 | showItemInFolder: (src: string) => any; 59 | selectFolders: () => any; 60 | onAsyncImageLinksAppend: (handle: (event: IpcRendererEvent, taskName: string, paths: WImage[]) => void) => void; 61 | }; 62 | 63 | import { contextBridge, ipcRenderer } from 'electron'; 64 | import type { IpcRendererEvent } from 'electron'; 65 | import { BrowserWindow, app, shell } from '@electron/remote'; 66 | import type { WImage } from './traverseFolder'; 67 | 68 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 69 | 70 | const myToolAPIs: myToolAPI = { 71 | async traverseFolder (path, pFormats) { 72 | return await ipcRenderer.invoke('tool-traverseFolder', path, pFormats); 73 | }, 74 | async traverseFolderAsync (path, pFormats, vFormats, perPageNum, sortMethod) { 75 | return await ipcRenderer.invoke('tool-traverseFolder-async', path, pFormats, vFormats,perPageNum, sortMethod); 76 | }, 77 | async openLink (link) { 78 | return await ipcRenderer.invoke('tool-openLink', link); 79 | }, 80 | async delPic (src) { 81 | return await ipcRenderer.invoke('tool-delPic', src); 82 | }, 83 | async openPath (src) { 84 | return await ipcRenderer.invoke('tool-openPath', src); 85 | }, 86 | async showItemInFolder (src) { 87 | shell.showItemInFolder(src); 88 | }, 89 | async selectFolders() { 90 | console.log('selectFolders'); 91 | return await ipcRenderer.invoke('tool-selectFolders'); 92 | }, 93 | onAsyncImageLinksAppend(handle) { 94 | ipcRenderer.on('async:imageLinks-append', handle); 95 | } 96 | }; 97 | 98 | contextBridge.exposeInMainWorld('myToolAPI', myToolAPIs); 99 | 100 | const myStoreAPI: storeAPI = { 101 | async get (key) { 102 | return await ipcRenderer.invoke('store-get', key); 103 | }, 104 | 105 | async set (key, value) { 106 | return await ipcRenderer.invoke('store-set', key, value); 107 | // store.set(key, value); 108 | }, 109 | 110 | async delete (key) { 111 | return await ipcRenderer.invoke('store-delete', key); 112 | }, 113 | 114 | ini () { 115 | ipcRenderer.invoke('store-ini'); 116 | }, 117 | 118 | initData () { 119 | // store = new Store(); 120 | // // 重构判断是否为空并 121 | // if (!store.get('itemNum')) store.store = schema; 122 | console.log('data inited'); 123 | } 124 | }; 125 | 126 | contextBridge.exposeInMainWorld('storeAPI', myStoreAPI); 127 | 128 | contextBridge.exposeInMainWorld('myWindowAPI', { 129 | minimize () { 130 | BrowserWindow.getFocusedWindow()!.minimize(); 131 | }, 132 | 133 | toggleMaximize () { 134 | const win = BrowserWindow.getFocusedWindow()!; 135 | 136 | if (win.isMaximized()) { 137 | win.unmaximize(); 138 | } else { 139 | win.maximize(); 140 | } 141 | }, 142 | 143 | close () { 144 | BrowserWindow.getFocusedWindow()!.close(); 145 | }, 146 | 147 | openDevTool () { 148 | BrowserWindow.getFocusedWindow()!.webContents.openDevTools(); 149 | }, 150 | 151 | isPackaged () { 152 | return app.isPackaged; 153 | } 154 | }); 155 | -------------------------------------------------------------------------------- /src-electron/icons/electron-env.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | declare namespace NodeJS { 4 | interface ProcessEnv { 5 | QUASAR_PUBLIC_FOLDER: string; 6 | QUASAR_ELECTRON_PRELOAD: string; 7 | APP_URL: string; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src-electron/icons/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/src-electron/icons/icon.icns -------------------------------------------------------------------------------- /src-electron/icons/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/src-electron/icons/icon.ico -------------------------------------------------------------------------------- /src-electron/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/src-electron/icons/icon.png -------------------------------------------------------------------------------- /src-electron/traverseFolder.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-unused-vars */ 2 | /* eslint-disable @typescript-eslint/no-explicit-any */ 3 | // import remote from '@electron/remote' 4 | import fs from 'fs-extra'; 5 | import * as path from 'path'; 6 | import AsyncReadFilePath from './AsyncReadFilePath'; 7 | 8 | // import { promisify } from 'util'; 9 | import sizeOf from 'image-size'; 10 | 11 | // 指定要遍历的根路径 12 | // const rootPath = 'D:\\Picture\\五维介质'; 13 | 14 | // 存储图片文件链接的数组 15 | // 数据结构更改:使用对象数组,同时使用 map 维护数组下标,异步获取图片大小和其他参数。 16 | let imageLinks: any[] = []; 17 | 18 | let picLinks: WImage[]; 19 | const picMetaMap = new Map(); 20 | let picFormats: any[] = [ 21 | '.webp', 22 | '.jxl', 23 | '.jpg', 24 | '.jpeg', 25 | '.png', 26 | '.gif', 27 | '.bmp', 28 | '.jfif' 29 | ]; 30 | let videoFormats: any[] = [ 31 | '.mp4', 32 | '.webm', 33 | ] 34 | 35 | export interface WImage { 36 | source: string; 37 | src: string|any; 38 | srcThumb: string; 39 | width?: number; 40 | height?: number; 41 | isVideo?: boolean; 42 | originalSrc?: string; 43 | } 44 | 45 | function isPathDirectory (thepath: string) { 46 | const thefile = fs.statSync(thepath); 47 | return thefile.isDirectory(); 48 | } 49 | 50 | export function imageRetrieval (thepath, pFormats) { 51 | if (pFormats) picFormats = pFormats; 52 | // console.log(pFormats, picFormats); 53 | // 清空 54 | imageLinks = []; 55 | picLinks = []; 56 | traverseFolderObjects(thepath); 57 | console.log('Finish traverseFolderObjects!'); 58 | return picLinks; 59 | } 60 | 61 | // ANCHOR Async Entry 62 | // * Main Entry 63 | export function imageRetrievalAsync(thepath: String | Array, pFormats, vFormats, pageSize, sortMethod) { 64 | if (pFormats) picFormats = pFormats; 65 | if (vFormats) videoFormats = vFormats; 66 | const taskName = Array.isArray(thepath) ? thepath[0] : thepath; 67 | const asyncReadFilePath = new AsyncReadFilePath(taskName, picFormats, videoFormats, pageSize, sortMethod); 68 | asyncReadFilePath.readDirectory(thepath) 69 | } 70 | 71 | 72 | function generateRamStr(len, charSet?: string) { 73 | const chars = charSet || "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; 74 | let randomStr = ""; 75 | for (var i = 0; i < len; i++) { 76 | randomStr += chars.charAt(Math.floor(Math.random() * chars.length)); 77 | } 78 | return randomStr; 79 | } 80 | 81 | 82 | 83 | // // 开始遍历 84 | // traverseFolder(rootPath); 85 | 86 | // // 打印图片链接列表 87 | // console.log(imageLinks); 88 | 89 | // 递归遍历文件夹 90 | export function traverseFolder (currentPath) { 91 | const files = fs.readdirSync(currentPath); 92 | // console.log(files); 93 | files.sort((a, b) => a.localeCompare(b, undefined, { sensitivity: 'base' })); 94 | 95 | for (const file of files) { 96 | const filePath = path.join(currentPath, file); 97 | // const thefile = fs.statSync(filePath); 98 | // console.log(sha256sum(filePath)) 99 | if (isPathDirectory(filePath)) { 100 | // 如果是文件夹,递归遍历 101 | traverseFolder(filePath); 102 | } else { 103 | // 如果是文件,检查文件扩展名是否是图片格式 104 | const extname = path.extname(filePath).toLowerCase(); 105 | if (picFormats.includes(extname)) { 106 | // 如果是图片文件,将其链接添加到数组中 107 | // const relativePath = path.relative(rootPath, filePath); 108 | imageLinks.push('atom://' + filePath); 109 | } 110 | } 111 | } 112 | } 113 | 114 | function traverseFolderObjects (currentPath) { 115 | const files = fs.readdirSync(currentPath); 116 | // console.log(files); 117 | files.sort((a, b) => a.localeCompare(b, undefined, { sensitivity: 'base' })); 118 | 119 | for (const file of files) { 120 | const filePath = path.join(currentPath, file); 121 | // const thefile = fs.statSync(filePath); 122 | // console.log(sha256sum(filePath)) 123 | if (isPathDirectory(filePath)) { 124 | // 如果是文件夹,递归遍历 125 | traverseFolderObjects(filePath); 126 | } else { 127 | // 如果是文件,检查文件扩展名是否是图片格式 128 | const extname = path.extname(filePath).toLowerCase(); 129 | if (picFormats.includes(extname)) { 130 | // 如果是图片文件,将其链接添加到数组中 131 | // const relativePath = path.relative(rootPath, filePath); 132 | picLinks.push({ 133 | source: filePath, 134 | src: 'atom://' + filePath, 135 | srcThumb: 'atom://' + filePath 136 | }); 137 | picMetaMap.set(filePath, picLinks.length - 1); 138 | try{ 139 | const dimensions = sizeOf(filePath); 140 | picLinks[picMetaMap.get(filePath)].height = dimensions.height; 141 | picLinks[picMetaMap.get(filePath)].width = dimensions.width; 142 | }catch (err) {} 143 | // console.log(picLinks[picMetaMap.get(filePath)]); 144 | 145 | // 异步方案,有问题 146 | // sizeOf(filePath) 147 | // .then(dimensions => { 148 | // console.log(dimensions.width, dimensions.height); 149 | // console.log(filePath); 150 | // picLinks[picMetaMap.get(filePath)].height = dimensions.height; 151 | // picLinks[picMetaMap.get(filePath)].width = dimensions.width; 152 | // console.log(picLinks[picMetaMap.get(filePath)]); 153 | // }) 154 | // .catch(err => console.error(err)); 155 | } 156 | } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src-old/old-components/SetComponent.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 设置 Setting 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 基础设置 31 | 32 | 33 | 35 | 保存设置 36 | 37 | 38 | 39 | 40 | 41 | 43 | 保存设置 44 | 45 | 46 | 47 | 48 | 50 | 自动重排:如果您经常遇到瀑布流排版不正确导致需要手动点击以重新排版的情况,可以开启此选项。 51 | 52 | 54 | 保存设置 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 保存意味着存储,会影响之后使用软件 64 | 65 | 66 | 67 | 68 | 临时使用没有任何副作用,仅会影响本次的体验! 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 瀑布流设置 77 | 79 | 81 | 保存设置 82 | 83 | 84 | 85 | 键值对设置 瀑布流断点:e.g. 1200 4 代表宽度大于 1200 之后每行显示四张图片~ 86 | 但是众所周知,软件是不会显示具体宽度的,所以你们量子调整一下就好。 87 | 88 | 89 | 90 | 91 | 92 | 高级 93 | 94 | 高级就是,什么都没有。。。。 95 | 96 | 最动人是秋林映着落日。那酡红如醉,衬托着天边加深的暮色。晚风带着清澈的凉意,随着暮色浸染,那是一种十分艳丽的凄楚之美,让你想流几行感怀身世之泪,却又被那逐渐淡去的醉红所慑住,而情愿把奔放的情感凝结。 97 | 98 | 这上面的夜的天空,奇怪而高,我生平没有见过这样奇怪而高的天空。他仿佛要离开人间而去,使人们仰面不再看见。然而现在却非常之蓝,闪闪地眨着几十个星星的眼,冷眼。他的口角上现出微笑,似乎自以为大有深意,而将繁霜洒在我的园里的野花草上。 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | Field hint 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 214 | 215 | 220 | -------------------------------------------------------------------------------- /src-old/old-components/WaterFall.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 11 | 13 | 15 | 18 | 19 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 353 | 354 | 411 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 67 | -------------------------------------------------------------------------------- /src/assets/20240128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/src/assets/20240128.png -------------------------------------------------------------------------------- /src/assets/edge-avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/src/assets/edge-avatar.jpg -------------------------------------------------------------------------------- /src/assets/error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/src/assets/error.png -------------------------------------------------------------------------------- /src/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/src/assets/favicon.ico -------------------------------------------------------------------------------- /src/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/src/assets/icon.png -------------------------------------------------------------------------------- /src/assets/loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/src/assets/loading.png -------------------------------------------------------------------------------- /src/assets/quasar-logo-vertical.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 8 | 10 | 12 | 14 | 15 | -------------------------------------------------------------------------------- /src/boot/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/src/boot/.gitkeep -------------------------------------------------------------------------------- /src/boot/i18n.ts: -------------------------------------------------------------------------------- 1 | import { boot } from 'quasar/wrappers'; 2 | import { createI18n } from 'vue-i18n'; 3 | 4 | import messages from 'src/i18n'; 5 | 6 | export type MessageLanguages = keyof typeof messages; 7 | // Type-define 'en-US' as the master schema for the resource 8 | export type MessageSchema = typeof messages['en-US']; 9 | 10 | // See https://vue-i18n.intlify.dev/guide/advanced/typescript.html#global-resource-schema-type-definition 11 | /* eslint-disable @typescript-eslint/no-empty-interface */ 12 | declare module 'vue-i18n' { 13 | // define the locale messages schema 14 | export interface DefineLocaleMessage extends MessageSchema {} 15 | 16 | // define the datetime format schema 17 | export interface DefineDateTimeFormat {} 18 | 19 | // define the number format schema 20 | export interface DefineNumberFormat {} 21 | } 22 | /* eslint-enable @typescript-eslint/no-empty-interface */ 23 | 24 | export default boot(({ app }) => { 25 | // create default i18n instance with en-US locale 26 | const i18n = createI18n({ 27 | locale: 'en-US', 28 | legacy: false, 29 | messages, 30 | }); 31 | 32 | // 禁用 警告 33 | app.config.warnHandler = (msg, vm, trace) => { 34 | if (msg.includes("Invalid prop: type check failed")) { 35 | return; // 直接跳过这个警告,不输出 36 | } 37 | console.warn(msg, trace); // 其他警告仍然打印 38 | }; 39 | 40 | // Set i18n instance on app 41 | app.use(i18n); 42 | }); 43 | -------------------------------------------------------------------------------- /src/components/DropdownReader.vue: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Edge-coordinates/Waterfall_picture_viewer/2373a51d8468baa03a62fd7a00c132f7990f734f/src/components/DropdownReader.vue -------------------------------------------------------------------------------- /src/components/EssentialLink.vue: -------------------------------------------------------------------------------- 1 | 2 | 8 | 12 | 13 | 14 | 15 | 16 | {{ title }} 17 | {{ caption }} 18 | 19 | 20 | 21 | 22 | 35 | -------------------------------------------------------------------------------- /src/components/ExampleComponent.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{ title }} 4 | 5 | 6 | {{ todo.id }} - {{ todo.content }} 7 | 8 | 9 | Count: {{ todoCount }} / {{ meta.totalCount }} 10 | Active: {{ active ? 'yes' : 'no' }} 11 | Clicks on todos: {{ clickCount }} 12 | 13 | 14 | 15 | 38 | -------------------------------------------------------------------------------- /src/components/UploadComponent.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 点击或者拖动文件夹到该区域来打开 8 | 支持多选 9 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | 108 | 109 | 135 | -------------------------------------------------------------------------------- /src/components/WaterFall_biggerpicture.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 15 | 16 | 19 | 27 | 32 | 33 | 34 | 43 | 45 | 53 | 54 | 55 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 227 | 228 | 233 | -------------------------------------------------------------------------------- /src/components/WaterFall_photoswipe.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{ page }} 5 | 6 | 18 | 19 | 22 | 32 | 34 | 42 | 43 | 44 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 390 | 391 | 447 | -------------------------------------------------------------------------------- /src/components/WaterFall_viewerjs.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | 21 | 22 | 39 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 263 | 264 | 277 | -------------------------------------------------------------------------------- /src/components/models.ts: -------------------------------------------------------------------------------- 1 | export interface Todo { 2 | id: number; 3 | content: string; 4 | } 5 | 6 | export interface Meta { 7 | totalCount: number; 8 | } 9 | -------------------------------------------------------------------------------- /src/components/settings/BaseSetting.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 基础设置 5 | 6 | 7 | 9 | 保存设置 10 | 11 | 12 | 13 | 14 | 15 | 17 | 保存设置 18 | 19 | 20 | 21 | 22 | 24 | 自动重排:如果您经常遇到瀑布流排版不正确导致需要手动点击以重新排版的情况,可以开启此选项。 25 | 26 | 28 | 保存设置 29 | 30 | 31 | 32 | 33 | 34 | 35 | 保存意味着存储,会影响之后使用软件 36 | 37 | 38 | 39 | 40 | 临时使用没有任何副作用,仅会影响本次的体验! 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 53 | -------------------------------------------------------------------------------- /src/components/settings/LanguageChoose.vue: -------------------------------------------------------------------------------- 1 | 2 | 语言选择 3 | 4 | 5 | 12 | 保存设置 13 | 15 | 16 | 17 | 18 | 19 | 50 | -------------------------------------------------------------------------------- /src/components/settings/ViewerSelection.vue: -------------------------------------------------------------------------------- 1 | 2 | 查看器选择 3 | 4 | 5 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | photoswipe 17 | Default, Full Functions! 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | Biggerpicture 27 | Better Performance 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | Viewerjs 37 | {{ $t('unOpen') }} 40 | 41 | 42 | 43 | 44 | 45 | Your selection is: {{ viewerName }} 46 | 54 | 保存设置 55 | 57 | 58 | 59 | 60 | 61 | 83 | -------------------------------------------------------------------------------- /src/components/settings/initLanguage.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Please choose your preferred language: 7 | 请选择您偏好的语言: 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 43 | -------------------------------------------------------------------------------- /src/components/testc/TEST.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/components/testc/WaterFall.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 11 | 12 | 15 | 16 | 17 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 241 | 242 | 286 | -------------------------------------------------------------------------------- /src/components/testc/waterfall.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: Yaowen Liu 3 | * @Date: 2022-03-17 14:41:05 4 | * @LastEditors: Yaowen Liu 5 | * @LastEditTime: 2022-03-23 14:44:20 6 | */ 7 | export interface ViewCard { 8 | src: any; 9 | id?: string; 10 | name?: string; 11 | star?: boolean; 12 | backgroundColor?: string; 13 | [attr: string]: any; 14 | } 15 | 16 | interface Point { 17 | rowPerView: number; 18 | } 19 | 20 | export type Breakpoints = Record; 21 | 22 | export interface WaterfallProps { 23 | breakpoints: Breakpoints; 24 | width: number; 25 | animationDuration: number; 26 | animationDelay: number; 27 | animationEffect: string; 28 | hasAroundGutter: boolean; 29 | gutter: number; 30 | list: ViewCard[]; 31 | animationPrefix: string; 32 | align: string; 33 | } 34 | 35 | export interface ItemWidthProps { 36 | breakpoints: Breakpoints; 37 | wrapperWidth: number; 38 | gutter: number; 39 | hasAroundGutter: boolean; 40 | initWidth: number; 41 | } 42 | 43 | export interface ItemWidthByBreakpointProps extends ItemWidthProps { 44 | size: number; 45 | } 46 | -------------------------------------------------------------------------------- /src/components/w-viewer/BPControllers.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 18 | -------------------------------------------------------------------------------- /src/components/w-viewer/WPagination.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/components/waterfall.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /* 3 | * @Author: Yaowen Liu 4 | * @Date: 2022-03-17 14:41:05 5 | * @LastEditors: Yaowen Liu 6 | * @LastEditTime: 2022-03-23 14:44:20 7 | */ 8 | export interface ViewCard { 9 | src: any; 10 | id?: string; 11 | name?: string; 12 | star?: boolean; 13 | backgroundColor?: string; 14 | [attr: string]: any; 15 | } 16 | 17 | interface Point { 18 | rowPerView: number; 19 | } 20 | 21 | export type Breakpoints = Record; 22 | 23 | export interface WaterfallProps { 24 | breakpoints: Breakpoints; 25 | width: number; 26 | animationDuration: number; 27 | animationDelay: number; 28 | animationEffect: string; 29 | hasAroundGutter: boolean; 30 | gutter: number; 31 | list: ViewCard[]; 32 | animationPrefix: string; 33 | align: string; 34 | } 35 | 36 | export interface ItemWidthProps { 37 | breakpoints: Breakpoints; 38 | wrapperWidth: number; 39 | gutter: number; 40 | hasAroundGutter: boolean; 41 | initWidth: number; 42 | } 43 | 44 | export interface ItemWidthByBreakpointProps extends ItemWidthProps { 45 | size: number; 46 | } 47 | -------------------------------------------------------------------------------- /src/css/app.scss: -------------------------------------------------------------------------------- 1 | // app global css in SCSS form 2 | @tailwind base; 3 | @tailwind components; 4 | @tailwind utilities; 5 | 6 | body { 7 | background-color: rgba(18, 100, 253, 0); 8 | } 9 | 10 | #win { 11 | text-align: center; 12 | background-color: #fffae8; 13 | box-shadow: 8px 8px 10px grey; 14 | -webkit-app-region: drag; 15 | } 16 | 17 | /*滚动条*/ 18 | 19 | ::-webkit-scrollbar { 20 | width: 6px; 21 | height: 6px; 22 | } 23 | ::-webkit-scrollbar-track-piece { 24 | background-color: #cccccc; 25 | -webkit-border-radius: 6px; 26 | } 27 | ::-webkit-scrollbar-thumb:vertical { 28 | height: 5px; 29 | background-color: #999999; 30 | -webkit-border-radius: 6px; 31 | } 32 | ::-webkit-scrollbar-thumb:horizontal { 33 | width: 5px; 34 | background-color: #cccccc; 35 | -webkit-border-radius: 6px; 36 | } 37 | ::-webkit-scrollbar { 38 | width: 9px; 39 | height: 9px; 40 | } 41 | ::-webkit-scrollbar-track-piece { 42 | background-color: transparent; 43 | } 44 | ::-webkit-scrollbar-track-piece:no-button { 45 | } 46 | ::-webkit-scrollbar-thumb { 47 | background-color: #3994ef; 48 | border-radius: 3px; 49 | } 50 | ::-webkit-scrollbar-thumb:hover { 51 | background-color: #a4eef0; 52 | } 53 | ::-webkit-scrollbar-thumb:active { 54 | background-color: #666; 55 | } 56 | ::-webkit-scrollbar-button:vertical { 57 | width: 9px; 58 | } 59 | ::-webkit-scrollbar-button:horizontal { 60 | width: 9px; 61 | } 62 | ::-webkit-scrollbar-button:vertical:start:decrement { 63 | background-color: white; 64 | } 65 | ::-webkit-scrollbar-button:vertical:end:increment { 66 | background-color: white; 67 | } 68 | ::-webkit-scrollbar-button:horizontal:start:decrement { 69 | background-color: white; 70 | } 71 | ::-webkit-scrollbar-button:horizontal:end:increment { 72 | background-color: white; 73 | } 74 | body::-webkit-scrollbar-track-piece { 75 | background-color: white; 76 | } 77 | -------------------------------------------------------------------------------- /src/css/quasar.variables.scss: -------------------------------------------------------------------------------- 1 | // Quasar SCSS (& Sass) Variables 2 | // -------------------------------------------------- 3 | // To customize the look and feel of this app, you can override 4 | // the Sass/SCSS variables found in Quasar's source Sass/SCSS files. 5 | 6 | // Check documentation for full list of Quasar variables 7 | 8 | // Your own variables (that are declared here) and Quasar's own 9 | // ones will be available out of the box in your .vue/.scss/.sass files 10 | 11 | // It's highly recommended to change the default colors 12 | // to match your app's branding. 13 | // Tip: Use the "Theme Builder" on Quasar's documentation website. 14 | 15 | $primary : #1976D2; 16 | $secondary : #26A69A; 17 | $accent : #9C27B0; 18 | 19 | $dark : #1D1D1D; 20 | $dark-page : #121212; 21 | 22 | $positive : #21BA45; 23 | $negative : #C10015; 24 | $info : #31CCEC; 25 | $warning : #F2C037; 26 | -------------------------------------------------------------------------------- /src/env.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | declare namespace NodeJS { 4 | interface ProcessEnv { 5 | NODE_ENV: string; 6 | VUE_ROUTER_MODE: 'hash' | 'history' | 'abstract' | undefined; 7 | VUE_ROUTER_BASE: string | undefined; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/i18n/en-US/index.ts: -------------------------------------------------------------------------------- 1 | // This is just an example, 2 | // so you can safely delete all default props below 3 | 4 | export default { 5 | hello: 'Hello', 6 | unOpen: 'This function not yet open', 7 | failed: 'Action failed', 8 | success: 'Action was successful', 9 | deleteConfirmation: 'Are you sure you want to delete this picture?', 10 | saveSuccess: 'Save Success!' 11 | }; 12 | -------------------------------------------------------------------------------- /src/i18n/index.ts: -------------------------------------------------------------------------------- 1 | import enUS from './en-US'; 2 | import zhCN from './zh-CN'; 3 | 4 | export default { 5 | 'zh-CN': zhCN, 6 | 'en-US': enUS 7 | }; 8 | -------------------------------------------------------------------------------- /src/i18n/zh-CN/index.ts: -------------------------------------------------------------------------------- 1 | // This is just an example, 2 | // so you can safely delete all default props below 3 | 4 | export default { 5 | hello: '你好呀~', 6 | unOpen: '该功能暂未开放', 7 | failed: 'Action failed', 8 | success: 'Action was successful', 9 | deleteConfirmation: '确定删除这张图片吗?', 10 | saveSuccess: '保存成功!' 11 | }; 12 | -------------------------------------------------------------------------------- /src/layouts/MainLayout.vue: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | Img View 11 | 12 | 13 | File 14 | 15 | 16 | 17 | Open... 18 | 19 | 20 | 所有按键均没有功能 21 | 22 | 23 | 24 | 25 | 26 | Preferences 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | Submenu Label 35 | 36 | 37 | 38 | 39 | 40 | 41 | 3rd level Label 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | Quit 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | Edit 62 | 63 | 64 | 65 | Cut 66 | 67 | 68 | Copy 69 | 70 | 71 | Paste 72 | 73 | 74 | 75 | Select All 76 | 77 | 78 | 79 | 80 | View 81 | 82 | Window 83 | 84 | 85 | 86 | Open Dev Tool 87 | 88 | 91 | 92 | 93 | 94 | 95 | Help 96 | 97 | 98 | 99 | About 100 | 101 | 102 | TEST 103 | TEST A 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 153 | -------------------------------------------------------------------------------- /src/layouts/TestLayout.vue: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | Img View 11 | 12 | Home 13 | 14 | 15 | 35 | View 36 | 37 | Window 38 | 39 | 40 | 41 | Open Dev Tool 42 | 43 | 46 | 47 | 48 | 49 | 50 | Help 51 | 52 | 53 | 54 | About 55 | 56 | 57 | TEST 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 104 | -------------------------------------------------------------------------------- /src/layouts/WViewerLayout.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | Please choose a WViewer 13 | 14 | 15 | 16 | 17 | 18 | 19 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 213 | 214 | 227 | -------------------------------------------------------------------------------- /src/pages/AboutPage.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 返回 7 | 8 | 9 | 10 | 11 | 12 | 13 | About Program 14 | 简约瀑布流查看器哦~Version: {{ getVersion() }} 15 | 给个Star吧~ 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | About Author 25 | 边缘坐标是也! 26 | GitHub 27 | 28 | 29 | 30 | 31 | 32 | 33 | 47 | -------------------------------------------------------------------------------- /src/pages/ErrorNotFound.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 404 6 | 7 | 8 | 9 | Oops. Nothing here... 10 | 11 | 12 | 21 | 22 | 23 | 24 | 25 | 28 | -------------------------------------------------------------------------------- /src/pages/IndexPage.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | Tips: 14 | 15 | 16 | 17 | 18 | 打开图片查看器之后,使用 19 | Ctrl + 20 | 鼠标滚轮调整图片大小;图片查看器支持◀︎ 24 | ▶︎快捷键切换图片~ 25 | 26 | 27 | 28 | 29 | 30 | 31 | 图片查看器支持左右快捷键翻页,支持Esc快捷键退出。您也可以按Delete键来删除当前正在浏览的图片!(会删除到回收站,不保证稳定!) 36 | 37 | 38 | 39 | 40 | 按时间排序不完全支持多选文件夹,大部分需要重启的功能,或者BUG都可以用 41 | Ctrl + 42 | R解决 43 | 44 | 45 | 46 | 47 | 因为不想写文档所以请您自行研究设置看看有哪些功能 48 | 49 | 50 | 51 | 祝您使用愉快! {{ $t('hello') }} 52 | 53 | 54 | 55 | 56 | 62 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 161 | -------------------------------------------------------------------------------- /src/pages/test/APage.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 18 | -------------------------------------------------------------------------------- /src/pages/test/IndexPage.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | TEST INDEX 4 | Home Page 5 | 6 | Upload 7 | 8 | LanguageInit 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/pages/test/WIndexPage.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 16 | 17 | 18 | 19 | 20 | 21 | 点击或者拖动文件夹到该区域来打开 22 | 23 | 24 | 目前仅支持文件夹 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 117 | -------------------------------------------------------------------------------- /src/pages/test/initLanguage.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Please choose your preferred language: 7 | 请选择您偏好的语言: 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 37 | -------------------------------------------------------------------------------- /src/pages/test/normalize-path.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * normalize-path 3 | * 4 | * Copyright (c) 2014-2018, Jon Schlinkert. 5 | * Released under the MIT License. 6 | */ 7 | 8 | export function normalizePath (path, stripTrailing: boolean = true) { 9 | if (typeof path !== 'string') { 10 | throw new TypeError('expected path to be a string'); 11 | } 12 | 13 | if (path === '\\' || path === '/') return '/'; 14 | 15 | const len = path.length; 16 | if (len <= 1) return path; 17 | 18 | // ensure that win32 namespaces has two leading slashes, so that the path is 19 | // handled properly by the win32 version of path.parse() after being normalized 20 | // https://msdn.microsoft.com/library/windows/desktop/aa365247(v=vs.85).aspx#namespaces 21 | let prefix = ''; 22 | if (len > 4 && path[3] === '\\') { 23 | const ch = path[2]; 24 | if ((ch === '?' || ch === '.') && path.slice(0, 2) === '\\\\') { 25 | path = path.slice(2); 26 | prefix = '//'; 27 | } 28 | } 29 | 30 | const segs = path.split(/[/\\]+/); 31 | if (stripTrailing !== false && segs[segs.length - 1] === '') { 32 | segs.pop(); 33 | } 34 | return prefix + segs.join('/'); 35 | } 36 | -------------------------------------------------------------------------------- /src/quasar.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | // Forces TS to apply `@quasar/app-vite` augmentations of `quasar` package 4 | // Removing this would break `quasar/wrappers` imports as those typings are declared 5 | // into `@quasar/app-vite` 6 | // As a side effect, since `@quasar/app-vite` reference `quasar` to augment it, 7 | // this declaration also apply `quasar` own 8 | // augmentations (eg. adds `$q` into Vue component context) 9 | /// 10 | -------------------------------------------------------------------------------- /src/router/index.ts: -------------------------------------------------------------------------------- 1 | import { route } from 'quasar/wrappers'; 2 | import { 3 | createMemoryHistory, 4 | createRouter, 5 | createWebHashHistory, 6 | createWebHistory, 7 | } from 'vue-router'; 8 | 9 | import routes from './routes'; 10 | 11 | /* 12 | * If not building with SSR mode, you can 13 | * directly export the Router instantiation; 14 | * 15 | * The function below can be async too; either use 16 | * async/await or return a Promise which resolves 17 | * with the Router instance. 18 | */ 19 | 20 | export default route(function (/* { store, ssrContext } */) { 21 | const createHistory = process.env.SERVER 22 | ? createMemoryHistory 23 | : (process.env.VUE_ROUTER_MODE === 'history' ? createWebHistory : createWebHashHistory); 24 | 25 | const Router = createRouter({ 26 | scrollBehavior: () => ({ left: 0, top: 0 }), 27 | routes, 28 | 29 | // Leave this as is and make changes in quasar.conf.js instead! 30 | // quasar.conf.js -> build -> vueRouterMode 31 | // quasar.conf.js -> build -> publicPath 32 | history: createHistory(process.env.VUE_ROUTER_BASE), 33 | }); 34 | 35 | return Router; 36 | }); 37 | -------------------------------------------------------------------------------- /src/router/routes.ts: -------------------------------------------------------------------------------- 1 | import { RouteRecordRaw } from 'vue-router'; 2 | 3 | const routes: RouteRecordRaw[] = [ 4 | { 5 | path: '/', 6 | component: () => import('layouts/MainLayout.vue'), 7 | children: [ 8 | { path: '', component: () => import('pages/IndexPage.vue') }, 9 | { path: 'about/', component: () => import('pages/AboutPage.vue') } 10 | ] 11 | }, 12 | { 13 | path: '/test/', 14 | component: () => import('layouts/MainLayout.vue'), 15 | children: [ 16 | { path: '', component: () => import('src/pages/test/IndexPage.vue') }, 17 | { path: 'a/', component: () => import('src/pages/test/APage.vue') }, 18 | { path: 'initLanguage/', component: () => import('src/pages/test/initLanguage.vue') } 19 | ] 20 | }, 21 | // Always leave this as last one, 22 | // but you can also remove it 23 | { 24 | path: '/:catchAll(.*)*', 25 | component: () => import('pages/ErrorNotFound.vue') 26 | } 27 | ]; 28 | 29 | export default routes; 30 | -------------------------------------------------------------------------------- /src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | /// 4 | 5 | // Mocks all files ending in `.vue` showing them as plain Vue instances 6 | declare module '*.vue' { 7 | import type { DefineComponent } from 'vue'; 8 | const component: DefineComponent<{}, {}, any>; 9 | export default component; 10 | } 11 | -------------------------------------------------------------------------------- /src/stores/example-store.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia'; 2 | 3 | export const useCounterStore = defineStore('counter', { 4 | state: () => ({ 5 | counter: 0, 6 | }), 7 | getters: { 8 | doubleCount: (state) => state.counter * 2, 9 | }, 10 | actions: { 11 | increment() { 12 | this.counter++; 13 | }, 14 | }, 15 | }); 16 | -------------------------------------------------------------------------------- /src/stores/index.ts: -------------------------------------------------------------------------------- 1 | import { store } from 'quasar/wrappers' 2 | import { createPinia } from 'pinia' 3 | import { Router } from 'vue-router'; 4 | 5 | /* 6 | * When adding new properties to stores, you should also 7 | * extend the `PiniaCustomProperties` interface. 8 | * @see https://pinia.vuejs.org/core-concepts/plugins.html#typing-new-store-properties 9 | */ 10 | declare module 'pinia' { 11 | export interface PiniaCustomProperties { 12 | readonly router: Router; 13 | } 14 | } 15 | 16 | /* 17 | * If not building with SSR mode, you can 18 | * directly export the Store instantiation; 19 | * 20 | * The function below can be async too; either use 21 | * async/await or return a Promise which resolves 22 | * with the Store instance. 23 | */ 24 | 25 | export default store((/* { ssrContext } */) => { 26 | const pinia = createPinia() 27 | 28 | // You can add Pinia plugins here 29 | // pinia.use(SomePiniaPlugin) 30 | 31 | return pinia 32 | }) 33 | -------------------------------------------------------------------------------- /src/stores/store-flag.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | // THIS FEATURE-FLAG FILE IS AUTOGENERATED, 3 | // REMOVAL OR CHANGES WILL CAUSE RELATED TYPES TO STOP WORKING 4 | import "quasar/dist/types/feature-flag"; 5 | 6 | declare module "quasar/dist/types/feature-flag" { 7 | interface QuasarFeatureFlags { 8 | store: true; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/stores/viewerSet-store.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia'; 2 | 3 | // TODO Describe the structure of the store's state and the purpose of this store. 4 | /** 5 | * Defines a Pinia store named 'setting'. 6 | * 7 | * This store is used to manage application settings. 8 | * 9 | * @constant {StoreDefinition} useSettingStore - The Pinia store definition for settings. 10 | */ 11 | export const useSettingStore = defineStore('setting', { 12 | state: () => ({ 13 | language: { 14 | label: '', 15 | value: '', 16 | }, 17 | languages: [ 18 | { 19 | label: 'English', 20 | value: 'en-US', 21 | }, 22 | { 23 | label: '简体中文', 24 | value: 'zh-CN', 25 | }, 26 | ], 27 | isOpen: false, 28 | perPageNum: 20, 29 | // cycleUpdate: false, 30 | cycleUpdate: true, 31 | viewerName: 'photoswipe', 32 | autoPositioning: true, 33 | singlePageLoop: false, 34 | globalDropFolderOpen: true, 35 | sortMethod: 'name-asc', 36 | sortMethodOptions: [ 37 | 'name-asc', 38 | 'name-desc', 39 | 'time-asc', 40 | 'time-desc', 41 | ], 42 | imageFormat: [ 43 | '.webp', 44 | '.jxl', 45 | '.jpg', 46 | '.jpeg', 47 | '.png', 48 | '.gif', 49 | '.bmp', 50 | '.jfif' 51 | ], 52 | videoFormat: [ 53 | '.mp4', 54 | '.webm', 55 | ], 56 | vNavbar: 1, 57 | waterfallBreakpoint: { 58 | 1400: { 59 | rowPerView: 5 60 | }, 61 | 1200: { 62 | // When the screen width is less than or equal to 1200 63 | rowPerView: 4 64 | }, 65 | 800: { 66 | // When the screen width is less than or equal to 800 67 | rowPerView: 3 68 | }, 69 | 500: { 70 | // When the screen width is less than or equal to 500 71 | rowPerView: 2 72 | } 73 | }, 74 | hello: '草泥马' 75 | }), 76 | getters: { 77 | // doubleCount: state => state.counter * 2 78 | getPFormat: state => state.imageFormat, 79 | getVFormat: state => state.videoFormat, 80 | getPerPageNum: state => state.perPageNum, 81 | }, 82 | actions: { 83 | increment () { 84 | // this.counter++; 85 | } 86 | } 87 | }); 88 | 89 | /* 90 | TODO - List of videos waiting to be supported by other methods 91 | '.mkv', 92 | '.flv', 93 | '.avi', 94 | '.mov', 95 | '.wmv', 96 | '.rmvb', 97 | '.rm', 98 | '.asf', 99 | '.mpg', 100 | '.mpeg', 101 | '.m4v', 102 | '.3gp', 103 | '.3g2', 104 | '.f4v', 105 | '.hlv', 106 | '.swf', 107 | '.vob', 108 | '.ts', 109 | */ 110 | -------------------------------------------------------------------------------- /src/stores/wViewerState-store.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia'; 2 | import type { WImage } from 'app/src-electron/traverseFolder'; 3 | 4 | // TODO Describe the structure of the store's state and the purpose of this store. 5 | /** 6 | * Defines a Pinia store named 'setting'. 7 | * 8 | * This store is used to manage application settings. 9 | * 10 | * @constant {StoreDefinition} useSettingStore - The Pinia store definition for settings. 11 | */ 12 | export const useWViewerStateStore = defineStore('wViewerState', { 13 | state: () => ({ 14 | ifViewerOpen: false, 15 | thisPicDecode: '', 16 | openPicNum: -1, 17 | imgs: [] as WImage[], 18 | lastPageTunningCommand : '', 19 | }), 20 | // getters: { 21 | // doubleCount: (state) => state.counter * 2, 22 | // }, 23 | // actions: { 24 | // increment() { 25 | // this.counter++; 26 | // }, 27 | // }, 28 | }); 29 | -------------------------------------------------------------------------------- /src/window.d.ts: -------------------------------------------------------------------------------- 1 | // This file should augment the properties of the `Window` with the type of the 2 | // `ContextBridgeApi` from `Electron.contextBridge` declared in `src/preload.ts`. 3 | import type { 4 | myWindowAPI, 5 | storeAPI, 6 | myToolAPI 7 | } from 'app/src-electron/electron-preload'; 8 | 9 | declare global { 10 | interface Window { 11 | myWindowAPI: myWindowAPI; 12 | storeAPI: storeAPI; 13 | myToolAPI: myToolAPI; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ['./src/**/*.{html,js,ts,vue}'], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [require('daisyui')], 8 | 9 | // daisyUI config (optional - here are the default values) 10 | daisyui: { 11 | darkTheme: 'dark', // name of one of the included themes for dark mode 12 | base: true, // applies background color and foreground color for root element by default 13 | styled: true, // include daisyUI colors and design decisions for all components 14 | utils: true, // adds responsive and modifier utility classes 15 | rtl: false, // rotate style direction from left-to-right to right-to-left. You also need to add dir="rtl" to your html tag and install `tailwindcss-flip` plugin for Tailwind CSS. 16 | prefix: '', // prefix for daisyUI classnames (components, modifiers and responsive class names. Not colors) 17 | logs: true, // Shows info about daisyUI version and used config in the console when building your CSS 18 | themes: [ 19 | { 20 | mytheme: { 21 | primary: '#570df8', 22 | secondary: '#f000b8', 23 | accent: '#1dcdbc', 24 | neutral: '#45B4E6', 25 | 'base-100': '#ffffff', 26 | 'base-200': '#45B4E6', 27 | info: '#3abff8', 28 | success: '#36d399', 29 | warning: '#fbbd23', 30 | error: '#f87272', 31 | }, 32 | }, 33 | ], 34 | }, 35 | }; 36 | -------------------------------------------------------------------------------- /tmp/a.ts: -------------------------------------------------------------------------------- 1 | // const relativePath = '/64Gram Desktop/a/d/b/1.txt'; 2 | // const currentAbsolutePath = 3 | // 'atom://C://Users/Edge/Downloads/64Gram Desktop/a/d/b/1.txt'; 4 | 5 | // // 移除文件名部分,得到文件夹的相对路径 6 | 7 | // const rt = relativePath.split('/')[1]; 8 | // const cc = currentAbsolutePath.split(rt)[0] + rt + '/'; 9 | 10 | // console.log(rt); 11 | 12 | // console.log(cc); 13 | 14 | // function getFolder (filePath: string, regexPattern: RegExp): string { 15 | // const directory = filePath.substring(0, filePath.lastIndexOf('/')); 16 | // return directory.replace(regexPattern, ''); 17 | // } 18 | 19 | // console.log(getFolder(currentAbsolutePath, /atom:\/\//g)); 20 | 21 | // !错误案例 22 | /* 23 | Dishwasher1910 24 | /Dishwasher1910/2016/Dish_2016.08/1.png 25 | D:\Edge-coordinates\R-18\图片\0 backup\新建文件夹\Dishwasher1910作品合集\Dishwasher1910\Dishwasher1910\2016\Dish_2016.08\1.png 26 | */ 27 | 28 | const text = String.raw`D:\Edge-coordinates\R-18\图片\0 backup\新建文件夹\Dishwasher1910作品合集\Dishwasher1910\Dishwasher1910\2016\Dish_2016.08\1.png`; 29 | const regex = 'Dishwasher1910'; 30 | 31 | const match = regex.exec(text); 32 | 33 | if (match) { 34 | const matchedText = match[0]; 35 | const beforeMatch = text.substring(0, match.index); 36 | 37 | console.log('匹配到的内容及之前的所有内容:', beforeMatch); 38 | console.log('匹配到的内容:', matchedText); 39 | } else { 40 | console.log('未匹配到任何内容'); 41 | } 42 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@quasar/app-vite/tsconfig-preset", 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "allowJs": true, 6 | "noImplicitAny": false, 7 | }, 8 | "exclude": [ 9 | "./dist", 10 | "./.quasar", 11 | "./node_modules", 12 | "./src-capacitor", 13 | "./src-cordova", 14 | "./quasar.config.*.temporary.compiled*" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /tsconfig.vue-tsc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "skipLibCheck": true 5 | } 6 | } 7 | --------------------------------------------------------------------------------
94 | 高级就是,什么都没有。。。。 95 |
最动人是秋林映着落日。那酡红如醉,衬托着天边加深的暮色。晚风带着清澈的凉意,随着暮色浸染,那是一种十分艳丽的凄楚之美,让你想流几行感怀身世之泪,却又被那逐渐淡去的醉红所慑住,而情愿把奔放的情感凝结。
98 | 这上面的夜的天空,奇怪而高,我生平没有见过这样奇怪而高的天空。他仿佛要离开人间而去,使人们仰面不再看见。然而现在却非常之蓝,闪闪地眨着几十个星星的眼,冷眼。他的口角上现出微笑,似乎自以为大有深意,而将繁霜洒在我的园里的野花草上。 99 |
{{ title }}
Count: {{ todoCount }} / {{ meta.totalCount }}
Active: {{ active ? 'yes' : 'no' }}
Clicks on todos: {{ clickCount }}
简约瀑布流查看器哦~Version: {{ getVersion() }}
边缘坐标是也!
Tips:
18 | 打开图片查看器之后,使用 19 | Ctrl + 20 | 鼠标滚轮调整图片大小;图片查看器支持◀︎ 24 | ▶︎快捷键切换图片~ 25 |
31 | 图片查看器支持左右快捷键翻页,支持Esc快捷键退出。您也可以按Delete键来删除当前正在浏览的图片!(会删除到回收站,不保证稳定!) 36 |
按时间排序不完全支持多选文件夹,大部分需要重启的功能,或者BUG都可以用 41 | Ctrl + 42 | R解决 43 |
因为不想写文档所以请您自行研究设置看看有哪些功能
祝您使用愉快! {{ $t('hello') }}