├── .editorconfig ├── .env.example ├── .github └── workflows │ └── nuxthub.yml ├── .gitignore ├── .npmrc ├── .vscode ├── extensions.json ├── launch.json └── settings.json ├── LICENSE ├── README.md ├── README_zh.md ├── app ├── app.vue ├── components │ ├── AdminIndex.vue │ ├── AdminUpload.vue │ ├── EditPhotoDialog.vue │ ├── Header.vue │ ├── InputDatetime.vue │ ├── ItemStatus.vue │ ├── LoginForm.vue │ ├── Logo.vue │ ├── PhotoItem.vue │ ├── PhotoItemCard.vue │ ├── PhotoItemCardDefault.vue │ ├── Tag.vue │ ├── Tags.vue │ ├── ThemeCustomizer.vue │ ├── ThemePopover.vue │ ├── TooltipIconButton.vue │ ├── UploadConfig.vue │ ├── UploadConfigCard.vue │ ├── UploadPhoto.vue │ ├── UploadPhotoForm.vue │ ├── UploadPhotoImage.vue │ ├── inspira │ │ ├── BorderBeam.vue │ │ ├── IInput.vue │ │ ├── InteractiveHoverButton.vue │ │ ├── file-upload │ │ │ ├── FileUpload.vue │ │ │ └── FileUploadGrid.vue │ │ └── three-d-card │ │ │ ├── CardContainer.vue │ │ │ └── CardItem.vue │ └── ui │ │ ├── badge │ │ ├── Badge.vue │ │ └── index.ts │ │ ├── button │ │ ├── Button.vue │ │ └── index.ts │ │ ├── card │ │ ├── Card.vue │ │ ├── CardContent.vue │ │ ├── CardDescription.vue │ │ ├── CardFooter.vue │ │ ├── CardHeader.vue │ │ ├── CardTitle.vue │ │ └── index.ts │ │ ├── checkbox │ │ ├── Checkbox.vue │ │ └── index.ts │ │ ├── collapsible │ │ ├── Collapsible.vue │ │ ├── CollapsibleContent.vue │ │ ├── CollapsibleTrigger.vue │ │ └── index.ts │ │ ├── dialog │ │ ├── Dialog.vue │ │ ├── DialogClose.vue │ │ ├── DialogContent.vue │ │ ├── DialogDescription.vue │ │ ├── DialogFooter.vue │ │ ├── DialogHeader.vue │ │ ├── DialogScrollContent.vue │ │ ├── DialogTitle.vue │ │ ├── DialogTrigger.vue │ │ └── index.ts │ │ ├── form │ │ ├── FormControl.vue │ │ ├── FormDescription.vue │ │ ├── FormItem.vue │ │ ├── FormLabel.vue │ │ ├── FormMessage.vue │ │ ├── index.ts │ │ ├── injectionKeys.ts │ │ └── useFormField.ts │ │ ├── input │ │ ├── Input.vue │ │ └── index.ts │ │ ├── label │ │ ├── Label.vue │ │ └── index.ts │ │ ├── number-field │ │ ├── NumberField.vue │ │ ├── NumberFieldContent.vue │ │ ├── NumberFieldDecrement.vue │ │ ├── NumberFieldIncrement.vue │ │ ├── NumberFieldInput.vue │ │ └── index.ts │ │ ├── popover │ │ ├── Popover.vue │ │ ├── PopoverContent.vue │ │ ├── PopoverTrigger.vue │ │ └── index.ts │ │ ├── scroll-area │ │ ├── ScrollArea.vue │ │ ├── ScrollBar.vue │ │ └── index.ts │ │ ├── select │ │ ├── Select.vue │ │ ├── SelectContent.vue │ │ ├── SelectGroup.vue │ │ ├── SelectItem.vue │ │ ├── SelectItemText.vue │ │ ├── SelectLabel.vue │ │ ├── SelectScrollDownButton.vue │ │ ├── SelectScrollUpButton.vue │ │ ├── SelectSeparator.vue │ │ ├── SelectTrigger.vue │ │ ├── SelectValue.vue │ │ └── index.ts │ │ ├── skeleton │ │ ├── Skeleton.vue │ │ └── index.ts │ │ ├── sonner │ │ ├── Sonner.vue │ │ └── index.ts │ │ ├── tags-input │ │ ├── TagsInput.vue │ │ ├── TagsInputInput.vue │ │ ├── TagsInputItem.vue │ │ ├── TagsInputItemDelete.vue │ │ ├── TagsInputItemText.vue │ │ ├── TagsInputString.vue │ │ └── index.ts │ │ ├── textarea │ │ ├── Textarea.vue │ │ └── index.ts │ │ └── tooltip │ │ ├── Tooltip.vue │ │ ├── TooltipContent.vue │ │ ├── TooltipProvider.vue │ │ ├── TooltipTrigger.vue │ │ └── index.ts ├── composables │ ├── infos.ts │ ├── useAIConfig.ts │ ├── useFile.ts │ ├── useMouseState.ts │ ├── usePhotos.ts │ ├── useTheme.ts │ └── useUploadConfig.ts ├── layouts │ ├── admin-demo.vue │ ├── admin.vue │ ├── default.vue │ └── home.vue ├── lib │ └── utils.ts ├── middleware │ ├── auth.ts │ └── disable-vue-transitions.global.ts ├── pages │ ├── admin │ │ ├── demo │ │ │ ├── index.vue │ │ │ └── upload.vue │ │ ├── index.vue │ │ ├── login.vue │ │ └── upload.vue │ ├── grid.vue │ ├── index.vue │ ├── p │ │ └── [...id].vue │ └── tag │ │ └── [...tag].vue ├── stores │ └── photos.ts ├── utils │ ├── ai.ts │ ├── compress.ts │ ├── cuid.ts │ ├── date.ts │ ├── exif.ts │ ├── index.ts │ ├── shadcn.ts │ ├── types.ts │ └── worker.ts └── workers │ ├── decode.worker.ts │ └── encode.worker.ts ├── components.json ├── drizzle.config.ts ├── eslint.config.mjs ├── i18n └── locales │ ├── en.yml │ └── zh.yml ├── nuxt.config.ts ├── package.json ├── pnpm-lock.yaml ├── public ├── exif-gallery-nuxt.jpg ├── exif-gallery.svg └── favicon.svg ├── server ├── api │ ├── auth.post.ts │ ├── photos │ │ ├── [id].delete.ts │ │ ├── [id].get.ts │ │ ├── [id].put.ts │ │ ├── index.get.ts │ │ └── upload.post.ts │ └── tags │ │ └── index.get.ts ├── database │ ├── migrations │ │ ├── 0000_serious_shinobi_shaw.sql │ │ ├── 0001_noisy_lightspeed.sql │ │ ├── 0002_colorful_reptil.sql │ │ ├── 0003_smooth_doctor_doom.sql │ │ ├── fix_tags.sql │ │ └── meta │ │ │ ├── 0000_snapshot.json │ │ │ ├── 0001_snapshot.json │ │ │ ├── 0002_snapshot.json │ │ │ ├── 0003_snapshot.json │ │ │ └── _journal.json │ └── schema.ts ├── routes │ └── photos │ │ └── [pathname].get.ts ├── tsconfig.json └── utils │ ├── drizzle.ts │ ├── tag.ts │ └── utils.ts ├── tsconfig.json ├── types ├── auth.d.ts └── index.ts └── uno.config.ts /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_size = 2 6 | indent_style = space 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | # Used by nuxt-auth-utils 2 | NUXT_SESSION_PASSWORD= 3 | # Used to login and upload images 4 | NUXT_ADMIN_PASSWORD= 5 | -------------------------------------------------------------------------------- /.github/workflows/nuxthub.yml: -------------------------------------------------------------------------------- 1 | name: Deploy to NuxtHub 2 | on: 3 | push: 4 | branches: 5 | - main 6 | 7 | jobs: 8 | deploy: 9 | name: Deploy to NuxtHub 10 | runs-on: ubuntu-latest 11 | environment: 12 | name: ${{ github.ref == 'refs/heads/main' && 'production' || 'preview' }} 13 | url: ${{ steps.deploy.outputs.deployment-url }} 14 | permissions: 15 | contents: read 16 | id-token: write 17 | 18 | steps: 19 | - uses: actions/checkout@v4 20 | 21 | - name: Install pnpm 22 | uses: pnpm/action-setup@v4 23 | 24 | - name: Install Node.js 25 | uses: actions/setup-node@v4 26 | with: 27 | node-version: lts/* 28 | cache: pnpm 29 | 30 | - name: Install dependencies 31 | run: pnpm install 32 | 33 | - name: Build application 34 | run: pnpm build 35 | 36 | - name: Deploy to NuxtHub 37 | uses: nuxt-hub/action@v1 38 | id: deploy 39 | with: 40 | project-key: exif-gallery-nu-9wft 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Nuxt dev/build outputs 2 | .output 3 | .data 4 | .nuxt 5 | .nitro 6 | .cache 7 | dist 8 | 9 | # Node dependencies 10 | node_modules 11 | 12 | # Logs 13 | logs 14 | *.log 15 | 16 | # Misc 17 | .DS_Store 18 | .fleet 19 | .idea 20 | 21 | # Local env files 22 | .env 23 | .env.* 24 | !.env.example 25 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | shamefully-hoist=true 2 | strict-peer-dependencies=false 3 | auto-install-peers=false 4 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "vue.volar", 4 | "dbaeumer.vscode-eslint", 5 | "yoavbls.pretty-ts-errors" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | "version": "0.2.0", 5 | "configurations": [ 6 | { 7 | "type": "chrome", 8 | "request": "launch", 9 | "name": "client: chrome", 10 | "url": "http://localhost:3000", 11 | "webRoot": "${workspaceFolder}" 12 | }, 13 | { 14 | "type": "node", 15 | "request": "launch", 16 | "name": "server: nuxt", 17 | "outputCapture": "std", 18 | "program": "${workspaceFolder}/node_modules/nuxi/bin/nuxi.mjs", 19 | "args": [ 20 | "dev" 21 | ] 22 | } 23 | ], 24 | "compounds": [ 25 | { 26 | "name": "fullstack: nuxt", 27 | "configurations": [ 28 | "server: nuxt", 29 | "client: chrome" 30 | ] 31 | } 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | // Disable the default formatter, use eslint instead 3 | "prettier.enable": false, 4 | "editor.formatOnSave": false, 5 | 6 | // Auto fix 7 | "editor.codeActionsOnSave": { 8 | "source.fixAll.eslint": "explicit", 9 | "source.organizeImports": "never" 10 | }, 11 | 12 | "i18n-ally.localesPaths": [ 13 | "i18n/locales" 14 | ], 15 | "i18n-ally.keystyle": "nested", 16 | 17 | // Silent the stylistic rules in you IDE, but still auto fix them 18 | "eslint.rules.customizations": [ 19 | { "rule": "style/*", "severity": "off", "fixable": true }, 20 | { "rule": "format/*", "severity": "off", "fixable": true }, 21 | { "rule": "*-indent", "severity": "off", "fixable": true }, 22 | { "rule": "*-spacing", "severity": "off", "fixable": true }, 23 | { "rule": "*-spaces", "severity": "off", "fixable": true }, 24 | { "rule": "*-order", "severity": "off", "fixable": true }, 25 | { "rule": "*-dangle", "severity": "off", "fixable": true }, 26 | { "rule": "*-newline", "severity": "off", "fixable": true }, 27 | { "rule": "*quotes", "severity": "off", "fixable": true }, 28 | { "rule": "*semi", "severity": "off", "fixable": true } 29 | ], 30 | 31 | // Enable eslint for all supported languages 32 | "eslint.validate": [ 33 | "javascript", 34 | "javascriptreact", 35 | "typescript", 36 | "typescriptreact", 37 | "vue", 38 | "html", 39 | "markdown", 40 | "json", 41 | "jsonc", 42 | "yaml", 43 | "toml", 44 | "xml", 45 | "gql", 46 | "graphql", 47 | "astro", 48 | "svelte", 49 | "css", 50 | "less", 51 | "scss", 52 | "pcss" 53 | ], 54 | 55 | "editor.quickSuggestions": { 56 | "strings": true 57 | }, 58 | 59 | // "typescript.tsdk": "node_modules/typescript/lib", 60 | "cSpell.words": [ 61 | "exif", 62 | "sonner" 63 | ] 64 | } 65 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 wiidede 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EXIF Gallery Nuxt 2 | 3 | [English](README.md) | [简体中文](README_zh.md) 4 | 5 | A full-stack photo album solution that integrates AI intelligent processing, browser image compression, and other functions. 6 | 7 | ![exif-gallery-nuxt](./public/exif-gallery-nuxt.jpg) 8 | 9 | [![Deploy to NuxtHub](https://hub.nuxt.com/button.svg)](https://admin.hub.nuxt.com/new) 10 | 11 | ## Features 12 | 13 | - 📷 Image upload and display with [`hubBlob()`](http://hub.nuxt.com/docs/storage/blob) 14 | - 🌐 Cloud Storage: Blob powered by NuxtHub (cloudflare R2) 15 | - 🤖 AI Integration: Support for OpenAI and Gemini for intelligent image processing 16 | - 🗜️ Image Compression: Multiple format support (JPEG, WebP, AVIF) with JSQuash 17 | - 🎨 Modern UI: Beautiful components with shadcn-vue and inspira-ui 18 | - 🏃🏻 [View transition API](https://developer.chrome.com/docs/web-platform/view-transitions) The View Transitions API provides a mechanism for easily creating animated transitions between different DOM states while also updating the DOM contents in a single step 19 | - 🔑 [Nuxt Auth Utils](https://github.com/Atinux/nuxt-auth-utils) Minimalist Authentication module for Nuxt exposing Vue composables and server utils 20 | 21 | ## Stack 22 | 23 | - [NuxtHub](https://hub.nuxt.com) - A Nuxt toolkit to build fullstack applications on the edge 24 | - [UnoCSS](https://unocss.dev/) - The instant on-demand atomic CSS engine 25 | - [shadcn-vue](https://www.shadcn-vue.com/) - Beautifully designed components built with Reka UI and Tailwind CSS 26 | - [inspira-ui](https://inspira-ui.com/) - Collection of beautiful UI components to build stunning animated interfaces 27 | - [VueUse](https://github.com/antfu/vueuse) - Collection of useful composition APIs 28 | - [ESLint](https://eslint.org/) with [@nuxt/eslint-config](https://github.com/nuxt/eslint), single quotes, no semi 29 | - [TypeScript](https://www.typescriptlang.org/) 30 | 31 | ### Deploy 32 | 33 | You can deploy this project on your Cloudflare account for free using [NuxtHub](https://hub.nuxt.com). 34 | 35 | 1. Make sure you have a Cloudflare account and R2 plan. 36 | 2. fork this repository to your own GitHub account. 37 | 3. Go to [NuxtHub](https://hub.nuxt.com) and sign in with your GitHub account. 38 | 4. Click on the "Deploy" button and select your forked repository. 39 | 5. Configure the environment variables as needed. 40 | 41 | > [!NOTE] 42 | > If Github Actions created but not triggered, you can create a new commit to trigger the deployment (like modify README.md and commit). 43 | 44 | ```bash 45 | npx nuxthub deploy 46 | ``` 47 | 48 | It's also possible to leverage Cloudflare Pages CI for deploying, learn more about the different options on 49 | 50 | Learn more about remote storage on 51 | 52 | ## Environment Variables 53 | 54 | - `NUXT_ADMIN_PASSWORD` (required) - A password to access the admin panel and upload images, will default to `admin` if not provided. 55 | - `NUXT_SESSION_PASSWORD` (required) - A secret key for session encryption used by [nuxt-auth-utils](https://github.com/Atinux/nuxt-auth-utils), will be generated automatically if not provided in development mode. 56 | - `NUXT_PUBLIC_TITLE` - The title of the application, will default to `Exif Gallery Nuxt` if not provided. 57 | - `NUXT_PUBLIC_DESCRIPTION` - The description of the application, will default to `A full-stack photo album solution that integrates AI intelligent processing, browser image compression, and other functions` if not provided. 58 | 59 | ## Setup 60 | 61 | 1. Clone this repository to your local machine. 62 | 2. Install dependencies using the command `pnpm install` or your favorite package manager. 63 | 3. Run the application with the command `pnpm dev` or your favorite package manager. 64 | 65 | > If you don't have pnpm installed, run: `corepack enable pnpm` 66 | 67 | ## Development 68 | 69 | ```bash 70 | pnpm dev 71 | ``` 72 | 73 | ### Remote Storage 74 | 75 | Once you deployed your project, you can connect to your remote database locally running: 76 | 77 | ```bash 78 | pnpm dev --remote 79 | ``` 80 | 81 | ## Contribution 82 | 83 | Contributions are welcome! Feel free to open an issue to report a bug or submit a feature request via a pull request. 84 | 85 | ## Credits 86 | 87 | Thanks to [exif-photo-blog](https://github.com/sambecker/exif-photo-blog) 88 | Thanks to [nuxt-image-gallery](https://github.com/Flosciante/nuxt-image-gallery) 89 | -------------------------------------------------------------------------------- /README_zh.md: -------------------------------------------------------------------------------- 1 | # EXIF Gallery Nuxt 2 | 3 | 集成 AI 智能处理、浏览器图片压缩等功能的全栈相册解决方案。 4 | 5 | ![exif-gallery-nuxt](./public/exif-gallery-nuxt.jpg) 6 | 7 | [![部署到 NuxtHub](https://hub.nuxt.com/button.svg)](https://admin.hub.nuxt.com/new) 8 | 9 | ## 功能特性 10 | 11 | - 📷 图片上传与展示,支持 [`hubBlob()`](http://hub.nuxt.com/docs/storage/blob) 12 | - 🌐 云存储:基于 NuxtHub 的 Blob 存储(Cloudflare R2) 13 | - 🤖 AI 集成:支持 OpenAI 和 Gemini 智能图像处理 14 | - 🗜️ 图片压缩:支持多种格式(JPEG、WebP、AVIF),使用 JSQuash 库 15 | - 🎨 现代化界面:采用 shadcn-vue 和 inspira-ui 构建的精美组件 16 | - 🏃🏻 [视图过渡 API](https://developer.chrome.com/docs/web-platform/view-transitions) 提供在不同 DOM 状态间创建动画过渡的机制,同时实现单步 DOM 内容更新 17 | - 🔑 [Nuxt Auth Utils](https://github.com/Atinux/nuxt-auth-utils) 提供 Vue 组合式 API 和服务端工具的简约身份验证模块 18 | 19 | ## 技术栈 20 | 21 | - [NuxtHub](https://hub.nuxt.com) - 基于边缘计算构建全栈应用的 Nuxt 工具包 22 | - [UnoCSS](https://unocss.dev/) - 即时按需原子化 CSS 引擎 23 | - [shadcn-vue](https://www.shadcn-vue.com/) - 基于 Reka UI 和 Tailwind CSS 构建的精美组件库 24 | - [inspira-ui](https://inspira-ui.com/) - 用于构建惊艳动画界面的 UI 组件集合 25 | - [VueUse](https://github.com/antfu/vueuse) - 实用组合式 API 集合 26 | - [ESLint](https://eslint.org/) 配合 [@nuxt/eslint-config](https://github.com/nuxt/eslint) - 使用单引号且不带分号的代码规范 27 | - [TypeScript](https://www.typescriptlang.org/) 28 | 29 | ### 部署说明 30 | 31 | 使用 [NuxtHub](https://hub.nuxt.com) 可免费部署到 Cloudflare。 32 | 33 | 1. 确保您已拥有Cloudflare账户并开通R2服务。 34 | 2. 将此代码库(Repository) fork 到您的GitHub账户中。 35 | 3. 前往 [NuxtHub官网](https://hub.nuxt.com),使用GitHub账户登录。 36 | 4. 点击"Deploy"部署按钮,选择您fork的代码库。 37 | 5. 根据需要配置环境变量。 38 | 39 | > [!NOTE] 40 | > 如果Github Actions已创建但未自动触发,您可以通过提交新更改(例如修改README.md文件并提交)来手动触发部署。 41 | 42 | ```bash 43 | npx nuxthub deploy 44 | ``` 45 | 46 | 也可通过 Cloudflare Pages CI 部署,更多部署选项请参考: 47 | 48 | 远程存储相关文档: 49 | 50 | ## 环境变量 51 | 52 | - `NUXT_ADMIN_PASSWORD` - 管理后台访问密码,未设置时默认为 `admin` 53 | - `NUXT_SESSION_PASSWORD` - [nuxt-auth-utils](https://github.com/Atinux/nuxt-auth-utils) 使用的会话加密密钥,开发环境未设置时将自动生成 54 | - `NUXT_PUBLIC_TITLE` - 应用标题,未设置时默认为 `Exif Gallery Nuxt` 55 | - `NUXT_PUBLIC_DESCRIPTION` - 应用描述,未设置时默认为 `一个集成了 AI 智能处理、浏览器图片压缩等功能的全栈相册解决方案` 56 | 57 | ## 安装步骤 58 | 59 | 1. 将本仓库克隆到本地 60 | 2. 使用 `pnpm install` 或其他包管理器安装依赖 61 | 3. 运行 `pnpm dev` 或其他包管理器启动应用 62 | 63 | > 若未安装 pnpm,可运行:`corepack enable pnpm` 64 | 65 | ## 开发指南 66 | 67 | ```bash 68 | pnpm dev 69 | ``` 70 | 71 | ### 远程存储 72 | 73 | 部署项目后,可通过以下命令连接远程数据库进行本地开发: 74 | 75 | ```bash 76 | pnpm dev --remote 77 | ``` 78 | 79 | ## 贡献指南 80 | 81 | 欢迎贡献代码!可通过提交 issue 反馈问题,或通过 pull request 提交新功能。 82 | 83 | ## 致谢 84 | 85 | 感谢 [exif-photo-blog](https://github.com/sambecker/exif-photo-blog) 86 | 感谢 [nuxt-image-gallery](https://github.com/Flosciante/nuxt-image-gallery) 87 | -------------------------------------------------------------------------------- /app/app.vue: -------------------------------------------------------------------------------- 1 | 48 | 49 | 60 | -------------------------------------------------------------------------------- /app/components/AdminIndex.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | 112 | -------------------------------------------------------------------------------- /app/components/EditPhotoDialog.vue: -------------------------------------------------------------------------------- 1 | 57 | 58 | 97 | 98 | 100 | -------------------------------------------------------------------------------- /app/components/Header.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 68 | -------------------------------------------------------------------------------- /app/components/InputDatetime.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 32 | -------------------------------------------------------------------------------- /app/components/ItemStatus.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 20 | 21 | 24 | -------------------------------------------------------------------------------- /app/components/LoginForm.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 83 | -------------------------------------------------------------------------------- /app/components/Logo.vue: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /app/components/PhotoItem.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 101 | 102 | 105 | -------------------------------------------------------------------------------- /app/components/PhotoItemCard.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 31 | 32 | 35 | -------------------------------------------------------------------------------- /app/components/PhotoItemCardDefault.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 28 | 29 | 32 | -------------------------------------------------------------------------------- /app/components/Tag.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 19 | 20 | 23 | -------------------------------------------------------------------------------- /app/components/Tags.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 23 | -------------------------------------------------------------------------------- /app/components/ThemeCustomizer.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 122 | -------------------------------------------------------------------------------- /app/components/ThemePopover.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 32 | -------------------------------------------------------------------------------- /app/components/TooltipIconButton.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 41 | 42 | 45 | -------------------------------------------------------------------------------- /app/components/UploadConfigCard.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 33 | -------------------------------------------------------------------------------- /app/components/UploadPhotoForm.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 |