├── .github └── workflows │ └── main.yml ├── .gitignore ├── .husky └── pre-commit ├── .npmrc ├── Dockerfile ├── LICENSE ├── README.md ├── eslint.config.js ├── extension └── moon-chrome-extension.zip ├── index.html ├── netlify.toml ├── package-lock.json ├── package.json ├── pnpm-lock.yaml ├── public ├── favicon.svg ├── png │ └── QQ截图20231019212432.png ├── site │ ├── clougence.com.svg │ ├── github.com.svg │ ├── jd.com.svg │ ├── pinduoduo.com.svg │ └── taobao.com.svg └── svg │ ├── baidu.svg │ ├── bing.svg │ ├── blog.svg │ ├── btc.svg │ ├── chatgpt.svg │ ├── db.svg │ ├── google.svg │ ├── juejin.svg │ ├── moonset.svg │ ├── sogou.svg │ └── wikipedia.svg ├── src ├── App.vue ├── auto-imports.d.ts ├── components.d.ts ├── components │ ├── AppContainer.vue │ ├── AppProvider │ │ └── index.vue │ ├── Blank.vue │ ├── TheDoc.vue │ └── TheFooter │ │ └── index.vue ├── composables │ ├── app.ts │ ├── draggable.ts │ ├── icon_style.ts │ └── theme.ts ├── global-types.d.ts ├── main.ts ├── pages │ ├── about │ │ └── index.vue │ └── home │ │ ├── components │ │ ├── Favicon.vue │ │ ├── MainClock.vue │ │ ├── MainHeader.vue │ │ ├── MainSearch.vue │ │ ├── MainSetting.vue │ │ ├── SettingSelection.vue │ │ ├── SiteContainer.vue │ │ ├── SiteGroupList.vue │ │ ├── SiteModal.vue │ │ └── SiteNavBar.vue │ │ └── index.vue ├── preset.json ├── router │ └── index.ts ├── stores │ ├── modal.ts │ ├── render.ts │ ├── setting.ts │ └── site.ts ├── styles │ ├── base.scss │ ├── index.scss │ ├── public.scss │ └── vars.scss ├── types │ ├── common.ts │ ├── index.ts │ ├── setting.ts │ └── site.ts └── utils │ ├── common.ts │ ├── favicon.ts │ ├── index.ts │ ├── search-engine │ ├── eng-baidu.js │ ├── eng-bing.js │ ├── eng-google.js │ ├── eng-qiuwenbaike.js │ ├── eng-sogou.js │ ├── eng-wikipedia.js │ └── index.js │ └── settings │ ├── icon_style.ts │ ├── index.ts │ ├── search.ts │ ├── show_lunar.ts │ └── theme.ts ├── tsconfig.json ├── uno.config.ts └── vite.config.ts /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # 此文件相当简单 2 | # 1. 检测到main分支有提交触发jobs任务/手动触发 3 | # 2. 指定一个github服务器的环境ubuntu-latest 4 | # 3. 执行pull当前的代码到github服务器 5 | # 4. 登录docker, 并制作镜像(所有安装依赖等都写在dockerfile中, 不在此执行), 上传镜像到dockerhub和本地镜像仓库 6 | # 5. 连接远程服务器, 停止/删除容器, 拉取本地最新镜像, 调整镜像名称, 删除本地镜像, 启动容器 7 | 8 | name: CI/CD 9 | on: 10 | push: 11 | branches: 12 | - main # 触发条件为 push main分支 13 | jobs: 14 | docker: # 描述词, 无实际意义 15 | runs-on: ubuntu-latest # 运行的环境 16 | # 步骤 17 | steps: 18 | - uses: actions/checkout@v2 # 相当于执行git pull操作,以获取最新的代码 19 | 20 | - name: 打包镜像, 上传 Docker Hub # 描述词, 无实际意义 21 | run: | # 提交到dockerhub仓库和本地镜像仓库 22 | docker login -u luode0320 -p ${{ secrets.REGISTRY_PASSWORD }} 23 | docker build -t luode0320/web-start:latest . 24 | docker push luode0320/web-start:latest 25 | 26 | - name: 登录服务器, 启动 # 描述词, 无实际意义 27 | 28 | uses: appleboy/ssh-action@master # 通过SSH登录到远程服务器 29 | with: # 配置actions/setup-node@v1的一些属性 30 | host: ${{ secrets.REMOTE_HOST }} 31 | username: root 32 | password: ${{ secrets.REMOTE_PASSWORD }} 33 | port: 22 34 | script: | # 远程服务器依次执行下面的命令, 删除容器, 删除镜像, 启动容器 35 | docker rm -f web 36 | docker rmi luode0320/web-start:latest 37 | docker run -d \ 38 | --restart=always \ 39 | --name web \ 40 | -p 2000:2000 \ 41 | luode0320/web-start:latest 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | stats.html 2 | 3 | # Logs 4 | logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | pnpm-debug.log* 10 | lerna-debug.log* 11 | 12 | node_modules 13 | .DS_Store 14 | dist 15 | dist-ssr 16 | coverage 17 | *.local 18 | 19 | /cypress/videos/ 20 | /cypress/screenshots/ 21 | 22 | # Editor directories and files 23 | !.vscode/extensions.json 24 | .idea 25 | *.suo 26 | *.ntvs* 27 | *.njsproj 28 | *.sln 29 | *.sw? 30 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | pnpm lint 5 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | shamefully-hoist=true 2 | strict-peer-dependencies=false -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # 注意: 此 Dockerfile 配合GitHub action自动化配置, GitHub action自动化配置文件在 .github/workflows/main.yml 2 | # 如果手动执行此dockerfile, 注意将不必要的文件夹删除, 例如.idea, .github, 依赖等 3 | 4 | # 使用一个基础的Node.js镜像 5 | FROM node:18-alpine 6 | 7 | # 复制当前目录所有文件到 /aap下面 8 | COPY . /app 9 | 10 | # 设置主目录为 / app 11 | WORKDIR /app 12 | 13 | # 安装依赖 14 | RUN npm install 15 | # 打包 16 | RUN npm run build 17 | 18 | # 在每次启动时从 GitHub 更新文件 19 | CMD ["sh", "-c", "npm run preview"] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023-PRESENT Jic999 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 预览 3 | 4 |

简约风格的网址导航页

5 | 6 | [演示环境](https://www.luode.vip) 7 | 8 | ![Snipaste_2023-05-08_09-15-08](public/png/QQ截图20231019212432.png) 9 | 10 |
11 | 12 | # 启动 13 | 14 | ## 安装依赖 15 | ```bash 16 | npm install -g pnpm 17 | pnpm install 18 | ``` 19 | 20 | ## 启动项目 21 | ```bash 22 | npm run dev 23 | ``` 24 | 25 | # docker容器 26 | 27 | ## latest 版本 28 | 29 | ```shell 30 | docker pull luode0320/web-start:latest 31 | ``` 32 | 33 | 启动: 34 | 35 | ```shell 36 | docker run -d \ 37 | --restart=always \ 38 | --name web \ 39 | -p 2000:2000 \ 40 | luode0320/web-start:latest 41 | ``` 42 | 43 | 挂载配置(要先准备好配置文件): 44 | ```shell 45 | docker run -d \ 46 | --restart=always \ 47 | --name web \ 48 | -p 2000:2000 \ 49 | -v /usr/local/src/web/preset.json:/app/src/preset.json 50 | luode0320/web-start:latest 51 | ``` 52 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import antfu from '@antfu/eslint-config' 2 | import unocss from '@unocss/eslint-plugin' 3 | 4 | export default antfu( 5 | { 6 | ignores: ['src/preset.json'], 7 | }, 8 | unocss.configs.flat, 9 | { 10 | rules: { 11 | 'eslint-comments/no-unlimited-disable': 'off', 12 | 'vue/singleline-html-element-content-newline': 'off', 13 | 'vue/use-v-on-exact': 'off', 14 | }, 15 | }, 16 | ) 17 | -------------------------------------------------------------------------------- /extension/moon-chrome-extension.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luode0320/web-start-vue/e2ed2230e14312737253da2835c54df1c9e5b8d2/extension/moon-chrome-extension.zip -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Moon 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build.environment] 2 | NODE_VERSION = "16" 3 | 4 | [build] 5 | publish = "dist" 6 | command = "pnpm run build" 7 | 8 | [[redirects]] 9 | from = "/*" 10 | to = "/index.html" 11 | status = 200 -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "moon-web-start", 3 | "type": "module", 4 | "version": "0.0.0", 5 | "private": true, 6 | "author": "jic999", 7 | "license": "MIT", 8 | "scripts": { 9 | "dev": "vite", 10 | "build": "vite build", 11 | "preview": "vite preview --port 2000", 12 | "lint": "eslint .", 13 | "lint:fix": "eslint . --fix", 14 | "prepare": "husky install" 15 | }, 16 | "dependencies": { 17 | "@vueuse/components": "^10.1.2", 18 | "@vueuse/core": "^10.0.2", 19 | "axios": "^1.4.0", 20 | "axios-jsonp": "^1.0.4", 21 | "dayjs": "^1.11.7", 22 | "default-passive-events": "^2.0.0", 23 | "moon-web-start": "file:", 24 | "pinia": "^2.0.32", 25 | "solarlunar-es": "^1.0.9", 26 | "vue": "^3.2.47", 27 | "vue-router": "^4.1.6", 28 | "vuedraggable": "^4.1.0" 29 | }, 30 | "devDependencies": { 31 | "@antfu/eslint-config": "1.0.0-beta.18", 32 | "@iconify-json/carbon": "^1.1.16", 33 | "@iconify/utils": "^2.1.5", 34 | "@types/node": "^18.15.11", 35 | "@unocss/eslint-config": "^0.51.8", 36 | "@unocss/eslint-plugin": "^0.56.4", 37 | "@vitejs/plugin-vue": "^4.1.0", 38 | "eslint": "^8.50.0", 39 | "eslint-plugin-vue": "^9.10.0", 40 | "husky": "^8.0.0", 41 | "lint-staged": "^13.2.2", 42 | "naive-ui": "^2.34.3", 43 | "rollup-plugin-visualizer": "^5.9.2", 44 | "sass": "^1.60.0", 45 | "typescript": "^5.0.4", 46 | "unocss": "^0.56.4", 47 | "unocss-preset-scalpel": "^1.2.6", 48 | "unplugin-auto-import": "^0.15.2", 49 | "unplugin-vue-components": "^0.24.1", 50 | "unplugin-vue-macros": "^2.5.1", 51 | "vite": "^4.3.5", 52 | "vite-plugin-pages": "^0.29.0" 53 | }, 54 | "lint-staged": { 55 | "*": "eslint --fix" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /public/favicon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/png/QQ截图20231019212432.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luode0320/web-start-vue/e2ed2230e14312737253da2835c54df1c9e5b8d2/public/png/QQ截图20231019212432.png -------------------------------------------------------------------------------- /public/site/clougence.com.svg: -------------------------------------------------------------------------------- 1 | 2 | logo 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /public/site/github.com.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/site/jd.com.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/site/pinduoduo.com.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/site/taobao.com.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/svg/baidu.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/svg/bing.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/svg/blog.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/svg/chatgpt.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/svg/db.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/svg/google.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/svg/juejin.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | Created by potrace 1.14, written by Peter Selinger 2001-2017 9 | 10 | 12 | 21 | 47 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /public/svg/moonset.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/svg/sogou.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/svg/wikipedia.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 12 | -------------------------------------------------------------------------------- /src/auto-imports.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* prettier-ignore */ 3 | // @ts-nocheck 4 | // Generated by unplugin-auto-import 5 | export {} 6 | declare global { 7 | const $$: typeof import('vue/macros')['$$'] 8 | const $: typeof import('vue/macros')['$'] 9 | const $computed: typeof import('vue/macros')['$computed'] 10 | const $customRef: typeof import('vue/macros')['$customRef'] 11 | const $ref: typeof import('vue/macros')['$ref'] 12 | const $shallowRef: typeof import('vue/macros')['$shallowRef'] 13 | const $toRef: typeof import('vue/macros')['$toRef'] 14 | const EffectScope: typeof import('vue')['EffectScope'] 15 | const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate'] 16 | const asyncComputed: typeof import('@vueuse/core')['asyncComputed'] 17 | const autoResetRef: typeof import('@vueuse/core')['autoResetRef'] 18 | const computed: typeof import('vue')['computed'] 19 | const computedAsync: typeof import('@vueuse/core')['computedAsync'] 20 | const computedEager: typeof import('@vueuse/core')['computedEager'] 21 | const computedInject: typeof import('@vueuse/core')['computedInject'] 22 | const computedWithControl: typeof import('@vueuse/core')['computedWithControl'] 23 | const controlledComputed: typeof import('@vueuse/core')['controlledComputed'] 24 | const controlledRef: typeof import('@vueuse/core')['controlledRef'] 25 | const createApp: typeof import('vue')['createApp'] 26 | const createEventHook: typeof import('@vueuse/core')['createEventHook'] 27 | const createGlobalState: typeof import('@vueuse/core')['createGlobalState'] 28 | const createInjectionState: typeof import('@vueuse/core')['createInjectionState'] 29 | const createPinia: typeof import('pinia')['createPinia'] 30 | const createReactiveFn: typeof import('@vueuse/core')['createReactiveFn'] 31 | const createReusableTemplate: typeof import('@vueuse/core')['createReusableTemplate'] 32 | const createSharedComposable: typeof import('@vueuse/core')['createSharedComposable'] 33 | const createTemplatePromise: typeof import('@vueuse/core')['createTemplatePromise'] 34 | const createUnrefFn: typeof import('@vueuse/core')['createUnrefFn'] 35 | const customRef: typeof import('vue')['customRef'] 36 | const debouncedRef: typeof import('@vueuse/core')['debouncedRef'] 37 | const debouncedWatch: typeof import('@vueuse/core')['debouncedWatch'] 38 | const defineAsyncComponent: typeof import('vue')['defineAsyncComponent'] 39 | const defineComponent: typeof import('vue')['defineComponent'] 40 | const defineStore: typeof import('pinia')['defineStore'] 41 | const eagerComputed: typeof import('@vueuse/core')['eagerComputed'] 42 | const effectScope: typeof import('vue')['effectScope'] 43 | const extendRef: typeof import('@vueuse/core')['extendRef'] 44 | const getActivePinia: typeof import('pinia')['getActivePinia'] 45 | const getCurrentInstance: typeof import('vue')['getCurrentInstance'] 46 | const getCurrentScope: typeof import('vue')['getCurrentScope'] 47 | const h: typeof import('vue')['h'] 48 | const ignorableWatch: typeof import('@vueuse/core')['ignorableWatch'] 49 | const inject: typeof import('vue')['inject'] 50 | const isDark: typeof import('./composables/app')['isDark'] 51 | const isDefined: typeof import('@vueuse/core')['isDefined'] 52 | const isLgScreen: typeof import('./composables/app')['isLgScreen'] 53 | const isMdScreen: typeof import('./composables/app')['isMdScreen'] 54 | const isProxy: typeof import('vue')['isProxy'] 55 | const isReactive: typeof import('vue')['isReactive'] 56 | const isReadonly: typeof import('vue')['isReadonly'] 57 | const isRef: typeof import('vue')['isRef'] 58 | const isSmScreen: typeof import('./composables/app')['isSmScreen'] 59 | const isXlScreen: typeof import('./composables/app')['isXlScreen'] 60 | const isXsScreen: typeof import('./composables/app')['isXsScreen'] 61 | const loadSettings: typeof import('./stores/setting')['loadSettings'] 62 | const makeDestructurable: typeof import('@vueuse/core')['makeDestructurable'] 63 | const mapActions: typeof import('pinia')['mapActions'] 64 | const mapGetters: typeof import('pinia')['mapGetters'] 65 | const mapState: typeof import('pinia')['mapState'] 66 | const mapStores: typeof import('pinia')['mapStores'] 67 | const mapWritableState: typeof import('pinia')['mapWritableState'] 68 | const markRaw: typeof import('vue')['markRaw'] 69 | const nextTick: typeof import('vue')['nextTick'] 70 | const onActivated: typeof import('vue')['onActivated'] 71 | const onBeforeMount: typeof import('vue')['onBeforeMount'] 72 | const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave'] 73 | const onBeforeRouteUpdate: typeof import('vue-router')['onBeforeRouteUpdate'] 74 | const onBeforeUnmount: typeof import('vue')['onBeforeUnmount'] 75 | const onBeforeUpdate: typeof import('vue')['onBeforeUpdate'] 76 | const onClickOutside: typeof import('@vueuse/core')['onClickOutside'] 77 | const onDeactivated: typeof import('vue')['onDeactivated'] 78 | const onErrorCaptured: typeof import('vue')['onErrorCaptured'] 79 | const onKeyStroke: typeof import('@vueuse/core')['onKeyStroke'] 80 | const onLongPress: typeof import('@vueuse/core')['onLongPress'] 81 | const onMounted: typeof import('vue')['onMounted'] 82 | const onRenderTracked: typeof import('vue')['onRenderTracked'] 83 | const onRenderTriggered: typeof import('vue')['onRenderTriggered'] 84 | const onScopeDispose: typeof import('vue')['onScopeDispose'] 85 | const onServerPrefetch: typeof import('vue')['onServerPrefetch'] 86 | const onStartTyping: typeof import('@vueuse/core')['onStartTyping'] 87 | const onUnmounted: typeof import('vue')['onUnmounted'] 88 | const onUpdated: typeof import('vue')['onUpdated'] 89 | const pausableWatch: typeof import('@vueuse/core')['pausableWatch'] 90 | const provide: typeof import('vue')['provide'] 91 | const reactify: typeof import('@vueuse/core')['reactify'] 92 | const reactifyObject: typeof import('@vueuse/core')['reactifyObject'] 93 | const reactive: typeof import('vue')['reactive'] 94 | const reactiveComputed: typeof import('@vueuse/core')['reactiveComputed'] 95 | const reactiveOmit: typeof import('@vueuse/core')['reactiveOmit'] 96 | const reactivePick: typeof import('@vueuse/core')['reactivePick'] 97 | const readonly: typeof import('vue')['readonly'] 98 | const ref: typeof import('vue')['ref'] 99 | const refAutoReset: typeof import('@vueuse/core')['refAutoReset'] 100 | const refDebounced: typeof import('@vueuse/core')['refDebounced'] 101 | const refDefault: typeof import('@vueuse/core')['refDefault'] 102 | const refThrottled: typeof import('@vueuse/core')['refThrottled'] 103 | const refWithControl: typeof import('@vueuse/core')['refWithControl'] 104 | const resolveComponent: typeof import('vue')['resolveComponent'] 105 | const resolveRef: typeof import('@vueuse/core')['resolveRef'] 106 | const resolveUnref: typeof import('@vueuse/core')['resolveUnref'] 107 | const setActivePinia: typeof import('pinia')['setActivePinia'] 108 | const setMapStoreSuffix: typeof import('pinia')['setMapStoreSuffix'] 109 | const shallowReactive: typeof import('vue')['shallowReactive'] 110 | const shallowReadonly: typeof import('vue')['shallowReadonly'] 111 | const shallowRef: typeof import('vue')['shallowRef'] 112 | const storeToRefs: typeof import('pinia')['storeToRefs'] 113 | const syncRef: typeof import('@vueuse/core')['syncRef'] 114 | const syncRefs: typeof import('@vueuse/core')['syncRefs'] 115 | const templateRef: typeof import('@vueuse/core')['templateRef'] 116 | const themeVars: typeof import('./composables/theme')['themeVars'] 117 | const throttledRef: typeof import('@vueuse/core')['throttledRef'] 118 | const throttledWatch: typeof import('@vueuse/core')['throttledWatch'] 119 | const toRaw: typeof import('vue')['toRaw'] 120 | const toReactive: typeof import('@vueuse/core')['toReactive'] 121 | const toRef: typeof import('@vueuse/core')['toRef'] 122 | const toRefs: typeof import('vue')['toRefs'] 123 | const toValue: typeof import('@vueuse/core')['toValue'] 124 | const toggleDark: typeof import('./composables/app')['toggleDark'] 125 | const toggleTheme: typeof import('./composables/theme')['toggleTheme'] 126 | const triggerRef: typeof import('vue')['triggerRef'] 127 | const tryOnBeforeMount: typeof import('@vueuse/core')['tryOnBeforeMount'] 128 | const tryOnBeforeUnmount: typeof import('@vueuse/core')['tryOnBeforeUnmount'] 129 | const tryOnMounted: typeof import('@vueuse/core')['tryOnMounted'] 130 | const tryOnScopeDispose: typeof import('@vueuse/core')['tryOnScopeDispose'] 131 | const tryOnUnmounted: typeof import('@vueuse/core')['tryOnUnmounted'] 132 | const unref: typeof import('vue')['unref'] 133 | const unrefElement: typeof import('@vueuse/core')['unrefElement'] 134 | const until: typeof import('@vueuse/core')['until'] 135 | const useActiveElement: typeof import('@vueuse/core')['useActiveElement'] 136 | const useAnimate: typeof import('@vueuse/core')['useAnimate'] 137 | const useArrayDifference: typeof import('@vueuse/core')['useArrayDifference'] 138 | const useArrayEvery: typeof import('@vueuse/core')['useArrayEvery'] 139 | const useArrayFilter: typeof import('@vueuse/core')['useArrayFilter'] 140 | const useArrayFind: typeof import('@vueuse/core')['useArrayFind'] 141 | const useArrayFindIndex: typeof import('@vueuse/core')['useArrayFindIndex'] 142 | const useArrayFindLast: typeof import('@vueuse/core')['useArrayFindLast'] 143 | const useArrayIncludes: typeof import('@vueuse/core')['useArrayIncludes'] 144 | const useArrayJoin: typeof import('@vueuse/core')['useArrayJoin'] 145 | const useArrayMap: typeof import('@vueuse/core')['useArrayMap'] 146 | const useArrayReduce: typeof import('@vueuse/core')['useArrayReduce'] 147 | const useArraySome: typeof import('@vueuse/core')['useArraySome'] 148 | const useArrayUnique: typeof import('@vueuse/core')['useArrayUnique'] 149 | const useAsyncQueue: typeof import('@vueuse/core')['useAsyncQueue'] 150 | const useAsyncState: typeof import('@vueuse/core')['useAsyncState'] 151 | const useAttrs: typeof import('vue')['useAttrs'] 152 | const useBase64: typeof import('@vueuse/core')['useBase64'] 153 | const useBattery: typeof import('@vueuse/core')['useBattery'] 154 | const useBluetooth: typeof import('@vueuse/core')['useBluetooth'] 155 | const useBreakpoints: typeof import('@vueuse/core')['useBreakpoints'] 156 | const useBroadcastChannel: typeof import('@vueuse/core')['useBroadcastChannel'] 157 | const useBrowserLocation: typeof import('@vueuse/core')['useBrowserLocation'] 158 | const useCached: typeof import('@vueuse/core')['useCached'] 159 | const useClipboard: typeof import('@vueuse/core')['useClipboard'] 160 | const useCloned: typeof import('@vueuse/core')['useCloned'] 161 | const useColorMode: typeof import('@vueuse/core')['useColorMode'] 162 | const useConfirmDialog: typeof import('@vueuse/core')['useConfirmDialog'] 163 | const useCounter: typeof import('@vueuse/core')['useCounter'] 164 | const useCssModule: typeof import('vue')['useCssModule'] 165 | const useCssVar: typeof import('@vueuse/core')['useCssVar'] 166 | const useCssVars: typeof import('vue')['useCssVars'] 167 | const useCurrentElement: typeof import('@vueuse/core')['useCurrentElement'] 168 | const useCycleList: typeof import('@vueuse/core')['useCycleList'] 169 | const useDark: typeof import('@vueuse/core')['useDark'] 170 | const useDateFormat: typeof import('@vueuse/core')['useDateFormat'] 171 | const useDebounce: typeof import('@vueuse/core')['useDebounce'] 172 | const useDebounceFn: typeof import('@vueuse/core')['useDebounceFn'] 173 | const useDebouncedRefHistory: typeof import('@vueuse/core')['useDebouncedRefHistory'] 174 | const useDeviceMotion: typeof import('@vueuse/core')['useDeviceMotion'] 175 | const useDeviceOrientation: typeof import('@vueuse/core')['useDeviceOrientation'] 176 | const useDevicePixelRatio: typeof import('@vueuse/core')['useDevicePixelRatio'] 177 | const useDevicesList: typeof import('@vueuse/core')['useDevicesList'] 178 | const useDisplayMedia: typeof import('@vueuse/core')['useDisplayMedia'] 179 | const useDocumentVisibility: typeof import('@vueuse/core')['useDocumentVisibility'] 180 | const useDrag: typeof import('./composables/draggable')['useDrag'] 181 | const useDraggable: typeof import('@vueuse/core')['useDraggable'] 182 | const useDropZone: typeof import('@vueuse/core')['useDropZone'] 183 | const useElementBounding: typeof import('@vueuse/core')['useElementBounding'] 184 | const useElementByPoint: typeof import('@vueuse/core')['useElementByPoint'] 185 | const useElementHover: typeof import('@vueuse/core')['useElementHover'] 186 | const useElementSize: typeof import('@vueuse/core')['useElementSize'] 187 | const useElementVisibility: typeof import('@vueuse/core')['useElementVisibility'] 188 | const useEventBus: typeof import('@vueuse/core')['useEventBus'] 189 | const useEventListener: typeof import('@vueuse/core')['useEventListener'] 190 | const useEventSource: typeof import('@vueuse/core')['useEventSource'] 191 | const useEyeDropper: typeof import('@vueuse/core')['useEyeDropper'] 192 | const useFavicon: typeof import('@vueuse/core')['useFavicon'] 193 | const useFetch: typeof import('@vueuse/core')['useFetch'] 194 | const useFileDialog: typeof import('@vueuse/core')['useFileDialog'] 195 | const useFileSystemAccess: typeof import('@vueuse/core')['useFileSystemAccess'] 196 | const useFocus: typeof import('@vueuse/core')['useFocus'] 197 | const useFocusWithin: typeof import('@vueuse/core')['useFocusWithin'] 198 | const useFps: typeof import('@vueuse/core')['useFps'] 199 | const useFullscreen: typeof import('@vueuse/core')['useFullscreen'] 200 | const useGamepad: typeof import('@vueuse/core')['useGamepad'] 201 | const useGeolocation: typeof import('@vueuse/core')['useGeolocation'] 202 | const useIconStyle: typeof import('./composables/icon_style')['useIconStyle'] 203 | const useIdle: typeof import('@vueuse/core')['useIdle'] 204 | const useImage: typeof import('@vueuse/core')['useImage'] 205 | const useInfiniteScroll: typeof import('@vueuse/core')['useInfiniteScroll'] 206 | const useIntersectionObserver: typeof import('@vueuse/core')['useIntersectionObserver'] 207 | const useInterval: typeof import('@vueuse/core')['useInterval'] 208 | const useIntervalFn: typeof import('@vueuse/core')['useIntervalFn'] 209 | const useKeyModifier: typeof import('@vueuse/core')['useKeyModifier'] 210 | const useLastChanged: typeof import('@vueuse/core')['useLastChanged'] 211 | const useLink: typeof import('vue-router')['useLink'] 212 | const useLocalStorage: typeof import('@vueuse/core')['useLocalStorage'] 213 | const useMagicKeys: typeof import('@vueuse/core')['useMagicKeys'] 214 | const useManualRefHistory: typeof import('@vueuse/core')['useManualRefHistory'] 215 | const useMediaControls: typeof import('@vueuse/core')['useMediaControls'] 216 | const useMediaQuery: typeof import('@vueuse/core')['useMediaQuery'] 217 | const useMemoize: typeof import('@vueuse/core')['useMemoize'] 218 | const useMemory: typeof import('@vueuse/core')['useMemory'] 219 | const useModalStore: typeof import('./stores/modal')['useModalStore'] 220 | const useMounted: typeof import('@vueuse/core')['useMounted'] 221 | const useMouse: typeof import('@vueuse/core')['useMouse'] 222 | const useMouseInElement: typeof import('@vueuse/core')['useMouseInElement'] 223 | const useMousePressed: typeof import('@vueuse/core')['useMousePressed'] 224 | const useMutationObserver: typeof import('@vueuse/core')['useMutationObserver'] 225 | const useNavigatorLanguage: typeof import('@vueuse/core')['useNavigatorLanguage'] 226 | const useNetwork: typeof import('@vueuse/core')['useNetwork'] 227 | const useNow: typeof import('@vueuse/core')['useNow'] 228 | const useObjectUrl: typeof import('@vueuse/core')['useObjectUrl'] 229 | const useOffsetPagination: typeof import('@vueuse/core')['useOffsetPagination'] 230 | const useOnline: typeof import('@vueuse/core')['useOnline'] 231 | const usePageLeave: typeof import('@vueuse/core')['usePageLeave'] 232 | const useParallax: typeof import('@vueuse/core')['useParallax'] 233 | const useParentElement: typeof import('@vueuse/core')['useParentElement'] 234 | const usePerformanceObserver: typeof import('@vueuse/core')['usePerformanceObserver'] 235 | const usePermission: typeof import('@vueuse/core')['usePermission'] 236 | const usePointer: typeof import('@vueuse/core')['usePointer'] 237 | const usePointerLock: typeof import('@vueuse/core')['usePointerLock'] 238 | const usePointerSwipe: typeof import('@vueuse/core')['usePointerSwipe'] 239 | const usePreferredColorScheme: typeof import('@vueuse/core')['usePreferredColorScheme'] 240 | const usePreferredContrast: typeof import('@vueuse/core')['usePreferredContrast'] 241 | const usePreferredDark: typeof import('@vueuse/core')['usePreferredDark'] 242 | const usePreferredLanguages: typeof import('@vueuse/core')['usePreferredLanguages'] 243 | const usePreferredReducedMotion: typeof import('@vueuse/core')['usePreferredReducedMotion'] 244 | const usePrevious: typeof import('@vueuse/core')['usePrevious'] 245 | const useRafFn: typeof import('@vueuse/core')['useRafFn'] 246 | const useRefHistory: typeof import('@vueuse/core')['useRefHistory'] 247 | const useRenderStore: typeof import('./stores/render')['useRenderStore'] 248 | const useResizeObserver: typeof import('@vueuse/core')['useResizeObserver'] 249 | const useRoute: typeof import('vue-router')['useRoute'] 250 | const useRouter: typeof import('vue-router')['useRouter'] 251 | const useScreenOrientation: typeof import('@vueuse/core')['useScreenOrientation'] 252 | const useScreenSafeArea: typeof import('@vueuse/core')['useScreenSafeArea'] 253 | const useScriptTag: typeof import('@vueuse/core')['useScriptTag'] 254 | const useScroll: typeof import('@vueuse/core')['useScroll'] 255 | const useScrollLock: typeof import('@vueuse/core')['useScrollLock'] 256 | const useSessionStorage: typeof import('@vueuse/core')['useSessionStorage'] 257 | const useSettingStore: typeof import('./stores/setting')['useSettingStore'] 258 | const useShare: typeof import('@vueuse/core')['useShare'] 259 | const useSiteStore: typeof import('./stores/site')['useSiteStore'] 260 | const useSlots: typeof import('vue')['useSlots'] 261 | const useSorted: typeof import('@vueuse/core')['useSorted'] 262 | const useSpeechRecognition: typeof import('@vueuse/core')['useSpeechRecognition'] 263 | const useSpeechSynthesis: typeof import('@vueuse/core')['useSpeechSynthesis'] 264 | const useStepper: typeof import('@vueuse/core')['useStepper'] 265 | const useStorage: typeof import('@vueuse/core')['useStorage'] 266 | const useStorageAsync: typeof import('@vueuse/core')['useStorageAsync'] 267 | const useStyleTag: typeof import('@vueuse/core')['useStyleTag'] 268 | const useSupported: typeof import('@vueuse/core')['useSupported'] 269 | const useSwipe: typeof import('@vueuse/core')['useSwipe'] 270 | const useTemplateRefsList: typeof import('@vueuse/core')['useTemplateRefsList'] 271 | const useTextDirection: typeof import('@vueuse/core')['useTextDirection'] 272 | const useTextSelection: typeof import('@vueuse/core')['useTextSelection'] 273 | const useTextareaAutosize: typeof import('@vueuse/core')['useTextareaAutosize'] 274 | const useThrottle: typeof import('@vueuse/core')['useThrottle'] 275 | const useThrottleFn: typeof import('@vueuse/core')['useThrottleFn'] 276 | const useThrottledRefHistory: typeof import('@vueuse/core')['useThrottledRefHistory'] 277 | const useTimeAgo: typeof import('@vueuse/core')['useTimeAgo'] 278 | const useTimeout: typeof import('@vueuse/core')['useTimeout'] 279 | const useTimeoutFn: typeof import('@vueuse/core')['useTimeoutFn'] 280 | const useTimeoutPoll: typeof import('@vueuse/core')['useTimeoutPoll'] 281 | const useTimestamp: typeof import('@vueuse/core')['useTimestamp'] 282 | const useTitle: typeof import('@vueuse/core')['useTitle'] 283 | const useToNumber: typeof import('@vueuse/core')['useToNumber'] 284 | const useToString: typeof import('@vueuse/core')['useToString'] 285 | const useToggle: typeof import('@vueuse/core')['useToggle'] 286 | const useTransition: typeof import('@vueuse/core')['useTransition'] 287 | const useUrlSearchParams: typeof import('@vueuse/core')['useUrlSearchParams'] 288 | const useUserMedia: typeof import('@vueuse/core')['useUserMedia'] 289 | const useVModel: typeof import('@vueuse/core')['useVModel'] 290 | const useVModels: typeof import('@vueuse/core')['useVModels'] 291 | const useVibrate: typeof import('@vueuse/core')['useVibrate'] 292 | const useVirtualList: typeof import('@vueuse/core')['useVirtualList'] 293 | const useWakeLock: typeof import('@vueuse/core')['useWakeLock'] 294 | const useWebNotification: typeof import('@vueuse/core')['useWebNotification'] 295 | const useWebSocket: typeof import('@vueuse/core')['useWebSocket'] 296 | const useWebWorker: typeof import('@vueuse/core')['useWebWorker'] 297 | const useWebWorkerFn: typeof import('@vueuse/core')['useWebWorkerFn'] 298 | const useWindowFocus: typeof import('@vueuse/core')['useWindowFocus'] 299 | const useWindowScroll: typeof import('@vueuse/core')['useWindowScroll'] 300 | const useWindowSize: typeof import('@vueuse/core')['useWindowSize'] 301 | const watch: typeof import('vue')['watch'] 302 | const watchArray: typeof import('@vueuse/core')['watchArray'] 303 | const watchAtMost: typeof import('@vueuse/core')['watchAtMost'] 304 | const watchDebounced: typeof import('@vueuse/core')['watchDebounced'] 305 | const watchDeep: typeof import('@vueuse/core')['watchDeep'] 306 | const watchEffect: typeof import('vue')['watchEffect'] 307 | const watchIgnorable: typeof import('@vueuse/core')['watchIgnorable'] 308 | const watchImmediate: typeof import('@vueuse/core')['watchImmediate'] 309 | const watchOnce: typeof import('@vueuse/core')['watchOnce'] 310 | const watchPausable: typeof import('@vueuse/core')['watchPausable'] 311 | const watchPostEffect: typeof import('vue')['watchPostEffect'] 312 | const watchSyncEffect: typeof import('vue')['watchSyncEffect'] 313 | const watchThrottled: typeof import('@vueuse/core')['watchThrottled'] 314 | const watchTriggerable: typeof import('@vueuse/core')['watchTriggerable'] 315 | const watchWithFilter: typeof import('@vueuse/core')['watchWithFilter'] 316 | const whenever: typeof import('@vueuse/core')['whenever'] 317 | } 318 | // for type re-export 319 | declare global { 320 | // @ts-ignore 321 | export type { Component, ComponentPublicInstance, ComputedRef, InjectionKey, PropType, Ref, VNode } from 'vue' 322 | } 323 | // for vue template auto import 324 | import { UnwrapRef } from 'vue' 325 | declare module 'vue' { 326 | interface ComponentCustomProperties { 327 | readonly $$: UnwrapRef 328 | readonly $: UnwrapRef 329 | readonly $computed: UnwrapRef 330 | readonly $customRef: UnwrapRef 331 | readonly $ref: UnwrapRef 332 | readonly $shallowRef: UnwrapRef 333 | readonly $toRef: UnwrapRef 334 | readonly EffectScope: UnwrapRef 335 | readonly acceptHMRUpdate: UnwrapRef 336 | readonly asyncComputed: UnwrapRef 337 | readonly autoResetRef: UnwrapRef 338 | readonly computed: UnwrapRef 339 | readonly computedAsync: UnwrapRef 340 | readonly computedEager: UnwrapRef 341 | readonly computedInject: UnwrapRef 342 | readonly computedWithControl: UnwrapRef 343 | readonly controlledComputed: UnwrapRef 344 | readonly controlledRef: UnwrapRef 345 | readonly createApp: UnwrapRef 346 | readonly createEventHook: UnwrapRef 347 | readonly createGlobalState: UnwrapRef 348 | readonly createInjectionState: UnwrapRef 349 | readonly createPinia: UnwrapRef 350 | readonly createReactiveFn: UnwrapRef 351 | readonly createReusableTemplate: UnwrapRef 352 | readonly createSharedComposable: UnwrapRef 353 | readonly createTemplatePromise: UnwrapRef 354 | readonly createUnrefFn: UnwrapRef 355 | readonly customRef: UnwrapRef 356 | readonly debouncedRef: UnwrapRef 357 | readonly debouncedWatch: UnwrapRef 358 | readonly defineAsyncComponent: UnwrapRef 359 | readonly defineComponent: UnwrapRef 360 | readonly defineStore: UnwrapRef 361 | readonly eagerComputed: UnwrapRef 362 | readonly effectScope: UnwrapRef 363 | readonly extendRef: UnwrapRef 364 | readonly getActivePinia: UnwrapRef 365 | readonly getCurrentInstance: UnwrapRef 366 | readonly getCurrentScope: UnwrapRef 367 | readonly h: UnwrapRef 368 | readonly ignorableWatch: UnwrapRef 369 | readonly inject: UnwrapRef 370 | readonly isDark: UnwrapRef 371 | readonly isDefined: UnwrapRef 372 | readonly isLgScreen: UnwrapRef 373 | readonly isMdScreen: UnwrapRef 374 | readonly isProxy: UnwrapRef 375 | readonly isReactive: UnwrapRef 376 | readonly isReadonly: UnwrapRef 377 | readonly isRef: UnwrapRef 378 | readonly isSmScreen: UnwrapRef 379 | readonly isXlScreen: UnwrapRef 380 | readonly isXsScreen: UnwrapRef 381 | readonly loadSettings: UnwrapRef 382 | readonly makeDestructurable: UnwrapRef 383 | readonly mapActions: UnwrapRef 384 | readonly mapGetters: UnwrapRef 385 | readonly mapState: UnwrapRef 386 | readonly mapStores: UnwrapRef 387 | readonly mapWritableState: UnwrapRef 388 | readonly markRaw: UnwrapRef 389 | readonly nextTick: UnwrapRef 390 | readonly onActivated: UnwrapRef 391 | readonly onBeforeMount: UnwrapRef 392 | readonly onBeforeRouteLeave: UnwrapRef 393 | readonly onBeforeRouteUpdate: UnwrapRef 394 | readonly onBeforeUnmount: UnwrapRef 395 | readonly onBeforeUpdate: UnwrapRef 396 | readonly onClickOutside: UnwrapRef 397 | readonly onDeactivated: UnwrapRef 398 | readonly onErrorCaptured: UnwrapRef 399 | readonly onKeyStroke: UnwrapRef 400 | readonly onLongPress: UnwrapRef 401 | readonly onMounted: UnwrapRef 402 | readonly onRenderTracked: UnwrapRef 403 | readonly onRenderTriggered: UnwrapRef 404 | readonly onScopeDispose: UnwrapRef 405 | readonly onServerPrefetch: UnwrapRef 406 | readonly onStartTyping: UnwrapRef 407 | readonly onUnmounted: UnwrapRef 408 | readonly onUpdated: UnwrapRef 409 | readonly pausableWatch: UnwrapRef 410 | readonly provide: UnwrapRef 411 | readonly reactify: UnwrapRef 412 | readonly reactifyObject: UnwrapRef 413 | readonly reactive: UnwrapRef 414 | readonly reactiveComputed: UnwrapRef 415 | readonly reactiveOmit: UnwrapRef 416 | readonly reactivePick: UnwrapRef 417 | readonly readonly: UnwrapRef 418 | readonly ref: UnwrapRef 419 | readonly refAutoReset: UnwrapRef 420 | readonly refDebounced: UnwrapRef 421 | readonly refDefault: UnwrapRef 422 | readonly refThrottled: UnwrapRef 423 | readonly refWithControl: UnwrapRef 424 | readonly resolveComponent: UnwrapRef 425 | readonly resolveRef: UnwrapRef 426 | readonly resolveUnref: UnwrapRef 427 | readonly setActivePinia: UnwrapRef 428 | readonly setMapStoreSuffix: UnwrapRef 429 | readonly shallowReactive: UnwrapRef 430 | readonly shallowReadonly: UnwrapRef 431 | readonly shallowRef: UnwrapRef 432 | readonly storeToRefs: UnwrapRef 433 | readonly syncRef: UnwrapRef 434 | readonly syncRefs: UnwrapRef 435 | readonly templateRef: UnwrapRef 436 | readonly themeVars: UnwrapRef 437 | readonly throttledRef: UnwrapRef 438 | readonly throttledWatch: UnwrapRef 439 | readonly toRaw: UnwrapRef 440 | readonly toReactive: UnwrapRef 441 | readonly toRef: UnwrapRef 442 | readonly toRefs: UnwrapRef 443 | readonly toValue: UnwrapRef 444 | readonly toggleDark: UnwrapRef 445 | readonly toggleTheme: UnwrapRef 446 | readonly triggerRef: UnwrapRef 447 | readonly tryOnBeforeMount: UnwrapRef 448 | readonly tryOnBeforeUnmount: UnwrapRef 449 | readonly tryOnMounted: UnwrapRef 450 | readonly tryOnScopeDispose: UnwrapRef 451 | readonly tryOnUnmounted: UnwrapRef 452 | readonly unref: UnwrapRef 453 | readonly unrefElement: UnwrapRef 454 | readonly until: UnwrapRef 455 | readonly useActiveElement: UnwrapRef 456 | readonly useAnimate: UnwrapRef 457 | readonly useArrayDifference: UnwrapRef 458 | readonly useArrayEvery: UnwrapRef 459 | readonly useArrayFilter: UnwrapRef 460 | readonly useArrayFind: UnwrapRef 461 | readonly useArrayFindIndex: UnwrapRef 462 | readonly useArrayFindLast: UnwrapRef 463 | readonly useArrayIncludes: UnwrapRef 464 | readonly useArrayJoin: UnwrapRef 465 | readonly useArrayMap: UnwrapRef 466 | readonly useArrayReduce: UnwrapRef 467 | readonly useArraySome: UnwrapRef 468 | readonly useArrayUnique: UnwrapRef 469 | readonly useAsyncQueue: UnwrapRef 470 | readonly useAsyncState: UnwrapRef 471 | readonly useAttrs: UnwrapRef 472 | readonly useBase64: UnwrapRef 473 | readonly useBattery: UnwrapRef 474 | readonly useBluetooth: UnwrapRef 475 | readonly useBreakpoints: UnwrapRef 476 | readonly useBroadcastChannel: UnwrapRef 477 | readonly useBrowserLocation: UnwrapRef 478 | readonly useCached: UnwrapRef 479 | readonly useClipboard: UnwrapRef 480 | readonly useCloned: UnwrapRef 481 | readonly useColorMode: UnwrapRef 482 | readonly useConfirmDialog: UnwrapRef 483 | readonly useCounter: UnwrapRef 484 | readonly useCssModule: UnwrapRef 485 | readonly useCssVar: UnwrapRef 486 | readonly useCssVars: UnwrapRef 487 | readonly useCurrentElement: UnwrapRef 488 | readonly useCycleList: UnwrapRef 489 | readonly useDark: UnwrapRef 490 | readonly useDateFormat: UnwrapRef 491 | readonly useDebounce: UnwrapRef 492 | readonly useDebounceFn: UnwrapRef 493 | readonly useDebouncedRefHistory: UnwrapRef 494 | readonly useDeviceMotion: UnwrapRef 495 | readonly useDeviceOrientation: UnwrapRef 496 | readonly useDevicePixelRatio: UnwrapRef 497 | readonly useDevicesList: UnwrapRef 498 | readonly useDisplayMedia: UnwrapRef 499 | readonly useDocumentVisibility: UnwrapRef 500 | readonly useDrag: UnwrapRef 501 | readonly useDraggable: UnwrapRef 502 | readonly useDropZone: UnwrapRef 503 | readonly useElementBounding: UnwrapRef 504 | readonly useElementByPoint: UnwrapRef 505 | readonly useElementHover: UnwrapRef 506 | readonly useElementSize: UnwrapRef 507 | readonly useElementVisibility: UnwrapRef 508 | readonly useEventBus: UnwrapRef 509 | readonly useEventListener: UnwrapRef 510 | readonly useEventSource: UnwrapRef 511 | readonly useEyeDropper: UnwrapRef 512 | readonly useFavicon: UnwrapRef 513 | readonly useFetch: UnwrapRef 514 | readonly useFileDialog: UnwrapRef 515 | readonly useFileSystemAccess: UnwrapRef 516 | readonly useFocus: UnwrapRef 517 | readonly useFocusWithin: UnwrapRef 518 | readonly useFps: UnwrapRef 519 | readonly useFullscreen: UnwrapRef 520 | readonly useGamepad: UnwrapRef 521 | readonly useGeolocation: UnwrapRef 522 | readonly useIconStyle: UnwrapRef 523 | readonly useIdle: UnwrapRef 524 | readonly useImage: UnwrapRef 525 | readonly useInfiniteScroll: UnwrapRef 526 | readonly useIntersectionObserver: UnwrapRef 527 | readonly useInterval: UnwrapRef 528 | readonly useIntervalFn: UnwrapRef 529 | readonly useKeyModifier: UnwrapRef 530 | readonly useLastChanged: UnwrapRef 531 | readonly useLink: UnwrapRef 532 | readonly useLocalStorage: UnwrapRef 533 | readonly useMagicKeys: UnwrapRef 534 | readonly useManualRefHistory: UnwrapRef 535 | readonly useMediaControls: UnwrapRef 536 | readonly useMediaQuery: UnwrapRef 537 | readonly useMemoize: UnwrapRef 538 | readonly useMemory: UnwrapRef 539 | readonly useModalStore: UnwrapRef 540 | readonly useMounted: UnwrapRef 541 | readonly useMouse: UnwrapRef 542 | readonly useMouseInElement: UnwrapRef 543 | readonly useMousePressed: UnwrapRef 544 | readonly useMutationObserver: UnwrapRef 545 | readonly useNavigatorLanguage: UnwrapRef 546 | readonly useNetwork: UnwrapRef 547 | readonly useNow: UnwrapRef 548 | readonly useObjectUrl: UnwrapRef 549 | readonly useOffsetPagination: UnwrapRef 550 | readonly useOnline: UnwrapRef 551 | readonly usePageLeave: UnwrapRef 552 | readonly useParallax: UnwrapRef 553 | readonly useParentElement: UnwrapRef 554 | readonly usePerformanceObserver: UnwrapRef 555 | readonly usePermission: UnwrapRef 556 | readonly usePointer: UnwrapRef 557 | readonly usePointerLock: UnwrapRef 558 | readonly usePointerSwipe: UnwrapRef 559 | readonly usePreferredColorScheme: UnwrapRef 560 | readonly usePreferredContrast: UnwrapRef 561 | readonly usePreferredDark: UnwrapRef 562 | readonly usePreferredLanguages: UnwrapRef 563 | readonly usePreferredReducedMotion: UnwrapRef 564 | readonly usePrevious: UnwrapRef 565 | readonly useRafFn: UnwrapRef 566 | readonly useRefHistory: UnwrapRef 567 | readonly useRenderStore: UnwrapRef 568 | readonly useResizeObserver: UnwrapRef 569 | readonly useRoute: UnwrapRef 570 | readonly useRouter: UnwrapRef 571 | readonly useScreenOrientation: UnwrapRef 572 | readonly useScreenSafeArea: UnwrapRef 573 | readonly useScriptTag: UnwrapRef 574 | readonly useScroll: UnwrapRef 575 | readonly useScrollLock: UnwrapRef 576 | readonly useSessionStorage: UnwrapRef 577 | readonly useSettingStore: UnwrapRef 578 | readonly useShare: UnwrapRef 579 | readonly useSiteStore: UnwrapRef 580 | readonly useSlots: UnwrapRef 581 | readonly useSorted: UnwrapRef 582 | readonly useSpeechRecognition: UnwrapRef 583 | readonly useSpeechSynthesis: UnwrapRef 584 | readonly useStepper: UnwrapRef 585 | readonly useStorage: UnwrapRef 586 | readonly useStorageAsync: UnwrapRef 587 | readonly useStyleTag: UnwrapRef 588 | readonly useSupported: UnwrapRef 589 | readonly useSwipe: UnwrapRef 590 | readonly useTemplateRefsList: UnwrapRef 591 | readonly useTextDirection: UnwrapRef 592 | readonly useTextSelection: UnwrapRef 593 | readonly useTextareaAutosize: UnwrapRef 594 | readonly useThrottle: UnwrapRef 595 | readonly useThrottleFn: UnwrapRef 596 | readonly useThrottledRefHistory: UnwrapRef 597 | readonly useTimeAgo: UnwrapRef 598 | readonly useTimeout: UnwrapRef 599 | readonly useTimeoutFn: UnwrapRef 600 | readonly useTimeoutPoll: UnwrapRef 601 | readonly useTimestamp: UnwrapRef 602 | readonly useTitle: UnwrapRef 603 | readonly useToNumber: UnwrapRef 604 | readonly useToString: UnwrapRef 605 | readonly useToggle: UnwrapRef 606 | readonly useTransition: UnwrapRef 607 | readonly useUrlSearchParams: UnwrapRef 608 | readonly useUserMedia: UnwrapRef 609 | readonly useVModel: UnwrapRef 610 | readonly useVModels: UnwrapRef 611 | readonly useVibrate: UnwrapRef 612 | readonly useVirtualList: UnwrapRef 613 | readonly useWakeLock: UnwrapRef 614 | readonly useWebNotification: UnwrapRef 615 | readonly useWebSocket: UnwrapRef 616 | readonly useWebWorker: UnwrapRef 617 | readonly useWebWorkerFn: UnwrapRef 618 | readonly useWindowFocus: UnwrapRef 619 | readonly useWindowScroll: UnwrapRef 620 | readonly useWindowSize: UnwrapRef 621 | readonly watch: UnwrapRef 622 | readonly watchArray: UnwrapRef 623 | readonly watchAtMost: UnwrapRef 624 | readonly watchDebounced: UnwrapRef 625 | readonly watchDeep: UnwrapRef 626 | readonly watchEffect: UnwrapRef 627 | readonly watchIgnorable: UnwrapRef 628 | readonly watchImmediate: UnwrapRef 629 | readonly watchOnce: UnwrapRef 630 | readonly watchPausable: UnwrapRef 631 | readonly watchPostEffect: UnwrapRef 632 | readonly watchSyncEffect: UnwrapRef 633 | readonly watchThrottled: UnwrapRef 634 | readonly watchTriggerable: UnwrapRef 635 | readonly watchWithFilter: UnwrapRef 636 | readonly whenever: UnwrapRef 637 | } 638 | } 639 | -------------------------------------------------------------------------------- /src/components.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* prettier-ignore */ 3 | // @ts-nocheck 4 | // Generated by unplugin-vue-components 5 | // Read more: https://github.com/vuejs/core/pull/3399 6 | import '@vue/runtime-core' 7 | 8 | export {} 9 | 10 | declare module '@vue/runtime-core' { 11 | export interface GlobalComponents { 12 | AppContainer: typeof import('./components/AppContainer.vue')['default'] 13 | AppProvider: typeof import('./components/AppProvider/index.vue')['default'] 14 | Blank: typeof import('./components/Blank.vue')['default'] 15 | NButton: typeof import('naive-ui')['NButton'] 16 | NConfigProvider: typeof import('naive-ui')['NConfigProvider'] 17 | NDialogProvider: typeof import('naive-ui')['NDialogProvider'] 18 | NInput: typeof import('naive-ui')['NInput'] 19 | NModal: typeof import('naive-ui')['NModal'] 20 | NNotificationProvider: typeof import('naive-ui')['NNotificationProvider'] 21 | NSelect: typeof import('naive-ui')['NSelect'] 22 | RouterLink: typeof import('vue-router')['RouterLink'] 23 | RouterView: typeof import('vue-router')['RouterView'] 24 | TheDoc: typeof import('./components/TheDoc.vue')['default'] 25 | TheFooter: typeof import('./components/TheFooter/index.vue')['default'] 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/components/AppContainer.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | -------------------------------------------------------------------------------- /src/components/AppProvider/index.vue: -------------------------------------------------------------------------------- 1 | 44 | 45 | 62 | -------------------------------------------------------------------------------- /src/components/Blank.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/components/TheDoc.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 11 | 12 | 15 | -------------------------------------------------------------------------------- /src/components/TheFooter/index.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/composables/app.ts: -------------------------------------------------------------------------------- 1 | export const isDark = useDark() 2 | 3 | export const toggleDark = useToggle(isDark) 4 | 5 | export const isXsScreen = useMediaQuery('(max-width: 639px)') 6 | 7 | export const isSmScreen = useMediaQuery('(min-width: 640px)') 8 | 9 | export const isMdScreen = useMediaQuery('(min-width: 768px)') 10 | 11 | export const isLgScreen = useMediaQuery('(min-width: 1024px)') 12 | 13 | export const isXlScreen = useMediaQuery('(min-width: 1280px)') 14 | -------------------------------------------------------------------------------- /src/composables/draggable.ts: -------------------------------------------------------------------------------- 1 | export function useDrag() { 2 | const settingStore = useSettingStore() 3 | 4 | const draggableOptions = computed(() => ({ 5 | animation: 200, 6 | disabled: !settingStore.isSetting, 7 | ghostClass: 'ghost', 8 | forceFallback: true, 9 | delay: isSmScreen.value ? 0 : 100, 10 | fallbackTolerance: 3, 11 | })) 12 | 13 | function handleStart() { 14 | document.body.style.cursor = 'pointer' 15 | settingStore.setIsDragging(true) 16 | } 17 | function handleEnd() { 18 | document.body.style.cursor = '' 19 | settingStore.setIsDragging(false) 20 | } 21 | 22 | return { 23 | draggableOptions, 24 | handleStart, 25 | handleEnd, 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/composables/icon_style.ts: -------------------------------------------------------------------------------- 1 | import type { StyleValue } from 'vue' 2 | import type { IconStyle } from '@/types' 3 | 4 | export function useIconStyle() { 5 | const settingStore = useSettingStore() 6 | const iconStyle = computed(() => (settingStore.getSettingItem('iconStyle').value as IconStyle).style as StyleValue) 7 | 8 | return { 9 | iconStyle, 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/composables/theme.ts: -------------------------------------------------------------------------------- 1 | import type { Theme } from '@/types' 2 | import { theme } from '@/utils' 3 | import preset from '@/preset.json' 4 | 5 | type ThemeVar = keyof Theme 6 | type ThemeVars = { [K in keyof Theme]: Ref } 7 | 8 | const settingsCache = loadSettings() 9 | const defaultTheme: string = settingsCache ? settingsCache.theme : preset.settings.theme 10 | 11 | function camelToCssVar(str: string) { 12 | return `--${str.replace(/[A-Z]|[0-9]+/g, (match: string) => `-${match.toLowerCase()}`)}` 13 | } 14 | 15 | const currentTheme = theme.children.find(item => item.enName === defaultTheme)!.value 16 | 17 | // 根据主题设置 CSS 变量 18 | export const themeVars: ThemeVars = Object.keys( 19 | currentTheme, 20 | ).reduce( 21 | (obj, key) => { 22 | const cssVar = camelToCssVar(key) 23 | document.documentElement.style.setProperty(cssVar, currentTheme[key as ThemeVar]) 24 | obj[key as ThemeVar] = useCssVar(cssVar) 25 | return obj 26 | }, 27 | {} as Partial, 28 | ) as ThemeVars 29 | 30 | export function toggleTheme(key: string) { 31 | const newTheme = theme.children.find(item => item.enName === key)!.value 32 | for (const key in newTheme) 33 | themeVars[key as ThemeVar].value = newTheme[key as ThemeVar] 34 | } 35 | -------------------------------------------------------------------------------- /src/global-types.d.ts: -------------------------------------------------------------------------------- 1 | import type { DialogProviderInst, NotificationProviderInst } from 'naive-ui' 2 | 3 | export {} 4 | 5 | declare module 'vue' { 6 | interface HTMLAttributes { 7 | [key: string]: unknown 8 | } 9 | } 10 | 11 | declare global { 12 | interface Window { 13 | $dialog: DialogProviderInst 14 | $notification: NotificationProviderInst 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import { createPinia } from 'pinia' 3 | import App from './App.vue' 4 | import router from './router' 5 | import 'uno.css' 6 | import '@/styles/index.scss' 7 | 8 | const app = createApp(App) 9 | 10 | app.use(createPinia()) 11 | app.use(router) 12 | 13 | app.mount('#app') 14 | -------------------------------------------------------------------------------- /src/pages/about/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | -------------------------------------------------------------------------------- /src/pages/home/components/Favicon.vue: -------------------------------------------------------------------------------- 1 | 40 | 41 | 55 | -------------------------------------------------------------------------------- /src/pages/home/components/MainClock.vue: -------------------------------------------------------------------------------- 1 | 56 | 57 | 71 | -------------------------------------------------------------------------------- /src/pages/home/components/MainHeader.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 28 | -------------------------------------------------------------------------------- /src/pages/home/components/MainSearch.vue: -------------------------------------------------------------------------------- 1 | 166 | 167 | 246 | -------------------------------------------------------------------------------- /src/pages/home/components/MainSetting.vue: -------------------------------------------------------------------------------- 1 | 93 | 94 | 131 | -------------------------------------------------------------------------------- /src/pages/home/components/SettingSelection.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 38 | -------------------------------------------------------------------------------- /src/pages/home/components/SiteContainer.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 14 | -------------------------------------------------------------------------------- /src/pages/home/components/SiteGroupList.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 128 | 129 | 155 | -------------------------------------------------------------------------------- /src/pages/home/components/SiteModal.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 67 | -------------------------------------------------------------------------------- /src/pages/home/components/SiteNavBar.vue: -------------------------------------------------------------------------------- 1 | 41 | 42 | 85 | 86 | 117 | -------------------------------------------------------------------------------- /src/pages/home/index.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 27 | 28 | 29 | { 30 | "path": "/", 31 | "children": [ 32 | { 33 | "name": "setting", 34 | "path": "setting", 35 | "component": "@/components/Blank.vue" 36 | } 37 | ] 38 | } 39 | 40 | -------------------------------------------------------------------------------- /src/preset.json: -------------------------------------------------------------------------------- 1 | {"data":[{"id":0,"name":"日常","groupList":[{"id":1,"name":"常用","siteList":[{"id":1730221198177,"name":"ChatGPT","url":"https://chatgpt.com/","favicon":"https://cdn.oaistatic.com/assets/favicon-32x32-p4ktpm1x.webp","bgColor":"#73272d"},{"id":4,"name":"通义千问","url":"https://tongyi.aliyun.com/qianwen/","favicon":" https://img.alicdn.com/imgextra/i1/O1CN01asLYeX1WhbsyEZn5u_!!6000000002820-55-tps-56-56.svg","bgColor":"#ea348e"},{"id":6,"name":"腾讯元宝","url":"https://yuanbao.tencent.com/chat/naQivTmsDa","favicon":"https://cdn-bot.hunyuan.tencent.com/logo.png","bgColor":"#1d3014"},{"id":11,"name":"推特视频下载","url":"https://x.luode.vip/","favicon":" https://x.luode.vip/static/favicon.svg","bgColor":"#323209"},{"id":3,"name":"比特币","url":"https://btc.luode.vip/","favicon":"/svg/btc.svg","bgColor":"#4323fa"}]},{"id":29,"name":"常用","siteList":[{"id":3,"name":"GitHub","url":"https://github.com/","favicon":"https://github.githubassets.com/favicons/favicon.png","bgColor":""},{"id":66,"name":"Gitee","url":"https://gitee.com/","favicon":" https://gitee.com/favicon.ico","bgColor":"#7a6128"},{"id":32,"name":"Docker","url":"https://hub.docker.com/u/luode0320","favicon":" https://hub.docker.com/favicon.ico","bgColor":"#4901f4"},{"id":30,"name":"QQ邮箱","url":"https://mail.qq.com/","favicon":" https://mail.qq.com/zh_CN/htmledition/images/favicon/qqmail_favicon_16h.png","bgColor":"#5d3a33"},{"id":31,"name":"谷歌邮箱","url":"https://mail.google.com/","favicon":" https://ssl.gstatic.com/ui/v1/icons/mail/rfr/gmail.ico","bgColor":"#720e2c"}]},{"id":65,"name":"常用","siteList":[{"id":33,"name":"在线工具","url":"https://tool.lu/","favicon":" https://tool.lu/favicon.ico","bgColor":"#588019"},{"id":34,"name":"json工具","url":"https://tool.lu/json/","favicon":" https://tool.lu/favicon.ico","bgColor":"#330d3d"},{"id":67,"name":"时间戳工具","url":"https://tool.lu/timestamp/","favicon":" https://tool.lu/favicon.ico","bgColor":"#2c0aa9"},{"id":68,"name":"蓝奏云","url":"https://luode.lanzouo.com/","favicon":" https://up.woozooo.com/favicon.ico","bgColor":"#2fae79"},{"id":70,"name":"蓝奏云优享版","url":"https://www.ilanzou.com/","favicon":" https://www.ilanzou.com/favicon.ico","bgColor":"#056a92"}]},{"id":8,"name":"常用","siteList":[{"id":9,"name":"博客","url":"https://blog.luode.vip/","favicon":"/svg/blog.svg","bgColor":"#342123"},{"id":13,"name":"Arco Design Vue","url":"https://arco.design/vue/docs/start","favicon":" https://unpkg.byted-static.com/latest/byted/arco-config/assets/favicon.ico","bgColor":"#c05701"},{"id":10,"name":"知乎","url":"https://www.zhihu.com/","favicon":" https://static.zhihu.com/heifetz/favicon.ico","bgColor":"#4c720a"},{"id":2,"name":"一元机场","url":"https://xn--4gq62f52gdss.com/#/dashboard","favicon":"https://tse4-mm.cn.bing.net/th/id/OIP-C.rL_5Z1zJxSDUmgPlhJaHrAAAAA?w=166&h=176&c=7&r=0&o=5&pid=1.7","bgColor":"#054c01"},{"id":69,"name":"哔哩哔哩","url":"https://www.bilibili.com/","favicon":" https://www.bilibili.com/favicon.ico","bgColor":"#367c24"}]},{"id":15,"name":"视频","siteList":[{"id":16,"name":"腾讯视频","url":"https://v.qq.com/","favicon":" https://v.qq.com/favicon.ico","bgColor":"#015132"},{"id":17,"name":"爱奇艺","url":"https://www.iqiyi.com/","favicon":" https://www.iqiyi.com/static/20241021185025/media/logo.0f63ac5d8ac0e4d5c368.svg","bgColor":"#46651b"},{"id":18,"name":"快手","url":"https://kuaishou.cn/new-reco","favicon":" https://static.yximgs.com/udata/pkg/WEB-LIVE/kwai_icon.8f6787d8.ico","bgColor":"#0c0756"},{"id":19,"name":"阿里盘搜","url":"https://www.alipansou.com/","favicon":" https://www.alipansou.com/favicon.ico","bgColor":"#613b2d"},{"id":20,"name":"磁力猫","url":"https://clm.la/","favicon":" https://clm41.top/static/img/logo.gif","bgColor":"#251001"}]}]},{"id":228,"name":"开发","groupList":[{"id":229,"name":"社区","siteList":[{"id":231,"name":"稀土掘金","url":"https://juejin.cn/","favicon":" https://lf-web-assets.juejin.cn/obj/juejin-web/xitu_juejin_web/static/favicons/favicon-32x32.png","bgColor":"#13c032"},{"id":235,"name":"CSDN","url":"https://www.csdn.net/","favicon":"https://tse3-mm.cn.bing.net/th/id/OIP-C.3BFONbkeAhiFJ-gdX8m0twAAAA?w=126&h=150&c=7&r=0&o=5&pid=1.7","bgColor":"#262708"},{"id":233,"name":"开源中国","url":"https://www.oschina.net/","favicon":"https://tse1-mm.cn.bing.net/th/id/OIP-C.k5gsOwBVoCfBWWwhPuE-3gHaHa?w=191&h=192&c=7&r=0&o=5&pid=1.7","bgColor":"#1f1315"},{"id":234,"name":"LearnKu","url":"https://learnku.com/","favicon":" https://cdn.learnku.com/uploads/images/201901/24/1/OyBnfB2vlk.png","bgColor":"#4125c4"},{"id":230,"name":"StackOverflow","url":"https://stackoverflow.com/","favicon":" https://cdn.sstatic.net/Sites/stackoverflow/Img/favicon.ico?v=ec617d715196","bgColor":"#33a178"}]},{"id":236,"name":"平台","siteList":[{"id":237,"name":"GitHub","url":"https://github.com/","favicon":"https://github.githubassets.com/favicons/favicon.png","bgColor":""},{"id":238,"name":"Gitee","url":"https://gitee.com/","favicon":" https://gitee.com/favicon.ico","bgColor":"#f57a30"},{"id":239,"name":"GitLab","url":"https://gitlab.com/","favicon":" https://about.gitlab.com/nuxt-images/ico/favicon.ico?cache=20220414","bgColor":"#092677"},{"id":240,"name":"Docker","url":"https://hub.docker.com/","favicon":" https://hub.docker.com/favicon.ico","bgColor":"#cc2946"},{"id":241,"name":"LeetCode","url":"https://leetcode.cn/","favicon":"https://assets.leetcode.cn/aliyun-lc-upload/users/leetcode-solution/avatar_1582018938.png?x-oss-process=image%2Fformat%2Cwebp","bgColor":"#6b8945"}]},{"id":243,"name":"云计算","siteList":[{"id":246,"name":"NameSilo","url":"https://www.namesilo.com/","favicon":"https://tse1-mm.cn.bing.net/th/id/OIP-C.sRmAa_9qhaC6lxtVxyyMAgAAAA?w=133&h=150&c=7&r=0&o=5&pid=1.7","bgColor":"#bd7a2a"},{"id":249,"name":"Cloudflare","url":"https://www.cloudflare.com/","favicon":"https://www.cloudflare.com/favicon.ico","bgColor":"#3b0d19"},{"id":244,"name":"阿里云","url":"https://cn.aliyun.com/","favicon":" https://img.alicdn.com/tfs/TB1_ZXuNcfpK1RjSZFOXXa6nFXa-32-32.ico","bgColor":"#92522e"},{"id":245,"name":"腾讯云","url":"https://cloud.tencent.com/","favicon":" https://cloudcache.tencent-cloud.com/qcloud/favicon.ico?t=201902181234","bgColor":"#812f23"},{"id":247,"name":"华为云","url":"https://activity.huaweicloud.com/","favicon":" https://www.huaweicloud.com/favicon.ico","bgColor":"#6b350b"}]},{"id":250,"name":"工具","siteList":[{"id":255,"name":"ProcessOn","url":"https://processon.com/","favicon":" https://www.processon.com/public_login/favicon.983368c6.ico","bgColor":"#531c10"},{"id":256,"name":"菜鸟教程","url":"https://www.runoob.com/","favicon":"https://tse1-mm.cn.bing.net/th/id/OIP-C.BSQ0Q9Hnj2AGhf6U3OEL3wAAAA?w=121&h=128&c=7&r=0&o=5&pid=1.7","bgColor":"#0f1139"},{"id":257,"name":"虚拟号码","url":"https://sms-activate.org/cn","favicon":" https://smsactivate.s3.eu-central-1.amazonaws.com/assets/img/activate_favicon.png","bgColor":"#37b752"},{"id":258,"name":"临时邮箱","url":"https://temp-mail.org/zh/","favicon":" https://temp-mail.org/images/favicon.ico","bgColor":"#3c1d76"},{"id":259,"name":"在线工具","url":"https://tool.lu/","favicon":" https://tool.lu/favicon.ico","bgColor":"#1fc715"}]},{"id":264,"name":"文档","siteList":[{"id":265,"name":"Ant Design Vue","url":"https://antdv.com/components/overview-cn","favicon":" https://next.antdv.com/assets/logo.1ef800a8.svg","bgColor":"#2a196f"},{"id":266,"name":"Arco Design Vue","url":"https://arco.design/vue/docs/start","favicon":" https://unpkg.byted-static.com/latest/byted/arco-config/assets/favicon.ico","bgColor":"#db041b"},{"id":269,"name":"博客园","url":"https://www.cnblogs.com/cmt","favicon":"https://tse2-mm.cn.bing.net/th/id/OIP-C.tCgQcVHae9eGONRhfmpwdAHaHa?w=166&h=180&c=7&r=0&o=5&pid=1.7","bgColor":"#0d350d"},{"id":270,"name":"阿里巴巴镜像站","url":"https://developer.aliyun.com/mirror/","favicon":" https://img.alicdn.com/tfs/TB1_ZXuNcfpK1RjSZFOXXa6nFXa-32-32.ico","bgColor":"#4f0572"},{"id":267,"name":"美团技术团队","url":"https://tech.meituan.com/","favicon":" https://awps-assets.meituan.net/mit/blog/v20190629/asset/icon/android-icon-192x192.png?v=Whistle&t=20181017-1r","bgColor":"#03b878"}]}]}],"settings":{"iconStyle":"Vivid","search":"Bing","showLunar":"Show","theme":"MoonWhite"}} -------------------------------------------------------------------------------- /src/router/index.ts: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHistory } from 'vue-router' 2 | import routes from 'virtual:generated-pages' 3 | 4 | const router = createRouter({ 5 | history: createWebHistory(import.meta.env.BASE_URL), 6 | routes, 7 | }) 8 | 9 | export default router 10 | -------------------------------------------------------------------------------- /src/stores/modal.ts: -------------------------------------------------------------------------------- 1 | import { getCommonProps } from '@/utils' 2 | 3 | export type ActionType = 'add' | 'update' 4 | export type ActionTarget = 'cate' | 'group' | 'site' 5 | 6 | const ACTION_NAME = { 7 | add: '添加', 8 | update: '编辑', 9 | } 10 | const TARGET_NAME = { 11 | cate: '分类', 12 | group: '分组', 13 | site: '网页', 14 | } 15 | export const useModalStore = defineStore('modal', () => { 16 | const modalVisible = ref(false) 17 | const action = ref('add') 18 | const target = ref('site') 19 | const title = computed(() => ACTION_NAME[action.value] + TARGET_NAME[target.value]) 20 | 21 | const siteStore = useSiteStore() 22 | const inputValues = reactive({ 23 | name: '', 24 | url: '', 25 | favicon: '', 26 | bgColor: '', 27 | }) 28 | 29 | function showModal(actionType: ActionType, actionTarget: ActionTarget, groupIndex = -1, siteIndex = -1) { 30 | action.value = actionType 31 | target.value = actionTarget 32 | if (groupIndex !== -1) 33 | siteStore.setGroupIndex(groupIndex) 34 | if (siteIndex !== -1) 35 | siteStore.setSiteIndex(siteIndex) 36 | modalVisible.value = true 37 | // init inputs 38 | if (actionType === 'update') { 39 | const updateTarget = { 40 | site: () => getCommonProps(inputValues, siteStore.data[siteStore.cateIndex].groupList[groupIndex].siteList[siteIndex]), 41 | group: () => getCommonProps(inputValues, siteStore.data[siteStore.cateIndex].groupList[groupIndex]), 42 | cate: () => getCommonProps(inputValues, siteStore.data[siteStore.cateIndex]), 43 | } 44 | Object.assign(inputValues, updateTarget[actionTarget]()) 45 | } 46 | } 47 | 48 | let now = 0 49 | const commitHandler = { 50 | add: { 51 | site: () => siteStore.addSite({ id: now, ...inputValues }), 52 | group: () => siteStore.addGroup({ id: now, name: inputValues.name, siteList: [] }), 53 | cate: () => siteStore.addCate({ id: now, name: inputValues.name, groupList: [] }), 54 | }, 55 | update: { 56 | site: () => siteStore.updateSite({ ...inputValues }), 57 | group: () => siteStore.updateGroup({ name: inputValues.name }), 58 | cate: () => siteStore.updateCate({ name: inputValues.name }), 59 | }, 60 | } 61 | const deleteHandler = { 62 | site: () => siteStore.deleteSite(), 63 | group: () => siteStore.deleteGroup(), 64 | cate: () => siteStore.deleteCate(), 65 | } 66 | function handleCancel() { 67 | modalVisible.value = false 68 | } 69 | let isCommit = false 70 | function handleCommit() { 71 | if (isCommit) 72 | return 73 | isCommit = true 74 | now = Date.now() 75 | commitHandler[action.value][target.value]() 76 | modalVisible.value = false 77 | setTimeout(() => isCommit = false, 1000) 78 | } 79 | function handleDelete() { 80 | deleteHandler[target.value]() 81 | modalVisible.value = false 82 | // If delete a cate, cateIndex-- 83 | if (target.value === 'cate' && siteStore.cateIndex > 0) 84 | siteStore.setCateIndex(siteStore.cateIndex - 1) 85 | } 86 | function clearInput() { 87 | let key: keyof typeof inputValues 88 | for (key in inputValues) inputValues[key] = '' 89 | } 90 | 91 | return { 92 | modalVisible, 93 | action, 94 | target, 95 | title, 96 | inputValues, 97 | showModal, 98 | handleCancel, 99 | handleCommit, 100 | handleDelete, 101 | clearInput, 102 | } 103 | }) 104 | -------------------------------------------------------------------------------- /src/stores/render.ts: -------------------------------------------------------------------------------- 1 | import { getRandomComplexNumber } from '@/utils' 2 | 3 | export const useRenderStore = defineStore('render', () => { 4 | const siteGroupListKey = ref(getRandomComplexNumber()) 5 | 6 | function refreshSiteGroupList() { 7 | siteGroupListKey.value = getRandomComplexNumber() 8 | } 9 | 10 | return { 11 | siteGroupListKey, 12 | refreshSiteGroupList, 13 | } 14 | }) 15 | -------------------------------------------------------------------------------- /src/stores/setting.ts: -------------------------------------------------------------------------------- 1 | import * as settingData from '@/utils/settings' 2 | import preset from '@/preset.json' 3 | import type { Settings } from '@/types' 4 | 5 | export type SettingKey = keyof Settings 6 | 7 | export function loadSettings(): Settings | undefined { 8 | const settings = localStorage.getItem('settings') 9 | return settings ? JSON.parse(settings) : undefined 10 | } 11 | 12 | const defaultSetting: Settings = Object.fromEntries( 13 | Object.keys(settingData).map(key => [key, settingData[key as SettingKey].defaultKey]), 14 | ) as Settings 15 | 16 | export const useSettingStore = defineStore('setting', () => { 17 | const route = useRoute() 18 | const isSetting = ref(false) 19 | 20 | watch(route, () => { 21 | if (route.name === 'setting') 22 | isSetting.value = true 23 | else 24 | isSetting.value = false 25 | }, { immediate: true }) 26 | 27 | const settingCache = loadSettings() 28 | const presetSetting = preset.settings 29 | 30 | const settings = reactive((() => { 31 | let settings: Settings 32 | if (settingCache) 33 | settings = Object.assign(presetSetting, { ...defaultSetting, ...settingCache }) 34 | else 35 | settings = Object.assign(defaultSetting, presetSetting) 36 | // 排除非法值 37 | Object.keys(settings).forEach((key) => { 38 | if (!settingData[key as SettingKey].children.find(item => item.enName === settings[key as SettingKey])) 39 | settings[key as SettingKey] = settingData[key as SettingKey].defaultKey 40 | }) 41 | return settings 42 | })()) 43 | 44 | function getSettingItem(key: keyof typeof settingData) { 45 | return settingData[key].children.find(item => item.enName === settings[key])! 46 | } 47 | function getSettingValue(key: keyof typeof settingData) { 48 | return getSettingItem(key).value 49 | } 50 | 51 | function setSettings(newSettings: Partial) { 52 | Object.assign(settings, newSettings) 53 | } 54 | watch(settings, () => { 55 | localStorage.setItem('settings', JSON.stringify(toRaw(settings))) 56 | }, { deep: true }) 57 | 58 | // ----------------- 拖拽 ----------------- 59 | const isDragging = ref(false) 60 | 61 | function setIsDragging(status: boolean) { 62 | isDragging.value = status 63 | } 64 | 65 | return { 66 | isSetting, 67 | settings, 68 | isDragging, 69 | setSettings, 70 | setIsDragging, 71 | getSettingItem, 72 | getSettingValue, 73 | } 74 | }) 75 | -------------------------------------------------------------------------------- /src/stores/site.ts: -------------------------------------------------------------------------------- 1 | import preset from '@/preset.json' 2 | import type { Category, Group, Site } from '@/types' 3 | 4 | function loadData(): Category[] | undefined { 5 | const data = localStorage.getItem('cache') 6 | return data ? JSON.parse(data) : undefined 7 | } 8 | export const useSiteStore = defineStore('site', () => { 9 | const data = ref(loadData() || preset.data) 10 | const cateIndex = ref(0) 11 | const groupIndex = ref(0) 12 | const siteIndex = ref(0) 13 | 14 | const setCateIndex = (i: number) => cateIndex.value = i 15 | const setGroupIndex = (i: number) => groupIndex.value = i 16 | const setSiteIndex = (i: number) => siteIndex.value = i 17 | 18 | const cateList = computed(() => data.value.map(cate => ({ id: cate.id, name: cate.name }))) 19 | const currentCateData = computed(() => data.value[cateIndex.value] || { groupList: [] }) 20 | 21 | function addSite(site: Site) { 22 | data.value[cateIndex.value].groupList[groupIndex.value].siteList.push(site) 23 | } 24 | function addGroup(group: Group) { 25 | data.value[cateIndex.value].groupList.push(group) 26 | } 27 | function addCate(cate: Category) { 28 | data.value.push(cate) 29 | } 30 | function updateSite(site: Partial) { 31 | Object.assign(data.value[cateIndex.value].groupList[groupIndex.value].siteList[siteIndex.value], site) 32 | } 33 | function updateGroup(group: Partial) { 34 | Object.assign(data.value[cateIndex.value].groupList[groupIndex.value], group) 35 | } 36 | function updateCate(cate: Partial) { 37 | Object.assign(data.value[cateIndex.value], cate) 38 | } 39 | function deleteSite() { 40 | data.value[cateIndex.value].groupList[groupIndex.value].siteList.splice(siteIndex.value, 1) 41 | } 42 | function deleteGroup() { 43 | data.value[cateIndex.value].groupList.splice(groupIndex.value, 1) 44 | } 45 | function deleteCate() { 46 | data.value.splice(cateIndex.value, 1) 47 | } 48 | function cachingData() { 49 | localStorage.setItem('cache', JSON.stringify(data.value)) 50 | } 51 | function setData(value: Category[]) { 52 | data.value = value 53 | } 54 | 55 | watch(data, () => { 56 | cachingData() 57 | }, { deep: true }) 58 | 59 | return { 60 | data, 61 | cateIndex, 62 | cateList, 63 | currentCateData, 64 | setCateIndex, 65 | setGroupIndex, 66 | setSiteIndex, 67 | addSite, 68 | addGroup, 69 | addCate, 70 | updateSite, 71 | updateGroup, 72 | updateCate, 73 | deleteSite, 74 | deleteGroup, 75 | deleteCate, 76 | setData, 77 | } 78 | }) 79 | -------------------------------------------------------------------------------- /src/styles/base.scss: -------------------------------------------------------------------------------- 1 | *, 2 | ::after, 3 | ::before { 4 | box-sizing: border-box; 5 | border-width: 0; 6 | border-style: solid; 7 | border-color: #e8e8e8; 8 | } 9 | 10 | .dark *, ::after, ::before { 11 | border-color: #282828; 12 | } 13 | 14 | * { 15 | -webkit-tap-highlight-color: rgba(255, 255, 255, 0); 16 | -webkit-focus-ring-color: rgba(0, 0, 0, 0); 17 | -webkit-touch-callout: none; 18 | } 19 | 20 | body { 21 | margin: 0; 22 | padding: 0; 23 | } 24 | 25 | a { 26 | color: inherit; 27 | text-decoration: none; 28 | } 29 | 30 | input { 31 | outline: none; 32 | } 33 | 34 | img { 35 | display: block; 36 | } -------------------------------------------------------------------------------- /src/styles/index.scss: -------------------------------------------------------------------------------- 1 | @import './base.scss'; 2 | @import './vars.scss'; 3 | @import './public.scss'; -------------------------------------------------------------------------------- /src/styles/public.scss: -------------------------------------------------------------------------------- 1 | :root { 2 | font-size: 4px; 3 | } 4 | html { 5 | color: var(--text-c); 6 | background-color: #f3f4f6; 7 | transition: background 0.8s; 8 | } 9 | html, body { 10 | height: 100vh; 11 | } 12 | ::-webkit-scrollbar { 13 | width: 0px; 14 | height: 9px; 15 | background-color: var(--bg-c); 16 | } 17 | ::-webkit-scrollbar-thumb { 18 | border: 3px solid transparent; 19 | background-clip: padding-box; 20 | background-color: #8888; 21 | border-radius: 9999px; 22 | &:hover { 23 | background-color: #888b; 24 | } 25 | } 26 | 27 | @media (max-width: 640px) { 28 | ::-webkit-scrollbar { 29 | display: none; 30 | } 31 | } 32 | 33 | // draggable 托拖拽时 原元素样式 34 | .ghost { 35 | visibility: hidden; 36 | } 37 | .dragging { 38 | opacity: 1 !important; 39 | .site__handle { 40 | background-color: var(--site-hover-c); 41 | } 42 | .group__handle { 43 | background-color: var(--site-hover-c); 44 | } 45 | } 46 | 47 | .no_select { 48 | -moz-user-select: none; /* 火狐 */ 49 | -webkit-user-select: none; /* 谷歌、Safari */ 50 | -ms-user-select: none; /* IE10+ */ 51 | user-select: none; 52 | user-drag: none; 53 | } 54 | -------------------------------------------------------------------------------- /src/styles/vars.scss: -------------------------------------------------------------------------------- 1 | :root { 2 | --text-c: #374151; 3 | --text-c-1: #606060; 4 | 5 | // ------ Dark theme 6 | --dark-bg-c: #18181a; 7 | --dark-main-bg-c: #1e1e20; 8 | --dark-text-c: #e1e1e1; 9 | --dark-text-c-1: #c1c1c1; 10 | } 11 | 12 | html.dark { 13 | --bg-c: var(--dark-bg-c) !important; 14 | --main-bg-c: var(--dark-main-bg-c) !important; 15 | --text-c: var(--dark-text-c) !important; 16 | --text-c-1: var(--dark-text-c-1) !important; 17 | } -------------------------------------------------------------------------------- /src/types/common.ts: -------------------------------------------------------------------------------- 1 | export type CommonProperties = { 2 | [K in Extract]: T[K] | U[K]; 3 | } 4 | -------------------------------------------------------------------------------- /src/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from './setting' 2 | export * from './site' 3 | export * from './common' 4 | -------------------------------------------------------------------------------- /src/types/setting.ts: -------------------------------------------------------------------------------- 1 | import type * as settingData from '@/utils/settings' 2 | 3 | export class SettingItem { 4 | name: string 5 | enName: string 6 | children: SettingItemChildren 7 | defaultKey: string 8 | value: T 9 | 10 | constructor(options: { 11 | name: string 12 | enName: string 13 | children: SettingItemChildren 14 | defaultKey?: string 15 | }) { 16 | this.name = options.name 17 | this.enName = options.enName 18 | this.children = options.children 19 | this.defaultKey = options.defaultKey || options.children[0].enName 20 | this.value = options.defaultKey ? options.children.find(item => item.enName === options.defaultKey)!.value : options.children[0].value 21 | } 22 | } 23 | 24 | export interface SettingItemsChild { 25 | name: string 26 | enName: string 27 | value: T 28 | } 29 | 30 | export type SettingItemChildren = SettingItemsChild[] 31 | 32 | export type Settings = Record 33 | 34 | /* Theme */ 35 | export interface Theme { 36 | primaryC: string 37 | primaryLightC: string 38 | primaryDarkC: string 39 | siteHoverC: string 40 | settingBorderC: string 41 | settingGroupBgC: string 42 | bgC: string 43 | mainBgC: string 44 | } 45 | 46 | /* Search */ 47 | export interface Search { 48 | name: string 49 | enName: string 50 | url: string 51 | key: string 52 | favicon: string 53 | s: string 54 | } 55 | 56 | /* IconStyle */ 57 | export interface IconStyle { 58 | name: string 59 | enName: string 60 | style: Partial 61 | } 62 | -------------------------------------------------------------------------------- /src/types/site.ts: -------------------------------------------------------------------------------- 1 | export interface Site { 2 | id: number 3 | name: string 4 | url: string 5 | favicon?: string 6 | bgColor?: string 7 | } 8 | export interface Group { 9 | id: number 10 | name: string 11 | siteList: Site[] 12 | } 13 | export interface Category { 14 | id: number 15 | name: string 16 | groupList: Group[] 17 | } 18 | -------------------------------------------------------------------------------- /src/utils/common.ts: -------------------------------------------------------------------------------- 1 | import type { CommonProperties } from '@/types' 2 | 3 | export function deepClone(obj: T): T { 4 | if (obj === null || typeof obj !== 'object') 5 | return obj 6 | 7 | let clone: any 8 | if (Array.isArray(obj)) 9 | clone = [] 10 | else 11 | clone = Object.create(null) 12 | 13 | for (const key of Object.keys(obj)) 14 | (clone as any)[key] = deepClone(obj[key as keyof typeof obj]) 15 | return clone 16 | } 17 | 18 | export function getRandomDarkColor() { 19 | const ranges = [64, 128, 256] 20 | const rRange = ranges[Math.floor(Math.random() * ranges.length)] 21 | const gRange = ranges[Math.floor(Math.random() * ranges.length)] 22 | const bRange = ranges[Math.floor(Math.random() * ranges.length)] 23 | const r = Math.floor(Math.random() * rRange).toString(16).padStart(2, '0') 24 | const g = Math.floor(Math.random() * (gRange === 256 ? gRange * 0.8 : gRange)).toString(16).padStart(2, '0') 25 | const b = Math.floor(Math.random() * bRange).toString(16).padStart(2, '0') 26 | return `#${r}${g}${b}` 27 | } 28 | 29 | // ! get common props 30 | export function getCommonProps(obj1: T, obj2: U): CommonProperties { 31 | const commonProps = {} as CommonProperties 32 | Object.keys(obj1).forEach((key) => { 33 | const K = key as keyof CommonProperties 34 | if (obj1[K] !== undefined && obj2[K] !== undefined) 35 | commonProps[K] = obj2[K] 36 | }) 37 | return commonProps 38 | } 39 | 40 | // 防抖 41 | export function debounce any>(fn: T, delay = 100) { 42 | let timer: NodeJS.Timeout 43 | return function (this: any, ...args: any[]) { 44 | clearTimeout(timer) 45 | timer = setTimeout(() => { 46 | fn.apply(this, args) 47 | }, delay) 48 | } as T 49 | } 50 | 51 | export function getRandomComplexNumber() { 52 | const random = Math.random() 53 | const randomStr = random.toString().split('.')[1] 54 | const randomLength = randomStr.length 55 | const randomComplexNumber = Number(randomStr) / 10 ** randomLength 56 | return randomComplexNumber 57 | } 58 | -------------------------------------------------------------------------------- /src/utils/favicon.ts: -------------------------------------------------------------------------------- 1 | const FAVICON_API = 'https://api.iowen.cn/favicon/' 2 | 3 | const siteToUrl: Map = new Map() 4 | const sites: string[] = [ 5 | 'clougence.com', 6 | 'jd.com', 7 | 'taobao.com', 8 | 'pinduoduo.com', 9 | ] 10 | const otherUrls: string[] = [ 11 | 'github.com', 12 | 'coding.net', 13 | '500px.com', 14 | 'themeforest.net', 15 | ] 16 | 17 | sites.forEach((e: string) => { 18 | siteToUrl.set(e, `/site/${e}.svg`) 19 | }) 20 | 21 | otherUrls.forEach((url: string) => { 22 | siteToUrl.set(url, `https://0x3.com/icon?host=${url}`) 23 | }) 24 | 25 | function getDomainName(url: string) { 26 | let domain = url.replace(/(^\w+:|^)\/\//, '') 27 | domain = domain.replace(/^www\./, '') 28 | 29 | const matches = domain.match(/([a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+)/) 30 | 31 | if (matches && matches.length > 1) 32 | return matches[1] 33 | 34 | return null 35 | } 36 | 37 | export function getFaviconUrl(url: string) { 38 | const paramsUrl = getDomainName(url) 39 | if (paramsUrl == null) 40 | return '' 41 | 42 | const optUrl = siteToUrl.get(paramsUrl) 43 | if (optUrl) 44 | return optUrl 45 | 46 | return `${FAVICON_API + paramsUrl}.png` 47 | } 48 | -------------------------------------------------------------------------------- /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './favicon' 2 | export * from './common' 3 | export * from './settings' 4 | -------------------------------------------------------------------------------- /src/utils/search-engine/eng-baidu.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import jsonpAdapter from 'axios-jsonp' 3 | 4 | function target(wd) { 5 | return `https://www.baidu.com/s?wd=${encodeURIComponent(wd)}` 6 | } 7 | 8 | function complete(wd, callback) { 9 | axios.get('https://www.baidu.com/sugrec', { 10 | params: { 11 | prod: 'wise', 12 | wd, 13 | }, 14 | adapter: jsonpAdapter, 15 | }).then((response) => { 16 | callback({ 17 | eng: 'baidu', 18 | wd: response.data.q, 19 | list: response.data.g 20 | ? response.data.g.map(g => g.q) 21 | : [], 22 | }) 23 | }).catch(error => console.error(error)) 24 | } 25 | 26 | export default { 27 | target, 28 | complete, 29 | } 30 | -------------------------------------------------------------------------------- /src/utils/search-engine/eng-bing.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import jsonpAdapter from 'axios-jsonp' 3 | 4 | function target(wd) { 5 | return `https://www.bing.com/search?mkt=zh-cn&q=${encodeURIComponent(wd)}` 6 | } 7 | 8 | function complete(wd, callback) { 9 | axios.get('https://api.bing.com/qsonhs.aspx', { 10 | params: { 11 | type: 'cb', 12 | mkt: 'zh-cn', 13 | q: wd, 14 | }, 15 | adapter: jsonpAdapter, 16 | callbackParamName: 'cb', 17 | }).then((resp) => { 18 | const asRes = resp.data.AS.Results 19 | let list 20 | if (!asRes) { 21 | list = [] 22 | } 23 | else { 24 | list = asRes.map(r => r.Suggests) 25 | .reduce( 26 | (sa1, sa2) => sa1.concat(sa2), 27 | [], 28 | ) 29 | .sort( 30 | (s1, s2) => (s1.Sk > s2.Sk) ? 1 : -1, 31 | ) 32 | .map(s => s.Txt) 33 | } 34 | 35 | callback({ 36 | eng: 'bing', 37 | wd: resp.data.AS.Query, 38 | list, 39 | }) 40 | }).catch(error => console.error(error)) 41 | } 42 | 43 | export default { 44 | target, 45 | complete, 46 | } 47 | -------------------------------------------------------------------------------- /src/utils/search-engine/eng-google.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import jsonpAdapter from 'axios-jsonp' 3 | 4 | function target(wd) { 5 | return `https://www.google.com/search?q=${encodeURIComponent(wd)}` 6 | } 7 | 8 | function complete(wd, callback) { 9 | axios.get('https://www.google.com/complete/search', { 10 | params: { 11 | client: 'psy-ab', 12 | hl: 'zh-CN', 13 | q: wd, 14 | }, 15 | adapter: jsonpAdapter, 16 | }).then((response) => { 17 | callback({ 18 | eng: 'google', 19 | wd: response.data[0], 20 | list: response.data[1].map(a => a[0]), 21 | }) 22 | }).catch(error => console.error(error)) 23 | } 24 | 25 | export default { 26 | target, 27 | complete, 28 | } 29 | -------------------------------------------------------------------------------- /src/utils/search-engine/eng-qiuwenbaike.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import jsonpAdapter from 'axios-jsonp' 3 | 4 | function target(wd) { 5 | return `https://www.qiuwenbaike.cn/index.php?search=${encodeURIComponent(wd)}` 6 | } 7 | 8 | function complete(wd, callback) { 9 | axios.get('https://www.qiuwenbaike.cn/api.php', { 10 | params: { 11 | action: 'opensearch', 12 | format: 'json', 13 | formatversion: 2, 14 | limit: 10, 15 | search: wd, 16 | }, 17 | adapter: jsonpAdapter, 18 | }).then((response) => { 19 | callback({ 20 | eng: 'qiuwenbaike', 21 | wd: response.data[0], 22 | list: response.data[1], 23 | }) 24 | }).catch(error => console.error(error)) 25 | } 26 | 27 | export default { 28 | target, 29 | complete, 30 | } 31 | -------------------------------------------------------------------------------- /src/utils/search-engine/eng-sogou.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import jsonpAdapter from 'axios-jsonp' 3 | 4 | function target(wd) { 5 | return `https://www.sogou.com/web?query=${encodeURIComponent(wd)}` 6 | } 7 | 8 | function complete(wd, callback) { 9 | const url = `https://wap.sogou.com/web/sugg/${encodeURIComponent(wd)}` 10 | window.sug = function (response) { 11 | callback({ 12 | eng: 'sogou', 13 | wd: response.q, 14 | list: response.s 15 | ? response.s.map(s => s.q) 16 | : [], 17 | }) 18 | } 19 | axios.get(url, { 20 | params: { 21 | vr: 1, 22 | s: 1, 23 | source: 'wapsearch', 24 | encrypt: 0, 25 | cb: 'sug', 26 | }, 27 | adapter: jsonpAdapter, 28 | }).catch(error => console.error(error)) 29 | } 30 | 31 | export default { 32 | target, 33 | complete, 34 | } 35 | -------------------------------------------------------------------------------- /src/utils/search-engine/eng-wikipedia.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import jsonpAdapter from 'axios-jsonp' 3 | 4 | function target(wd) { 5 | return `https://zh.wikipedia.org/w/index.php?search=${encodeURIComponent(wd)}` 6 | } 7 | 8 | function complete(wd, callback) { 9 | axios.get('https://zh.wikipedia.org/w/api.php', { 10 | params: { 11 | action: 'opensearch', 12 | format: 'json', 13 | formatversion: 2, 14 | limit: 10, 15 | search: wd, 16 | }, 17 | adapter: jsonpAdapter, 18 | }).then((response) => { 19 | callback({ 20 | eng: 'wikipedia', 21 | wd: response.data[0], 22 | list: response.data[1], 23 | }) 24 | }).catch(error => console.error(error)) 25 | } 26 | 27 | export default { 28 | target, 29 | complete, 30 | } 31 | -------------------------------------------------------------------------------- /src/utils/search-engine/index.js: -------------------------------------------------------------------------------- 1 | import engBaidu from './eng-baidu' 2 | import engGoogle from './eng-google' 3 | import engBing from './eng-bing' 4 | import engQiuwenbaike from './eng-qiuwenbaike' 5 | import engWikipedia from './eng-wikipedia' 6 | import engSogou from './eng-sogou' 7 | 8 | const ENGINES = { 9 | Baidu: engBaidu, 10 | Google: engGoogle, 11 | Bing: engBing, 12 | Qiuwenbaike: engQiuwenbaike, 13 | Wikipedia: engWikipedia, 14 | Sogou: engSogou, 15 | } 16 | 17 | function target(eng, wd) { 18 | return ENGINES[eng].target(wd) 19 | } 20 | 21 | function complete(eng, wd, callback) { 22 | return ENGINES[eng].complete(wd, callback) 23 | } 24 | 25 | export default { 26 | target, 27 | complete, 28 | } 29 | -------------------------------------------------------------------------------- /src/utils/settings/icon_style.ts: -------------------------------------------------------------------------------- 1 | import type { IconStyle, SettingItemChildren } from '@/types' 2 | import { SettingItem } from '@/types' 3 | 4 | const iconStyleList: SettingItemChildren = [ 5 | { 6 | name: '鲜艳', 7 | enName: 'Vivid', 8 | value: { name: '鲜艳', enName: 'Vivid', style: {} }, 9 | }, 10 | { 11 | name: '朴素', 12 | enName: 'Plain', 13 | value: { name: '朴素', enName: 'Plain', style: { opacity: '0.8', filter: 'saturate(64%)' } }, 14 | }, 15 | { 16 | name: '灰白', 17 | enName: 'Gray', 18 | value: { name: '灰白', enName: 'Gray', style: { opacity: '0.7', filter: 'grayscale(72%)' } }, 19 | }, 20 | ] 21 | 22 | export const iconStyle = new SettingItem({ 23 | name: '图标风格', 24 | enName: 'IconStyle', 25 | children: iconStyleList, 26 | defaultKey: 'Vivid', 27 | }) 28 | -------------------------------------------------------------------------------- /src/utils/settings/index.ts: -------------------------------------------------------------------------------- 1 | export * from './theme' 2 | export * from './search' 3 | export * from './icon_style' 4 | export * from './show_lunar' 5 | -------------------------------------------------------------------------------- /src/utils/settings/search.ts: -------------------------------------------------------------------------------- 1 | import type { Search, SettingItemChildren } from '@/types' 2 | import { SettingItem } from '@/types' 3 | 4 | const searchList: SettingItemChildren = [ 5 | { 6 | name: '必应', 7 | enName: 'Bing', 8 | value: { name: '必应', enName: 'Bing', url: 'https://www.bing.com/search', key: 'q', favicon: '/svg/bing.svg', s: 'bi' }, 9 | }, 10 | { 11 | name: '百度', 12 | enName: 'Baidu', 13 | value: { name: '百度', enName: 'Baidu', url: 'https://www.baidu.com/s', key: 'wd', favicon: '/svg/baidu.svg', s: 'bd' }, 14 | }, 15 | { 16 | name: '谷歌', 17 | enName: 'Google', 18 | value: { name: '谷歌', enName: 'Google', url: 'https://www.google.com/search', key: 'q', favicon: '/svg/google.svg', s: 'gg' }, 19 | }, 20 | { 21 | name: '搜狗', 22 | enName: 'Sougou', 23 | value: { name: '搜狗', enName: 'Sougou', url: 'https://www.sogou.com/web', key: 'query', favicon: '/svg/sogou.svg', s: 'sg' }, 24 | }, 25 | { 26 | name: '维基', 27 | enName: 'Wikipedia', 28 | value: { name: '维基百科', enName: 'Wikipedia', url: 'https://zh.wikipedia.org/w/index.php', key: 'search', favicon: '/svg/wikipedia.svg', s: 'vk' }, 29 | }, 30 | ] 31 | 32 | export const search = new SettingItem({ 33 | name: '搜索引擎', 34 | enName: 'Search', 35 | children: searchList, 36 | defaultKey: 'Google', 37 | }) 38 | -------------------------------------------------------------------------------- /src/utils/settings/show_lunar.ts: -------------------------------------------------------------------------------- 1 | import { SettingItem, type SettingItemChildren } from '@/types' 2 | 3 | const showLunarList: SettingItemChildren = [ 4 | { name: '显示', enName: 'Show', value: true }, 5 | { name: '隐藏', enName: 'Hide', value: false }, 6 | ] 7 | 8 | export const showLunar = new SettingItem({ 9 | name: '显示农历', 10 | enName: 'Show Lunar', 11 | children: showLunarList, 12 | defaultKey: 'Show', 13 | }) 14 | -------------------------------------------------------------------------------- /src/utils/settings/theme.ts: -------------------------------------------------------------------------------- 1 | import type { SettingItemChildren, Theme } from '@/types' 2 | import { SettingItem } from '@/types' 3 | 4 | const themeChildren: SettingItemChildren = [ 5 | { 6 | name: '初春', 7 | enName: 'EarlySpring', 8 | value: { 9 | primaryC: '#0d9488', 10 | primaryLightC: '#37a399', 11 | primaryDarkC: '#096b62', 12 | siteHoverC: '#37a39922', 13 | settingBorderC: '#096b62aa', 14 | settingGroupBgC: '#87a8a422', 15 | bgC: '#87a8a4', 16 | mainBgC: '#f3f4f6', 17 | }, 18 | }, 19 | { 20 | name: '瀚海', 21 | enName: 'VastOcean', 22 | value: { 23 | primaryC: '#146C94', 24 | primaryLightC: '#1a8dc2', 25 | primaryDarkC: '#115d80', 26 | siteHoverC: '#1a8dc222', 27 | settingBorderC: '#115d80aa', 28 | settingGroupBgC: '#0A4D6822', 29 | bgC: '#0A4D68', 30 | mainBgC: '#f3f4f6', 31 | }, 32 | }, 33 | { 34 | name: '大漠', 35 | enName: 'EndlessDesert', 36 | value: { 37 | primaryC: '#bc6c25', 38 | primaryLightC: '#d47a2a', 39 | primaryDarkC: '#96561e', 40 | siteHoverC: '#d47a2a22', 41 | settingBorderC: '#96561eaa', 42 | settingGroupBgC: '#BF927022', 43 | bgC: '#BF9270', 44 | mainBgC: '#f3f4f6', 45 | }, 46 | }, 47 | { 48 | name: '月白', 49 | enName: 'MoonWhite', 50 | value: { 51 | primaryC: '#555555', 52 | primaryLightC: '#777777 ', 53 | primaryDarkC: '#333333', 54 | siteHoverC: '#77777722', 55 | settingBorderC: '#333333aa', 56 | settingGroupBgC: '#00000033', 57 | bgC: '#E7E6E1', 58 | mainBgC: '#f3f4f6', 59 | }, 60 | }, 61 | ] 62 | 63 | export const theme = new SettingItem({ 64 | name: '主题', 65 | enName: 'Theme', 66 | children: themeChildren, 67 | defaultKey: 'MoonWhite', 68 | }) 69 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2016", 4 | "lib": ["DOM", "ESNext"], 5 | "jsx": "preserve", 6 | "module": "ESNext", 7 | "moduleResolution": "node", 8 | "baseUrl": ".", 9 | "paths": { 10 | "@/*": ["src/*"] 11 | }, 12 | "types": [ 13 | "vite/client", 14 | "vite-plugin-pages/client", 15 | "unplugin-vue-macros/macros-global" 16 | ], 17 | "resolveJsonModule": true, 18 | "allowJs": true, 19 | "outDir": "dist", 20 | "esModuleInterop": true, 21 | "forceConsistentCasingInFileNames": true, 22 | "strict": true, 23 | "strictNullChecks": true, 24 | "noUnusedLocals": true, 25 | "skipLibCheck": true 26 | }, 27 | "exclude": ["dist", "node_modules"] 28 | } 29 | -------------------------------------------------------------------------------- /uno.config.ts: -------------------------------------------------------------------------------- 1 | import { 2 | defineConfig, 3 | presetAttributify, 4 | presetIcons, 5 | presetUno, 6 | transformerAttributifyJsx, 7 | transformerDirectives, 8 | } from 'unocss' 9 | import { presetScalpel } from 'unocss-preset-scalpel' 10 | import { FileSystemIconLoader } from '@iconify/utils/lib/loader/node-loaders' 11 | 12 | export default defineConfig({ 13 | shortcuts: { 14 | 'flex-center': 'flex justify-center items-center', 15 | 'btn': 'px-4 py-1 rounded inline-block bg-$primary-c text-white cursor-pointer hover:bg-$primary-light-c active:bg-$primary-dark-c disabled:cursor-default disabled:bg-gray-600 disabled:opacity-50 transition-300 duration-200', 16 | 'icon-btn': 'text-16 inline-block cursor-pointer select-none opacity-75 transition duration-200 ease-in-out hover:opacity-100 hover:text-$primary-c', 17 | }, 18 | rules: [ 19 | [ 20 | /^ellipsis-(\d+)$/, 21 | ([, n]) => ({ 22 | 'overflow': 'hidden', 23 | 'text-overflow': 'ellipsis', 24 | 'display': ' -webkit-box', 25 | '-webkit-line-clamp': `${n}`, 26 | '-webkit-box-orient': 'vertical', 27 | }), 28 | ], 29 | ], 30 | presets: [ 31 | presetUno(), 32 | presetAttributify(), 33 | presetIcons({ 34 | scale: 1.2, 35 | collections: { 36 | cus: FileSystemIconLoader('./public/svg'), 37 | }, 38 | }), 39 | presetScalpel(), 40 | ], 41 | transformers: [ 42 | transformerDirectives(), 43 | transformerAttributifyJsx(), 44 | ], 45 | }) 46 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import path from 'node:path' 2 | import { defineConfig } from 'vite' 3 | import Vue from '@vitejs/plugin-vue' 4 | import Pages from 'vite-plugin-pages' 5 | import AutoImport from 'unplugin-auto-import/vite' 6 | import Components from 'unplugin-vue-components/vite' 7 | import VueMacros from 'unplugin-vue-macros/vite' 8 | import Unocss from 'unocss/vite' 9 | import { NaiveUiResolver } from 'unplugin-vue-components/resolvers' 10 | import { visualizer } from 'rollup-plugin-visualizer' 11 | 12 | export default defineConfig({ 13 | plugins: [ 14 | Vue(), 15 | Unocss(), 16 | Pages(), 17 | VueMacros(), 18 | AutoImport({ 19 | imports: ['vue', 'vue-router', 'pinia', '@vueuse/core', 'vue/macros'], 20 | dts: 'src/auto-imports.d.ts', 21 | dirs: ['src/composables', 'src/stores'], 22 | vueTemplate: true, 23 | }), 24 | Components({ 25 | resolvers: [NaiveUiResolver()], 26 | dirs: ['src/components/**'], 27 | extensions: ['vue', 'tsx'], 28 | dts: 'src/components.d.ts', 29 | }), 30 | visualizer({ 31 | open: true, 32 | gzipSize: true, 33 | brotliSize: true, 34 | }), 35 | ], 36 | resolve: { 37 | alias: { 38 | '@': path.resolve(__dirname, './src'), 39 | }, 40 | }, 41 | server: { 42 | port: 2000, 43 | host: '0.0.0.0', 44 | }, 45 | }) 46 | --------------------------------------------------------------------------------