init web ui
4
resources/.browserslistrc
Normal file
@@ -0,0 +1,4 @@
|
||||
> 1%
|
||||
last 2 versions
|
||||
not dead
|
||||
not ie 11
|
2
resources/.dockerignore
Normal file
@@ -0,0 +1,2 @@
|
||||
node_modules
|
||||
npm-debug.log
|
5
resources/.editorconfig
Normal file
@@ -0,0 +1,5 @@
|
||||
[*.{js,jsx,ts,tsx,vue}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
10
resources/.env.dev
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
|
||||
# Firebase 🔥
|
||||
VITE_FIREBASE_API_KEY=
|
||||
VITE_FIREBASE_AUTH_DOMAIN=
|
||||
VITE_FIREBASE_PROJECT_ID=
|
||||
VITE_FIREBASE_STORAGE_BUCKET=
|
||||
VITE_FIREBASE_MESSAGING_SENDER_ID=
|
||||
VITE_FIREBASE_APP_ID=
|
||||
VITE_FIREBASE_MEASUREMENT_ID=
|
1
resources/.env.pro
Normal file
@@ -0,0 +1 @@
|
||||
VITE_API_BASE_URL=https://api.example.com
|
7
resources/.env.template
Normal file
@@ -0,0 +1,7 @@
|
||||
VITE_OPENAI_API_KEY = XXXXXXXXXXXX
|
||||
VITE_UNSPLASH_ACCESS_KEY = XXXXXXXXXXXX
|
||||
VITE_GITHUB_CLIENT_ID = XXXXXXXXXXXX
|
||||
# Aruze TextToSpeech Key (required for tts)
|
||||
VITE_TTS_KEY=XXXXXXXXXXXX
|
||||
# Aruze TextToSpeech Region
|
||||
VITE_TTS_REGION = XXXXXXXXXXXX
|
2
resources/.gitattributes
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
32
resources/.gitignore
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
/dist
|
||||
/.vite_cache
|
||||
|
||||
|
||||
# local env files
|
||||
.env
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Log files
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
# Local Netlify folder
|
||||
.netlify
|
||||
.env
|
||||
|
||||
# yarn.lock
|
||||
yarn.lock
|
13
resources/Dockerfile
Normal file
@@ -0,0 +1,13 @@
|
||||
# 构建阶段
|
||||
FROM node:lts-alpine as build-stage
|
||||
WORKDIR /app
|
||||
COPY package*.json ./
|
||||
RUN npm install
|
||||
COPY . .
|
||||
RUN npm run build
|
||||
|
||||
# 生产阶段
|
||||
FROM nginx:stable-alpine as production-stage
|
||||
COPY --from=build-stage /app/dist /usr/share/nginx/html
|
||||
EXPOSE 80
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
6
resources/Dockerfile.dev
Normal file
@@ -0,0 +1,6 @@
|
||||
FROM node:lts-alpine
|
||||
WORKDIR /app
|
||||
COPY package*.json ./
|
||||
RUN npm install
|
||||
EXPOSE 8080
|
||||
CMD ["npm", "run", "dev"]
|
21
resources/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 jk.yang
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
169
resources/README.jp.md
Normal file
@@ -0,0 +1,169 @@
|
||||
<br><br>
|
||||
|
||||
<p align='center' >
|
||||
<img src='/src/assets/logo.png' alt='Vuetify3' width='300'/>
|
||||
</p>
|
||||
<br><br>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://vuejs.org/">
|
||||
<img src="https://img.shields.io/badge/vue-v3.2.47-brightgreen.svg" alt="vue">
|
||||
</a>
|
||||
<a href="https://vuetifyjs.com/">
|
||||
<img src="https://img.shields.io/badge/vuetify-v3.1.13-blue.svg" alt="element-ui">
|
||||
</a>
|
||||
<a href="https://vitejs.dev/">
|
||||
<img src="https://img.shields.io/badge/vite-v4.2.1-blueviolet.svg" alt="element-ui">
|
||||
</a>
|
||||
|
||||
<a href="https://github.com/yangjiakai/lux-admin-vuetify3/blob/main/LICENSE">
|
||||
<img src="https://img.shields.io/github/license/mashape/apistatus.svg" alt="license">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<h4 align='center'>
|
||||
<a href="https://lux.vuetify3.com/">ライブ・デモ</a>
|
||||
</h4>
|
||||
|
||||
<br>
|
||||
|
||||
<p align='center'>
|
||||
<a href="https://github.com/yangjiakai/jk-vuetify3-lux-admin/blob/main/README.md">English</a> | <a href="https://github.com/yangjiakai/jk-vuetify3-lux-admin/blob/main/README.zh-CN.md">简体中文</a>| <b >日本語</b>
|
||||
</p>
|
||||
|
||||
## 📖 序文
|
||||
|
||||
> 目標は、最も優れた Vuetify 3 の Admin オープンソーステンプレートを作成することです。
|
||||
|
||||
Vuetify の洗練されたテーマを基盤に、明確で効率的なプロジェクト構造を構築し、最新の技術フレームワークを統合しています。このプロジェクトは、さまざまな一般的な技術要件や機能に対応することを目指し、AI アシスタントを組み込むことで、よりインテリジェントな体験を提供します。さらに、すべてのページが複数のデバイスで適応的に表示されるようにし、シームレスなクロスプラットフォーム互換性を実現しています。
|
||||
|
||||
## 📖Other Versions
|
||||
|
||||
SPA Full Version: [lux-ui](https://github.com/yangjiakai/lux-nuxt3)
|
||||
|
||||
SPA Simplified i18n Version[lux-ui-i18n](https://github.com/yangjiakai/vuetify3-lux-admin-template-i18n)
|
||||
|
||||
SPA Simplified chinese Version[lux-ui-zh](https://github.com/yangjiakai/vuetify3-lux-admin-template-zh)
|
||||
|
||||
Nuxt3 version:
|
||||
|
||||
Nuxt3 Full Version [lux-nuxt3](https://github.com/yangjiakai/lux-nuxt3)
|
||||
|
||||
Nuxt3 Simplified Version [lux-nuxt3-template](https://github.com/yangjiakai/lux-nuxt3-template)
|
||||
|
||||
## 📖Documents
|
||||
|
||||
- 📖 [Document 1.0 Chinese](https://www.craft.me/s/tAMVv4hUxZIH6G)
|
||||
|
||||
## 📚 特徴
|
||||
|
||||
- 📖 [Vue 3.2](https://github.com/vuejs/core)
|
||||
- 📖 [Vite 4.x](https://github.com/vitejs/vite)
|
||||
- 📖 UI Framework [Vuetify 3](https://next.vuetifyjs.com/en/)
|
||||
- 📖 TypeScript
|
||||
- 📦 Component Auto Importing
|
||||
- 🍍 [Pinia](https://pinia.vuejs.org/)
|
||||
- 📔 `<script setup>`
|
||||
- 📚 Use icons from any icon sets in [Iconify](https://icon-sets.iconify.design/)
|
||||
- ☁️ Deploy on Netlify, zero-config
|
||||
- 🔑 Firebase auth
|
||||
- 📈 Echarts, ApexChart
|
||||
- 🧭 Openai, Chatgpt
|
||||
- 🌍 vue-i18n
|
||||
- 📚 virtual-scroller , vuedraggable , perfect-scrollbar
|
||||
- 📝 Rich Text Editor
|
||||
- 📇 Responsive multi-platform adaptive
|
||||
|
||||
## 📈 Project Activity
|
||||
|
||||

|
||||
|
||||
## 💬 連絡
|
||||
|
||||
- Email <a href="mailto:yjkbako@gmail.com">yjkbako@gmail.com</a>
|
||||
- Twitter https://twitter.com/baibaixiang
|
||||
- Wechat <img src='/src/assets/wechat-qrcode.png' alt='DashBoard' width='300' />
|
||||
|
||||
## 💌 プレビュー
|
||||
|
||||
<img src='/src/assets/previews/DashBoard.png' alt='DashBoard' />
|
||||
<img src='/src/assets/previews/TaskBoard.png' alt='ChatGPT' />
|
||||
<img src='/src/assets/previews/DataTable.png' alt='DataTable' />
|
||||
<img src='/src/assets/previews/Todo.png' alt='ChatGPT' />
|
||||
<img src='/src/assets/previews/ChatGPT.png' alt='ChatGPT' />
|
||||
<img src='/src/assets/previews/Card.png' alt='Card' />
|
||||
<img src='/src/assets/previews/Color.png' alt='Color' />
|
||||
<img src='/src/assets/previews/Gradient.png' alt='Gradient' />
|
||||
<img src='/src/assets/previews/Login.png' alt='ChatGPT' />
|
||||
<img src='/src/assets/previews/Unsplash.png' alt='ChatGPT' />
|
||||
<img src='/src/assets/previews/Unsplash2.png' alt='ChatGPT' />
|
||||
|
||||
<br>
|
||||
|
||||
## 📦 プリパック
|
||||
|
||||
### 🏷️UI Frameworks
|
||||
|
||||
- [Vuetify3](https://next.vuetifyjs.com/en/) - Vuetify は、美しく手作りされた Vue コンポーネントで構成された、デザインスキル不要の UI フレームワークです。
|
||||
|
||||
### 🏷️Icons
|
||||
|
||||
- [Iconify](https://iconify.design) - 任意のアイコンセットを使用 [🔍Icônes](https://icones.netlify.app/)
|
||||
- [Pure CSS Icons via UnoCSS](https://github.com/antfu/unocss/tree/main/packages/preset-icons)
|
||||
|
||||
### 🏷️ プラグイン
|
||||
|
||||
- [Vue Router4](https://router.vuejs.org/)
|
||||
- [VueUse](https://github.com/antfu/vueuse) - 便利なコンポジション API 集
|
||||
- [VuedDaggable](https://github.com/SortableJS/Vue.Draggable) - 配列モデルと同期したドラッグ&ドロップによる配置操作が可能
|
||||
- [Vue-Masonry-Wall](https://github.com/DerYeger/yeger/tree/main/packages/vue-masonry-wall) -Vue 3 のレスポンシブな Masonry レイアウト SSR をサポートしています
|
||||
- [Vue-Virtual-Scroller](https://github.com/Akryum/vue-virtual-scroller) - 超高速の任意のデータ量のスクロール
|
||||
|
||||
## 👻 今すぐ試す!
|
||||
|
||||
```
|
||||
git clone https://github.com/yangjiakai/lux-admin-vuetify3.git
|
||||
|
||||
cd lux-admin-vuetify3
|
||||
|
||||
yarn install
|
||||
|
||||
yarn dev
|
||||
```
|
||||
|
||||
## 👻Docker it!
|
||||
|
||||
1. 开发环境构建镜像:
|
||||
|
||||
```
|
||||
docker-compose build dev
|
||||
```
|
||||
|
||||
2. 启动开发环境:
|
||||
|
||||
```
|
||||
docker-compose up dev
|
||||
```
|
||||
|
||||
3. 生产环境构建镜像:
|
||||
|
||||
```
|
||||
docker-compose build app
|
||||
```
|
||||
|
||||
4. 启动生产环境:
|
||||
```
|
||||
docker-compose up app
|
||||
```
|
||||
|
||||
### 🔑Set ApiKey
|
||||
|
||||
Find the `.env.template` file in the root directory, remove the `.template` suffix, and replace` VITE_OPENAI_API_KEY`, `VITE_UNSPLASH_ACCESS_KEY`, and `VITE_GITHUB_CLIENT_ID`, and `VITE_TTS_KEY` and `VITE_TTS_REGION` with your own keys.
|
||||
|
||||
> openai apikey: https://platform.openai.com/account/api-keys
|
||||
|
||||
> unsplash apikey: https://unsplash.com/oauth/applications
|
||||
|
||||
> github apikey: https://github.com/settings/tokens
|
||||
|
||||
> azure textToSpeech : https://speech.microsoft.com/
|
171
resources/README.md
Normal file
@@ -0,0 +1,171 @@
|
||||
<br><br>
|
||||
|
||||
<p align='center' >
|
||||
<img src='/src/assets/logo.png' alt='Vuetify3' width='300'/>
|
||||
</p>
|
||||
<br><br>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://vuejs.org/">
|
||||
<img src="https://img.shields.io/badge/vue-v3.2.47-brightgreen.svg" alt="vue">
|
||||
</a>
|
||||
<a href="https://vuetifyjs.com/">
|
||||
<img src="https://img.shields.io/badge/vuetify-v3.1.13-blue.svg" alt="element-ui">
|
||||
</a>
|
||||
<a href="https://vitejs.dev/">
|
||||
<img src="https://img.shields.io/badge/vite-v4.2.1-blueviolet.svg" alt="element-ui">
|
||||
</a>
|
||||
|
||||
<a href="https://github.com/yangjiakai/lux-admin-vuetify3/blob/main/LICENSE">
|
||||
<img src="https://img.shields.io/github/license/mashape/apistatus.svg" alt="license">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<h4 align='center'>
|
||||
<a href="https://lux.vuetify3.com/">Live Demo</a>
|
||||
</h4>
|
||||
|
||||
<br>
|
||||
|
||||
<p align='center'>
|
||||
<b>English</b> | <a href="https://github.com/yangjiakai/jk-vuetify3-lux-admin/blob/main/README.zh-CN.md">简体中文</a>| <a href="https://github.com/yangjiakai/jk-vuetify3-lux-admin/blob/main/README.jp.md">日本語</a>
|
||||
</p>
|
||||
|
||||
## 📖Introduction
|
||||
|
||||
> Goal: Creating the best Vuetify 3 Admin open-source template.
|
||||
|
||||
Built upon the elegant themes of Vuetify, we have established a clear and efficient project structure, integrating the latest technology frameworks. This project aims to address a wide range of common technical requirements and features, while incorporating an AI assistant for a more intelligent experience. Additionally, we ensure that all pages are adaptive across multiple devices, achieving a seamless cross-platform compatibility.
|
||||
|
||||
## 📖Other Versions
|
||||
|
||||
SPA Full Version: [lux-vuetify3](https://github.com/yangjiakai/lux-nuxt3)
|
||||
|
||||
SPA Simplified i18n Version[lux-vuetify3-i18n](https://github.com/yangjiakai/vuetify3-lux-admin-template-i18n)
|
||||
|
||||
SPA Simplified chinese Version[lux-vuetify3-zh](https://github.com/yangjiakai/vuetify3-lux-admin-template-zh)
|
||||
|
||||
Nuxt3 version:
|
||||
|
||||
Nuxt3 Full Version [lux-nuxt3](https://github.com/yangjiakai/lux-nuxt3)
|
||||
|
||||
Nuxt3 Simplified Version [lux-nuxt3-template](https://github.com/yangjiakai/lux-nuxt3-template)
|
||||
|
||||
## 📖Documents
|
||||
|
||||
- 📖 [Document 1.0 Chinese](https://www.craft.me/s/tAMVv4hUxZIH6G)
|
||||
|
||||
## 📚Features
|
||||
|
||||
- 📖 [Vue 3.2](https://github.com/vuejs/core)
|
||||
- 📖 [Vite 4.x](https://github.com/vitejs/vite)
|
||||
- 📖 UI Framework [Vuetify 3](https://next.vuetifyjs.com/en/)
|
||||
- 📖 TypeScript
|
||||
- 📦 Component Auto Importing
|
||||
- 🍍 [Pinia](https://pinia.vuejs.org/)
|
||||
- 📔 `<script setup>`
|
||||
- 📚 Use icons from any icon sets in [Iconify](https://icon-sets.iconify.design/)
|
||||
- ☁️ Deploy on Netlify, zero-config
|
||||
- 🔑 Firebase auth
|
||||
- 📈 Echarts, ApexChart
|
||||
- 🧭 Openai, Chatgpt
|
||||
- 🌍 vue-i18n
|
||||
- 📚 virtual-scroller , vuedraggable , perfect-scrollbar
|
||||
- 📝 Rich Text Editor
|
||||
- 📇 Responsive multi-platform adaptive
|
||||
|
||||
## 📈 Project Activity
|
||||
|
||||

|
||||
|
||||
## 💬Contact Me
|
||||
|
||||
- Email <a href="mailto:yjkbako@gmail.com">yjkbako@gmail.com</a>
|
||||
- Twitter https://twitter.com/baibaixiang
|
||||
- Wechat <img src='/src/assets/wechat-qrcode.png' alt='DashBoard' width='300' />
|
||||
|
||||
## 💌Preview
|
||||
|
||||
<img src='/src/assets/previews/DashBoard.png' alt='DashBoard' />
|
||||
<img src='/src/assets/previews/TaskBoard.png' alt='ChatGPT' />
|
||||
<img src='/src/assets/previews/DataTable.png' alt='DataTable' />
|
||||
<img src='/src/assets/previews/Todo.png' alt='ChatGPT' />
|
||||
<img src='/src/assets/previews/ChatGPT.png' alt='ChatGPT' />
|
||||
<img src='/src/assets/previews/Card.png' alt='Card' />
|
||||
<img src='/src/assets/previews/Color.png' alt='Color' />
|
||||
<img src='/src/assets/previews/Gradient.png' alt='Gradient' />
|
||||
<img src='/src/assets/previews/Login.png' alt='ChatGPT' />
|
||||
<img src='/src/assets/previews/Unsplash.png' alt='ChatGPT' />
|
||||
<img src='/src/assets/previews/Unsplash2.png' alt='ChatGPT' />
|
||||
|
||||
<br>
|
||||
|
||||
## 📦Pre-packed
|
||||
|
||||
### 🏷️ UI Frameworks
|
||||
|
||||
- [Vuetify3](https://next.vuetifyjs.com/en/) - Vuetify is a no design skills required UI Framework with beautifully handcrafted Vue Components.
|
||||
|
||||
### 🏷️ Icons
|
||||
|
||||
- [Iconify](https://iconify.design) - use icons from any icon sets [🔍Icônes](https://icones.netlify.app/)
|
||||
- [Pure CSS Icons via UnoCSS](https://github.com/antfu/unocss/tree/main/packages/preset-icons)
|
||||
|
||||
### 🏷️ Plugins
|
||||
|
||||
- [Vue Router4](https://router.vuejs.org/)
|
||||
- [VueUse](https://github.com/antfu/vueuse) - collection of useful composition APIs
|
||||
- [VuedDaggable](https://github.com/SortableJS/Vue.Draggable) - allowing drag-and-drop and synchronization with view model array.
|
||||
- [Vue-Masonry-Wall](https://github.com/DerYeger/yeger/tree/main/packages/vue-masonry-wall) - Responsive masonry layout with SSR support and zero dependencies for Vue 3.
|
||||
- [Vue-Virtual-Scroller](https://github.com/Akryum/vue-virtual-scroller) - Blazing fast scrolling of any amount of data
|
||||
|
||||
## 👻Try it now!
|
||||
|
||||
```
|
||||
git clone https://github.com/yangjiakai/lux-admin-vuetify3.git
|
||||
|
||||
cd lux-admin-vuetify3
|
||||
|
||||
yarn install
|
||||
|
||||
yarn dev
|
||||
```
|
||||
|
||||
## 👻Docker it!
|
||||
|
||||
1. Build the development environment image:
|
||||
|
||||
```
|
||||
docker-compose build dev
|
||||
```
|
||||
|
||||
2. Start the development environment:
|
||||
|
||||
```
|
||||
docker-compose up dev
|
||||
```
|
||||
|
||||
3. Build the production environment image:
|
||||
|
||||
```
|
||||
docker-compose build app
|
||||
```
|
||||
|
||||
4. Start the production environment:
|
||||
```
|
||||
docker-compose up app
|
||||
```
|
||||
|
||||
这应该能解决实时更新的问题。如果您还有任何疑问或遇到其他问题,请随时告诉我。
|
||||
|
||||
### 🔑Set ApiKey
|
||||
|
||||
Find the `.env.template` file in the root directory, remove the `.template` suffix, and replace` VITE_OPENAI_API_KEY`, `VITE_UNSPLASH_ACCESS_KEY`, and `VITE_GITHUB_CLIENT_ID`, and `VITE_TTS_KEY` and `VITE_TTS_REGION` with your own keys.
|
||||
|
||||
> openai apikey: https://platform.openai.com/account/api-keys
|
||||
|
||||
> unsplash apikey: https://unsplash.com/oauth/applications
|
||||
|
||||
> github apikey: https://github.com/settings/tokens
|
||||
|
||||
> azure textToSpeech : https://speech.microsoft.com/
|
210
resources/README.zh-CN.md
Normal file
@@ -0,0 +1,210 @@
|
||||
<br><br>
|
||||
|
||||
<p align='center' >
|
||||
<img src='/src/assets/logo.png' alt='Vuetify3' width='300'/>
|
||||
</p>
|
||||
<br><br>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://vuejs.org/">
|
||||
<img src="https://img.shields.io/badge/vue-v3.2.47-brightgreen.svg" alt="vue">
|
||||
</a>
|
||||
<a href="https://vuetifyjs.com/">
|
||||
<img src="https://img.shields.io/badge/vuetify-v3.1.13-blue.svg" alt="element-ui">
|
||||
</a>
|
||||
<a href="https://vitejs.dev/">
|
||||
<img src="https://img.shields.io/badge/vite-v4.2.1-blueviolet.svg" alt="element-ui">
|
||||
</a>
|
||||
|
||||
<a href="https://github.com/yangjiakai/lux-admin-vuetify3/blob/main/LICENSE">
|
||||
<img src="https://img.shields.io/github/license/mashape/apistatus.svg" alt="license">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<h4 align='center'>
|
||||
<a href="https://lux.vuetify3.com/">在线 Demo</a>
|
||||
</h4>
|
||||
|
||||
<br>
|
||||
|
||||
<p align='center'>
|
||||
<a href="https://github.com/yangjiakai/jk-vuetify3-lux-admin/blob/main/README.md">English</a> | <b>简体中文</b>| <a href="https://github.com/yangjiakai/jk-vuetify3-lux-admin/blob/main/README.jp.md">日本語</a>
|
||||
</p>
|
||||
|
||||
## 📖 序文
|
||||
|
||||
> 目标创造最优秀的 vuetify3 的 Admin 开源模板
|
||||
|
||||
在 Vuetify 精美的主题基础上,我们构建了一个清晰且高效的项目逻辑架构,整合了最新的技术框架。本项目旨在实现各种常见的技术需求和功能,同时融合了 AI 助手,以提供更智能化的体验。此外,我们确保所有页面在多种设备上均能自适应展示,实现优雅的跨平台兼容性。
|
||||
|
||||
## 📖 其他版本
|
||||
|
||||
### SPA Version
|
||||
|
||||
SPA 完整版: [lux-vuetify3](https://github.com/yangjiakai/lux-nuxt3)
|
||||
|
||||
SPA 简化国际化模板 [lux-vuetify3-i18n](https://github.com/yangjiakai/vuetify3-lux-admin-template-i18n)
|
||||
|
||||
SPA 简化中文模板 [lux-vuetify3-zh](https://github.com/yangjiakai/vuetify3-lux-admin-template-zh)
|
||||
|
||||
### Nuxt3 Version
|
||||
|
||||
Nuxt3 完整版 [lux-nuxt3](https://github.com/yangjiakai/lux-nuxt3)
|
||||
|
||||
Nuxt3 简化版 [lux-nuxt3-template](https://github.com/yangjiakai/lux-nuxt3-template)
|
||||
|
||||
## 文档
|
||||
|
||||
- 📖 [中文版文档 1.0 ](https://www.craft.me/s/tAMVv4hUxZIH6G)
|
||||
|
||||
## 📚 特性
|
||||
|
||||
- 📖 [Vue 3.2](https://github.com/vuejs/core)
|
||||
- 📖 [Vite 4.x](https://github.com/vitejs/vite)
|
||||
- 📖 UI Framework [Vuetify 3](https://next.vuetifyjs.com/en/)
|
||||
- 📖 TypeScript
|
||||
- 📦 组件自动导入
|
||||
- 🍍 通过 [Pinia](https://pinia.vuejs.org/)进行状态管理
|
||||
- 📔 使用新的 `<script setup>` 语法
|
||||
- 📚 使用任意的图标集 [Iconify](https://icon-sets.iconify.design/)
|
||||
- ☁️ 零配置部署在 Netlify
|
||||
- 🔑 Firebase 授权
|
||||
- 📈 Echarts, ApexChart
|
||||
- 🧭 Openai, Chatgpt 支持
|
||||
- 🌍 vue-i18n 多语言支持
|
||||
- 📚 virtual-scroller , vuedraggable , perfect-scrollbar
|
||||
- 📝 富文本编辑器
|
||||
- 📇 响应式多平台自适应
|
||||
|
||||
## 📈 项目活跃度
|
||||
|
||||

|
||||
|
||||
## 💬 联络我
|
||||
|
||||
- 邮箱 <a href="mailto:yjkbako@gmail.com">yjkbako@gmail.com</a>
|
||||
- 推特 https://twitter.com/baibaixiang
|
||||
- 微信 <img src='/src/assets/wechat-qrcode.png' alt='DashBoard' width='300' />
|
||||
|
||||
## 💌 预览
|
||||
|
||||
<img src='/src/assets/previews/DashBoard.png' alt='DashBoard' />
|
||||
<img src='/src/assets/previews/TaskBoard.png' alt='ChatGPT' />
|
||||
<img src='/src/assets/previews/DataTable.png' alt='DataTable' />
|
||||
<img src='/src/assets/previews/Todo.png' alt='ChatGPT' />
|
||||
<img src='/src/assets/previews/ChatGPT.png' alt='ChatGPT' />
|
||||
<img src='/src/assets/previews/Card.png' alt='Card' />
|
||||
<img src='/src/assets/previews/Color.png' alt='Color' />
|
||||
<img src='/src/assets/previews/Gradient.png' alt='Gradient' />
|
||||
<img src='/src/assets/previews/Login.png' alt='ChatGPT' />
|
||||
<img src='/src/assets/previews/Unsplash.png' alt='ChatGPT' />
|
||||
<img src='/src/assets/previews/Unsplash2.png' alt='ChatGPT' />
|
||||
|
||||
<br>
|
||||
|
||||
## 📦Pre-packed
|
||||
|
||||
### 🏷️UI 框架
|
||||
|
||||
- [Vuetify3](https://next.vuetifyjs.com/en/) - Vuetify 是一个不要求设计能力的 Vue 界面组件框架,自带了许多自行设计实现的 Vue 组件。
|
||||
|
||||
### 🏷️Icons
|
||||
|
||||
- [Iconify](https://iconify.design) - 使用任意的图标集 [🔍Icônes](https://icones.netlify.app/)
|
||||
- [Pure CSS Icons via UnoCSS](https://github.com/antfu/unocss/tree/main/packages/preset-icons)
|
||||
|
||||
## 目标功能
|
||||
|
||||
- [x] 明暗主题切换 -- 完成
|
||||
- [x] 主题色切换 -- 完成
|
||||
- [x] 中日英三语言切换-- 完成
|
||||
- [x] 整合 ChatGpt-- 完成
|
||||
|
||||
## 目标页面
|
||||
|
||||
### 认证相关
|
||||
|
||||
- [x] 登录 -- 完成
|
||||
- [x] 注册 -- 完成
|
||||
- [x] 验证邮件 -- 完成
|
||||
- [ ] 密码重置 -- 施工中
|
||||
|
||||
### 公共页面
|
||||
|
||||
- [x] 404 -- 完成
|
||||
- [x] 500 -- 施工中
|
||||
- [x] 系统维护 -- 施工中
|
||||
- [x] 常见问题 -- 施工中
|
||||
|
||||
### UI 相关
|
||||
|
||||
- [x] 瀑布流布局 -- 完成
|
||||
- [x] 大数据虚拟列表 -- 完成
|
||||
- [ ] 骨架屏 -- 施工中
|
||||
|
||||
### 功能页面
|
||||
|
||||
- [x] 任务版(拖拽功能) -- 完成
|
||||
- [x] 任务列表() -- 施工中
|
||||
|
||||
### 站点仿写
|
||||
|
||||
- [ ] ......
|
||||
|
||||
### 🏷️ 插件
|
||||
|
||||
- [Vue Router4](https://router.vuejs.org/)
|
||||
- [VueUse](https://github.com/antfu/vueuse) - 非常有用的组合式 API 合集
|
||||
- [VuedDaggable](https://github.com/SortableJS/Vue.Draggable) - 允许进行与数组模型同步的拖拽放置操作
|
||||
- [Vue-Masonry-Wall](https://github.com/DerYeger/yeger/tree/main/packages/vue-masonry-wall) - 是一种 Vue3 响应式,支持 SSR,且零依的的瀑布流布局方案
|
||||
- [Vue-Virtual-Scroller](https://github.com/Akryum/vue-virtual-scroller) - 大数据快速虚拟滚动插件
|
||||
|
||||
## 👻 现在可以试试!
|
||||
|
||||
```
|
||||
git clone https://github.com/yangjiakai/lux-admin-vuetify3.git
|
||||
|
||||
cd lux-admin-vuetify3
|
||||
|
||||
yarn install
|
||||
|
||||
yarn dev
|
||||
```
|
||||
|
||||
## 👻Docker it!
|
||||
|
||||
1. 开发环境构建镜像:
|
||||
|
||||
```
|
||||
docker-compose build dev
|
||||
```
|
||||
|
||||
2. 启动开发环境:
|
||||
|
||||
```
|
||||
docker-compose up dev
|
||||
```
|
||||
|
||||
3. 生产环境构建镜像:
|
||||
|
||||
```
|
||||
docker-compose build app
|
||||
```
|
||||
|
||||
4. 启动生产环境:
|
||||
```
|
||||
docker-compose up app
|
||||
```
|
||||
|
||||
### 🔑 配置 ApiKey
|
||||
|
||||
找到根目录下的`.env.template`文件,去掉`.template`后缀
|
||||
把`VITE_OPENAI_API_KEY`,`VITE_UNSPLASH_ACCESS_KEY`,`VITE_GITHUB_CLIENT_ID`,`VITE_TTS_KEY` , `VITE_TTS_REGION`分别替换成你自己的
|
||||
|
||||
> openai apikey: https://platform.openai.com/account/api-keys
|
||||
|
||||
> unsplash apikey: https://unsplash.com/oauth/applications
|
||||
|
||||
> github apikey: https://github.com/settings/tokens
|
||||
|
||||
> azure textToSpeech : https://speech.microsoft.com/
|
73
resources/auto-imports.d.ts
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
// Generated by 'unplugin-auto-import'
|
||||
export {};
|
||||
declare global {
|
||||
const EffectScope: typeof import("vue")["EffectScope"];
|
||||
const acceptHMRUpdate: typeof import("pinia")["acceptHMRUpdate"];
|
||||
const computed: typeof import("vue")["computed"];
|
||||
const createApp: typeof import("vue")["createApp"];
|
||||
const createPinia: typeof import("pinia")["createPinia"];
|
||||
const customRef: typeof import("vue")["customRef"];
|
||||
const defineAsyncComponent: typeof import("vue")["defineAsyncComponent"];
|
||||
const defineComponent: typeof import("vue")["defineComponent"];
|
||||
const defineStore: typeof import("pinia")["defineStore"];
|
||||
const effectScope: typeof import("vue")["effectScope"];
|
||||
const getActivePinia: typeof import("pinia")["getActivePinia"];
|
||||
const getCurrentInstance: typeof import("vue")["getCurrentInstance"];
|
||||
const getCurrentScope: typeof import("vue")["getCurrentScope"];
|
||||
const h: typeof import("vue")["h"];
|
||||
const inject: typeof import("vue")["inject"];
|
||||
const isProxy: typeof import("vue")["isProxy"];
|
||||
const isReactive: typeof import("vue")["isReactive"];
|
||||
const isReadonly: typeof import("vue")["isReadonly"];
|
||||
const isRef: typeof import("vue")["isRef"];
|
||||
const mapActions: typeof import("pinia")["mapActions"];
|
||||
const mapGetters: typeof import("pinia")["mapGetters"];
|
||||
const mapState: typeof import("pinia")["mapState"];
|
||||
const mapStores: typeof import("pinia")["mapStores"];
|
||||
const mapWritableState: typeof import("pinia")["mapWritableState"];
|
||||
const markRaw: typeof import("vue")["markRaw"];
|
||||
const nextTick: typeof import("vue")["nextTick"];
|
||||
const onActivated: typeof import("vue")["onActivated"];
|
||||
const onBeforeMount: typeof import("vue")["onBeforeMount"];
|
||||
const onBeforeRouteLeave: typeof import("vue-router")["onBeforeRouteLeave"];
|
||||
const onBeforeRouteUpdate: typeof import("vue-router")["onBeforeRouteUpdate"];
|
||||
const onBeforeUnmount: typeof import("vue")["onBeforeUnmount"];
|
||||
const onBeforeUpdate: typeof import("vue")["onBeforeUpdate"];
|
||||
const onDeactivated: typeof import("vue")["onDeactivated"];
|
||||
const onErrorCaptured: typeof import("vue")["onErrorCaptured"];
|
||||
const onMounted: typeof import("vue")["onMounted"];
|
||||
const onRenderTracked: typeof import("vue")["onRenderTracked"];
|
||||
const onRenderTriggered: typeof import("vue")["onRenderTriggered"];
|
||||
const onScopeDispose: typeof import("vue")["onScopeDispose"];
|
||||
const onServerPrefetch: typeof import("vue")["onServerPrefetch"];
|
||||
const onUnmounted: typeof import("vue")["onUnmounted"];
|
||||
const onUpdated: typeof import("vue")["onUpdated"];
|
||||
const provide: typeof import("vue")["provide"];
|
||||
const reactive: typeof import("vue")["reactive"];
|
||||
const readonly: typeof import("vue")["readonly"];
|
||||
const ref: typeof import("vue")["ref"];
|
||||
const resolveComponent: typeof import("vue")["resolveComponent"];
|
||||
const resolveDirective: typeof import("vue")["resolveDirective"];
|
||||
const setActivePinia: typeof import("pinia")["setActivePinia"];
|
||||
const setMapStoreSuffix: typeof import("pinia")["setMapStoreSuffix"];
|
||||
const shallowReactive: typeof import("vue")["shallowReactive"];
|
||||
const shallowReadonly: typeof import("vue")["shallowReadonly"];
|
||||
const shallowRef: typeof import("vue")["shallowRef"];
|
||||
const storeToRefs: typeof import("pinia")["storeToRefs"];
|
||||
const toRaw: typeof import("vue")["toRaw"];
|
||||
const toRef: typeof import("vue")["toRef"];
|
||||
const toRefs: typeof import("vue")["toRefs"];
|
||||
const triggerRef: typeof import("vue")["triggerRef"];
|
||||
const unref: typeof import("vue")["unref"];
|
||||
const useAttrs: typeof import("vue")["useAttrs"];
|
||||
const useCssModule: typeof import("vue")["useCssModule"];
|
||||
const useCssVars: typeof import("vue")["useCssVars"];
|
||||
const useLink: typeof import("vue-router")["useLink"];
|
||||
const useRoute: typeof import("vue-router")["useRoute"];
|
||||
const useRouter: typeof import("vue-router")["useRouter"];
|
||||
const useSlots: typeof import("vue")["useSlots"];
|
||||
const watch: typeof import("vue")["watch"];
|
||||
const watchEffect: typeof import("vue")["watchEffect"];
|
||||
const watchPostEffect: typeof import("vue")["watchPostEffect"];
|
||||
const watchSyncEffect: typeof import("vue")["watchSyncEffect"];
|
||||
}
|
25
resources/docker-compose.yml
Normal file
@@ -0,0 +1,25 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
app:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
ports:
|
||||
- "80:80"
|
||||
|
||||
dev:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.dev
|
||||
volumes:
|
||||
- .:/app
|
||||
- /app/node_modules
|
||||
ports:
|
||||
- "8080:8080"
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
command: npm run dev
|
||||
|
||||
volumes:
|
||||
node_modules:
|
32
resources/index.html
Normal file
@@ -0,0 +1,32 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vuetify-Lux</title>
|
||||
<!-- Google tag (gtag.js) -->
|
||||
<script
|
||||
async
|
||||
src="https://www.googletagmanager.com/gtag/js?id=G-WVWG4VLCL2"
|
||||
></script>
|
||||
<script>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag() {
|
||||
dataLayer.push(arguments);
|
||||
}
|
||||
gtag("js", new Date());
|
||||
|
||||
gtag("config", "G-WVWG4VLCL2");
|
||||
</script>
|
||||
<link
|
||||
href="https://fonts.loli.net/css2?family=Quicksand:wght@300;400;500;600;700&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
2
resources/netlify.toml
Normal file
@@ -0,0 +1,2 @@
|
||||
[build.environment]
|
||||
NODE_VERSION = "21.1.0"
|
68
resources/package.json
Normal file
@@ -0,0 +1,68 @@
|
||||
{
|
||||
"name": "vuetify3-design",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"dev": "vite --mode dev",
|
||||
"build": "vite build --base=/ --mode pro",
|
||||
"preview": "vite preview",
|
||||
"test": "vitest",
|
||||
"test:ui": "vitest --ui",
|
||||
"coverage": "vitest run --coverage"
|
||||
},
|
||||
"dependencies": {
|
||||
"@formkit/auto-animate": "^0.8.2",
|
||||
"@mdi/font": "7.4.47",
|
||||
"@tiptap/pm": "^2.5.9",
|
||||
"@tiptap/starter-kit": "^2.5.9",
|
||||
"@tiptap/vue-3": "^2.5.9",
|
||||
"@vueup/vue-quill": "^1.2.0",
|
||||
"@vueuse/core": "^10.11.0",
|
||||
"@vueuse/integrations": "^10.11.0",
|
||||
"@yeger/vue-masonry-wall": "^5.0.14",
|
||||
"apexcharts": "^3.52.0",
|
||||
"axios": "^1.7.5",
|
||||
"clipboard": "^2.0.11",
|
||||
"echarts": "^5.5.1",
|
||||
"flag-icons": "^7.2.3",
|
||||
"focus-trap": "^7.5.4",
|
||||
"happy-dom": "^14.12.3",
|
||||
"md-editor-v3": "^4.18.0",
|
||||
"microsoft-cognitiveservices-speech-sdk": "^1.38.0",
|
||||
"moment": "^2.30.1",
|
||||
"openai": "^4.55.1",
|
||||
"pinia": "^2.2.1",
|
||||
"pinia-plugin-persist": "^1.0.0",
|
||||
"plantuml-encoder": "^1.4.0",
|
||||
"roboto-fontface": "*",
|
||||
"unsplash-js": "^7.0.19",
|
||||
"vue": "^3.4.36",
|
||||
"vue-echarts": "^7.0.1",
|
||||
"vue-i18n": "^9.13.1",
|
||||
"vue-router": "^4.4.3",
|
||||
"vue-virtual-scroller": "^2.0.0-beta.8",
|
||||
"vue-waterfall-plugin-next": "^2.4.3",
|
||||
"vue3-apexcharts": "^1.5.3",
|
||||
"vue3-lottie": "^3.3.0",
|
||||
"vue3-perfect-scrollbar": "^2.0.0",
|
||||
"vuedraggable": "^4.1.0",
|
||||
"vuetify": "^3.6.14",
|
||||
"webfontloader": "^1.6.28"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@faker-js/faker": "^8.4.1",
|
||||
"@fortawesome/fontawesome-free": "^6.6.0",
|
||||
"@iconify/vue": "^4.1.2",
|
||||
"@types/node": "^22.1.0",
|
||||
"@vitejs/plugin-vue": "^5.1.2",
|
||||
"@vitest/ui": "^2.0.5",
|
||||
"@vue/test-utils": "^2.4.6",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"postcss": "^8.4.41",
|
||||
"sass": "^1.77.8",
|
||||
"tailwindcss": "^3.4.8",
|
||||
"unplugin-auto-import": "^0.18.2",
|
||||
"vite": "^5.4.0",
|
||||
"vite-plugin-vuetify": "^2.0.4",
|
||||
"vitest": "^2.0.5"
|
||||
}
|
||||
}
|
6
resources/postcss.config.js
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
1
resources/public/_redirects
Normal file
@@ -0,0 +1 @@
|
||||
/* /index.html 200
|
BIN
resources/public/favicon.ico
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
resources/public/favicon.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
55
resources/src/App.vue
Normal file
@@ -0,0 +1,55 @@
|
||||
<script setup lang="ts">
|
||||
import UILayout from "@/layouts/UILayout.vue";
|
||||
import LandingLayout from "@/layouts/LandingLayout.vue";
|
||||
import DefaultLayout from "@/layouts/DefaultLayout.vue";
|
||||
import AuthLayout from "@/layouts/AuthLayout.vue";
|
||||
import CustomizationMenu from "@/components/CustomizationMenu.vue";
|
||||
|
||||
import BackToTop from "@/components/common/BackToTop.vue";
|
||||
import Snackbar from "@/components/common/Snackbar.vue";
|
||||
import { useAppStore } from "@/stores/appStore";
|
||||
import { useTheme } from "vuetify";
|
||||
const appStore = useAppStore();
|
||||
const theme = useTheme();
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const isRouterLoaded = computed(() => {
|
||||
if (route.name !== null) return true;
|
||||
return false;
|
||||
});
|
||||
|
||||
const layouts = {
|
||||
default: DefaultLayout,
|
||||
ui: UILayout,
|
||||
landing: LandingLayout,
|
||||
auth: AuthLayout,
|
||||
};
|
||||
|
||||
type LayoutName = "default" | "ui" | "landing" | "auth" | "error";
|
||||
|
||||
const currentLayout = computed(() => {
|
||||
const layoutName = route.meta.layout as LayoutName;
|
||||
if (!layoutName) {
|
||||
return DefaultLayout;
|
||||
}
|
||||
return layouts[layoutName];
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
theme.global.name.value = appStore.theme;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-app>
|
||||
<component :is="currentLayout" v-if="isRouterLoaded">
|
||||
<router-view> </router-view>
|
||||
</component>
|
||||
<CustomizationMenu />
|
||||
<BackToTop />
|
||||
<Snackbar />
|
||||
</v-app>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
67
resources/src/api/aiApi.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import axios from "axios";
|
||||
import { useSnackbarStore } from "@/stores/snackbarStore";
|
||||
import { useChatGPTStore } from "@/stores/chatGPTStore";
|
||||
|
||||
const gptInstance = axios.create({
|
||||
timeout: 100000,
|
||||
});
|
||||
|
||||
gptInstance.interceptors.request.use((config) => {
|
||||
const chatGPTStore = useChatGPTStore();
|
||||
config.baseURL = chatGPTStore.proxyUrl;
|
||||
return config;
|
||||
});
|
||||
|
||||
|
||||
gptInstance.interceptors.response.use(
|
||||
(response) => {
|
||||
return response;
|
||||
},
|
||||
(error) => {
|
||||
const snackbarStore = useSnackbarStore();
|
||||
if (error.response) {
|
||||
const status = error.response.status;
|
||||
const data = error.response.data;
|
||||
snackbarStore.showErrorMessage(data.error);
|
||||
} else {
|
||||
snackbarStore.showErrorMessage("Network Error");
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
// Get all models.
|
||||
export const getModelsApi = (apiKey: string) => {
|
||||
return gptInstance.get("/v1/models", {
|
||||
headers: {
|
||||
Authorization: "Bearer " + apiKey,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// Get account balance information.
|
||||
export const getBalanceApi = (apiKey: string) => {
|
||||
return gptInstance.get("/dashboard/billing/credit_grants", {
|
||||
headers: {
|
||||
Authorization: "Bearer " + apiKey,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// speech-to-text
|
||||
export const createTranscriptionApi = (formData: any, apiKey: string) => {
|
||||
return gptInstance.post("/v1/audio/transcriptions", formData, {
|
||||
headers: {
|
||||
Authorization: "Bearer " + apiKey,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// completions(Stream UnUsed)
|
||||
export const createCompletionApi = (data: any, apiKey: string) => {
|
||||
return gptInstance.post("/v1/chat/completions", data, {
|
||||
headers: {
|
||||
Authorization: "Bearer " + apiKey,
|
||||
},
|
||||
});
|
||||
};
|
39
resources/src/api/githubApi.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import axios from "axios";
|
||||
import { useSnackbarStore } from "@/stores/snackbarStore";
|
||||
const snackbarStore = useSnackbarStore();
|
||||
// change the access key to your own
|
||||
const ACCESS_KEY = import.meta.env.VITE_GITHUB_CLIENT_ID;
|
||||
|
||||
const instance = axios.create({
|
||||
baseURL: "https://api.github.com",
|
||||
timeout: 20000,
|
||||
headers: { Authorization: "Bearer" + " " + ACCESS_KEY },
|
||||
});
|
||||
|
||||
instance.interceptors.response.use(
|
||||
(response) => {
|
||||
return response;
|
||||
},
|
||||
(error) => {
|
||||
if (error.response) {
|
||||
const status = error.response.status;
|
||||
const data = error.response.data;
|
||||
snackbarStore.showErrorMessage(data.message);
|
||||
} else {
|
||||
snackbarStore.showErrorMessage("Network Error");
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
// https://api.github.com/users/yangjiakai/events/public
|
||||
|
||||
// Get public events for a user
|
||||
export const getPublicEventsApi = (username: string) => {
|
||||
return instance.get("/users/" + username + "/events/public");
|
||||
};
|
||||
|
||||
// Get public events for a network of repositories
|
||||
export const getPublicEventsForNetworkApi = (username: string) => {
|
||||
return instance.get("/networks/" + username + "/events");
|
||||
};
|
33
resources/src/api/googleApi.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import axios from "axios";
|
||||
|
||||
export const textToSpeech = async () => {
|
||||
const googleInstance = axios.create({
|
||||
baseURL: "https://us-central1-texttospeech.googleapis.com",
|
||||
timeout: 100000,
|
||||
});
|
||||
|
||||
const res = await googleInstance.post(
|
||||
"/v1/chat/completions/text:synthesize",
|
||||
{
|
||||
audioConfig: {
|
||||
audioEncoding: "LINEAR16",
|
||||
effectsProfileId: ["small-bluetooth-speaker-class-device"],
|
||||
pitch: 0,
|
||||
speakingRate: 1,
|
||||
},
|
||||
input: {
|
||||
text: "Google Cloud Text-to-Speech enables developers to synthesize natural-sounding speech with 100+ voices, available in multiple languages and variants. It applies DeepMind’s groundbreaking research in WaveNet and Google’s powerful neural networks to deliver the highest fidelity possible. As an easy-to-use API, you can create lifelike interactions with your users, across many applications and devices.",
|
||||
},
|
||||
voice: {
|
||||
languageCode: "en-US",
|
||||
name: "en-US-Neural2-J",
|
||||
},
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"x-goog-api-key": "AIzaSyBSXdkeyAvIZX5n_bj4KsqSjJf1W-_TfCntvk",
|
||||
},
|
||||
}
|
||||
);
|
||||
};
|
39
resources/src/api/stableDiffusionApi.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
// import Axios library
|
||||
import axios from "axios";
|
||||
// Set default validation status for Axios
|
||||
axios.defaults.validateStatus = function (status) {
|
||||
// Return true if status is between 200 and 300 inclusive
|
||||
return status >= 200 && status < 300;
|
||||
};
|
||||
// Create a new Axios instance with base URL and timeout
|
||||
const diffusion = axios.create({
|
||||
baseURL: "/sdApi",
|
||||
// baseURL: 'http://127.0.0.1:7861',
|
||||
timeout: 100000,
|
||||
});
|
||||
|
||||
// 添加请求拦截器
|
||||
diffusion.interceptors.request.use();
|
||||
|
||||
// 添加响应拦截器
|
||||
diffusion.interceptors.response.use();
|
||||
|
||||
//getmodels
|
||||
|
||||
//txt2img
|
||||
export const txt2imgApi = (data: any) => {
|
||||
return diffusion.post("/sdapi/v1/txt2img", data);
|
||||
};
|
||||
//img2img
|
||||
export const img2imgApi = (data: any) => {
|
||||
return diffusion.post("/sdapi/v1/img2img", data);
|
||||
};
|
||||
|
||||
//getProgeress
|
||||
export const getProgressApi = (data: any) => {
|
||||
return diffusion.get("/sdapi/v1/progress");
|
||||
};
|
||||
//getSamplers
|
||||
export const getSamplersApi = () => {
|
||||
return diffusion.get("/sdapi/v1/samplers");
|
||||
};
|
160
resources/src/api/unsplashApi.ts
Normal file
@@ -0,0 +1,160 @@
|
||||
import axios from "axios";
|
||||
import { useSnackbarStore } from "@/stores/snackbarStore";
|
||||
const snackbarStore = useSnackbarStore();
|
||||
// change the access key to your own
|
||||
const ACCESS_KEY = import.meta.env.VITE_UNSPLASH_ACCESS_KEY;
|
||||
|
||||
const instance = axios.create({
|
||||
baseURL: "https://api.unsplash.com",
|
||||
timeout: 20000,
|
||||
headers: { Authorization: "Client-ID" + " " + ACCESS_KEY },
|
||||
});
|
||||
|
||||
instance.interceptors.response.use(
|
||||
(response) => {
|
||||
return response;
|
||||
},
|
||||
(error) => {
|
||||
if (error.response) {
|
||||
const status = error.response.status;
|
||||
const data = error.response.data;
|
||||
snackbarStore.showErrorMessage(data.errors[0]);
|
||||
} else {
|
||||
snackbarStore.showErrorMessage("Network Error");
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
interface Query {
|
||||
page?: number;
|
||||
per_page?: number;
|
||||
}
|
||||
|
||||
// List photos 图片一览
|
||||
export const getPhotosApi = (query?: Query) => {
|
||||
return instance.get("/photos/", { params: query });
|
||||
};
|
||||
|
||||
// Get a photo 获取图片信息
|
||||
export const getPhotoApi = (id: string) => {
|
||||
return instance.get("/photos/" + id);
|
||||
};
|
||||
|
||||
// Get a random photo 获取一张随机图片
|
||||
export const getRandomPhotoApi = () => {
|
||||
return instance.get("/photos/random");
|
||||
};
|
||||
|
||||
// Get a photo’s statistics 获取照片的统计数据
|
||||
export const getPhotoStatisticsApi = (id: string) => {
|
||||
return instance.get("/photos/" + id + "/statistics");
|
||||
};
|
||||
|
||||
// Get a photo’s related 获取照片的相关照片
|
||||
export const getPhotoRelatedApi = (id: string) => {
|
||||
return instance.get("/photos/" + id + "/related");
|
||||
};
|
||||
|
||||
// Track a photo download
|
||||
// Update a photo
|
||||
// Like a photo
|
||||
// Unlike a photo
|
||||
|
||||
// Topic
|
||||
// List topics
|
||||
export const getTopicsApi = (query?: Query) => {
|
||||
return instance.get("/topics", { params: query });
|
||||
};
|
||||
|
||||
// Get a topic
|
||||
export const getTopicApi = (id_or_slug: string | string[]) => {
|
||||
return instance.get("/topics/" + id_or_slug);
|
||||
};
|
||||
|
||||
// Get a topic’s photos
|
||||
export const getTopicPhotosApi = (
|
||||
id_or_slug: string | string[],
|
||||
query?: Query
|
||||
) => {
|
||||
return instance.get("/topics/" + id_or_slug + "/photos", { params: query });
|
||||
};
|
||||
|
||||
// Get a user
|
||||
export const getUserApi = (username: string | string[]) => {
|
||||
return instance.get("/users/" + username);
|
||||
};
|
||||
// Get a user’s portfolio
|
||||
export const getUserPortfolioApi = (username: string | string[]) => {
|
||||
return instance.get("/users/" + username + "/portfolio");
|
||||
};
|
||||
// List a user’s photos
|
||||
export const getUserPhotosApi = (
|
||||
username: string | string[],
|
||||
query?: Query
|
||||
) => {
|
||||
return instance.get("/users/" + username + "/photos", { params: query });
|
||||
};
|
||||
// List a user’s liked photos
|
||||
export const getUserLikesApi = (username: string | string[], query?: Query) => {
|
||||
return instance.get("/users/" + username + "/likes", { params: query });
|
||||
};
|
||||
// List a user’s collections
|
||||
export const getUserCollectionsApi = (
|
||||
username: string | string[],
|
||||
query?: Query
|
||||
) => {
|
||||
return instance.get("/users/" + username + "/collections", { params: query });
|
||||
};
|
||||
// Get a user’s statistics
|
||||
export const getUserStatisticsApi = (username: string | string[]) => {
|
||||
return instance.get("/users/" + username + "/statistics");
|
||||
};
|
||||
|
||||
// Collections 图集
|
||||
// List collections 图集一览
|
||||
export const getCollectionsApi = (query?: Query) => {
|
||||
return instance.get("/collections", { params: query });
|
||||
};
|
||||
|
||||
// Get a collection 获取图集信息
|
||||
export const getCollectionApi = (id: string | string[]) => {
|
||||
return instance.get("/collections/" + id);
|
||||
};
|
||||
// Get a collection’s photos 获取该图集下所有图片
|
||||
export const getCollectionPhotosApi = (
|
||||
id: string | string[],
|
||||
query?: Query
|
||||
) => {
|
||||
return instance.get("/collections/" + id + "/photos", { params: query });
|
||||
};
|
||||
// List a collection’s related collections 获取该图集相关联图集
|
||||
export const getCollectionRelatedApi = (id: string | string[]) => {
|
||||
return instance.get("/collections/" + id + "/related");
|
||||
};
|
||||
// Create a new collection 新增图集
|
||||
// Update an existing collection 更新现存图集
|
||||
// Delete a collection 删除某个图集
|
||||
// Add a photo to a collection 添加图片到图集
|
||||
// Remove a photo from a collection 从图集删除图片
|
||||
|
||||
// Search
|
||||
// Search All
|
||||
export const searchAllApi = (query?: Query) => {
|
||||
return instance.get("/search", { params: query });
|
||||
};
|
||||
|
||||
// Search photos
|
||||
export const searchPhotosApi = (query?: Query) => {
|
||||
return instance.get("/search/photos", { params: query });
|
||||
};
|
||||
|
||||
// Search collections
|
||||
export const searchCollectionsApi = (query?: Query) => {
|
||||
return instance.get("/search/collections", { params: query });
|
||||
};
|
||||
|
||||
// Search users
|
||||
export const searchUsersApi = (query?: Query) => {
|
||||
return instance.get("/search/users", { params: query });
|
||||
};
|
1
resources/src/assets/images/404.svg
Normal file
After Width: | Height: | Size: 17 KiB |
1
resources/src/assets/images/500.svg
Normal file
After Width: | Height: | Size: 41 KiB |
BIN
resources/src/assets/images/avatars/avatar_assistant.jpg
Normal file
After Width: | Height: | Size: 7.5 KiB |
BIN
resources/src/assets/images/avatars/avatar_user.jpg
Normal file
After Width: | Height: | Size: 77 KiB |
BIN
resources/src/assets/images/card2/yoimiya.png
Normal file
After Width: | Height: | Size: 248 KiB |
BIN
resources/src/assets/images/card2/yoimiya_bg.jpg
Normal file
After Width: | Height: | Size: 520 KiB |
BIN
resources/src/assets/images/chat-bg-2.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
10
resources/src/assets/images/svg1.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||
<rect x="0" y="0" width="100" height="100" fill="purple"/>
|
||||
<circle cx="50" cy="50" r="30" fill="white"/>
|
||||
<circle cx="40" cy="40" r="4" fill="black"/>
|
||||
<circle cx="60" cy="40" r="4" fill="black"/>
|
||||
<path d="M 40 60 Q 50 70, 60 60" stroke="black" stroke-width="2" fill="none"/>
|
||||
<polygon points="90,90 80,70 70,90" fill="black"/>
|
||||
<rect x="78" y="65" width="2" height="25" fill="black" transform="rotate(-45, 79, 65)"/>
|
||||
<circle cx="79" cy="65" r="3" fill="white"/>
|
||||
</svg>
|
After Width: | Height: | Size: 545 B |
60
resources/src/assets/loading.svg
Normal file
@@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="margin: auto; background: rgb(255, 255, 255); display: block; shape-rendering: auto;" width="200px" height="200px" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid">
|
||||
<g transform="translate(80,50)">
|
||||
<g transform="rotate(0)">
|
||||
<circle cx="0" cy="0" r="6" fill="#72c9ff" fill-opacity="1">
|
||||
<animateTransform attributeName="transform" type="scale" begin="-0.875s" values="1.5 1.5;1 1" keyTimes="0;1" dur="1s" repeatCount="indefinite"></animateTransform>
|
||||
<animate attributeName="fill-opacity" keyTimes="0;1" dur="1s" repeatCount="indefinite" values="1;0" begin="-0.875s"></animate>
|
||||
</circle>
|
||||
</g>
|
||||
</g><g transform="translate(71.21320343559643,71.21320343559643)">
|
||||
<g transform="rotate(45)">
|
||||
<circle cx="0" cy="0" r="6" fill="#72c9ff" fill-opacity="0.875">
|
||||
<animateTransform attributeName="transform" type="scale" begin="-0.75s" values="1.5 1.5;1 1" keyTimes="0;1" dur="1s" repeatCount="indefinite"></animateTransform>
|
||||
<animate attributeName="fill-opacity" keyTimes="0;1" dur="1s" repeatCount="indefinite" values="1;0" begin="-0.75s"></animate>
|
||||
</circle>
|
||||
</g>
|
||||
</g><g transform="translate(50,80)">
|
||||
<g transform="rotate(90)">
|
||||
<circle cx="0" cy="0" r="6" fill="#72c9ff" fill-opacity="0.75">
|
||||
<animateTransform attributeName="transform" type="scale" begin="-0.625s" values="1.5 1.5;1 1" keyTimes="0;1" dur="1s" repeatCount="indefinite"></animateTransform>
|
||||
<animate attributeName="fill-opacity" keyTimes="0;1" dur="1s" repeatCount="indefinite" values="1;0" begin="-0.625s"></animate>
|
||||
</circle>
|
||||
</g>
|
||||
</g><g transform="translate(28.786796564403577,71.21320343559643)">
|
||||
<g transform="rotate(135)">
|
||||
<circle cx="0" cy="0" r="6" fill="#72c9ff" fill-opacity="0.625">
|
||||
<animateTransform attributeName="transform" type="scale" begin="-0.5s" values="1.5 1.5;1 1" keyTimes="0;1" dur="1s" repeatCount="indefinite"></animateTransform>
|
||||
<animate attributeName="fill-opacity" keyTimes="0;1" dur="1s" repeatCount="indefinite" values="1;0" begin="-0.5s"></animate>
|
||||
</circle>
|
||||
</g>
|
||||
</g><g transform="translate(20,50.00000000000001)">
|
||||
<g transform="rotate(180)">
|
||||
<circle cx="0" cy="0" r="6" fill="#72c9ff" fill-opacity="0.5">
|
||||
<animateTransform attributeName="transform" type="scale" begin="-0.375s" values="1.5 1.5;1 1" keyTimes="0;1" dur="1s" repeatCount="indefinite"></animateTransform>
|
||||
<animate attributeName="fill-opacity" keyTimes="0;1" dur="1s" repeatCount="indefinite" values="1;0" begin="-0.375s"></animate>
|
||||
</circle>
|
||||
</g>
|
||||
</g><g transform="translate(28.78679656440357,28.786796564403577)">
|
||||
<g transform="rotate(225)">
|
||||
<circle cx="0" cy="0" r="6" fill="#72c9ff" fill-opacity="0.375">
|
||||
<animateTransform attributeName="transform" type="scale" begin="-0.25s" values="1.5 1.5;1 1" keyTimes="0;1" dur="1s" repeatCount="indefinite"></animateTransform>
|
||||
<animate attributeName="fill-opacity" keyTimes="0;1" dur="1s" repeatCount="indefinite" values="1;0" begin="-0.25s"></animate>
|
||||
</circle>
|
||||
</g>
|
||||
</g><g transform="translate(49.99999999999999,20)">
|
||||
<g transform="rotate(270)">
|
||||
<circle cx="0" cy="0" r="6" fill="#72c9ff" fill-opacity="0.25">
|
||||
<animateTransform attributeName="transform" type="scale" begin="-0.125s" values="1.5 1.5;1 1" keyTimes="0;1" dur="1s" repeatCount="indefinite"></animateTransform>
|
||||
<animate attributeName="fill-opacity" keyTimes="0;1" dur="1s" repeatCount="indefinite" values="1;0" begin="-0.125s"></animate>
|
||||
</circle>
|
||||
</g>
|
||||
</g><g transform="translate(71.21320343559643,28.78679656440357)">
|
||||
<g transform="rotate(315)">
|
||||
<circle cx="0" cy="0" r="6" fill="#72c9ff" fill-opacity="0.125">
|
||||
<animateTransform attributeName="transform" type="scale" begin="0s" values="1.5 1.5;1 1" keyTimes="0;1" dur="1s" repeatCount="indefinite"></animateTransform>
|
||||
<animate attributeName="fill-opacity" keyTimes="0;1" dur="1s" repeatCount="indefinite" values="1;0" begin="0s"></animate>
|
||||
</circle>
|
||||
</g>
|
||||
</g>
|
||||
<!-- [ldio] generated by https://loading.io/ --></svg>
|
After Width: | Height: | Size: 3.9 KiB |
BIN
resources/src/assets/logo.back.png
Normal file
After Width: | Height: | Size: 12 KiB |
6
resources/src/assets/logo.back.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M261.126 140.65L164.624 307.732L256.001 466L377.028 256.5L498.001 47H315.192L261.126 140.65Z" fill="#1697F6"/>
|
||||
<path d="M135.027 256.5L141.365 267.518L231.64 111.178L268.731 47H256H14L135.027 256.5Z" fill="#AEDDFF"/>
|
||||
<path d="M315.191 47C360.935 197.446 256 466 256 466L164.624 307.732L315.191 47Z" fill="#1867C0"/>
|
||||
<path d="M268.731 47C76.0026 47 141.366 267.518 141.366 267.518L268.731 47Z" fill="#7BC6FF"/>
|
||||
</svg>
|
After Width: | Height: | Size: 526 B |
BIN
resources/src/assets/logo.png
Normal file
After Width: | Height: | Size: 12 KiB |
65
resources/src/assets/logo.svg
Normal file
@@ -0,0 +1,65 @@
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="100%" viewBox="0 0 292 350" enable-background="new 0 0 292 350" xml:space="preserve">
|
||||
<path fill="#FEFFFF" opacity="1.000000" stroke="none"
|
||||
d="
|
||||
M1.000000,128.000000
|
||||
C1.000000,85.349754 1.000000,43.199505 1.000000,1.024629
|
||||
C98.279007,1.024629 195.558014,1.024629 292.918518,1.024629
|
||||
C292.918518,117.566864 292.918518,234.133804 292.918518,350.850372
|
||||
C195.666840,350.850372 98.333450,350.850372 1.000000,350.850372
|
||||
C1.000000,276.775421 1.000000,202.637711 1.000000,128.000000
|
||||
M60.945122,103.841530
|
||||
C42.713490,103.841530 24.481857,103.841530 5.292197,103.841530
|
||||
C52.947048,186.459473 100.087868,268.186249 147.480728,350.349976
|
||||
C195.001831,268.033112 242.189346,186.294067 289.779907,103.856873
|
||||
C270.104889,103.856873 251.620804,103.856873 233.073532,103.150719
|
||||
C233.940903,101.371254 234.704636,99.532021 235.691330,97.821396
|
||||
C248.489014,75.634048 261.324280,53.468391 274.136688,31.289537
|
||||
C279.085297,22.723295 284.000122,14.137545 289.584625,4.422431
|
||||
C286.640228,5.343472 284.933716,5.822242 283.264526,6.407475
|
||||
C238.979431,21.934256 194.704971,37.491520 150.390533,52.934074
|
||||
C148.682556,53.529266 146.411621,53.569023 144.713531,52.976955
|
||||
C105.802299,39.409748 66.936913,25.711136 28.055904,12.057143
|
||||
C20.953766,9.563058 13.820872,7.156544 5.668715,4.354370
|
||||
C6.763332,6.406374 7.201184,7.288102 7.692324,8.139076
|
||||
C24.146460,36.648350 40.631691,65.139748 57.028324,93.682045
|
||||
C58.893276,96.928444 60.264931,100.458237 60.945122,103.841530
|
||||
z"/>
|
||||
<path fill="#2064EA" opacity="1.000000" stroke="none"
|
||||
d="
|
||||
M233.136719,103.856873
|
||||
C251.620804,103.856873 270.104889,103.856873 289.779907,103.856873
|
||||
C242.189346,186.294067 195.001831,268.033112 147.480728,350.349976
|
||||
C100.087868,268.186249 52.947048,186.459473 5.292197,103.841530
|
||||
C24.481857,103.841530 42.713490,103.841530 61.662327,103.928421
|
||||
C62.558861,104.006554 62.738190,103.997810 63.065762,104.306396
|
||||
C91.225380,153.133133 119.236748,201.642517 147.536011,250.650467
|
||||
C175.919067,201.411285 203.997498,152.700562 232.346878,104.002457
|
||||
C232.790787,103.962349 232.963760,103.909615 233.136719,103.856873
|
||||
z"/>
|
||||
<path fill="#7486FB" opacity="1.000000" stroke="none"
|
||||
d="
|
||||
M233.105133,103.503799
|
||||
C232.963760,103.909615 232.790787,103.962349 231.872528,103.984970
|
||||
C175.057327,103.966263 118.987419,103.977661 62.917519,103.989059
|
||||
C62.738190,103.997810 62.558861,104.006554 62.122425,103.935959
|
||||
C60.264931,100.458237 58.893276,96.928444 57.028324,93.682045
|
||||
C40.631691,65.139748 24.146460,36.648350 7.692324,8.139076
|
||||
C7.201184,7.288102 6.763332,6.406374 5.668715,4.354370
|
||||
C13.820872,7.156544 20.953766,9.563058 28.055904,12.057143
|
||||
C66.936913,25.711136 105.802299,39.409748 144.713531,52.976955
|
||||
C146.411621,53.569023 148.682556,53.529266 150.390533,52.934074
|
||||
C194.704971,37.491520 238.979431,21.934256 283.264526,6.407475
|
||||
C284.933716,5.822242 286.640228,5.343472 289.584625,4.422431
|
||||
C284.000122,14.137545 279.085297,22.723295 274.136688,31.289537
|
||||
C261.324280,53.468391 248.489014,75.634048 235.691330,97.821396
|
||||
C234.704636,99.532021 233.940903,101.371254 233.105133,103.503799
|
||||
z"/>
|
||||
<path fill="#FEFEFF" opacity="1.000000" stroke="none"
|
||||
d="
|
||||
M63.065762,104.306396
|
||||
C118.987419,103.977661 175.057327,103.966263 231.601578,103.972351
|
||||
C203.997498,152.700562 175.919067,201.411285 147.536011,250.650467
|
||||
C119.236748,201.642517 91.225380,153.133133 63.065762,104.306396
|
||||
z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 3.5 KiB |
24
resources/src/assets/logo1.svg
Normal file
@@ -0,0 +1,24 @@
|
||||
<svg y="558.665" x="988" viewBox="0 0 356 52" height="52" width="356" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Layer_1">
|
||||
<title>Layer 1</title>
|
||||
<metadata id="svg_28">image/svg+xml</metadata>
|
||||
<g id="svg_3" transform="translate(0 0)">
|
||||
<image id="svg_18" x="-333.96937" y="-358.11919" height="0" width="0"/>
|
||||
<g id="svg_17" transform="translate(0 0)" font-style="normal" font-weight="600" font-size="32px" font-family="Karma" text-anchor="middle"/>
|
||||
<g id="svg_5" transform="translate(0 0)" font-style="normal" font-weight="400" font-size="72px" font-family="Megrim" text-anchor="middle">
|
||||
<path id="svg_16" d="m32.76,53.35921l-16.38,38.6568l-16.38,-38.6568l2.09664,0l14.28336,33.61176l14.28336,-33.61176l2.09664,0z" stroke-width="0" stroke-miterlimit="2" fill="#ec9126" stroke="#ec9126" transform="translate(0 -357) translate(0 304.296)"/>
|
||||
<path id="svg_15" d="m41.41887,53.35921l0,19.0008q0,9.76248 3.07944,13.7592q3.14496,3.9312 8.58312,3.9312q5.43816,0 8.5176,-3.9312q3.14496,-3.99672 3.14496,-13.7592l0,-19.0008l1.9656,0l0,19.0008q0,19.656 -13.62816,19.656q-13.62816,0 -13.62816,-19.656l0,-19.0008l1.9656,0z" stroke-width="0" stroke-miterlimit="2" fill="#e58b25" stroke="#e58b25" transform="translate(0 -357) translate(0 304.296)"/>
|
||||
<path id="svg_14" d="m103.92086,87.49513q-3.6036,4.52088 -11.7936,4.52088q-8.19,0 -11.72808,-4.65192q-3.53808,-4.65192 -3.53808,-15.00408q0,-10.35216 3.53808,-15.00408q3.53808,-4.65192 11.72808,-4.65192q9.5004,0 12.90744,6.2244l-16.0524,13.4316l-3.07944,0l16.57656,-13.82472q-2.88288,-3.86568 -10.35216,-3.86568q-3.276,0 -5.50368,0.72072q-2.22768,0.6552 -4.12776,2.55528q-1.83456,1.83456 -2.75184,5.43816q-0.91728,3.53808 -0.91728,8.97624q0,5.43816 0.91728,9.04176q0.91728,3.53808 2.75184,5.43816q1.90008,1.83456 4.12776,2.55528q2.22768,0.6552 5.50368,0.6552q7.46928,0 10.35216,-3.86568l1.44144,1.3104z" stroke-width="0" stroke-miterlimit="2" fill="#dd8524" stroke="#dd8524" transform="translate(0 -357) translate(0 304.296)"/>
|
||||
<path id="svg_13" d="m139.02831,53.35921l0,1.9656l-13.7592,0l0,36.036l-1.96561,0l0,-36.036l-13.7592,0l0,-1.9656l29.48401,0z" stroke-width="0" stroke-miterlimit="2" fill="#d67f23" stroke="#d67f23" transform="translate(0 -357) translate(0 304.296)"/>
|
||||
<path id="svg_12" d="m146.57848,53.35921l1.9656,0l0,38.0016l-1.9656,0l0,-38.0016z" stroke-width="0" stroke-miterlimit="2" fill="#ce7922" stroke="#ce7922" transform="translate(0 -357) translate(0 304.296)"/>
|
||||
<path id="svg_11" d="m160.38372,91.36081l0,-19.0008q0,-10.35216 3.53808,-15.00408q3.60361,-4.65192 11.72809,-4.65192q9.5004,0 12.90744,6.2244l-13.69369,11.466l13.95577,0l0,1.9656l-19.39393,0l16.57657,-13.82472q-2.88288,-3.86568 -10.35216,-3.86568q-3.27601,0 -5.50368,0.72072q-2.22769,0.6552 -4.12777,2.55528q-1.83456,1.83456 -2.75184,5.43816q-0.91728,3.53808 -0.91728,8.97624l0,19.0008l-1.9656,0z" stroke-width="0" stroke-miterlimit="2" fill="#c77321" stroke="#c77321" transform="translate(0 -357) translate(0 304.296)"/>
|
||||
<path id="svg_10" d="m200.95496,53.35921l25.35624,0l-15.39721,36.3636l0,14.742l-1.9656,0l0,-14.742l-15.39719,-36.3636l2.09664,0l14.28336,33.61176l13.4316,-31.64616l-22.40784,0l0,-1.9656z" stroke-width="0" stroke-miterlimit="2" fill="#c06c20" stroke="#c06c20" transform="translate(0 -357) translate(0 304.296)"/>
|
||||
<path id="svg_9" d="m230.71435,97.25761l0,-1.9656l26.208,0l0,1.9656l-26.208,0z" stroke-width="0" stroke-miterlimit="2" fill="#b8661f" stroke="#b8661f" transform="translate(0 -357) translate(0 304.296)"/>
|
||||
<path id="svg_8" d="m288.36888,89.39521l0,1.9656l-21.88368,0l0,-38.0016l1.96559,0l0,36.036l19.91809,0z" stroke-width="0" stroke-miterlimit="2" fill="#b1601e" stroke="#b1601e" transform="translate(0 -357) translate(0 304.296)"/>
|
||||
<path id="svg_7" d="m297.22841,53.35921l0,19.0008q0,9.76248 3.07944,13.7592q3.14496,3.9312 8.58312,3.9312q5.43816,0 8.51761,-3.9312q3.14496,-3.99672 3.14496,-13.7592l0,-19.0008l1.96559,0l0,19.0008q0,19.656 -13.62816,19.656q-13.62816,0 -13.62816,-19.656l0,-19.0008l1.9656,0z" stroke-width="0" stroke-miterlimit="2" fill="#a95a1d" stroke="#a95a1d" transform="translate(0 -357) translate(0 304.296)"/>
|
||||
<path id="svg_6" d="m344.26767,70.85305l11.79361,20.50776l-2.2932,0l-10.67976,-18.47664l-10.67977,18.47664l-2.2932,0l11.7936,-20.50776l-10.09007,-17.49384l2.2932,0l8.97624,15.52824l8.97623,-15.52824l2.2932,0l-10.09008,17.49384z" stroke-width="0" stroke-miterlimit="2" fill="#a2541c" stroke="#a2541c" transform="translate(0 -357) translate(0 304.296)"/>
|
||||
</g>
|
||||
<image id="svg_4" x="-333.96937" y="-358.11919" height="0" width="0"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 4.5 KiB |
32
resources/src/assets/logo2.svg
Normal file
@@ -0,0 +1,32 @@
|
||||
<svg y="552.665" x="1007.5" version="1.1" width="317" height="64" viewBox="0 0 317 64" id="svg78192" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<metadata id="metadata78198">image/svg+xml</metadata>
|
||||
|
||||
<linearGradient id="3d_gradient2-logo-682f3222-452d-46d2-bf30-c93cee4278d2" x1="30%" y1="120%" x2="-10%" y2="30%" spreadMethod="pad">
|
||||
<stop offset="0%" stop-color="#ffffff" id="stop78173"/>
|
||||
<stop offset="100%" stop-color="#000000" id="stop78175"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="3d_gradient3-logo-682f3222-452d-46d2-bf30-c93cee4278d2" x1="30%" y1="120%" x2="-10%" y2="30%" spreadMethod="pad" gradientTransform="rotate(-30)">
|
||||
<stop offset="0%" stop-color="#ffffff" id="stop78178"/>
|
||||
<stop offset="50%" stop-color="#cccccc" id="stop78180"/>
|
||||
<stop offset="100%" stop-color="#000000" id="stop78182"/>
|
||||
</linearGradient>
|
||||
<g id="logo-group">
|
||||
<g id="logo-center">
|
||||
<image width="0" height="0" y="-351.98328" x="-353.51215" id="icon_container"/>
|
||||
<g text-anchor="middle" font-family="Orienta" font-size="32px" font-weight="400" font-style="normal" transform="translate(0 0)" id="slogan"/>
|
||||
<g text-anchor="middle" font-family="'Brandmark Didone 1'" font-size="72px" font-weight="normal" font-style="normal" transform="translate(0 0)" id="title">
|
||||
<path transform="translate(-353.512 -351.983) translate(0 280.783)" stroke="#3a456c" fill="#3a456c" stroke-miterlimit="2" stroke-width="0" d="m385.6421,97.05592l-11.07,32.454l-9.342,-27.378c-1.08,-3.186 -3.83399,-5.076 -7.12799,-5.076l-4.59,0l0,0.27c2.53799,0 3.99599,1.89 4.91399,4.32l11.34,33.588l3.94201,0l13.01399,-38.178l-1.08,0z" id="path78201"/>
|
||||
<path transform="translate(-353.512 -351.983) translate(0 280.783)" stroke="#465075" fill="#465075" stroke-miterlimit="2" stroke-width="0" d="m421.85332,97.05592l0,23.598c0,7.344 -6.534,13.284 -12.69,13.284c-6.156,0 -9.666,-5.94 -9.666,-13.284l0,-23.598l-10.692,0l0,0.27c2.7,0 4.86,2.16 4.86,4.806l0,18.522c0,8.046 6.534,14.58 14.634,14.58c8.1,0 14.634,-6.534 14.634,-14.58l0,-23.598l-1.08,0z" id="path78203"/>
|
||||
<path transform="translate(-353.512 -351.983) translate(0 280.783)" stroke="#525c7e" fill="#525c7e" stroke-miterlimit="2" stroke-width="0" d="m438.63129,98.13592l16.308,0l0,-1.08l-27,0l0,0.27c2.7,0 4.86001,2.16 4.86001,4.806l0,32.67l22.13999,0l0,-1.08l-16.308,0l0,-21.276l15.012,0l0,-1.08l-15.012,0l0,-13.23z" id="path78205"/>
|
||||
<path transform="translate(-353.512 -351.983) translate(0 280.783)" stroke="#5d6786" fill="#5d6786" stroke-miterlimit="2" stroke-width="0" d="m486.45842,98.13592l0,-1.08l-29.376,0l0,1.08l11.77199,0l0,36.666l5.832,0l0,-36.666l11.77201,0z" id="path78207"/>
|
||||
<path transform="translate(-353.512 -351.983) translate(0 280.783)" stroke="#69728f" fill="#69728f" stroke-miterlimit="2" stroke-width="0" d="m498.85395,97.05592l-10.692,0l0,0.27c2.7,0 4.86,2.16 4.86,4.806l0,32.67l5.832,0l0,-37.746z" id="path78209"/>
|
||||
<path transform="translate(-353.512 -351.983) translate(0 280.783)" stroke="#757e98" fill="#757e98" stroke-miterlimit="2" stroke-width="0" d="m515.09614,98.13592l16.308,0l0,-1.08l-27,0l0,0.27c2.7,0 4.86,2.16 4.86,4.806l0,32.67l5.832,0l0,-22.302l14.256,0l0,-1.08l-14.256,0l0,-13.284z" id="path78211"/>
|
||||
<path transform="translate(-353.512 -351.983) translate(0 280.783)" stroke="#8189a1" fill="#8189a1" stroke-miterlimit="2" stroke-width="0" d="m602.68245,82.52992c-0.054,-0.972 -0.162,-1.944 -0.432,-2.862c-0.54,-1.89 -1.62,-3.618 -2.97,-5.022c-1.404,-1.404 -3.186,-2.43 -5.076,-2.97c-3.726,-1.026 -7.668,-0.324 -10.854,1.458c-1.566,0.918 -2.97,2.052 -4.158,3.402c-0.594,0.648 -1.134,1.404 -1.566,2.106c-0.486,0.756 -0.972,1.458 -1.404,2.214c-1.944,2.808 -3.78,5.724 -5.616,8.64l-2.754,4.32c-0.648,1.134 -1.35,2.16 -1.998,3.294l-11.826,18.522l-10.908,-18.576l-10.26,0l0,0.27c2.7,0 4.752,2.16 6.318,4.86l-0.054,0l9.504,16.146l0,1.08l5.832,0l0,-2.808l0.054,-0.054l12.42,-19.494c0.594,-0.864 1.188,-1.782 1.782,-2.7l2.808,-4.32c1.89,-2.862 3.726,-5.724 5.508,-8.64l1.404,-2.214c0.432,-0.702 0.972,-1.35 1.566,-1.89c1.134,-1.188 2.538,-2.106 4.05,-2.7c0.756,-0.324 1.512,-0.54 2.322,-0.648c0.81,-0.162 1.566,-0.216 2.376,-0.216c0.756,0 1.566,0.108 2.322,0.216c0.756,0.162 1.512,0.378 2.16,0.648c2.808,1.08 4.968,3.294 6.048,6.048c0.54,1.35 0.81,2.862 0.648,4.428c0,0.756 -0.162,1.512 -0.432,2.214c-0.216,0.702 -0.594,1.404 -1.026,1.998c-0.486,0.648 -1.08,1.188 -1.674,1.674c-0.648,0.432 -1.404,0.81 -2.16,1.08c-1.512,0.486 -3.24,0.432 -4.752,-0.108c-1.458,-0.54 -2.754,-1.62 -3.51,-3.024c-0.81,-1.35 -1.026,-3.024 -0.648,-4.59l-1.08,-0.216c-0.432,1.782 -0.162,3.726 0.756,5.346c0.864,1.62 2.376,2.916 4.158,3.51c1.728,0.594 3.618,0.594 5.4,0.162c0.864,-0.216 1.728,-0.54 2.538,-0.972c0.81,-0.486 1.566,-1.026 2.214,-1.728c0.702,-0.648 1.242,-1.458 1.728,-2.268c0.216,-0.432 0.432,-0.864 0.594,-1.35c0.162,-0.432 0.27,-0.918 0.378,-1.35c0.216,-0.972 0.324,-1.944 0.27,-2.916zm-51.948,38.016l-2.106,0l0,14.256l5.832,0l0,-10.476c0,-2.106 -1.674,-3.78 -3.726,-3.78z" id="path78213"/>
|
||||
<path transform="translate(-353.512 -351.983) translate(0 280.783)" stroke="#8c94a9" fill="#8c94a9" stroke-miterlimit="2" stroke-width="0" d="m598.00805,133.72192l-18.576,0l0,-36.666l-10.692,0l0,0.27c2.7,0 4.86,2.16 4.86,4.806l0,32.67l24.408,0l0,-1.08z" id="path78215"/>
|
||||
<path transform="translate(-353.512 -351.983) translate(0 280.783)" stroke="#98a0b2" fill="#98a0b2" stroke-miterlimit="2" stroke-width="0" d="m627.41197,97.05592l0,23.598c0,7.344 -6.534,13.284 -12.69001,13.284c-6.156,0 -9.66599,-5.94 -9.66599,-13.284l0,-23.598l-10.692,0l0,0.27c2.69999,0 4.85999,2.16 4.85999,4.806l0,18.522c0,8.046 6.534,14.58 14.63401,14.58c8.09999,0 14.63399,-6.534 14.63399,-14.58l0,-23.598l-1.07999,0z" id="path78217"/>
|
||||
<path transform="translate(-353.512 -351.983) translate(0 280.783)" stroke="#a4abbb" fill="#a4abbb" stroke-miterlimit="2" stroke-width="0" d="m664.49391,130.58992l-11.772,-18.414l11.88,-15.12l-1.08,0l-11.34,14.364l-7.074,-11.124c-1.35,-2.052 -3.51,-3.24 -5.94,-3.24l-6.102,0l0,0.27c2.7,0 4.914,2.16 6.588,4.806l8.91,13.878l-14.796,18.792l1.134,0l14.148,-17.982l9.45,14.742c1.35,2.052 3.51,3.24 5.94,3.24l6.048,0l0,-0.27c-2.376,0 -4.374,-1.674 -5.994,-3.942z" id="path78219"/>
|
||||
</g>
|
||||
<image width="0" height="0" y="-351.98328" x="-353.51215" id="icon"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 6.3 KiB |
33
resources/src/assets/logo3.svg
Normal file
@@ -0,0 +1,33 @@
|
||||
<svg y="588.165" x="922" version="1.1" width="488" height="49" viewBox="0 0 488 49" id="svg164351" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<metadata id="metadata164357">image/svg+xml</metadata>
|
||||
|
||||
<linearGradient id="3d_gradient2-logo-b69ed5b7-9e72-4003-be77-c6c49a364658" x1="30%" y1="120%" x2="-10%" y2="30%" spreadMethod="pad">
|
||||
<stop offset="0%" stop-color="#ffffff" id="stop164332"/>
|
||||
<stop offset="100%" stop-color="#000000" id="stop164334"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="3d_gradient3-logo-b69ed5b7-9e72-4003-be77-c6c49a364658" x1="30%" y1="120%" x2="-10%" y2="30%" spreadMethod="pad" gradientTransform="rotate(-30)">
|
||||
<stop offset="0%" stop-color="#ffffff" id="stop164337"/>
|
||||
<stop offset="50%" stop-color="#cccccc" id="stop164339"/>
|
||||
<stop offset="100%" stop-color="#000000" id="stop164341"/>
|
||||
</linearGradient>
|
||||
<g id="logo-group">
|
||||
<g id="logo-center">
|
||||
<image width="0" height="0" y="-359.3645" x="-267.7655" id="icon_container"/>
|
||||
<g text-anchor="middle" font-family="'Nunito Sans'" font-size="32px" font-weight="300" font-style="oblique" transform="translate(0 0)" id="slogan"/>
|
||||
<g text-anchor="middle" font-family="'Meedori Sans'" font-size="72px" font-weight="700" font-style="normal" transform="translate(0 0)" id="title">
|
||||
<path transform="translate(-267.766 -359.365) translate(0 304.498)" stroke="#5e636e" fill="#5e636e" stroke-miterlimit="2" stroke-width="0" d="m307.79824,54.932l9.10727,0l-24.57,49.14l-24.57,-49.14l9.95904,0l14.61096,30.99096l15.46273,-30.99096z" id="path164360"/>
|
||||
<path transform="translate(-267.766 -359.365) translate(0 304.498)" stroke="#656a74" fill="#656a74" stroke-miterlimit="2" stroke-width="0" d="m362.42759,54.932l0,30.92544q0,0.6552 -0.13104,2.2932q-0.06552,1.57248 -0.58968,3.6036q-0.52416,2.03112 -1.70351,4.2588q-1.11385,2.16216 -3.27601,3.99672q-2.09663,1.76904 -5.37264,2.9484q-3.21048,1.11384 -7.99344,1.11384q-4.52088,0 -7.66583,-1.11384q-3.14496,-1.17936 -5.17609,-2.9484q-2.03111,-1.83456 -3.14495,-3.99672q-1.04833,-2.22768 -1.57248,-4.2588q-0.45865,-2.03112 -0.58969,-3.6036q-0.06552,-1.638 -0.06552,-2.2932l0,-30.92544l8.19,0l0,30.92544q0,0.72072 0.13104,2.48976q0.19656,1.76904 1.11384,3.66912q0.91729,1.83456 2.9484,3.276q2.09665,1.44144 5.83128,1.44144q4.06224,0 6.2244,-1.44144q2.22769,-1.44144 3.21048,-3.276q1.04832,-1.90008 1.17936,-3.66912q0.19656,-1.76904 0.19656,-2.48976l0,-30.92544l8.25552,0z" id="path164362"/>
|
||||
<path transform="translate(-267.766 -359.365) translate(0 304.498)" stroke="#6c707b" fill="#6c707b" stroke-miterlimit="2" stroke-width="0" d="m408.65397,63.122l-24.63552,0l0,12.25224l20.96641,0l0,8.19l-20.96641,0l0,12.31776l24.63552,0l0,8.19l-32.82552,0l0,-49.14l32.82552,0l0,8.19z" id="path164364"/>
|
||||
<path transform="translate(-267.766 -359.365) translate(0 304.498)" stroke="#727781" fill="#727781" stroke-miterlimit="2" stroke-width="0" d="m458.21678,63.122l-16.38,0l0,40.95l-8.19,0l0,-40.95l-16.38,0l0,-8.19l40.95,0l0,8.19z" id="path164366"/>
|
||||
<path transform="translate(-267.766 -359.365) translate(0 304.498)" stroke="#797d88" fill="#797d88" stroke-miterlimit="2" stroke-width="0" d="m468.09393,54.932l8.12448,0l0,49.14l-8.12448,0l0,-49.14z" id="path164368"/>
|
||||
<path transform="translate(-267.766 -359.365) translate(0 304.498)" stroke="#80848e" fill="#80848e" stroke-miterlimit="2" stroke-width="0" d="m498.0386,84.0884l0,19.9836l-8.19,0l0,-49.14l32.76,0l0,8.19l-24.57,0l0,12.71088l21.88368,0l0,8.25552l-21.88368,0z" id="path164370"/>
|
||||
<path transform="translate(-267.766 -359.365) translate(0 304.498)" stroke="#878b94" fill="#878b94" stroke-miterlimit="2" stroke-width="0" d="m557.82253,81.33656l0,22.73544l-8.19,0l0,-22.73544l-20.04912,-26.40456l10.02457,0l14.61096,18.21456l13.62815,-18.21456l10.94185,0l-20.96641,26.40456z" id="path164372"/>
|
||||
<path transform="translate(-267.766 -359.365) translate(0 304.498)" stroke="#8e919b" fill="#8e919b" stroke-miterlimit="2" stroke-width="0" d="m609.90891,76.68464l0,8.25552l-23.19407,0l0,-8.25552l23.19407,0z" id="path164374"/>
|
||||
<path transform="translate(-267.766 -359.365) translate(0 304.498)" stroke="#9498a1" fill="#9498a1" stroke-miterlimit="2" stroke-width="0" d="m651.95222,95.882l0,8.19l-32.82552,0l0,-49.20552l8.12448,0l0.06552,41.01552l24.63552,0z" id="path164376"/>
|
||||
<path transform="translate(-267.766 -359.365) translate(0 304.498)" stroke="#9b9ea8" fill="#9b9ea8" stroke-miterlimit="2" stroke-width="0" d="m698.34551,54.932l0,30.92544q0,0.6552 -0.13104,2.2932q-0.06552,1.57248 -0.58968,3.6036q-0.52416,2.03112 -1.70352,4.2588q-1.11384,2.16216 -3.276,3.99672q-2.09664,1.76904 -5.37264,2.9484q-3.21048,1.11384 -7.99344,1.11384q-4.52088,0 -7.66584,-1.11384q-3.14496,-1.17936 -5.17608,-2.9484q-2.03112,-1.83456 -3.14496,-3.99672q-1.04832,-2.22768 -1.57248,-4.2588q-0.45864,-2.03112 -0.58968,-3.6036q-0.06552,-1.638 -0.06552,-2.2932l0,-30.92544l8.19,0l0,30.92544q0,0.72072 0.13104,2.48976q0.19656,1.76904 1.11384,3.66912q0.91728,1.83456 2.9484,3.276q2.09664,1.44144 5.83128,1.44144q4.06224,0 6.2244,-1.44144q2.22768,-1.44144 3.21048,-3.276q1.04832,-1.90008 1.17936,-3.66912q0.19656,-1.76904 0.19656,-2.48976l0,-30.92544l8.25552,0z" id="path164378"/>
|
||||
<path transform="translate(-267.766 -359.365) translate(0 304.498)" stroke="#a2a5ae" fill="#a2a5ae" stroke-miterlimit="2" stroke-width="0" d="m707.02898,104.13752l19.06632,-24.50448l-19.06632,-24.70104l10.87632,0l13.62816,18.21456l13.7592,-18.21456l10.94184,0l-18.21456,24.70104l18.21456,24.50448l-10.94184,0l-13.7592,-18.14904l-13.62816,18.14904l-10.87632,0z" id="path164380"/>
|
||||
</g>
|
||||
<image width="0" height="0" y="-359.3645" x="-267.7655" id="icon"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 5.7 KiB |
33
resources/src/assets/logo5.svg
Normal file
@@ -0,0 +1,33 @@
|
||||
<svg y="563.665" x="996.5" version="1.1" width="335" height="38" viewBox="0 0 335 38" id="svg194368" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<metadata id="metadata194374">image/svg+xml</metadata>
|
||||
|
||||
<linearGradient id="3d_gradient2-logo-682f3222-452d-46d2-bf30-c93cee4278d2" x1="30%" y1="120%" x2="-10%" y2="30%" spreadMethod="pad">
|
||||
<stop offset="0%" stop-color="#ffffff" id="stop194349"/>
|
||||
<stop offset="100%" stop-color="#000000" id="stop194351"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="3d_gradient3-logo-682f3222-452d-46d2-bf30-c93cee4278d2" x1="30%" y1="120%" x2="-10%" y2="30%" spreadMethod="pad" gradientTransform="rotate(-30)">
|
||||
<stop offset="0%" stop-color="#ffffff" id="stop194354"/>
|
||||
<stop offset="50%" stop-color="#cccccc" id="stop194356"/>
|
||||
<stop offset="100%" stop-color="#000000" id="stop194358"/>
|
||||
</linearGradient>
|
||||
<g id="logo-group">
|
||||
<g id="logo-center">
|
||||
<image width="0" height="0" y="-364.91104" x="-344.33633" id="icon_container"/>
|
||||
<g text-anchor="middle" font-family="Orienta" font-size="32px" font-weight="400" font-style="normal" transform="translate(0 0)" id="slogan"/>
|
||||
<g text-anchor="middle" font-family="'Brandmark Didone 1'" font-size="72px" font-weight="normal" font-style="normal" transform="translate(0 0)" id="title">
|
||||
<path transform="translate(1.13687e-13 0) translate(0 302.329)" stroke="#3a456c" fill="#3a456c" stroke-miterlimit="2" stroke-width="0" d="m32.13001,-302.32904l-11.07,32.454l-9.342,-27.378c-1.08,-3.186 -3.834,-5.076 -7.128,-5.076l-4.59,0l0,0.27c2.538,0 3.996,1.89 4.914,4.32l11.34,33.588l3.942,0l13.014,-38.178l-1.08,0z" id="path194377"/>
|
||||
<path transform="translate(1.13687e-13 0) translate(0 302.329)" stroke="#454f74" fill="#454f74" stroke-miterlimit="2" stroke-width="0" d="m68.34123,-302.32904l0,23.598c0,7.344 -6.534,13.284 -12.69001,13.284c-6.156,0 -9.66599,-5.94 -9.66599,-13.284l0,-23.598l-10.69201,0l0,0.27c2.70001,0 4.86001,2.16 4.86001,4.806l0,18.522c0,8.046 6.53399,14.58 14.634,14.58c8.1,0 14.634,-6.534 14.634,-14.58l0,-23.598l-1.08,0z" id="path194379"/>
|
||||
<path transform="translate(1.13687e-13 0) translate(0 302.329)" stroke="#4f597c" fill="#4f597c" stroke-miterlimit="2" stroke-width="0" d="m85.11918,-301.24904l16.308,0l0,-1.08l-27,0l0,0.27c2.7,0 4.86,2.16 4.86,4.806l0,32.67l22.14,0l0,-1.08l-16.308,0l0,-21.276l15.012,0l0,-1.08l-15.012,0l0,-13.23z" id="path194381"/>
|
||||
<path transform="translate(1.13687e-13 0) translate(0 302.329)" stroke="#5a6484" fill="#5a6484" stroke-miterlimit="2" stroke-width="0" d="m132.94632,-301.24904l0,-1.08l-29.376,0l0,1.08l11.772,0l0,36.666l5.832,0l0,-36.666l11.772,0z" id="path194383"/>
|
||||
<path transform="translate(1.13687e-13 0) translate(0 302.329)" stroke="#646e8c" fill="#646e8c" stroke-miterlimit="2" stroke-width="0" d="m145.34183,-302.32904l-10.692,0l0,0.27c2.7,0 4.86001,2.16 4.86001,4.806l0,32.67l5.83199,0l0,-37.746z" id="path194385"/>
|
||||
<path transform="translate(1.13687e-13 0) translate(0 302.329)" stroke="#6f7894" fill="#6f7894" stroke-miterlimit="2" stroke-width="0" d="m161.58402,-301.24904l16.308,0l0,-1.08l-27,0l0,0.27c2.7,0 4.86001,2.16 4.86001,4.806l0,32.67l5.83199,0l0,-22.302l14.256,0l0,-1.08l-14.256,0l0,-13.284z" id="path194387"/>
|
||||
<path transform="translate(1.13687e-13 0) translate(0 302.329)" stroke="#7a829b" fill="#7a829b" stroke-miterlimit="2" stroke-width="0" d="m200.94833,-282.78104l12.47401,-19.548l-1.13401,0l-11.826,18.576l-8.856,-15.066c-1.296,-2.268 -3.564,-3.51 -6.156,-3.51l-6.15599,0l0,0.27c2.7,0 4.752,2.16 6.31799,4.806l9.45001,16.146l0,16.524l5.88599,0l0,-18.198z" id="path194389"/>
|
||||
<path transform="translate(1.13687e-13 0) translate(0 302.329)" stroke="#848ca3" fill="#848ca3" stroke-miterlimit="2" stroke-width="0" d="m212.79459,-280.40504l18.57599,0l0,-1.89l-18.57599,0l0,1.89z" id="path194391"/>
|
||||
<path transform="translate(1.13687e-13 0) translate(0 302.329)" stroke="#8f97ab" fill="#8f97ab" stroke-miterlimit="2" stroke-width="0" d="m262.8475,-265.66304l-18.576,0l0,-36.666l-10.69201,0l0,0.27c2.7,0 4.86,2.16 4.86,4.806l0,32.67l24.40801,0l0,-1.08z" id="path194393"/>
|
||||
<path transform="translate(1.13687e-13 0) translate(0 302.329)" stroke="#99a1b3" fill="#99a1b3" stroke-miterlimit="2" stroke-width="0" d="m292.25141,-302.32904l0,23.598c0,7.344 -6.534,13.284 -12.69,13.284c-6.156,0 -9.666,-5.94 -9.666,-13.284l0,-23.598l-10.692,0l0,0.27c2.7,0 4.86,2.16 4.86,4.806l0,18.522c0,8.046 6.534,14.58 14.634,14.58c8.1,0 14.634,-6.534 14.634,-14.58l0,-23.598l-1.08,0z" id="path194395"/>
|
||||
<path transform="translate(1.13687e-13 0) translate(0 302.329)" stroke="#a4abbb" fill="#a4abbb" stroke-miterlimit="2" stroke-width="0" d="m329.33336,-268.79504l-11.77201,-18.414l11.88001,-15.12l-1.08001,0l-11.34,14.364l-7.074,-11.124c-1.34999,-2.052 -3.50999,-3.24 -5.94,-3.24l-6.10199,0l0,0.27c2.69999,0 4.91399,2.16 6.588,4.806l8.91,13.878l-14.79601,18.792l1.13401,0l14.148,-17.982l9.44999,14.742c1.35001,2.052 3.51001,3.24 5.94001,3.24l6.04799,0l0,-0.27c-2.376,0 -4.374,-1.674 -5.99399,-3.942z" id="path194397"/>
|
||||
</g>
|
||||
<image width="0" height="0" y="-364.91104" x="-344.33633" id="icon"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 5.2 KiB |
33
resources/src/assets/logo_dark.svg
Normal file
@@ -0,0 +1,33 @@
|
||||
<svg y="551.165" x="960" version="1.1" width="412" height="67" viewBox="0 0 412 67" id="svg687764" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<metadata id="metadata687770">image/svg+xml</metadata>
|
||||
|
||||
<linearGradient id="3d_gradient2-logo-be9fcf93-76f3-4195-b816-3f382377cd48" x1="30%" y1="120%" x2="-10%" y2="30%" spreadMethod="pad">
|
||||
<stop offset="0%" stop-color="#ffffff" id="stop687745"/>
|
||||
<stop offset="100%" stop-color="#000000" id="stop687747"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="3d_gradient3-logo-be9fcf93-76f3-4195-b816-3f382377cd48" x1="30%" y1="120%" x2="-10%" y2="30%" spreadMethod="pad" gradientTransform="rotate(-30)">
|
||||
<stop offset="0%" stop-color="#ffffff" id="stop687750"/>
|
||||
<stop offset="50%" stop-color="#cccccc" id="stop687752"/>
|
||||
<stop offset="100%" stop-color="#000000" id="stop687754"/>
|
||||
</linearGradient>
|
||||
<g id="logo-group">
|
||||
<g transform="translate(0 0)" id="logo-center">
|
||||
<image width="0" height="0" y="-350.55203" x="-306.10635" id="icon_container"/>
|
||||
<g text-anchor="middle" font-family="Raleway" font-size="32px" font-weight="600" font-style="normal" transform="translate(0 0)" id="slogan"/>
|
||||
<g text-anchor="middle" font-family="'Comfortaa Bold Alt1'" font-size="72px" font-weight="700" font-style="normal" transform="translate(0 0)" id="title">
|
||||
<path transform="translate(-306.106 -350.552) translate(0 295.491)" stroke="#ffffff" fill="#ffffff" stroke-miterlimit="2" stroke-width="0" d="m340.17676,71.96504c-0.3276,-0.45864 -0.78624,-0.85176 -1.3104,-1.11384c-0.45864,-0.19656 -0.9828,-0.3276 -1.50696,-0.3276c-0.58968,0 -1.11384,0.19656 -1.638,0.45864c-0.52416,0.26208 -0.85176,0.6552 -1.11384,1.17936l-11.26944,24.57l-11.40048,-24.57c-0.26208,-0.52416 -0.6552,-0.91728 -1.11384,-1.17936c-0.52416,-0.26208 -0.9828,-0.45864 -1.50696,-0.45864c-0.52416,0 -0.9828,0.13104 -1.37592,0.3276c-1.24488,0.6552 -1.83456,1.50696 -1.83456,2.68632c0,0.45864 0.06552,0.85176 0.26208,1.17936l13.89024,29.28744c0.39312,0.78624 0.78624,1.37592 1.3104,1.70352c0.45864,0.3276 1.04832,0.45864 1.83456,0.45864c1.3104,0 2.35872,-0.72072 3.01392,-2.16216l13.89024,-29.28744c0.19656,-0.39312 0.3276,-0.85176 0.3276,-1.24488c0,-0.52416 -0.19656,-1.04832 -0.45864,-1.50696z" id="path687773"/>
|
||||
<path transform="translate(-306.106 -350.552) translate(0 295.491)" stroke="#fcfcfc" fill="#fcfcfc" stroke-miterlimit="2" stroke-width="0" d="m386.5027,102.89048l0,-29.22192c0,-0.9828 -0.3276,-1.76904 -0.91728,-2.42424c-0.6552,-0.58968 -1.44144,-0.91728 -2.42424,-0.91728c-0.9828,0 -1.76904,0.3276 -2.42424,0.91728c-0.6552,0.6552 -0.91728,1.44144 -0.91728,2.42424l0,18.47664c0,1.638 -0.45864,3.01392 -1.3104,4.2588c-0.85176,1.3104 -2.03112,2.2932 -3.53808,3.01392c-1.50696,0.78624 -3.21048,1.11384 -4.97952,1.11384c-3.21048,0 -5.70024,-0.85176 -7.60032,-2.68632c-1.90008,-1.83456 -2.81736,-4.52088 -2.81736,-8.05896l0,-16.11792c0,-0.91728 -0.3276,-1.70352 -0.9828,-2.35872c-0.6552,-0.6552 -1.44144,-0.9828 -2.35872,-0.9828c-0.9828,0 -1.76904,0.3276 -2.42424,0.9828c-0.6552,0.6552 -0.91728,1.44144 -0.91728,2.35872l0,16.11792c0,3.6036 0.6552,6.61752 2.03112,9.10728c1.3104,2.48976 3.21048,4.45536 5.63472,5.70024c2.2932,1.3104 4.78296,1.9656 7.5348,2.03112c0.13104,0 5.17608,0 15.33168,0c0.85176,0 1.57248,-0.3276 2.16216,-0.91728c0.58968,-0.58968 0.91728,-1.3104 0.91728,-2.22768l0,-0.58968z" id="path687775"/>
|
||||
<path transform="translate(-306.106 -350.552) translate(0 295.491)" stroke="#f8f8f8" fill="#f8f8f8" stroke-miterlimit="2" stroke-width="0" d="m435.96133,89.852c0.52416,-0.52416 0.85176,-1.24488 0.85176,-2.09664c0,-3.40704 -0.6552,-6.48648 -1.9656,-9.1728c-1.3104,-2.6208 -3.21048,-4.71744 -5.63472,-6.2244c-2.48976,-1.44144 -5.37264,-2.22768 -8.71416,-2.22768c-3.40704,0 -6.48648,0.78624 -9.1728,2.35872c-2.68632,1.57248 -4.78296,3.73464 -6.28992,6.48648c-1.50696,2.75184 -2.22768,5.8968 -2.22768,9.36936c0,3.53808 0.78624,6.61752 2.35872,9.36936c1.57248,2.75184 3.80016,4.914 6.68304,6.48648c2.81736,1.57248 6.02784,2.2932 9.63144,2.2932c1.9656,0 4.06224,-0.3276 6.28992,-1.11384c2.22768,-0.72072 4.06224,-1.70352 5.5692,-2.88288c0.6552,-0.52416 1.04832,-1.17936 1.04832,-1.9656c0,-0.78624 -0.39312,-1.57248 -1.17936,-2.22768c-0.52416,-0.39312 -1.17936,-0.6552 -1.9656,-0.6552c-0.85176,0 -1.57248,0.26208 -2.16216,0.72072c-0.91728,0.72072 -2.09664,1.3104 -3.53808,1.76904c-1.44144,0.52416 -2.75184,0.72072 -4.06224,0.72072c-3.34152,0 -6.15888,-0.91728 -8.45208,-2.81736c-2.2932,-1.83456 -3.66912,-4.32432 -4.12776,-7.40376l24.8976,0c0.85176,0 1.57248,-0.26208 2.16216,-0.78624zm-23.2596,-11.466c1.9656,-1.70352 4.5864,-2.6208 7.79688,-2.6208c2.88288,0 5.17608,0.91728 7.01064,2.6208c1.76904,1.76904 2.88288,4.12776 3.276,7.01064l-21.81816,0c0.52416,-2.88288 1.76904,-5.2416 3.73464,-7.01064z" id="path687777"/>
|
||||
<path transform="translate(-306.106 -350.552) translate(0 295.491)" stroke="#f5f5f5" fill="#f5f5f5" stroke-miterlimit="2" stroke-width="0" d="m453.21376,71.70296l0,5.63472l0,16.18344c0,2.42424 0.45864,4.5864 1.50696,6.48648c0.9828,1.9656 2.35872,3.47256 4.12776,4.5864c1.76904,1.11384 3.73464,1.638 5.8968,1.638l1.17936,0c1.11384,0 2.03112,-0.26208 2.75184,-0.91728c0.72072,-0.58968 1.11384,-1.37592 1.11384,-2.35872c0,-0.91728 -0.3276,-1.70352 -0.85176,-2.35872c-0.52416,-0.58968 -1.17936,-0.91728 -1.9656,-0.91728l-2.22768,0c-1.44144,0 -2.6208,-0.58968 -3.53808,-1.76904c-0.9828,-1.17936 -1.44144,-2.6208 -1.44144,-4.38984l0,-16.18344l5.5692,0c0.91728,0 1.638,-0.26208 2.22768,-0.78624c0.52416,-0.52416 0.85176,-1.17936 0.85176,-1.9656c0,-0.85176 -0.3276,-1.57248 -0.85176,-2.09664c-0.58968,-0.52416 -1.3104,-0.78624 -2.22768,-0.78624l-5.5692,0l0,-9.43488c0,-0.91728 -0.3276,-1.70352 -0.91728,-2.35872c-0.6552,-0.58968 -1.44144,-0.91728 -2.35872,-0.91728c-0.9828,0 -1.76904,0.3276 -2.35872,0.91728c-0.6552,0.6552 -0.91728,1.44144 -0.91728,2.35872l0,9.43488z" id="path687779"/>
|
||||
<path transform="translate(-306.106 -350.552) translate(0 295.491)" stroke="#f2f2f2" fill="#f2f2f2" stroke-miterlimit="2" stroke-width="0" d="m485.88287,71.30984c-0.6552,0.6552 -0.91728,1.44144 -0.91728,2.42424l0,29.1564c0,0.9828 0.26208,1.76904 0.91728,2.42424c0.6552,0.6552 1.44144,0.91728 2.42424,0.91728c0.9828,0 1.76904,-0.26208 2.42424,-0.91728c0.58968,-0.6552 0.91728,-1.44144 0.91728,-2.42424l0,-29.1564c0,-0.9828 -0.3276,-1.76904 -0.91728,-2.42424c-0.6552,-0.58968 -1.44144,-0.91728 -2.42424,-0.91728c-0.9828,0 -1.76904,0.3276 -2.42424,0.91728z" id="path687781"/>
|
||||
<path transform="translate(-306.106 -350.552) translate(0 295.491)" stroke="#efefef" fill="#efefef" stroke-miterlimit="2" stroke-width="0" d="m511.17077,71.70296l0,5.63472l0,25.5528c0,1.04832 0.26208,1.83456 0.91728,2.42424c0.6552,0.6552 1.37592,0.91728 2.2932,0.91728c1.04832,0 1.90008,-0.26208 2.55528,-0.91728c0.58968,-0.58968 0.91728,-1.37592 0.91728,-2.42424l0,-25.5528l6.68304,0c0.91728,0 1.638,-0.26208 2.22768,-0.78624c0.52416,-0.52416 0.85176,-1.17936 0.85176,-1.9656c0,-0.85176 -0.3276,-1.57248 -0.85176,-2.09664c-0.58968,-0.52416 -1.3104,-0.78624 -2.22768,-0.78624l-6.68304,0l0,-1.76904c0,-2.6208 0.72072,-4.78296 2.22768,-6.42096c1.44144,-1.57248 3.40704,-2.42424 5.8968,-2.42424c0.91728,0 1.70352,-0.26208 2.35872,-0.85176c0.6552,-0.52416 0.9828,-1.24488 0.9828,-2.16216c0,-0.85176 -0.3276,-1.57248 -0.9828,-2.16216c-0.6552,-0.52416 -1.44144,-0.85176 -2.35872,-0.85176c-2.9484,0 -5.50368,0.6552 -7.73136,1.9656c-2.22768,1.3104 -3.99672,3.07944 -5.2416,5.43816c-1.24488,2.35872 -1.83456,5.04504 -1.83456,8.12448l0,1.11384z" id="path687783"/>
|
||||
<path transform="translate(-306.106 -350.552) translate(0 295.491)" stroke="#ebebeb" fill="#ebebeb" stroke-miterlimit="2" stroke-width="0" d="m570.36214,73.27544c0,-1.17936 -0.72072,-2.03112 -2.03112,-2.55528c-0.52416,-0.19656 -1.04832,-0.3276 -1.57248,-0.3276c-1.17936,0 -2.03112,0.6552 -2.55528,1.90008l-10.54872,23.78376l-11.99016,-23.84928c-0.6552,-1.17936 -1.57248,-1.83456 -2.75184,-1.83456c-0.52416,0 -0.91728,0.13104 -1.3104,0.26208c-0.58968,0.26208 -1.04832,0.6552 -1.37592,1.11384c-0.39312,0.52416 -0.52416,1.04832 -0.52416,1.57248c0,0.52416 0.06552,0.9828 0.3276,1.37592l14.742,27.97704l-6.552,14.742c-0.26208,0.52416 -0.39312,1.04832 -0.39312,1.57248c0,1.17936 0.6552,2.03112 1.9656,2.55528c0.58968,0.26208 1.11384,0.39312 1.57248,0.39312c1.17936,0 2.03112,-0.6552 2.55528,-2.03112l20.04912,-45.07776c0.26208,-0.58968 0.39312,-1.11384 0.39312,-1.57248z" id="path687785"/>
|
||||
<path transform="translate(-306.106 -350.552) translate(0 295.491)" stroke="#e8e8e8" fill="#e8e8e8" stroke-miterlimit="2" stroke-width="0" d="m599.40619,85.98632c-0.6552,-0.6552 -1.44144,-0.9828 -2.35872,-0.9828l-14.61096,0c-0.9828,0 -1.76904,0.3276 -2.42424,0.9828c-0.6552,0.6552 -0.91728,1.44144 -0.91728,2.35872c0,0.9828 0.26208,1.76904 0.91728,2.35872c0.6552,0.6552 1.44144,0.91728 2.42424,0.91728l14.61096,0c0.91728,0 1.70352,-0.26208 2.35872,-0.91728c0.58968,-0.58968 0.91728,-1.37592 0.91728,-2.35872c0,-0.91728 -0.3276,-1.70352 -0.91728,-2.35872z" id="path687787"/>
|
||||
<path transform="translate(-306.106 -350.552) translate(0 295.491)" stroke="#e5e5e5" fill="#e5e5e5" stroke-miterlimit="2" stroke-width="0" d="m614.20374,55.97816c-0.6552,0.6552 -0.91728,1.44144 -0.91728,2.35872l0,35.77392c0,2.35872 0.39312,4.45536 1.24488,6.28992c0.78624,1.83456 1.9656,3.276 3.47256,4.32432c1.50696,1.04832 3.21048,1.50696 5.11056,1.50696l0.13104,0c1.3104,0 2.35872,-0.26208 3.21048,-0.91728c0.78624,-0.58968 1.24488,-1.37592 1.24488,-2.35872c0,-0.91728 -0.3276,-1.70352 -0.85176,-2.35872c-0.52416,-0.58968 -1.24488,-0.91728 -2.09664,-0.91728l-1.638,0c-0.9828,0 -1.76904,-0.52416 -2.35872,-1.57248c-0.6552,-1.04832 -0.91728,-2.35872 -0.91728,-3.99672l0,-35.77392c0,-0.91728 -0.3276,-1.70352 -0.91728,-2.35872c-0.6552,-0.58968 -1.44144,-0.91728 -2.35872,-0.91728c-0.9828,0 -1.76904,0.3276 -2.35872,0.91728z" id="path687789"/>
|
||||
<path transform="translate(-306.106 -350.552) translate(0 295.491)" stroke="#e1e1e1" fill="#e1e1e1" stroke-miterlimit="2" stroke-width="0" d="m673.56508,102.89048l0,-29.22192c0,-0.9828 -0.3276,-1.76904 -0.91728,-2.42424c-0.6552,-0.58968 -1.44144,-0.91728 -2.42424,-0.91728c-0.9828,0 -1.76904,0.3276 -2.42424,0.91728c-0.6552,0.6552 -0.91728,1.44144 -0.91728,2.42424l0,18.47664c0,1.638 -0.45864,3.01392 -1.3104,4.2588c-0.85176,1.3104 -2.03112,2.2932 -3.53808,3.01392c-1.50696,0.78624 -3.21048,1.11384 -4.97952,1.11384c-3.21048,0 -5.70024,-0.85176 -7.60032,-2.68632c-1.90008,-1.83456 -2.81736,-4.52088 -2.81736,-8.05896l0,-16.11792c0,-0.91728 -0.3276,-1.70352 -0.9828,-2.35872c-0.6552,-0.6552 -1.44144,-0.9828 -2.35872,-0.9828c-0.9828,0 -1.76904,0.3276 -2.42424,0.9828c-0.6552,0.6552 -0.91728,1.44144 -0.91728,2.35872l0,16.11792c0,3.6036 0.6552,6.61752 2.03112,9.10728c1.3104,2.48976 3.21048,4.45536 5.63472,5.70024c2.2932,1.3104 4.78296,1.9656 7.5348,2.03112c0.13104,0 5.17608,0 15.33168,0c0.85176,0 1.57248,-0.3276 2.16216,-0.91728c0.58968,-0.58968 0.91728,-1.3104 0.91728,-2.22768l0,-0.58968z" id="path687791"/>
|
||||
<path transform="translate(-306.106 -350.552) translate(0 295.491)" stroke="#dedede" fill="#dedede" stroke-miterlimit="2" stroke-width="0" d="m717.89364,103.48016c0,-0.85176 -0.3276,-1.638 -0.91728,-2.2932l-10.41768,-12.4488l9.89352,-12.90744c0.58968,-0.78624 0.91728,-1.638 0.91728,-2.6208c0,-0.78624 -0.26208,-1.44144 -0.78624,-1.9656c-0.52416,-0.52416 -1.17936,-0.85176 -2.03112,-0.85176c-1.04832,0 -1.90008,0.39312 -2.42424,1.11384l-9.63144,12.84192l-10.74528,-12.84192c-0.6552,-0.72072 -1.44144,-1.11384 -2.48976,-1.11384c-0.91728,0 -1.638,0.26208 -2.16216,0.78624c-0.52416,0.52416 -0.72072,1.17936 -0.72072,1.90008c0,0.91728 0.3276,1.70352 0.9828,2.48976l10.8108,12.7764l-10.1556,12.71088c-0.6552,0.78624 -0.91728,1.57248 -0.91728,2.42424c0,0.78624 0.19656,1.44144 0.72072,1.9656c0.52416,0.52416 1.17936,0.78624 2.09664,0.78624c0.91728,0 1.70352,-0.3276 2.35872,-1.04832l9.89352,-12.4488l10.41768,12.4488c0.52416,0.72072 1.3104,1.04832 2.35872,1.04832c0.85176,0 1.57248,-0.26208 2.09664,-0.78624c0.52416,-0.52416 0.85176,-1.17936 0.85176,-1.9656z" id="path687793"/>
|
||||
</g>
|
||||
<image width="0" height="0" y="-350.55203" x="-306.10635" id="icon"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 12 KiB |
33
resources/src/assets/logo_light.svg
Normal file
@@ -0,0 +1,33 @@
|
||||
<svg y="551.165" x="960" version="1.1" width="412" height="67" viewBox="0 0 412 67" id="svg4578" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<metadata id="metadata4584">image/svg+xml</metadata>
|
||||
|
||||
<linearGradient id="3d_gradient2-logo-be9fcf93-76f3-4195-b816-3f382377cd48" x1="30%" y1="120%" x2="-10%" y2="30%" spreadMethod="pad">
|
||||
<stop offset="0%" stop-color="#ffffff" id="stop4559"/>
|
||||
<stop offset="100%" stop-color="#000000" id="stop4561"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="3d_gradient3-logo-be9fcf93-76f3-4195-b816-3f382377cd48" x1="30%" y1="120%" x2="-10%" y2="30%" spreadMethod="pad" gradientTransform="rotate(-30)">
|
||||
<stop offset="0%" stop-color="#ffffff" id="stop4564"/>
|
||||
<stop offset="50%" stop-color="#cccccc" id="stop4566"/>
|
||||
<stop offset="100%" stop-color="#000000" id="stop4568"/>
|
||||
</linearGradient>
|
||||
<g id="logo-group">
|
||||
<g id="logo-center">
|
||||
<image width="0" height="0" y="-350.55203" x="-306.10635" id="icon_container"/>
|
||||
<g text-anchor="middle" font-family="Raleway" font-size="32px" font-weight="600" font-style="normal" transform="translate(0 0)" id="slogan"/>
|
||||
<g text-anchor="middle" font-family="'Comfortaa Bold Alt1'" font-size="72px" font-weight="700" font-style="normal" transform="translate(0 0)" id="title">
|
||||
<path transform="translate(-306.106 -350.552) translate(0 295.491)" stroke="#5e636e" fill="#5e636e" stroke-miterlimit="2" stroke-width="0" d="m340.17676,71.96504c-0.3276,-0.45864 -0.78624,-0.85176 -1.3104,-1.11384c-0.45864,-0.19656 -0.9828,-0.3276 -1.50696,-0.3276c-0.58968,0 -1.11384,0.19656 -1.638,0.45864c-0.52416,0.26208 -0.85176,0.6552 -1.11384,1.17936l-11.26944,24.57l-11.40048,-24.57c-0.26208,-0.52416 -0.6552,-0.91728 -1.11384,-1.17936c-0.52416,-0.26208 -0.9828,-0.45864 -1.50696,-0.45864c-0.52416,0 -0.9828,0.13104 -1.37592,0.3276c-1.24488,0.6552 -1.83456,1.50696 -1.83456,2.68632c0,0.45864 0.06552,0.85176 0.26208,1.17936l13.89024,29.28744c0.39312,0.78624 0.78624,1.37592 1.3104,1.70352c0.45864,0.3276 1.04832,0.45864 1.83456,0.45864c1.3104,0 2.35872,-0.72072 3.01392,-2.16216l13.89024,-29.28744c0.19656,-0.39312 0.3276,-0.85176 0.3276,-1.24488c0,-0.52416 -0.19656,-1.04832 -0.45864,-1.50696z" id="path4587"/>
|
||||
<path transform="translate(-306.106 -350.552) translate(0 295.491)" stroke="#656a74" fill="#656a74" stroke-miterlimit="2" stroke-width="0" d="m386.5027,102.89048l0,-29.22192c0,-0.9828 -0.3276,-1.76904 -0.91728,-2.42424c-0.6552,-0.58968 -1.44144,-0.91728 -2.42424,-0.91728c-0.9828,0 -1.76904,0.3276 -2.42424,0.91728c-0.6552,0.6552 -0.91728,1.44144 -0.91728,2.42424l0,18.47664c0,1.638 -0.45864,3.01392 -1.3104,4.2588c-0.85176,1.3104 -2.03112,2.2932 -3.53808,3.01392c-1.50696,0.78624 -3.21048,1.11384 -4.97952,1.11384c-3.21048,0 -5.70024,-0.85176 -7.60032,-2.68632c-1.90008,-1.83456 -2.81736,-4.52088 -2.81736,-8.05896l0,-16.11792c0,-0.91728 -0.3276,-1.70352 -0.9828,-2.35872c-0.6552,-0.6552 -1.44144,-0.9828 -2.35872,-0.9828c-0.9828,0 -1.76904,0.3276 -2.42424,0.9828c-0.6552,0.6552 -0.91728,1.44144 -0.91728,2.35872l0,16.11792c0,3.6036 0.6552,6.61752 2.03112,9.10728c1.3104,2.48976 3.21048,4.45536 5.63472,5.70024c2.2932,1.3104 4.78296,1.9656 7.5348,2.03112c0.13104,0 5.17608,0 15.33168,0c0.85176,0 1.57248,-0.3276 2.16216,-0.91728c0.58968,-0.58968 0.91728,-1.3104 0.91728,-2.22768l0,-0.58968z" id="path4589"/>
|
||||
<path transform="translate(-306.106 -350.552) translate(0 295.491)" stroke="#6c707b" fill="#6c707b" stroke-miterlimit="2" stroke-width="0" d="m435.96133,89.852c0.52416,-0.52416 0.85176,-1.24488 0.85176,-2.09664c0,-3.40704 -0.6552,-6.48648 -1.9656,-9.1728c-1.3104,-2.6208 -3.21048,-4.71744 -5.63472,-6.2244c-2.48976,-1.44144 -5.37264,-2.22768 -8.71416,-2.22768c-3.40704,0 -6.48648,0.78624 -9.1728,2.35872c-2.68632,1.57248 -4.78296,3.73464 -6.28992,6.48648c-1.50696,2.75184 -2.22768,5.8968 -2.22768,9.36936c0,3.53808 0.78624,6.61752 2.35872,9.36936c1.57248,2.75184 3.80016,4.914 6.68304,6.48648c2.81736,1.57248 6.02784,2.2932 9.63144,2.2932c1.9656,0 4.06224,-0.3276 6.28992,-1.11384c2.22768,-0.72072 4.06224,-1.70352 5.5692,-2.88288c0.6552,-0.52416 1.04832,-1.17936 1.04832,-1.9656c0,-0.78624 -0.39312,-1.57248 -1.17936,-2.22768c-0.52416,-0.39312 -1.17936,-0.6552 -1.9656,-0.6552c-0.85176,0 -1.57248,0.26208 -2.16216,0.72072c-0.91728,0.72072 -2.09664,1.3104 -3.53808,1.76904c-1.44144,0.52416 -2.75184,0.72072 -4.06224,0.72072c-3.34152,0 -6.15888,-0.91728 -8.45208,-2.81736c-2.2932,-1.83456 -3.66912,-4.32432 -4.12776,-7.40376l24.8976,0c0.85176,0 1.57248,-0.26208 2.16216,-0.78624zm-23.2596,-11.466c1.9656,-1.70352 4.5864,-2.6208 7.79688,-2.6208c2.88288,0 5.17608,0.91728 7.01064,2.6208c1.76904,1.76904 2.88288,4.12776 3.276,7.01064l-21.81816,0c0.52416,-2.88288 1.76904,-5.2416 3.73464,-7.01064z" id="path4591"/>
|
||||
<path transform="translate(-306.106 -350.552) translate(0 295.491)" stroke="#727781" fill="#727781" stroke-miterlimit="2" stroke-width="0" d="m453.21376,71.70296l0,5.63472l0,16.18344c0,2.42424 0.45864,4.5864 1.50696,6.48648c0.9828,1.9656 2.35872,3.47256 4.12776,4.5864c1.76904,1.11384 3.73464,1.638 5.8968,1.638l1.17936,0c1.11384,0 2.03112,-0.26208 2.75184,-0.91728c0.72072,-0.58968 1.11384,-1.37592 1.11384,-2.35872c0,-0.91728 -0.3276,-1.70352 -0.85176,-2.35872c-0.52416,-0.58968 -1.17936,-0.91728 -1.9656,-0.91728l-2.22768,0c-1.44144,0 -2.6208,-0.58968 -3.53808,-1.76904c-0.9828,-1.17936 -1.44144,-2.6208 -1.44144,-4.38984l0,-16.18344l5.5692,0c0.91728,0 1.638,-0.26208 2.22768,-0.78624c0.52416,-0.52416 0.85176,-1.17936 0.85176,-1.9656c0,-0.85176 -0.3276,-1.57248 -0.85176,-2.09664c-0.58968,-0.52416 -1.3104,-0.78624 -2.22768,-0.78624l-5.5692,0l0,-9.43488c0,-0.91728 -0.3276,-1.70352 -0.91728,-2.35872c-0.6552,-0.58968 -1.44144,-0.91728 -2.35872,-0.91728c-0.9828,0 -1.76904,0.3276 -2.35872,0.91728c-0.6552,0.6552 -0.91728,1.44144 -0.91728,2.35872l0,9.43488z" id="path4593"/>
|
||||
<path transform="translate(-306.106 -350.552) translate(0 295.491)" stroke="#797d88" fill="#797d88" stroke-miterlimit="2" stroke-width="0" d="m485.88287,71.30984c-0.6552,0.6552 -0.91728,1.44144 -0.91728,2.42424l0,29.1564c0,0.9828 0.26208,1.76904 0.91728,2.42424c0.6552,0.6552 1.44144,0.91728 2.42424,0.91728c0.9828,0 1.76904,-0.26208 2.42424,-0.91728c0.58968,-0.6552 0.91728,-1.44144 0.91728,-2.42424l0,-29.1564c0,-0.9828 -0.3276,-1.76904 -0.91728,-2.42424c-0.6552,-0.58968 -1.44144,-0.91728 -2.42424,-0.91728c-0.9828,0 -1.76904,0.3276 -2.42424,0.91728z" id="path4595"/>
|
||||
<path transform="translate(-306.106 -350.552) translate(0 295.491)" stroke="#80848e" fill="#80848e" stroke-miterlimit="2" stroke-width="0" d="m511.17077,71.70296l0,5.63472l0,25.5528c0,1.04832 0.26208,1.83456 0.91728,2.42424c0.6552,0.6552 1.37592,0.91728 2.2932,0.91728c1.04832,0 1.90008,-0.26208 2.55528,-0.91728c0.58968,-0.58968 0.91728,-1.37592 0.91728,-2.42424l0,-25.5528l6.68304,0c0.91728,0 1.638,-0.26208 2.22768,-0.78624c0.52416,-0.52416 0.85176,-1.17936 0.85176,-1.9656c0,-0.85176 -0.3276,-1.57248 -0.85176,-2.09664c-0.58968,-0.52416 -1.3104,-0.78624 -2.22768,-0.78624l-6.68304,0l0,-1.76904c0,-2.6208 0.72072,-4.78296 2.22768,-6.42096c1.44144,-1.57248 3.40704,-2.42424 5.8968,-2.42424c0.91728,0 1.70352,-0.26208 2.35872,-0.85176c0.6552,-0.52416 0.9828,-1.24488 0.9828,-2.16216c0,-0.85176 -0.3276,-1.57248 -0.9828,-2.16216c-0.6552,-0.52416 -1.44144,-0.85176 -2.35872,-0.85176c-2.9484,0 -5.50368,0.6552 -7.73136,1.9656c-2.22768,1.3104 -3.99672,3.07944 -5.2416,5.43816c-1.24488,2.35872 -1.83456,5.04504 -1.83456,8.12448l0,1.11384z" id="path4597"/>
|
||||
<path transform="translate(-306.106 -350.552) translate(0 295.491)" stroke="#878b94" fill="#878b94" stroke-miterlimit="2" stroke-width="0" d="m570.36214,73.27544c0,-1.17936 -0.72072,-2.03112 -2.03112,-2.55528c-0.52416,-0.19656 -1.04832,-0.3276 -1.57248,-0.3276c-1.17936,0 -2.03112,0.6552 -2.55528,1.90008l-10.54872,23.78376l-11.99016,-23.84928c-0.6552,-1.17936 -1.57248,-1.83456 -2.75184,-1.83456c-0.52416,0 -0.91728,0.13104 -1.3104,0.26208c-0.58968,0.26208 -1.04832,0.6552 -1.37592,1.11384c-0.39312,0.52416 -0.52416,1.04832 -0.52416,1.57248c0,0.52416 0.06552,0.9828 0.3276,1.37592l14.742,27.97704l-6.552,14.742c-0.26208,0.52416 -0.39312,1.04832 -0.39312,1.57248c0,1.17936 0.6552,2.03112 1.9656,2.55528c0.58968,0.26208 1.11384,0.39312 1.57248,0.39312c1.17936,0 2.03112,-0.6552 2.55528,-2.03112l20.04912,-45.07776c0.26208,-0.58968 0.39312,-1.11384 0.39312,-1.57248z" id="path4599"/>
|
||||
<path transform="translate(-306.106 -350.552) translate(0 295.491)" stroke="#8e919b" fill="#8e919b" stroke-miterlimit="2" stroke-width="0" d="m599.40619,85.98632c-0.6552,-0.6552 -1.44144,-0.9828 -2.35872,-0.9828l-14.61096,0c-0.9828,0 -1.76904,0.3276 -2.42424,0.9828c-0.6552,0.6552 -0.91728,1.44144 -0.91728,2.35872c0,0.9828 0.26208,1.76904 0.91728,2.35872c0.6552,0.6552 1.44144,0.91728 2.42424,0.91728l14.61096,0c0.91728,0 1.70352,-0.26208 2.35872,-0.91728c0.58968,-0.58968 0.91728,-1.37592 0.91728,-2.35872c0,-0.91728 -0.3276,-1.70352 -0.91728,-2.35872z" id="path4601"/>
|
||||
<path transform="translate(-306.106 -350.552) translate(0 295.491)" stroke="#9498a1" fill="#9498a1" stroke-miterlimit="2" stroke-width="0" d="m614.20374,55.97816c-0.6552,0.6552 -0.91728,1.44144 -0.91728,2.35872l0,35.77392c0,2.35872 0.39312,4.45536 1.24488,6.28992c0.78624,1.83456 1.9656,3.276 3.47256,4.32432c1.50696,1.04832 3.21048,1.50696 5.11056,1.50696l0.13104,0c1.3104,0 2.35872,-0.26208 3.21048,-0.91728c0.78624,-0.58968 1.24488,-1.37592 1.24488,-2.35872c0,-0.91728 -0.3276,-1.70352 -0.85176,-2.35872c-0.52416,-0.58968 -1.24488,-0.91728 -2.09664,-0.91728l-1.638,0c-0.9828,0 -1.76904,-0.52416 -2.35872,-1.57248c-0.6552,-1.04832 -0.91728,-2.35872 -0.91728,-3.99672l0,-35.77392c0,-0.91728 -0.3276,-1.70352 -0.91728,-2.35872c-0.6552,-0.58968 -1.44144,-0.91728 -2.35872,-0.91728c-0.9828,0 -1.76904,0.3276 -2.35872,0.91728z" id="path4603"/>
|
||||
<path transform="translate(-306.106 -350.552) translate(0 295.491)" stroke="#9b9ea8" fill="#9b9ea8" stroke-miterlimit="2" stroke-width="0" d="m673.56508,102.89048l0,-29.22192c0,-0.9828 -0.3276,-1.76904 -0.91728,-2.42424c-0.6552,-0.58968 -1.44144,-0.91728 -2.42424,-0.91728c-0.9828,0 -1.76904,0.3276 -2.42424,0.91728c-0.6552,0.6552 -0.91728,1.44144 -0.91728,2.42424l0,18.47664c0,1.638 -0.45864,3.01392 -1.3104,4.2588c-0.85176,1.3104 -2.03112,2.2932 -3.53808,3.01392c-1.50696,0.78624 -3.21048,1.11384 -4.97952,1.11384c-3.21048,0 -5.70024,-0.85176 -7.60032,-2.68632c-1.90008,-1.83456 -2.81736,-4.52088 -2.81736,-8.05896l0,-16.11792c0,-0.91728 -0.3276,-1.70352 -0.9828,-2.35872c-0.6552,-0.6552 -1.44144,-0.9828 -2.35872,-0.9828c-0.9828,0 -1.76904,0.3276 -2.42424,0.9828c-0.6552,0.6552 -0.91728,1.44144 -0.91728,2.35872l0,16.11792c0,3.6036 0.6552,6.61752 2.03112,9.10728c1.3104,2.48976 3.21048,4.45536 5.63472,5.70024c2.2932,1.3104 4.78296,1.9656 7.5348,2.03112c0.13104,0 5.17608,0 15.33168,0c0.85176,0 1.57248,-0.3276 2.16216,-0.91728c0.58968,-0.58968 0.91728,-1.3104 0.91728,-2.22768l0,-0.58968z" id="path4605"/>
|
||||
<path transform="translate(-306.106 -350.552) translate(0 295.491)" stroke="#a2a5ae" fill="#a2a5ae" stroke-miterlimit="2" stroke-width="0" d="m717.89364,103.48016c0,-0.85176 -0.3276,-1.638 -0.91728,-2.2932l-10.41768,-12.4488l9.89352,-12.90744c0.58968,-0.78624 0.91728,-1.638 0.91728,-2.6208c0,-0.78624 -0.26208,-1.44144 -0.78624,-1.9656c-0.52416,-0.52416 -1.17936,-0.85176 -2.03112,-0.85176c-1.04832,0 -1.90008,0.39312 -2.42424,1.11384l-9.63144,12.84192l-10.74528,-12.84192c-0.6552,-0.72072 -1.44144,-1.11384 -2.48976,-1.11384c-0.91728,0 -1.638,0.26208 -2.16216,0.78624c-0.52416,0.52416 -0.72072,1.17936 -0.72072,1.90008c0,0.91728 0.3276,1.70352 0.9828,2.48976l10.8108,12.7764l-10.1556,12.71088c-0.6552,0.78624 -0.91728,1.57248 -0.91728,2.42424c0,0.78624 0.19656,1.44144 0.72072,1.9656c0.52416,0.52416 1.17936,0.78624 2.09664,0.78624c0.91728,0 1.70352,-0.3276 2.35872,-1.04832l9.89352,-12.4488l10.41768,12.4488c0.52416,0.72072 1.3104,1.04832 2.35872,1.04832c0.85176,0 1.57248,-0.26208 2.09664,-0.78624c0.52416,-0.52416 0.85176,-1.17936 0.85176,-1.9656z" id="path4607"/>
|
||||
</g>
|
||||
<image width="0" height="0" y="-350.55203" x="-306.10635" id="icon"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 12 KiB |
BIN
resources/src/assets/previews/Card.png
Normal file
After Width: | Height: | Size: 2.8 MiB |
BIN
resources/src/assets/previews/ChatGPT.png
Normal file
After Width: | Height: | Size: 387 KiB |
BIN
resources/src/assets/previews/Color.png
Normal file
After Width: | Height: | Size: 371 KiB |
BIN
resources/src/assets/previews/DashBoard.png
Normal file
After Width: | Height: | Size: 685 KiB |
BIN
resources/src/assets/previews/DataTable.png
Normal file
After Width: | Height: | Size: 580 KiB |
BIN
resources/src/assets/previews/Gradient.png
Normal file
After Width: | Height: | Size: 529 KiB |
BIN
resources/src/assets/previews/Login.png
Normal file
After Width: | Height: | Size: 180 KiB |
BIN
resources/src/assets/previews/TaskBoard.png
Normal file
After Width: | Height: | Size: 294 KiB |
BIN
resources/src/assets/previews/Todo.png
Normal file
After Width: | Height: | Size: 379 KiB |
BIN
resources/src/assets/previews/Unsplash.png
Normal file
After Width: | Height: | Size: 3.1 MiB |
BIN
resources/src/assets/previews/Unsplash2.png
Normal file
After Width: | Height: | Size: 2.0 MiB |
BIN
resources/src/assets/theme-avatar.png
Normal file
After Width: | Height: | Size: 130 KiB |
BIN
resources/src/assets/wechat-qrcode.png
Normal file
After Width: | Height: | Size: 173 KiB |
BIN
resources/src/assets/wechat.jpg
Normal file
After Width: | Height: | Size: 124 KiB |
138
resources/src/components/ApiKeyDialog.vue
Normal file
@@ -0,0 +1,138 @@
|
||||
<!--
|
||||
* @Component:
|
||||
* @Maintainer: J.K. Yang
|
||||
* @Description:
|
||||
-->
|
||||
<script setup lang="ts">
|
||||
import { useChatGPTStore } from "@/stores/chatGPTStore";
|
||||
import { useLocale } from "vuetify";
|
||||
import promptsZh from "@/data/ai/prompts-zh.json";
|
||||
import promptsEn from "@/data/ai/prompts-en.json";
|
||||
import promptsJa from "@/data/ai/prompts-ja.json";
|
||||
const { current } = useLocale();
|
||||
const chatGPTStore = useChatGPTStore();
|
||||
|
||||
const close = () => {
|
||||
chatGPTStore.configDialog = false;
|
||||
};
|
||||
|
||||
const apiKeyShow = ref(false);
|
||||
|
||||
const promptList = computed(() => {
|
||||
if (current.value === "zhHans") {
|
||||
return promptsZh;
|
||||
}
|
||||
if (current.value === "en") {
|
||||
return promptsEn;
|
||||
}
|
||||
if (current.value === "ja") {
|
||||
return promptsJa;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-dialog v-model="chatGPTStore.configDialog" width="600">
|
||||
<v-card>
|
||||
<v-card-title class="font-weight-bold pa-5">
|
||||
{{ $t("chatgpt.config.title") }}</v-card-title
|
||||
>
|
||||
<v-divider />
|
||||
<v-card-text>
|
||||
<!-- ---------------------------------------------- -->
|
||||
<!-- APIKEY -->
|
||||
<!-- ---------------------------------------------- -->
|
||||
|
||||
<v-label class="font-weight-medium mb-2 ml-2">{{
|
||||
$t("chatgpt.config.apikey")
|
||||
}}</v-label>
|
||||
<v-text-field
|
||||
color="primary"
|
||||
variant="outlined"
|
||||
v-model="chatGPTStore.apiKey"
|
||||
class="px-2 py-1"
|
||||
:placeholder="$t('chatgpt.config.apikeyPlaceholder')"
|
||||
prepend-inner-icon="mdi-key"
|
||||
autofocus
|
||||
clearable
|
||||
hide-details
|
||||
@click:prepend-inner="apiKeyShow = !apiKeyShow"
|
||||
></v-text-field>
|
||||
|
||||
<!-- ---------------------------------------------- -->
|
||||
<!-- Proxy Url -->
|
||||
<!-- ---------------------------------------------- -->
|
||||
<v-label class="font-weight-medium mb-2 ml-2 mt-5">{{
|
||||
$t("chatgpt.config.proxyUrl")
|
||||
}}</v-label>
|
||||
<v-text-field
|
||||
color="primary"
|
||||
variant="outlined"
|
||||
v-model="chatGPTStore.proxyUrl"
|
||||
class="px-2 py-1"
|
||||
:placeholder="$t('chatgpt.config.proxyUrlPlaceholder')"
|
||||
prepend-inner-icon="mdi-web"
|
||||
autofocus
|
||||
clearable
|
||||
hide-details
|
||||
>
|
||||
</v-text-field>
|
||||
|
||||
<!-- ---------------------------------------------- -->
|
||||
<!-- Model -->
|
||||
<!-- ---------------------------------------------- -->
|
||||
<v-label class="font-weight-medium mb-2 ml-2 mt-5">{{
|
||||
$t("chatgpt.config.model")
|
||||
}}</v-label>
|
||||
<v-card variant="outlined" style="width: 430px" class="flex ml-2 pa-2">
|
||||
<v-btn
|
||||
:variant="chatGPTStore.model === 'gpt-3.5-turbo' ? 'flat' : 'text'"
|
||||
color="primary"
|
||||
class="flex-fill mr-3"
|
||||
@click="chatGPTStore.updateModel('gpt-3.5-turbo')"
|
||||
width="200"
|
||||
>GPT-3.5</v-btn
|
||||
>
|
||||
<v-btn
|
||||
:variant="
|
||||
chatGPTStore.model === 'gpt-4-turbo-2024-04-09' ? 'flat' : 'text'
|
||||
"
|
||||
color="primary"
|
||||
class="flex-fill"
|
||||
width="200"
|
||||
@click="chatGPTStore.updateModel('gpt-4-turbo-2024-04-09')"
|
||||
>GPT-4 turbo</v-btn
|
||||
>
|
||||
</v-card>
|
||||
|
||||
<!-- ---------------------------------------------- -->
|
||||
<!-- Language -->
|
||||
<!-- ---------------------------------------------- -->
|
||||
<v-label class="font-weight-medium mb-2 ml-2 mt-5">{{
|
||||
$t("chatgpt.config.role")
|
||||
}}</v-label>
|
||||
|
||||
<v-select
|
||||
class="ml-2"
|
||||
v-model="chatGPTStore.propmpt"
|
||||
:items="promptList"
|
||||
item-title="act"
|
||||
item-value="prompt"
|
||||
label="Select"
|
||||
single-line
|
||||
></v-select>
|
||||
|
||||
<!-- ---------------------------------------------- -->
|
||||
<!-- prompts -->
|
||||
<!-- ---------------------------------------------- -->
|
||||
</v-card-text>
|
||||
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn variant="flat" color="primary" @click="close">OK</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
65
resources/src/components/BoardCard.vue
Normal file
@@ -0,0 +1,65 @@
|
||||
<template>
|
||||
<v-card @click="$emit('edit')" class="pa-5 mt-4 card-shadow">
|
||||
<div class="d-flex align-start font-weight-bold text-title">
|
||||
<span class="flex-fill">{{ card.title }}</span>
|
||||
<v-menu location="bottom end" transition="slide-x-transition">
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-btn
|
||||
v-bind="props"
|
||||
size="small"
|
||||
variant="text"
|
||||
icon="mdi-dots-vertical"
|
||||
rounded
|
||||
color="primary"
|
||||
class="my-n2"
|
||||
></v-btn>
|
||||
</template>
|
||||
<v-list density="compact">
|
||||
<v-list-item @click="$emit('edit')">
|
||||
<v-list-item-title class="d-inline-flex align-center">
|
||||
<Icon
|
||||
icon="flat-color-icons:edit-image"
|
||||
:rotate="2"
|
||||
:horizontalFlip="true"
|
||||
:verticalFlip="true"
|
||||
class="mr-1"
|
||||
/>
|
||||
<span> Edit</span>
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item @click="$emit('delete')">
|
||||
<v-list-item-title class="d-inline-flex align-center">
|
||||
<Icon
|
||||
icon="flat-color-icons:full-trash"
|
||||
:rotate="2"
|
||||
:horizontalFlip="true"
|
||||
:verticalFlip="true"
|
||||
:inline="true"
|
||||
class="mr-1"
|
||||
/>
|
||||
Delete</v-list-item-title
|
||||
>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</div>
|
||||
<div class="text-content">{{ props.card.description }}</div>
|
||||
</v-card>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { Icon } from "@iconify/vue";
|
||||
|
||||
const props = defineProps({
|
||||
// Card content to display
|
||||
card: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card-shadow {
|
||||
box-shadow: rgba(99, 99, 99, 0.2) 0px 2px 8px 0px !important;
|
||||
}
|
||||
</style>
|
35
resources/src/components/Breadcrumb.vue
Normal file
@@ -0,0 +1,35 @@
|
||||
<template>
|
||||
<v-breadcrumbs
|
||||
v-if="breadcrumbs.length > 0"
|
||||
:items="breadcrumbs"
|
||||
class="ml-n3 text-body-2"
|
||||
>
|
||||
<!-- <template v-slot:prepend>
|
||||
<v-icon size="small" icon="mdi-vuetify" color="blue"></v-icon>
|
||||
</template> -->
|
||||
</v-breadcrumbs>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const route = useRoute();
|
||||
|
||||
const breadcrumbs = ref<any>([]);
|
||||
|
||||
watchEffect(() => {
|
||||
// if you go to the redirect page, do not update the breadcrumbs
|
||||
// if (route.path.startsWith('/redirect/')) {
|
||||
// return
|
||||
// }
|
||||
if (route.meta && route.meta.title) {
|
||||
breadcrumbs.value = [
|
||||
{
|
||||
title: route.meta.category,
|
||||
disabled: false,
|
||||
},
|
||||
{ title: route.meta.title, disabled: true },
|
||||
];
|
||||
} else {
|
||||
breadcrumbs.value = [];
|
||||
}
|
||||
});
|
||||
</script>
|
199
resources/src/components/CustomizationMenu.vue
Normal file
@@ -0,0 +1,199 @@
|
||||
<script setup lang="ts">
|
||||
import { useTheme } from "vuetify";
|
||||
import { useCustomizeThemeStore } from "@/stores/customizeTheme";
|
||||
import { Icon } from "@iconify/vue";
|
||||
interface Color {
|
||||
colorId: number;
|
||||
colorName: string;
|
||||
colorValue: string;
|
||||
}
|
||||
const customizeTheme = useCustomizeThemeStore();
|
||||
const theme = useTheme();
|
||||
const themeDrawer = ref(false);
|
||||
const currentColor = ref<Color>({
|
||||
colorId: 1,
|
||||
colorName: "purple",
|
||||
colorValue: "#705CF6",
|
||||
});
|
||||
const primaryColors = ref([
|
||||
{
|
||||
colorId: 1,
|
||||
colorName: "purple",
|
||||
colorValue: "#705CF6",
|
||||
},
|
||||
{
|
||||
colorId: 2,
|
||||
colorName: "grey",
|
||||
colorValue: "#344767",
|
||||
},
|
||||
{
|
||||
colorId: 3,
|
||||
colorName: "info",
|
||||
colorValue: "#17C1E8",
|
||||
},
|
||||
{
|
||||
colorId: 4,
|
||||
colorName: "success",
|
||||
colorValue: "#82D616",
|
||||
},
|
||||
{
|
||||
colorId: 5,
|
||||
colorName: "warning",
|
||||
colorValue: "#F2825A",
|
||||
},
|
||||
{
|
||||
colorId: 6,
|
||||
colorName: "error",
|
||||
colorValue: "#EA0606",
|
||||
},
|
||||
]);
|
||||
|
||||
onMounted(() => updatePrimaryColor(customizeTheme.primaryColor));
|
||||
|
||||
watch(currentColor, (newVal) => {
|
||||
updatePrimaryColor(newVal);
|
||||
});
|
||||
|
||||
const updatePrimaryColor = (newColor: Color) => {
|
||||
theme.themes.value.light.colors.primary = newColor.colorValue;
|
||||
theme.themes.value.dark.colors.primary = newColor.colorValue;
|
||||
customizeTheme.setPrimaryColor(newColor);
|
||||
currentColor.value = newColor;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div class="drawer-button" @click="themeDrawer = true">
|
||||
<v-icon class="text-white">mdi-cog-outline</v-icon>
|
||||
</div>
|
||||
|
||||
<v-navigation-drawer
|
||||
v-model="themeDrawer"
|
||||
location="right"
|
||||
temporary
|
||||
width="300"
|
||||
class="theme-drawer"
|
||||
>
|
||||
<div class="pa-5">
|
||||
<div class="top-area">
|
||||
<div class="d-flex align-center">
|
||||
<b>UI Configurator</b>
|
||||
<v-spacer></v-spacer>
|
||||
</div>
|
||||
<div>See our dashboard options.</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-6" />
|
||||
|
||||
<div class="theme-area">
|
||||
<b>Global Theme Mode</b>
|
||||
<div class="px-3 pt-3" v-if="customizeTheme.darkTheme">
|
||||
<v-btn
|
||||
@click="customizeTheme.darkTheme = !customizeTheme.darkTheme"
|
||||
icon
|
||||
color="grey-darken-4"
|
||||
class="text-white"
|
||||
>
|
||||
<Icon width="30" icon="line-md:moon-filled-loop" />
|
||||
</v-btn>
|
||||
<span class="ml-5">Dark Mode</span>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="my-6" />
|
||||
|
||||
<div class="primary-color-area">
|
||||
<b>Primary Colors</b>
|
||||
<v-item-group
|
||||
class="mt-3"
|
||||
v-model="currentColor"
|
||||
selected-class="elevation-12"
|
||||
mandatory
|
||||
>
|
||||
<v-item
|
||||
v-for="color in primaryColors"
|
||||
:key="color.colorId"
|
||||
:value="color"
|
||||
v-slot="{ isSelected, toggle }"
|
||||
>
|
||||
<v-btn
|
||||
@click="toggle"
|
||||
class="text-white mr-1"
|
||||
icon
|
||||
size="30"
|
||||
:color="color.colorValue"
|
||||
>
|
||||
<Icon width="22" v-if="isSelected" icon="line-md:confirm" />
|
||||
</v-btn>
|
||||
</v-item>
|
||||
</v-item-group>
|
||||
</div>
|
||||
<hr class="my-6" />
|
||||
<div class="">
|
||||
<b>MiniSideBar</b>
|
||||
</div>
|
||||
<hr class="mb-6" />
|
||||
<div>
|
||||
<v-btn color="" class="gradient info" block size="large"
|
||||
>Contact Me</v-btn
|
||||
>
|
||||
</div>
|
||||
<div class="ml-5 mt-5 d-flex align-center">
|
||||
<v-icon color="primary" class="mr-6">mdi-email-outline</v-icon>
|
||||
<a href="mailto:yjkbako@gmail.com">yjkbako@gmail.com</a>
|
||||
</div>
|
||||
<div>
|
||||
<img src="@/assets/wechat.jpg" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
</v-navigation-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.drawer-button {
|
||||
position: fixed;
|
||||
background-color: #705cf6;
|
||||
top: 340px;
|
||||
right: -45px;
|
||||
z-index: 999;
|
||||
padding: 0.5rem 1rem;
|
||||
border-top-left-radius: 0.5rem;
|
||||
border-bottom-left-radius: 0.5rem;
|
||||
box-shadow: 1px 1px 9px #705cf6;
|
||||
transition: all 0.5s;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
box-shadow: 1px 1px 18px #705cf6;
|
||||
right: 0px;
|
||||
transition: all 0.5s;
|
||||
}
|
||||
|
||||
.v-icon {
|
||||
font-size: 1.3rem;
|
||||
animation: rotation 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes rotation {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hr {
|
||||
background-image: linear-gradient(
|
||||
90deg,
|
||||
transparent,
|
||||
rgba(0, 0, 0, 0.4),
|
||||
transparent
|
||||
) !important;
|
||||
background-color: transparent;
|
||||
opacity: 0.25;
|
||||
border: none;
|
||||
height: 1px;
|
||||
}
|
||||
</style>
|
36
resources/src/components/FeatureCard.vue
Normal file
@@ -0,0 +1,36 @@
|
||||
<!--
|
||||
* @Component:
|
||||
* @Maintainer: J.K. Yang
|
||||
* @Description:
|
||||
-->
|
||||
<script setup lang="ts">
|
||||
const props = defineProps<{
|
||||
title: string;
|
||||
}>();
|
||||
|
||||
const show = ref(true);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-card class="mb-5">
|
||||
<v-card-title>
|
||||
<span> {{ props.title }}</span>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn v-if="show" icon variant="flat" @click="show = false">
|
||||
<v-icon>mdi-menu-up</v-icon>
|
||||
</v-btn>
|
||||
<v-btn v-else icon variant="flat" @click="show = true">
|
||||
<v-icon>mdi-menu-down</v-icon>
|
||||
</v-btn>
|
||||
</v-card-title>
|
||||
|
||||
<div v-if="show">
|
||||
<v-divider></v-divider>
|
||||
<v-card-text>
|
||||
<slot></slot>
|
||||
</v-card-text>
|
||||
</div>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
27
resources/src/components/GlobalLoading.vue
Normal file
@@ -0,0 +1,27 @@
|
||||
<!--
|
||||
* @Component:
|
||||
* @Maintainer: J.K. Yang
|
||||
* @Description:
|
||||
-->
|
||||
<script setup lang="ts">
|
||||
import { useAppStore } from "@/stores/appStore";
|
||||
import Loading from "@/components/loading/Loading02.vue";
|
||||
const appStore = useAppStore();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-card
|
||||
v-if="appStore.globalLoading"
|
||||
color="white"
|
||||
class="global-loading d-flex align-center justify-center"
|
||||
height="100vh"
|
||||
>
|
||||
<Loading />
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.main-bg {
|
||||
min-height: calc(100vh - 64px);
|
||||
}
|
||||
</style>
|
64
resources/src/components/ImagePreview.vue
Normal file
@@ -0,0 +1,64 @@
|
||||
<!--
|
||||
* @Component:
|
||||
* @Maintainer: J.K. Yang
|
||||
* @Description: PhotoPreviewCard
|
||||
-->
|
||||
<script setup lang="ts">
|
||||
const props = defineProps({
|
||||
thumbnail: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
previewImage: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const showPreview = ref(false);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="image-preview">
|
||||
<v-img
|
||||
class="thumbnail"
|
||||
:src="props.thumbnail"
|
||||
height="100"
|
||||
width="160"
|
||||
cover
|
||||
attrs="props"
|
||||
@mounseh="showPreview = true"
|
||||
@mouseleave="showPreview = false"
|
||||
/>
|
||||
<v-card class="preview-card" v-if="showPreview">
|
||||
<v-img class="preview-image" :src="previewImage" />
|
||||
</v-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.image-preview {
|
||||
position: relative;
|
||||
|
||||
.thumbnail {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.preview-card {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 20px;
|
||||
width: 100%;
|
||||
padding: 5px;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.preview-card {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
19
resources/src/components/LoadingView.vue
Normal file
@@ -0,0 +1,19 @@
|
||||
<!--
|
||||
* @Component:
|
||||
* @Maintainer: J.K. Yang
|
||||
* @Description:
|
||||
-->
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<template>
|
||||
<div class="h-full d-flex align-center justify-center">
|
||||
<v-progress-circular
|
||||
:size="50"
|
||||
:width="5"
|
||||
color="primary"
|
||||
indeterminate
|
||||
></v-progress-circular>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
15
resources/src/components/PageTitle.vue
Normal file
@@ -0,0 +1,15 @@
|
||||
<template>
|
||||
<h1 class="text-h5 mt-5">{{ title }}</h1>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const route = useRoute();
|
||||
|
||||
const title = ref("");
|
||||
|
||||
watchEffect(() => {
|
||||
if (route.meta && route.meta.title) {
|
||||
title.value = route.meta.title as string;
|
||||
}
|
||||
});
|
||||
</script>
|
173
resources/src/components/RichEditorMenubar.vue
Normal file
@@ -0,0 +1,173 @@
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
// import EditorMenu from "./EditorMenu.vue";
|
||||
import { Icon } from "@iconify/vue";
|
||||
const props = defineProps({ editor: Object });
|
||||
const items = ref([
|
||||
{
|
||||
icon: " mdi-format-bold",
|
||||
title: "Bold",
|
||||
action: () => props.editor.chain().focus().toggleBold().run(),
|
||||
isActive: () => props.editor.isActive("bold"),
|
||||
},
|
||||
{
|
||||
icon: " mdi-format-italic",
|
||||
title: "Italic",
|
||||
action: () => props.editor.chain().focus().toggleItalic().run(),
|
||||
isActive: () => props.editor.isActive("italic"),
|
||||
},
|
||||
{
|
||||
icon: " mdi-format-strikethrough-variant",
|
||||
title: "Strike",
|
||||
action: () => props.editor.chain().focus().toggleStrike().run(),
|
||||
|
||||
isActive: () => props.editor.isActive("strike"),
|
||||
},
|
||||
{
|
||||
icon: " mdi-code-tags",
|
||||
title: "Code",
|
||||
action: () => props.editor.chain().focus().toggleCode().run(),
|
||||
|
||||
isActive: () => props.editor.isActive("code"),
|
||||
},
|
||||
{
|
||||
icon: " mdi-format-color-text",
|
||||
title: "Highlight",
|
||||
action: () => props.editor.chain().focus()?.toggleHighlight().run(),
|
||||
|
||||
isActive: () => props.editor.isActive("highlight"),
|
||||
},
|
||||
{
|
||||
type: "divider",
|
||||
},
|
||||
{
|
||||
icon: "mdi-format-header-1",
|
||||
title: "Heading 1",
|
||||
action: () =>
|
||||
props.editor.chain().focus().toggleHeading({ level: 1 }).run(),
|
||||
|
||||
isActive: () => props.editor.isActive("heading", { level: 1 }),
|
||||
},
|
||||
{
|
||||
icon: "mdi-format-header-2",
|
||||
title: "Heading 2",
|
||||
action: () =>
|
||||
props.editor.chain().focus().toggleHeading({ level: 2 }).run(),
|
||||
|
||||
isActive: () => props.editor.isActive("heading", { level: 2 }),
|
||||
},
|
||||
{
|
||||
icon: "mdi-format-paragraph",
|
||||
title: "Paragraph",
|
||||
action: () => props.editor.chain().focus().setParagraph().run(),
|
||||
|
||||
isActive: () => props.editor.isActive("paragraph"),
|
||||
},
|
||||
{
|
||||
icon: "mdi-format-list-bulleted",
|
||||
title: "Bullet List",
|
||||
action: () => props.editor.chain().focus().toggleBulletList().run(),
|
||||
|
||||
isActive: () => props.editor.isActive("bulletList"),
|
||||
},
|
||||
{
|
||||
icon: "mdi-format-list-numbered",
|
||||
title: "Ordered List",
|
||||
action: () => props.editor.chain().focus().toggleOrderedList().run(),
|
||||
|
||||
isActive: () => props.editor.isActive("orderedList"),
|
||||
},
|
||||
{
|
||||
icon: "mdi-format-list-checks",
|
||||
title: "Task List",
|
||||
action: () => props.editor.chain().focus()?.toggleTaskList().run(),
|
||||
|
||||
isActive: () => props.editor.isActive("taskList"),
|
||||
},
|
||||
{
|
||||
icon: "mdi-code-braces",
|
||||
title: "Code Block",
|
||||
action: () => props.editor.chain().focus().toggleCodeBlock().run(),
|
||||
|
||||
isActive: () => props.editor.isActive("codeBlock"),
|
||||
},
|
||||
{
|
||||
type: "divider",
|
||||
},
|
||||
{
|
||||
icon: "mdi-format-quote-open",
|
||||
title: "Blockquote",
|
||||
action: () => props.editor.chain().focus().toggleBlockquote().run(),
|
||||
|
||||
isActive: () => props.editor.isActive("blockquote"),
|
||||
},
|
||||
{
|
||||
icon: "mdi-minus",
|
||||
title: "Horizontal Rule",
|
||||
action: () => props.editor.chain().focus().setHorizontalRule().run(),
|
||||
},
|
||||
{
|
||||
type: "divider",
|
||||
},
|
||||
{
|
||||
icon: "mdi-wrap",
|
||||
title: "Hard Break",
|
||||
action: () => props.editor.chain().focus().setHardBreak().run(),
|
||||
},
|
||||
{
|
||||
title: "Clear Format",
|
||||
icon: "mdi-format-clear",
|
||||
action: () =>
|
||||
props.editor.chain().focus().clearNodes().unsetAllMarks().run(),
|
||||
},
|
||||
{
|
||||
type: "divider",
|
||||
},
|
||||
{
|
||||
icon: "mdi-undo",
|
||||
title: "Undo",
|
||||
action: () => props.editor.chain().focus().undo().run(),
|
||||
},
|
||||
{
|
||||
icon: "mdi-redo",
|
||||
title: "Redo",
|
||||
action: () => props.editor.chain().focus().redo().run(),
|
||||
},
|
||||
]);
|
||||
</script>
|
||||
<template>
|
||||
<perfect-scrollbar class="d-flex align-center menuBar">
|
||||
<template v-for="(item, index) in items">
|
||||
<v-divider
|
||||
thickness="3"
|
||||
class="mx-1"
|
||||
inset
|
||||
vertical
|
||||
v-if="item.type === 'divider'"
|
||||
></v-divider>
|
||||
<v-btn
|
||||
:active="item?.isActive ? item.isActive() : null"
|
||||
icon
|
||||
rounded="0"
|
||||
variant="text"
|
||||
@click="item.action"
|
||||
v-else
|
||||
>
|
||||
<v-icon>{{ item.icon }}</v-icon>
|
||||
<v-tooltip
|
||||
activator="parent"
|
||||
location="top"
|
||||
:text="item.title"
|
||||
></v-tooltip>
|
||||
</v-btn>
|
||||
</template>
|
||||
</perfect-scrollbar>
|
||||
</template>
|
||||
<style lang="scss">
|
||||
.menuBar {
|
||||
padding: 8px;
|
||||
overflow: auto;
|
||||
border-radius: 12px 12px 0 0;
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
</style>
|
122
resources/src/components/Toolbox.vue
Normal file
@@ -0,0 +1,122 @@
|
||||
<!--
|
||||
* @Component: BackToTop
|
||||
* @Maintainer: J.K. Yang
|
||||
* @Description:
|
||||
-->
|
||||
<script setup lang="ts">
|
||||
import { Icon } from "@iconify/vue";
|
||||
import ChatAssistant from "@/components/ai/ChatAssistant.vue";
|
||||
import TranslationAssistant from "@/components/ai/TranslationAssistant.vue";
|
||||
import { useChatGPTStore } from "@/stores/chatGPTStore";
|
||||
import ApiKeyDialog from "@/components/ApiKeyDialog.vue";
|
||||
const chatGPTStore = useChatGPTStore();
|
||||
const toolboxShow = ref(false);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-btn
|
||||
class="toolbox-activator elevation-10"
|
||||
@click="toolboxShow = !toolboxShow"
|
||||
size="50"
|
||||
color="white"
|
||||
>
|
||||
<Icon width="30" icon="ri:openai-fill" />
|
||||
</v-btn>
|
||||
|
||||
<transition name="slide-y">
|
||||
<v-card
|
||||
v-if="toolboxShow"
|
||||
elevation="10"
|
||||
class="d-flex flex-column mb-1 toolbox"
|
||||
>
|
||||
<!-- ---------------------------------------------- -->
|
||||
<!-- Close Btn -->
|
||||
<!-- ---------------------------------------------- -->
|
||||
<v-btn
|
||||
@click="toolboxShow = false"
|
||||
variant="text"
|
||||
size="50"
|
||||
color="error"
|
||||
>
|
||||
<v-icon size="30">mdi-close</v-icon>
|
||||
<v-tooltip
|
||||
activator="parent"
|
||||
location="left"
|
||||
text="Close Toolbox"
|
||||
></v-tooltip>
|
||||
</v-btn>
|
||||
<v-divider />
|
||||
<!-- ---------------------------------------------- -->
|
||||
<!-- ApiKey -->
|
||||
<!-- ---------------------------------------------- -->
|
||||
<v-btn
|
||||
@click="chatGPTStore.configDialog = true"
|
||||
variant="text"
|
||||
size="50"
|
||||
color="blue"
|
||||
>
|
||||
<v-icon size="30">mdi-key-outline</v-icon>
|
||||
<v-tooltip
|
||||
activator="parent"
|
||||
location="left"
|
||||
:text="$t('toolbox.chatgptConfig.title')"
|
||||
></v-tooltip>
|
||||
</v-btn>
|
||||
<ApiKeyDialog />
|
||||
<v-divider />
|
||||
<!-- ---------------------------------------------- -->
|
||||
<!-- Chat Assistant -->
|
||||
<!-- ---------------------------------------------- -->
|
||||
<ChatAssistant />
|
||||
<v-divider />
|
||||
<!-- ---------------------------------------------- -->
|
||||
<!-- Translation Assistant -->
|
||||
<!-- ---------------------------------------------- -->
|
||||
<TranslationAssistant />
|
||||
<v-divider />
|
||||
<!-- ---------------------------------------------- -->
|
||||
<!-- Code Assistant -->
|
||||
<!-- ---------------------------------------------- -->
|
||||
<v-btn size="50">
|
||||
<v-icon size="30">mdi-code-tags</v-icon>
|
||||
<v-tooltip
|
||||
activator="parent"
|
||||
location="left"
|
||||
:text="$t('toolbox.codeAssistant.title')"
|
||||
></v-tooltip>
|
||||
</v-btn>
|
||||
<!-- ---------------------------------------------- -->
|
||||
<!-- Code Assistant -->
|
||||
<!-- ---------------------------------------------- -->
|
||||
<v-btn size="50" to="/playground">
|
||||
<v-icon size="30">mdi-seesaw</v-icon>
|
||||
<v-tooltip
|
||||
activator="parent"
|
||||
location="left"
|
||||
:text="$t('toolbox.playGround.title')"
|
||||
></v-tooltip>
|
||||
</v-btn>
|
||||
</v-card>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.toolbox {
|
||||
z-index: 999;
|
||||
position: fixed;
|
||||
bottom: 150px;
|
||||
right: 5px;
|
||||
}
|
||||
|
||||
.toolbox-activator {
|
||||
position: fixed;
|
||||
transition: all 0.3s ease;
|
||||
bottom: 100px;
|
||||
right: 5px;
|
||||
z-index: 999;
|
||||
padding: 0.5rem;
|
||||
border-radius: 0.5rem;
|
||||
transition: all 0.3s;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
381
resources/src/components/ai/ChatAssistant.vue
Normal file
@@ -0,0 +1,381 @@
|
||||
<!--
|
||||
* @Component:
|
||||
* @Maintainer: J.K. Yang
|
||||
* @Description:
|
||||
-->
|
||||
<script setup lang="ts">
|
||||
import { useDisplay } from "vuetify";
|
||||
import { useSnackbarStore } from "@/stores/snackbarStore";
|
||||
import AnimationAi from "@/components/animations/AnimationBot1.vue";
|
||||
import { read, countAndCompleteCodeBlocks } from "@/utils/aiUtils";
|
||||
import { scrollToBottom } from "@/utils/common";
|
||||
import { MdPreview } from "md-editor-v3";
|
||||
import "md-editor-v3/lib/preview.css";
|
||||
|
||||
import { useChatGPTStore } from "@/stores/chatGPTStore";
|
||||
|
||||
const snackbarStore = useSnackbarStore();
|
||||
const chatGPTStore = useChatGPTStore();
|
||||
|
||||
interface Message {
|
||||
content: string;
|
||||
role: "user" | "assistant" | "system";
|
||||
}
|
||||
// User Input Message
|
||||
const userMessage = ref("");
|
||||
|
||||
// Prompt Message
|
||||
const promptMessage = computed(() => {
|
||||
console.log("chatGPTStore.propmpt", chatGPTStore.propmpt);
|
||||
|
||||
return [
|
||||
{
|
||||
content: chatGPTStore.propmpt,
|
||||
role: "system",
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
// Message List
|
||||
const messages = ref<Message[]>([]);
|
||||
|
||||
const requestMessages = computed(() => {
|
||||
if (messages.value.length <= 10) {
|
||||
return [...promptMessage.value, ...messages.value];
|
||||
} else {
|
||||
// 截取最新的10条信息
|
||||
const slicedMessages = messages.value.slice(-10);
|
||||
return [...promptMessage.value, ...slicedMessages];
|
||||
}
|
||||
});
|
||||
|
||||
const isLoading = ref(false);
|
||||
|
||||
// Send Messsage
|
||||
const sendMessage = async () => {
|
||||
if (userMessage.value) {
|
||||
// Add the message to the list
|
||||
messages.value.push({
|
||||
content: userMessage.value,
|
||||
role: "user",
|
||||
});
|
||||
|
||||
// Clear the input
|
||||
userMessage.value = "";
|
||||
|
||||
// Create a completion
|
||||
await createCompletion();
|
||||
}
|
||||
};
|
||||
|
||||
const createCompletion = async () => {
|
||||
// Check if the API key is set
|
||||
// if (!chatGPTStore.getApiKey) {
|
||||
// snackbarStore.showErrorMessage("请先输入API KEY");
|
||||
// return;
|
||||
// }
|
||||
|
||||
try {
|
||||
// Create a completion (axios is not used here because it does not support streaming)
|
||||
const completion = await fetch(
|
||||
`${chatGPTStore.proxyUrl}/v1/chat/completions`,
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${chatGPTStore.getApiKey}`,
|
||||
},
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
messages: requestMessages.value,
|
||||
model: chatGPTStore.model,
|
||||
stream: true,
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
// Handle errors
|
||||
if (!completion.ok) {
|
||||
const errorData = await completion.json();
|
||||
snackbarStore.showErrorMessage(errorData.error.message);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a reader
|
||||
const reader = completion.body?.getReader();
|
||||
if (!reader) {
|
||||
snackbarStore.showErrorMessage("Cannot read the stream.");
|
||||
}
|
||||
|
||||
// Add the bot message
|
||||
messages.value.push({
|
||||
content: "",
|
||||
role: "assistant",
|
||||
});
|
||||
|
||||
// Read the stream
|
||||
read(reader, messages);
|
||||
} catch (error) {
|
||||
snackbarStore.showErrorMessage(error.message);
|
||||
}
|
||||
};
|
||||
|
||||
watch(
|
||||
() => messages.value,
|
||||
(val) => {
|
||||
if (val) {
|
||||
scrollToBottom(document.querySelector(".message-container"));
|
||||
}
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
}
|
||||
);
|
||||
|
||||
const displayMessages = computed(() => {
|
||||
const messagesCopy = messages.value.slice(); // 创建原始数组的副本
|
||||
const lastMessage = messagesCopy[messagesCopy.length - 1];
|
||||
const updatedLastMessage = {
|
||||
...lastMessage,
|
||||
content: countAndCompleteCodeBlocks(lastMessage.content),
|
||||
};
|
||||
messagesCopy[messagesCopy.length - 1] = updatedLastMessage;
|
||||
return messagesCopy;
|
||||
});
|
||||
|
||||
const handleKeydown = (e) => {
|
||||
if (e.key === "Enter" && (e.altKey || e.shiftKey)) {
|
||||
// 当同时按下 alt或者shift 和 enter 时,插入一个换行符
|
||||
e.preventDefault();
|
||||
userMessage.value += "\n";
|
||||
} else if (e.key === "Enter") {
|
||||
// 当只按下 enter 时,发送消息
|
||||
e.preventDefault();
|
||||
sendMessage();
|
||||
}
|
||||
};
|
||||
|
||||
const inputRow = ref(1);
|
||||
|
||||
const dialog = ref(false);
|
||||
const { xs } = useDisplay();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-btn size="50" @click="dialog = !dialog">
|
||||
<v-icon size="30">mdi-chat-outline </v-icon>
|
||||
<v-tooltip
|
||||
activator="parent"
|
||||
location="left"
|
||||
:text="$t('toolbox.chatAssistant.title')"
|
||||
></v-tooltip>
|
||||
</v-btn>
|
||||
|
||||
<teleport to="body">
|
||||
<transition name="slide-y">
|
||||
<v-card
|
||||
v-if="dialog"
|
||||
class="dialog-bottom d-flex flex-column"
|
||||
:width="xs ? '100%' : '600px'"
|
||||
height="500px"
|
||||
>
|
||||
<v-card-title>
|
||||
<span class="flex-fill">
|
||||
<v-avatar size="40">
|
||||
<img
|
||||
src="https://img.icons8.com/color/96/null/filled-chat.png"
|
||||
alt="alt"
|
||||
/>
|
||||
</v-avatar>
|
||||
|
||||
OpenAi Chat
|
||||
</span>
|
||||
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn icon @click.stop="dialog = false">
|
||||
<v-icon>mdi-close</v-icon>
|
||||
</v-btn>
|
||||
</v-card-title>
|
||||
<v-divider />
|
||||
<v-card-text class="overflow-scroll">
|
||||
<perfect-scrollbar
|
||||
v-if="messages.length > 0"
|
||||
class="message-container"
|
||||
>
|
||||
<template v-for="message in displayMessages">
|
||||
<div v-if="message.role === 'user'">
|
||||
<div class="pa-2 user-message">
|
||||
<v-avatar class="ml-2" rounded="sm" variant="elevated">
|
||||
<img
|
||||
src="@/assets/images/avatars/avatar_user.jpg"
|
||||
alt="alt"
|
||||
/>
|
||||
</v-avatar>
|
||||
<v-card class="gradient gray text-pre-wrap" theme="dark">
|
||||
<v-card-text>
|
||||
<b> {{ message.content }}</b></v-card-text
|
||||
>
|
||||
</v-card>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="pa-2 assistant-message">
|
||||
<v-avatar
|
||||
class="d-none d-md-block mr-2"
|
||||
rounded="sm"
|
||||
variant="elevated"
|
||||
>
|
||||
<img
|
||||
src="@/assets/images/avatars/avatar_assistant.jpg"
|
||||
alt="alt"
|
||||
/>
|
||||
</v-avatar>
|
||||
<v-card>
|
||||
<div>
|
||||
<md-preview
|
||||
:modelValue="message.content"
|
||||
class="font-1"
|
||||
/>
|
||||
</div>
|
||||
</v-card>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div v-if="isLoading">
|
||||
<div class="pa-6">
|
||||
<div class="message">
|
||||
<AnimationAi :size="100" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</perfect-scrollbar>
|
||||
</v-card-text>
|
||||
<v-divider />
|
||||
|
||||
<v-sheet
|
||||
color="transparent"
|
||||
elevation="0"
|
||||
class="d-flex align-end justify-center pa-2"
|
||||
>
|
||||
<v-btn
|
||||
class="mb-1"
|
||||
variant="elevated"
|
||||
size="42"
|
||||
icon
|
||||
@click="chatGPTStore.configDialog = true"
|
||||
>
|
||||
<v-icon size="30" class="text-primary">mdi-cog-outline</v-icon>
|
||||
<v-tooltip
|
||||
activator="parent"
|
||||
location="top"
|
||||
text="ChatGPT Config"
|
||||
></v-tooltip>
|
||||
</v-btn>
|
||||
|
||||
<v-textarea
|
||||
class="mx-2"
|
||||
color="primary"
|
||||
type="text"
|
||||
clearable
|
||||
variant="solo"
|
||||
ref="input"
|
||||
v-model="userMessage"
|
||||
placeholder="Ask Anything"
|
||||
hide-details
|
||||
@keydown="handleKeydown"
|
||||
:rows="inputRow"
|
||||
@focus="inputRow = 2"
|
||||
@blur="inputRow = 1"
|
||||
>
|
||||
</v-textarea>
|
||||
|
||||
<v-btn size="42" class="mb-1" color="primary" variant="elevated" icon>
|
||||
<v-icon @click="sendMessage" size="20">mdi-send</v-icon>
|
||||
</v-btn>
|
||||
</v-sheet>
|
||||
</v-card>
|
||||
</transition>
|
||||
</teleport>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.dialog-bottom {
|
||||
z-index: 999;
|
||||
position: fixed;
|
||||
bottom: 10px;
|
||||
right: 0px;
|
||||
}
|
||||
|
||||
.chat-bot {
|
||||
background-repeat: repeat;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
|
||||
.messsage-area {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.input-area {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
padding: 1rem;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.user-message {
|
||||
display: flex;
|
||||
align-content: center;
|
||||
justify-content: end;
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
.assistant-message {
|
||||
display: flex;
|
||||
align-content: center;
|
||||
justify-content: start;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.message {
|
||||
margin: 0 auto;
|
||||
max-width: 1200px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.message-container {
|
||||
height: 300px;
|
||||
|
||||
background-repeat: repeat;
|
||||
}
|
||||
|
||||
.no-message-container {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
|
||||
h1 {
|
||||
font-size: 2rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.md-editor-preview-wrapper) {
|
||||
padding: 5px 15px;
|
||||
}
|
||||
|
||||
.font-1 {
|
||||
font-size: 13px !important;
|
||||
}
|
||||
|
||||
:deep(#md-editor-v3-preview),
|
||||
.user-message {
|
||||
font-size: 14px !important;
|
||||
}
|
||||
</style>
|
388
resources/src/components/ai/TranslationAssistant.vue
Normal file
@@ -0,0 +1,388 @@
|
||||
<!--
|
||||
* @Component:
|
||||
* @Maintainer: J.K. Yang
|
||||
* @Description:
|
||||
-->
|
||||
<script setup lang="ts">
|
||||
import { createTranscriptionApi } from "@/api/aiApi";
|
||||
import { useChatGPTStore } from "@/stores/chatGPTStore";
|
||||
import CopyBtn from "@/components/common/CopyBtn.vue";
|
||||
import { useDisplay } from "vuetify";
|
||||
import { read } from "@/utils/aiUtils";
|
||||
import { useSnackbarStore } from "@/stores/snackbarStore";
|
||||
import { useSpeechStore } from "@/stores/speechStore";
|
||||
import { Icon } from "@iconify/vue";
|
||||
const speechStore = useSpeechStore();
|
||||
const snackbarStore = useSnackbarStore();
|
||||
const chatGPTStore = useChatGPTStore();
|
||||
const langs = [
|
||||
{
|
||||
code: "en",
|
||||
name: "English",
|
||||
label: "English",
|
||||
},
|
||||
{
|
||||
code: "zh-CN",
|
||||
name: "Chinese Simplified",
|
||||
label: "中文(简体)",
|
||||
},
|
||||
{
|
||||
code: "zh-TW",
|
||||
name: "Chinese Traditional",
|
||||
label: "中文(繁體)",
|
||||
},
|
||||
{
|
||||
code: "ja",
|
||||
name: "Japanese",
|
||||
label: "日本語",
|
||||
},
|
||||
{
|
||||
code: "ko",
|
||||
name: "Korean",
|
||||
label: "한국어",
|
||||
},
|
||||
{
|
||||
code: "fr",
|
||||
name: "French",
|
||||
label: "Français",
|
||||
},
|
||||
{
|
||||
code: "de",
|
||||
name: "German",
|
||||
label: "Deutsch",
|
||||
},
|
||||
{
|
||||
code: "es",
|
||||
name: "Spanish",
|
||||
label: "Español",
|
||||
},
|
||||
];
|
||||
|
||||
const currentLang = ref({
|
||||
code: "en",
|
||||
name: "English",
|
||||
label: "English",
|
||||
});
|
||||
const setLang = (lang: any) => {
|
||||
currentLang.value = lang;
|
||||
};
|
||||
|
||||
const baseContent = ref("");
|
||||
const targetContent = ref("");
|
||||
|
||||
const prompt = computed(() => {
|
||||
return `Translate into ${currentLang.value.name}`;
|
||||
// return `I want you to act as an ${currentLangName.value} translator, spelling corrector and improver. I will speak to you in any language and you will detect the language, translate it and answer in the corrected and improved version of my text, in ${currentLang.value.name}. I want you to replace my simplified A0-level words and sentences with more beautiful and elegant, upper level ${currentLang.value.name} words and sentences. Keep the meaning same, but make them more literary. I want you to only reply the correction, the improvements and nothing else, do not write explanations.”`;
|
||||
});
|
||||
|
||||
const isLoading = ref(false);
|
||||
const translate = async () => {
|
||||
if (baseContent.value === "") {
|
||||
snackbarStore.showErrorMessage("请输入要翻译的内容");
|
||||
return;
|
||||
}
|
||||
|
||||
// if (!chatGPTStore.getApiKey) {
|
||||
// snackbarStore.showErrorMessage("请先输入API KEY");
|
||||
// return;
|
||||
// }
|
||||
isLoading.value = true;
|
||||
try {
|
||||
const completion = await fetch(
|
||||
`${chatGPTStore.proxyUrl}/v1/chat/completions`,
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${chatGPTStore.getApiKey}`,
|
||||
},
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
messages: [
|
||||
{ role: "user", content: prompt.value },
|
||||
{ role: "user", content: baseContent.value },
|
||||
],
|
||||
model: chatGPTStore.model,
|
||||
stream: true,
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
// Handle errors
|
||||
if (!completion.ok) {
|
||||
const errorData = await completion.json();
|
||||
snackbarStore.showErrorMessage(errorData.error.message);
|
||||
isLoading.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a reader
|
||||
const reader = completion.body?.getReader();
|
||||
if (!reader) {
|
||||
snackbarStore.showErrorMessage("Cannot read the stream.");
|
||||
isLoading.value = false;
|
||||
}
|
||||
|
||||
// Clear the target content
|
||||
targetContent.value = "";
|
||||
|
||||
// Read the stream
|
||||
read(reader, targetContent);
|
||||
} catch (error) {
|
||||
snackbarStore.showErrorMessage(error.message);
|
||||
}
|
||||
isLoading.value = false;
|
||||
};
|
||||
|
||||
const isBaseContentEmpty = ref(false);
|
||||
const recorder = ref<any>();
|
||||
const isRecording = ref(false);
|
||||
|
||||
const startRecording = async () => {
|
||||
// 获取用户媒体权限,视频的话参数{audio: true, video: true}
|
||||
navigator.mediaDevices
|
||||
.getUserMedia({ audio: true })
|
||||
.then((stream) => {
|
||||
// 创建媒体流
|
||||
recorder.value = new MediaRecorder(stream);
|
||||
const audioChunks = <any>[];
|
||||
// 录音开始
|
||||
recorder.value.start();
|
||||
isRecording.value = true;
|
||||
// 录音数据
|
||||
recorder.value.ondataavailable = (e: any) => {
|
||||
audioChunks.push(e.data);
|
||||
};
|
||||
// 录音结束
|
||||
recorder.value.onstop = async (e: any) => {
|
||||
const blob = new Blob(audioChunks, { type: "audio/wav" });
|
||||
const file = new File([blob], "recording.wav", {
|
||||
type: "audio/wav",
|
||||
});
|
||||
const formData = new FormData();
|
||||
formData.append("file", file);
|
||||
formData.append("model", "whisper-1");
|
||||
const res = await createTranscriptionApi(
|
||||
formData,
|
||||
chatGPTStore.getApiKey
|
||||
);
|
||||
baseContent.value = res.data.text;
|
||||
};
|
||||
})
|
||||
.catch((error) => {
|
||||
snackbarStore.showErrorMessage(error.message);
|
||||
});
|
||||
};
|
||||
|
||||
const stopRecording = () => {
|
||||
if (recorder.value) {
|
||||
recorder.value.stop();
|
||||
isRecording.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const record = () => {
|
||||
if (isRecording.value) {
|
||||
stopRecording();
|
||||
} else {
|
||||
startRecording();
|
||||
}
|
||||
};
|
||||
|
||||
const dialog = ref(false);
|
||||
const { xs } = useDisplay();
|
||||
|
||||
const readText = () => {
|
||||
if (targetContent.value) {
|
||||
speechStore.ssmlToSpeak(targetContent.value);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-btn size="50" @click="dialog = !dialog">
|
||||
<v-icon size="30">mdi-google-translate</v-icon>
|
||||
<v-tooltip
|
||||
activator="parent"
|
||||
location="left"
|
||||
:text="$t('toolbox.translationAssistant.title')"
|
||||
></v-tooltip>
|
||||
</v-btn>
|
||||
|
||||
<teleport to="body">
|
||||
<transition name="slide-y">
|
||||
<v-card
|
||||
v-if="dialog"
|
||||
class="dialog-bottom d-flex flex-column"
|
||||
:width="xs ? '100%' : '600px'"
|
||||
>
|
||||
<v-card-title>
|
||||
<span class="flex-fill">
|
||||
<v-avatar size="40">
|
||||
<v-img
|
||||
src="https://img.icons8.com/color/96/null/translation.png"
|
||||
/>
|
||||
</v-avatar>
|
||||
|
||||
OpenAi {{ $t("toolbox.translationAssistant.title") }}
|
||||
</span>
|
||||
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn icon @click.stop="dialog = false">
|
||||
<v-icon>mdi-close</v-icon>
|
||||
</v-btn>
|
||||
</v-card-title>
|
||||
<v-divider />
|
||||
<v-card-actions class="px-5">
|
||||
<span class="text-body-2"
|
||||
>{{ $t("toolbox.translationAssistant.targetLanguage") }}:</span
|
||||
>
|
||||
<!-- <v-btn-toggle
|
||||
v-model="currentLang"
|
||||
density="compact"
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
mandatory
|
||||
>
|
||||
<v-btn
|
||||
density="compact"
|
||||
size="small"
|
||||
v-for="lang in langs"
|
||||
:value="lang.code"
|
||||
>
|
||||
{{ lang.label }}
|
||||
</v-btn>
|
||||
</v-btn-toggle> -->
|
||||
<v-menu location="bottom end" scroll-y>
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-btn width="108" append-icon="mdi-menu-down" v-bind="props">
|
||||
<span class="text-body-2">{{ currentLang.label }}</span>
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-card>
|
||||
<div v-for="lang in langs">
|
||||
<v-btn block @click="setLang(lang)">{{ lang.label }}</v-btn>
|
||||
</div>
|
||||
</v-card>
|
||||
</v-menu>
|
||||
|
||||
<v-spacer></v-spacer>
|
||||
|
||||
<v-btn
|
||||
class="ml-2 text-white"
|
||||
:loading="isLoading"
|
||||
:disabled="isLoading"
|
||||
variant="elevated"
|
||||
color="primary"
|
||||
@click="translate"
|
||||
>{{ $t("toolbox.translationAssistant.translate") }}</v-btn
|
||||
>
|
||||
</v-card-actions>
|
||||
<v-divider />
|
||||
<v-card-text>
|
||||
<v-row no-gutters justify="center" dense>
|
||||
<v-col cols="12">
|
||||
<v-card elevation="0">
|
||||
<div class="pa-2">
|
||||
<v-textarea
|
||||
v-model="baseContent"
|
||||
hide-details
|
||||
variant="solo"
|
||||
class="elevation-1"
|
||||
color="white"
|
||||
clearable
|
||||
@focus="isBaseContentEmpty = false"
|
||||
:placeholder="
|
||||
$t(
|
||||
'toolbox.translationAssistant.sourceLanguagePlaceholder'
|
||||
)
|
||||
"
|
||||
></v-textarea>
|
||||
</div>
|
||||
<v-card-actions class="bg-grey-lighten-4 text-primary">
|
||||
<v-tooltip
|
||||
v-if="!isRecording"
|
||||
location="bottom"
|
||||
:text="$t('toolbox.translationAssistant.speech')"
|
||||
>
|
||||
<template #activator="{ props }">
|
||||
<v-btn @click="record" v-bind="props" icon>
|
||||
<v-icon v-if="isRecording">mdi-microphone</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-tooltip>
|
||||
<v-tooltip
|
||||
location="bottom"
|
||||
:text="$t('toolbox.translationAssistant.stopSpeech')"
|
||||
v-else
|
||||
>
|
||||
<template #activator="{ props }">
|
||||
<v-btn @click="record" v-bind="props" icon>
|
||||
<Icon icon="svg-spinners:bars-scale-fade" />
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-tooltip>
|
||||
<!-- <v-tooltip
|
||||
location="bottom"
|
||||
:text="$t('toolbox.translationAssistant.read')"
|
||||
>
|
||||
<template #activator="{ props }">
|
||||
<v-btn v-bind="props" icon
|
||||
><v-icon>mdi-volume-high</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-tooltip> -->
|
||||
<v-spacer></v-spacer>
|
||||
<CopyBtn :text="baseContent" />
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12">
|
||||
<v-card elevation="0">
|
||||
<div class="pa-2">
|
||||
<v-textarea
|
||||
v-model="targetContent"
|
||||
hide-details
|
||||
variant="solo"
|
||||
class="elevation-1"
|
||||
color="primary"
|
||||
clearable
|
||||
:placeholder="
|
||||
$t(
|
||||
'toolbox.translationAssistant.targetLanguagePlaceholder'
|
||||
)
|
||||
"
|
||||
></v-textarea>
|
||||
</div>
|
||||
<v-card-actions
|
||||
class="bg-grey-lighten-4 bg-grey-lighten-4 text-primary"
|
||||
>
|
||||
<v-tooltip
|
||||
location="bottom"
|
||||
:text="$t('toolbox.translationAssistant.read')"
|
||||
>
|
||||
<template #activator="{ props }">
|
||||
<v-btn @click="readText" v-bind="props" icon
|
||||
><v-icon>mdi-volume-high</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-tooltip>
|
||||
<v-spacer></v-spacer>
|
||||
<CopyBtn :text="targetContent" />
|
||||
</v-card-actions> </v-card
|
||||
></v-col> </v-row
|
||||
></v-card-text>
|
||||
<v-divider />
|
||||
</v-card>
|
||||
</transition>
|
||||
</teleport>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.dialog-bottom {
|
||||
z-index: 999;
|
||||
position: fixed;
|
||||
bottom: 10px;
|
||||
right: 0px;
|
||||
}
|
||||
</style>
|
211
resources/src/components/ai/VoiceConfigDialog.vue
Normal file
@@ -0,0 +1,211 @@
|
||||
<!--
|
||||
* @Component:
|
||||
* @Maintainer: J.K. Yang
|
||||
* @Description:
|
||||
-->
|
||||
<script setup lang="ts">
|
||||
import { useSpeechStore } from "@/stores/speechStore";
|
||||
import type { VoiceInfo } from "microsoft-cognitiveservices-speech-sdk";
|
||||
import { useSpeechService } from "@/hooks/useSpeechService";
|
||||
const { getVoices } = useSpeechService();
|
||||
const speechStore = useSpeechStore();
|
||||
|
||||
const allVoices = ref<VoiceInfo[]>([]);
|
||||
const selectVoice = (voiceInfo: VoiceInfo) => {
|
||||
speechStore.updateVoiceInfo(voiceInfo);
|
||||
};
|
||||
|
||||
const languages = ref(["ja-JP", "en-US", "zh-CN", "zh-HK"]);
|
||||
const getVoiceInfo = async () => {
|
||||
const res = await getVoices();
|
||||
allVoices.value = res.filter((voice) =>
|
||||
languages.value.includes(voice.locale)
|
||||
);
|
||||
|
||||
const currentVoice = allVoices.value.find(
|
||||
(voice) => voice.shortName === speechStore.speechSynthesisVoiceName
|
||||
) as VoiceInfo;
|
||||
|
||||
speechStore.updateVoiceInfo(currentVoice);
|
||||
|
||||
// fr-FR 法语 ja-JP 日语 en-US 英语 zh-CN 中文 zh-HK 粤语 ko-KR 韩语 de-DE 德语
|
||||
};
|
||||
|
||||
watch(
|
||||
() => speechStore.voiceConfigDialog,
|
||||
() => {
|
||||
getVoiceInfo();
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
getVoiceInfo();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-btn variant="elevated" icon @click="speechStore.voiceConfigDialog = true">
|
||||
<v-icon size="30" class="text-primary">mdi-cog-outline</v-icon>
|
||||
<v-tooltip
|
||||
activator="parent"
|
||||
location="top"
|
||||
text="Voice Config"
|
||||
></v-tooltip>
|
||||
</v-btn>
|
||||
|
||||
<v-dialog v-model="speechStore.voiceConfigDialog" width="500">
|
||||
<v-card>
|
||||
<v-card-title>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn
|
||||
variant="flat"
|
||||
icon
|
||||
@click="speechStore.voiceConfigDialog = false"
|
||||
><v-icon>mdi-close</v-icon></v-btn
|
||||
>
|
||||
</v-card-title>
|
||||
<v-container>
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
<v-card style="border-left: 5px solid #445573">
|
||||
<v-card-title> Voice Modal </v-card-title>
|
||||
<v-divider></v-divider>
|
||||
<v-list elevation="1" density="compact">
|
||||
<v-list-subheader
|
||||
><span>Total {{ allVoices.length }} Voices</span>
|
||||
<span class="ml-2 font-weight-bold"
|
||||
>(Current Modal: {{ speechStore.localName }}
|
||||
<v-chip
|
||||
density="comfortable"
|
||||
class="d-none d-sm-inline ml-1 font-weight-bold"
|
||||
label
|
||||
size="small"
|
||||
color="primary"
|
||||
>
|
||||
{{ speechStore.speechSynthesisLanguage }}</v-chip
|
||||
>
|
||||
)</span
|
||||
>
|
||||
</v-list-subheader>
|
||||
<RecycleScroller
|
||||
class="scroller"
|
||||
:items="allVoices"
|
||||
:item-size="50"
|
||||
key-field="name"
|
||||
v-slot="{ item }"
|
||||
>
|
||||
<v-list-item
|
||||
active-color="primary"
|
||||
@click="selectVoice(item)"
|
||||
:active="
|
||||
item.shortName === speechStore.speechSynthesisVoiceName
|
||||
"
|
||||
three-line
|
||||
>
|
||||
<!-- ---------------------------------------------- -->
|
||||
<!-- Prepend-->
|
||||
<!-- ---------------------------------------------- -->
|
||||
<template v-slot:prepend>
|
||||
<!-- <span class="mr-3">{{ item.id }}</span> -->
|
||||
<v-avatar v-if="item.gender === 1" size="30" color="red">
|
||||
<img
|
||||
src="https://img.icons8.com/bubbles/50/null/lock-female-user.png"
|
||||
/>
|
||||
</v-avatar>
|
||||
<v-avatar v-else size="30" color="blue">
|
||||
<img
|
||||
src="https://img.icons8.com/bubbles/50/null/user-male.png"
|
||||
/>
|
||||
</v-avatar>
|
||||
</template>
|
||||
<!-- ---------------------------------------------- -->
|
||||
<!-- Append-->
|
||||
<!-- ---------------------------------------------- -->
|
||||
<template v-slot:append>
|
||||
<div class="full-h d-flex align-center">
|
||||
<span class="text-body-2 text-grey">
|
||||
<v-chip
|
||||
size="small"
|
||||
class="font-weight-bold"
|
||||
color="blue"
|
||||
>{{ item.locale }}</v-chip
|
||||
>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<!-- ---------------------------------------------- -->
|
||||
<!-- Main Content-->
|
||||
<!-- ---------------------------------------------- -->
|
||||
<div>
|
||||
<v-list-item-title class="font-weight-bold text-primary"
|
||||
>{{ item.localName }}
|
||||
<span class="text-body-2 ml-2"
|
||||
>({{ item.shortName }})</span
|
||||
></v-list-item-title
|
||||
>
|
||||
</div>
|
||||
</v-list-item>
|
||||
</RecycleScroller>
|
||||
</v-list>
|
||||
</v-card>
|
||||
</v-col>
|
||||
<v-col cols="12">
|
||||
<v-card style="border-left: 5px solid #445573">
|
||||
<v-card-title> Voice Config </v-card-title>
|
||||
<v-divider></v-divider>
|
||||
<v-card-text>
|
||||
<!-- Rate -->
|
||||
|
||||
<v-row class="align-center">
|
||||
<v-col cols="12" sm="2" class="pb-sm-3 pb-0">
|
||||
<v-label class="font-weight-medium">Rate</v-label>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="10">
|
||||
<v-slider
|
||||
v-model="speechStore.voiceRate"
|
||||
thumb-label="always"
|
||||
:min="0.1"
|
||||
:max="2"
|
||||
:step="0.1"
|
||||
hide-details
|
||||
></v-slider>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<!-- Emotion -->
|
||||
<v-row class="align-center mb-3">
|
||||
<v-col cols="12" sm="2" class="pb-sm-3 pb-0">
|
||||
<v-label class="font-weight-medium">Emotion</v-label>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="10">
|
||||
<v-select
|
||||
v-model="speechStore.voiceEmotion"
|
||||
variant="outlined"
|
||||
hide-details
|
||||
density="compact"
|
||||
:items="speechStore.styleList"
|
||||
></v-select>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
<v-card-actions>
|
||||
<v-btn
|
||||
color="primary"
|
||||
block
|
||||
@click="speechStore.voiceConfigDialog = false"
|
||||
>Close
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.scroller {
|
||||
height: 300px;
|
||||
}
|
||||
</style>
|
53
resources/src/components/animations/AnimaitonCss01.vue
Normal file
@@ -0,0 +1,53 @@
|
||||
<!--
|
||||
* @Component:
|
||||
* @Maintainer: J.K. Yang
|
||||
* @Description:
|
||||
-->
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<template>
|
||||
<span class="loader"> </span>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.loader {
|
||||
width: 8px;
|
||||
height: 40px;
|
||||
border-radius: 4px;
|
||||
display: block;
|
||||
margin: 20px auto;
|
||||
position: relative;
|
||||
background: currentColor;
|
||||
color: #4c5271;
|
||||
box-sizing: border-box;
|
||||
animation: animloader 0.3s 0.3s linear infinite alternate;
|
||||
}
|
||||
|
||||
.loader::after,
|
||||
.loader::before {
|
||||
content: "";
|
||||
width: 8px;
|
||||
height: 40px;
|
||||
border-radius: 4px;
|
||||
background: currentColor;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
left: 20px;
|
||||
box-sizing: border-box;
|
||||
animation: animloader 0.3s 0.45s linear infinite alternate;
|
||||
}
|
||||
.loader::before {
|
||||
left: -20px;
|
||||
animation-delay: 0s;
|
||||
}
|
||||
|
||||
@keyframes animloader {
|
||||
0% {
|
||||
height: 48px;
|
||||
}
|
||||
100% {
|
||||
height: 4px;
|
||||
}
|
||||
}
|
||||
</style>
|
42
resources/src/components/animations/AnimaitonCss02.vue
Normal file
@@ -0,0 +1,42 @@
|
||||
<!--
|
||||
* @Component:
|
||||
* @Maintainer: J.K. Yang
|
||||
* @Description:
|
||||
-->
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<template>
|
||||
<span class="loader"> </span>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.loader {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
display: block;
|
||||
margin: 15px auto;
|
||||
position: relative;
|
||||
color: #4c5271;
|
||||
box-sizing: border-box;
|
||||
animation: animloader 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes animloader {
|
||||
0% {
|
||||
box-shadow: 14px 0 0 -2px, 38px 0 0 -2px, -14px 0 0 -2px, -38px 0 0 -2px;
|
||||
}
|
||||
25% {
|
||||
box-shadow: 14px 0 0 -2px, 38px 0 0 -2px, -14px 0 0 -2px, -38px 0 0 2px;
|
||||
}
|
||||
50% {
|
||||
box-shadow: 14px 0 0 -2px, 38px 0 0 -2px, -14px 0 0 2px, -38px 0 0 -2px;
|
||||
}
|
||||
75% {
|
||||
box-shadow: 14px 0 0 2px, 38px 0 0 -2px, -14px 0 0 -2px, -38px 0 0 -2px;
|
||||
}
|
||||
100% {
|
||||
box-shadow: 14px 0 0 -2px, 38px 0 0 2px, -14px 0 0 -2px, -38px 0 0 -2px;
|
||||
}
|
||||
}
|
||||
</style>
|
42
resources/src/components/animations/AnimaitonCss03.vue
Normal file
@@ -0,0 +1,42 @@
|
||||
<!--
|
||||
* @Component:
|
||||
* @Maintainer: J.K. Yang
|
||||
* @Description:
|
||||
-->
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<template>
|
||||
<span class="loader"> </span>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.loader {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
display: block;
|
||||
margin: 15px auto;
|
||||
position: relative;
|
||||
color: #4c5271;
|
||||
box-sizing: border-box;
|
||||
animation: animloader 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes animloader {
|
||||
0% {
|
||||
box-shadow: 14px 0 0 -2px, 38px 0 0 -2px, -14px 0 0 -2px, -38px 0 0 -2px;
|
||||
}
|
||||
25% {
|
||||
box-shadow: 14px 0 0 -2px, 38px 0 0 -2px, -14px 0 0 -2px, -38px 0 0 2px;
|
||||
}
|
||||
50% {
|
||||
box-shadow: 14px 0 0 -2px, 38px 0 0 -2px, -14px 0 0 2px, -38px 0 0 -2px;
|
||||
}
|
||||
75% {
|
||||
box-shadow: 14px 0 0 2px, 38px 0 0 -2px, -14px 0 0 -2px, -38px 0 0 -2px;
|
||||
}
|
||||
100% {
|
||||
box-shadow: 14px 0 0 -2px, 38px 0 0 2px, -14px 0 0 -2px, -38px 0 0 -2px;
|
||||
}
|
||||
}
|
||||
</style>
|
24
resources/src/components/animations/Animation404.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<!--
|
||||
* @Component:
|
||||
* @Maintainer: J.K. Yang
|
||||
* @Description:
|
||||
-->
|
||||
<script setup lang="ts">
|
||||
import { Vue3Lottie } from "vue3-lottie";
|
||||
const props = defineProps({
|
||||
size: {
|
||||
type: Number,
|
||||
default: 500,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Vue3Lottie
|
||||
animationLink="https://assets2.lottiefiles.com/packages/lf20_cr9slsdh.json"
|
||||
:height="props.size"
|
||||
:width="props.size"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
24
resources/src/components/animations/AnimationBot1.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<!--
|
||||
* @Component:
|
||||
* @Maintainer: J.K. Yang
|
||||
* @Description:
|
||||
-->
|
||||
<script setup lang="ts">
|
||||
import { Vue3Lottie } from "vue3-lottie";
|
||||
const props = defineProps({
|
||||
size: {
|
||||
type: Number,
|
||||
default: 400,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Vue3Lottie
|
||||
animationLink="https://assets6.lottiefiles.com/packages/lf20_ofa3xwo7.json"
|
||||
:height="props.size"
|
||||
:width="props.size"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
24
resources/src/components/animations/AnimationBot2.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<!--
|
||||
* @Component:
|
||||
* @Maintainer: J.K. Yang
|
||||
* @Description:
|
||||
-->
|
||||
<script setup lang="ts">
|
||||
import { Vue3Lottie } from "vue3-lottie";
|
||||
const props = defineProps({
|
||||
size: {
|
||||
type: Number,
|
||||
default: 400,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Vue3Lottie
|
||||
animationLink="https://assets4.lottiefiles.com/packages/lf20_zrqthn6o.json"
|
||||
:height="props.size"
|
||||
:width="props.size"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
24
resources/src/components/animations/AnimationChat1.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<!--
|
||||
* @Component:
|
||||
* @Maintainer: J.K. Yang
|
||||
* @Description:
|
||||
-->
|
||||
<script setup lang="ts">
|
||||
import { Vue3Lottie } from "vue3-lottie";
|
||||
const props = defineProps({
|
||||
size: {
|
||||
type: Number,
|
||||
default: 400,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Vue3Lottie
|
||||
animationLink="https://assets5.lottiefiles.com/packages/lf20_eYXADRNJPy.json"
|
||||
:height="props.size"
|
||||
:width="props.size"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
24
resources/src/components/animations/AnimationFile1.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<!--
|
||||
* @Component:
|
||||
* @Maintainer: J.K. Yang
|
||||
* @Description:
|
||||
-->
|
||||
<script setup lang="ts">
|
||||
import { Vue3Lottie } from "vue3-lottie";
|
||||
const props = defineProps({
|
||||
size: {
|
||||
type: Number,
|
||||
default: 400,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Vue3Lottie
|
||||
animationLink="https://assets3.lottiefiles.com/packages/lf20_komemhfl.json"
|
||||
:height="props.size"
|
||||
:width="props.size"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
24
resources/src/components/animations/AnimationFolder.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<!--
|
||||
* @Component:
|
||||
* @Maintainer: J.K. Yang
|
||||
* @Description:
|
||||
-->
|
||||
<script setup lang="ts">
|
||||
import { Vue3Lottie } from "vue3-lottie";
|
||||
const props = defineProps({
|
||||
size: {
|
||||
type: Number,
|
||||
default: 400,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Vue3Lottie
|
||||
animationLink="https://assets10.lottiefiles.com/private_files/lf30_0cwwprun.json"
|
||||
:height="props.size"
|
||||
:width="props.size"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
29
resources/src/components/animations/AnimationRecording.vue
Normal file
@@ -0,0 +1,29 @@
|
||||
<!--
|
||||
* @Component:
|
||||
* @Maintainer: J.K. Yang
|
||||
* @Description:
|
||||
-->
|
||||
<script setup lang="ts">
|
||||
import { Vue3Lottie } from "vue3-lottie";
|
||||
const props = defineProps({
|
||||
size: {
|
||||
type: Number,
|
||||
default: 500,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Vue3Lottie
|
||||
animationLink="https://assets10.lottiefiles.com/packages/lf20_jGOM8KIjDM.json"
|
||||
:height="props.size"
|
||||
:width="props.size"
|
||||
class="animation-recording"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.animation-recording {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
24
resources/src/components/animations/AnimationSearch1.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<!--
|
||||
* @Component:
|
||||
* @Maintainer: J.K. Yang
|
||||
* @Description:
|
||||
-->
|
||||
<script setup lang="ts">
|
||||
import { Vue3Lottie } from "vue3-lottie";
|
||||
const props = defineProps({
|
||||
size: {
|
||||
type: Number,
|
||||
default: 400,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Vue3Lottie
|
||||
animationLink="https://assets2.lottiefiles.com/private_files/lf30_fup2uejx.json"
|
||||
:height="props.size"
|
||||
:width="props.size"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
24
resources/src/components/animations/AnimationSearch2.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<!--
|
||||
* @Component:
|
||||
* @Maintainer: J.K. Yang
|
||||
* @Description:
|
||||
-->
|
||||
<script setup lang="ts">
|
||||
import { Vue3Lottie } from "vue3-lottie";
|
||||
const props = defineProps({
|
||||
size: {
|
||||
type: Number,
|
||||
default: 400,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Vue3Lottie
|
||||
animationLink="https://assets2.lottiefiles.com/packages/lf20_8QH3Blu2Xh.json"
|
||||
:height="props.size"
|
||||
:width="props.size"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
24
resources/src/components/animations/AnimationSpeech.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<!--
|
||||
* @Component:
|
||||
* @Maintainer: J.K. Yang
|
||||
* @Description:
|
||||
-->
|
||||
<script setup lang="ts">
|
||||
import { Vue3Lottie } from "vue3-lottie";
|
||||
const props = defineProps({
|
||||
size: {
|
||||
type: Number,
|
||||
default: 500,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Vue3Lottie
|
||||
animationLink="https://assets7.lottiefiles.com/packages/lf20_jZArKF.json"
|
||||
:height="props.size"
|
||||
:width="props.size"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
24
resources/src/components/animations/AnimationUpload.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<!--
|
||||
* @Component:
|
||||
* @Maintainer: J.K. Yang
|
||||
* @Description:
|
||||
-->
|
||||
<script setup lang="ts">
|
||||
import { Vue3Lottie } from "vue3-lottie";
|
||||
const props = defineProps({
|
||||
size: {
|
||||
type: Number,
|
||||
default: 500,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Vue3Lottie
|
||||
animationLink="https://assets3.lottiefiles.com/packages/lf20_GxMZME.json"
|
||||
:height="props.size"
|
||||
:width="props.size"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
157
resources/src/components/charts/apexchart/ApexChartsData.ts
Normal file
@@ -0,0 +1,157 @@
|
||||
const themeColors = ["#ee8a6a", "#0cb9c5", "#fec90f", "#05b187", "#fc4b6c"];
|
||||
const themeColors2 = ["#4782FB", "#47C4F4", "#fec90f", "#05b187", "#fc4b6c"];
|
||||
|
||||
function generateDataHeatMap(count: any, yrange: any) {
|
||||
var i = 0;
|
||||
var series: any[] = [];
|
||||
while (i < count) {
|
||||
var x = "w" + (i + 1).toString();
|
||||
var y =
|
||||
Math.floor(Math.random() * (yrange.max - yrange.min + 1)) + yrange.min;
|
||||
|
||||
series.push({
|
||||
x: x,
|
||||
y: y,
|
||||
});
|
||||
i++;
|
||||
}
|
||||
return series;
|
||||
}
|
||||
|
||||
export const heatMapChart = {
|
||||
series: [
|
||||
{
|
||||
name: "Metric1",
|
||||
data: generateDataHeatMap(18, {
|
||||
min: 0,
|
||||
max: 90,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Metric2",
|
||||
data: generateDataHeatMap(18, {
|
||||
min: 0,
|
||||
max: 90,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Metric3",
|
||||
data: generateDataHeatMap(18, {
|
||||
min: 0,
|
||||
max: 90,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Metric4",
|
||||
data: generateDataHeatMap(18, {
|
||||
min: 0,
|
||||
max: 90,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Metric5",
|
||||
data: generateDataHeatMap(18, {
|
||||
min: 0,
|
||||
max: 90,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Metric6",
|
||||
data: generateDataHeatMap(18, {
|
||||
min: 0,
|
||||
max: 90,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Metric7",
|
||||
data: generateDataHeatMap(18, {
|
||||
min: 0,
|
||||
max: 90,
|
||||
}),
|
||||
},
|
||||
],
|
||||
chartOptions: {
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
},
|
||||
colors: ["#1e88e5"],
|
||||
tooltip: {
|
||||
theme: "dark",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const lineAreaChartSpline = {
|
||||
series: [
|
||||
{
|
||||
name: "Open Rate",
|
||||
data: [0, 5, 6, 8, 25, 9, 8, 24],
|
||||
},
|
||||
{
|
||||
name: "Recurring Payments",
|
||||
data: [0, 3, 1, 2, 8, 1, 5, 1],
|
||||
},
|
||||
],
|
||||
|
||||
chartOptions: {
|
||||
grid: {
|
||||
show: true,
|
||||
borderColor: "rgba(0, 0, 0, .3)",
|
||||
strokeDashArray: 3,
|
||||
xaxis: {
|
||||
lines: {
|
||||
show: true,
|
||||
},
|
||||
},
|
||||
yaxis: {
|
||||
lines: {
|
||||
show: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
},
|
||||
chart: {
|
||||
toolbar: {
|
||||
show: true,
|
||||
},
|
||||
},
|
||||
stroke: {
|
||||
curve: "smooth",
|
||||
width: 2,
|
||||
},
|
||||
colors: themeColors2,
|
||||
fill: {
|
||||
type: "gradient",
|
||||
opacity: ["0.1", "0.1"],
|
||||
},
|
||||
xaxis: {
|
||||
categories: ["1", "2", "3", "4", "5", "6", "7", "8"],
|
||||
labels: {
|
||||
style: {
|
||||
cssClass: "grey--text lighten-2--text fill-color",
|
||||
},
|
||||
},
|
||||
},
|
||||
yaxis: {
|
||||
labels: {
|
||||
style: {
|
||||
cssClass: "grey--text lighten-2--text fill-color",
|
||||
},
|
||||
},
|
||||
},
|
||||
markers: {
|
||||
size: 3,
|
||||
},
|
||||
tooltip: {
|
||||
x: {
|
||||
format: "dd/MM/yy HH:mm",
|
||||
},
|
||||
theme: "dark",
|
||||
},
|
||||
legend: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
};
|
111
resources/src/components/charts/apexchart/ApexGradientChart.vue
Normal file
@@ -0,0 +1,111 @@
|
||||
<!--
|
||||
* @Component:
|
||||
* @Maintainer: J.K. Yang
|
||||
* @Description:
|
||||
-->
|
||||
<script setup lang="ts">
|
||||
const chartOptions = computed(() => {
|
||||
return {
|
||||
chart: {
|
||||
type: "line",
|
||||
height: 350,
|
||||
fontFamily: `inherit`,
|
||||
foreColor: "#a1aab2",
|
||||
toolbar: {
|
||||
show: false,
|
||||
},
|
||||
dropShadow: {
|
||||
enabled: true,
|
||||
color: "rgba(0,0,0,0.2)",
|
||||
top: 12,
|
||||
left: 4,
|
||||
blur: 3,
|
||||
opacity: 0.4,
|
||||
},
|
||||
},
|
||||
stroke: {
|
||||
width: 7,
|
||||
curve: "smooth",
|
||||
},
|
||||
|
||||
xaxis: {
|
||||
type: "datetime",
|
||||
categories: [
|
||||
"1/11/2000",
|
||||
"2/11/2000",
|
||||
"3/11/2000",
|
||||
"4/11/2000",
|
||||
"5/11/2000",
|
||||
"6/11/2000",
|
||||
"7/11/2000",
|
||||
"8/11/2000",
|
||||
"9/11/2000",
|
||||
"10/11/2000",
|
||||
"11/11/2000",
|
||||
"12/11/2000",
|
||||
"1/11/2001",
|
||||
"2/11/2001",
|
||||
"3/11/2001",
|
||||
"4/11/2001",
|
||||
"5/11/2001",
|
||||
"6/11/2001",
|
||||
],
|
||||
},
|
||||
fill: {
|
||||
type: "gradient",
|
||||
gradient: {
|
||||
shade: "dark",
|
||||
gradientToColors: ["#0b70fb"],
|
||||
shadeIntensity: 1,
|
||||
type: "horizontal",
|
||||
opacityFrom: 1,
|
||||
opacityTo: 0.9,
|
||||
stops: [0, 100, 100, 100],
|
||||
},
|
||||
},
|
||||
markers: {
|
||||
size: 4,
|
||||
opacity: 0.9,
|
||||
colors: ["#4e79ff"],
|
||||
strokeColor: "#fff",
|
||||
strokeWidth: 2,
|
||||
|
||||
hover: {
|
||||
size: 7,
|
||||
},
|
||||
},
|
||||
yaxis: {
|
||||
min: 0,
|
||||
max: 40,
|
||||
},
|
||||
grid: {
|
||||
show: false,
|
||||
},
|
||||
tooltip: {
|
||||
theme: "light",
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const gredientChart = {
|
||||
series: [
|
||||
{
|
||||
name: "Likes",
|
||||
data: [4, 3, 10, 9, 35, 19, 22, 9, 12, 7, 19, 5, 13, 9, 17, 2, 7, 5],
|
||||
},
|
||||
],
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<apexchart
|
||||
type="line"
|
||||
height="350"
|
||||
:options="chartOptions"
|
||||
:series="gredientChart.series"
|
||||
></apexchart>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
@@ -0,0 +1,83 @@
|
||||
<!--
|
||||
* @Component: ApexHeatMapCharts
|
||||
* @Maintainer: J.K. Yang
|
||||
* @Description: ApexHeatMapCharts
|
||||
-->
|
||||
<script setup lang="ts">
|
||||
const chartOptions = computed(() => {
|
||||
return {
|
||||
chart: {
|
||||
type: "area",
|
||||
height: 300,
|
||||
fontFamily: `inherit`,
|
||||
foreColor: "#adb0bb",
|
||||
zoom: {
|
||||
enabled: true,
|
||||
},
|
||||
toolbar: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
colors: ["#4782FB", "#47C4F4"],
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
},
|
||||
stroke: {
|
||||
width: "3",
|
||||
curve: "smooth",
|
||||
},
|
||||
xaxis: {
|
||||
type: "datetime",
|
||||
categories: [
|
||||
"2018-09-19T00:00:00",
|
||||
"2018-09-19T01:30:00",
|
||||
"2018-09-19T02:30:00",
|
||||
"2018-09-19T03:30:00",
|
||||
"2018-09-19T04:30:00",
|
||||
"2018-09-19T05:30:00",
|
||||
"2018-09-19T06:30:00",
|
||||
],
|
||||
},
|
||||
yaxis: {
|
||||
opposite: false,
|
||||
labels: {
|
||||
show: true,
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
show: true,
|
||||
position: "bottom",
|
||||
width: "50px",
|
||||
},
|
||||
grid: {
|
||||
show: false,
|
||||
},
|
||||
tooltip: {
|
||||
theme: "dark",
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const areaChart = {
|
||||
series: [
|
||||
{
|
||||
name: "Sales Summery 1",
|
||||
data: [31, 40, 28, 51, 42, 109, 100],
|
||||
},
|
||||
{
|
||||
name: "Sales Summery 2",
|
||||
data: [11, 32, 45, 32, 34, 52, 41],
|
||||
},
|
||||
],
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<apexchart
|
||||
type="area"
|
||||
height="310"
|
||||
:options="chartOptions"
|
||||
:series="areaChart.series"
|
||||
></apexchart>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
@@ -0,0 +1,83 @@
|
||||
<!--
|
||||
* @Component: ApexHeatMapCharts
|
||||
* @Maintainer: J.K. Yang
|
||||
* @Description: ApexHeatMapCharts
|
||||
-->
|
||||
<script setup lang="ts">
|
||||
const chartOptions = computed(() => {
|
||||
return {
|
||||
chart: {
|
||||
type: "area",
|
||||
height: 300,
|
||||
fontFamily: `inherit`,
|
||||
foreColor: "#adb0bb",
|
||||
zoom: {
|
||||
enabled: true,
|
||||
},
|
||||
toolbar: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
colors: ["#4782FB", "#47C4F4"],
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
},
|
||||
stroke: {
|
||||
width: "3",
|
||||
curve: "smooth",
|
||||
},
|
||||
xaxis: {
|
||||
type: "datetime",
|
||||
categories: [
|
||||
"2018-09-19T00:00:00",
|
||||
"2018-09-19T01:30:00",
|
||||
"2018-09-19T02:30:00",
|
||||
"2018-09-19T03:30:00",
|
||||
"2018-09-19T04:30:00",
|
||||
"2018-09-19T05:30:00",
|
||||
"2018-09-19T06:30:00",
|
||||
],
|
||||
},
|
||||
yaxis: {
|
||||
opposite: false,
|
||||
labels: {
|
||||
show: true,
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
show: true,
|
||||
position: "bottom",
|
||||
width: "50px",
|
||||
},
|
||||
grid: {
|
||||
show: false,
|
||||
},
|
||||
tooltip: {
|
||||
theme: "dark",
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const areaChart = {
|
||||
series: [
|
||||
{
|
||||
name: "Open Rate",
|
||||
data: [0, 5, 6, 8, 25, 9, 8, 24],
|
||||
},
|
||||
{
|
||||
name: "Recurring Payments",
|
||||
data: [0, 3, 1, 2, 8, 1, 5, 1],
|
||||
},
|
||||
],
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<apexchart
|
||||
type="area"
|
||||
height="310"
|
||||
:options="chartOptions"
|
||||
:series="areaChart.series"
|
||||
></apexchart>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
58
resources/src/components/common/BackToTop.vue
Normal file
@@ -0,0 +1,58 @@
|
||||
<!--
|
||||
* @Component: BackToTop
|
||||
* @Maintainer: J.K. Yang
|
||||
* @Description:
|
||||
-->
|
||||
<script setup lang="ts">
|
||||
import { Icon } from "@iconify/vue";
|
||||
const isVisible = ref(false);
|
||||
|
||||
const handleScroll = () => {
|
||||
isVisible.value = window.scrollY > 200;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener("scroll", handleScroll);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener("scroll", handleScroll);
|
||||
});
|
||||
|
||||
const scrollToTop = () => {
|
||||
window.scrollTo({ top: 0, behavior: "smooth" });
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="back-to-top" :class="{ visible: isVisible }" @click="scrollToTop">
|
||||
<Icon class="text-white" width="30" icon="ph:rocket-light" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.back-to-top {
|
||||
position: fixed;
|
||||
background-color: #705cf6;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: all 0.3s ease;
|
||||
bottom: 50px;
|
||||
right: 5px;
|
||||
z-index: 999;
|
||||
padding: 0.5rem;
|
||||
border-radius: 0.5rem;
|
||||
box-shadow: 1px 1px 9px #705cf6;
|
||||
transition: all 0.5s;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
box-shadow: 1px 1px 18px #705cf6;
|
||||
transition: all 0.5s;
|
||||
}
|
||||
}
|
||||
|
||||
.back-to-top.visible {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
</style>
|
55
resources/src/components/common/CopyBtn.vue
Normal file
@@ -0,0 +1,55 @@
|
||||
<!--
|
||||
* @Component: CopyLabel
|
||||
* @Maintainer: J.K. Yang
|
||||
* @Description:
|
||||
-->
|
||||
<script setup lang="ts">
|
||||
import clipboard from '@/utils/clipboardUtils';
|
||||
|
||||
// SnackBar
|
||||
const snackbar = ref(false);
|
||||
const timeout = ref("1000");
|
||||
const copiedText = "Copied to clipboard!";
|
||||
|
||||
// Props
|
||||
const props = defineProps({
|
||||
// Text to copy to clipboard
|
||||
text: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
});
|
||||
|
||||
// Copy Text
|
||||
const copyText = (event: Event) => {
|
||||
console.log(props.text);
|
||||
clipboard(props.text, event);
|
||||
|
||||
snackbar.value = true;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<v-snackbar v-model="snackbar" :timeout="timeout">
|
||||
{{ copiedText }}
|
||||
<template v-slot:actions>
|
||||
<v-btn color="blue" variant="text" @click="snackbar = false">
|
||||
Close
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-snackbar>
|
||||
|
||||
<v-btn v-bind="$attrs" icon @click="copyText($event)"
|
||||
><v-icon>mdi-content-copy</v-icon>
|
||||
<v-tooltip activator="parent" location="bottom" text="Copy"></v-tooltip>
|
||||
</v-btn>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.text {
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
}
|
||||
</style>
|
73
resources/src/components/common/CopyLabel.vue
Normal file
@@ -0,0 +1,73 @@
|
||||
<!--
|
||||
* @Component: CopyLabel
|
||||
* @Maintainer: J.K. Yang
|
||||
* @Description:
|
||||
-->
|
||||
<script setup lang="ts">
|
||||
import clipboard from '@/utils/clipboardUtils';
|
||||
|
||||
// ToolTip
|
||||
const tooltip = ref("Copy");
|
||||
// SnackBar
|
||||
const snackbar = ref(false);
|
||||
const timeout = ref("1000");
|
||||
const copiedText = "Copied to clipboard!";
|
||||
// Copy Animation Flag
|
||||
const heartBeat = ref(false);
|
||||
// Props
|
||||
const props = defineProps({
|
||||
// Text to copy to clipboard
|
||||
text: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
});
|
||||
|
||||
const { text } = toRefs(props);
|
||||
|
||||
// Copy Text
|
||||
const copyText = (text: string, event: Event) => {
|
||||
clipboard(text, event);
|
||||
heartBeat.value = true;
|
||||
snackbar.value = true;
|
||||
tooltip.value = "Copied!";
|
||||
setTimeout(() => {
|
||||
heartBeat.value = false;
|
||||
tooltip.value = "Copy!";
|
||||
}, 1000);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-snackbar v-model="snackbar" :timeout="timeout">
|
||||
{{ copiedText }}
|
||||
<template v-slot:actions>
|
||||
<v-btn color="blue" variant="text" @click="snackbar = false">
|
||||
Close
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-snackbar>
|
||||
<v-tooltip location="bottom">
|
||||
<template v-slot:activator="{ props }">
|
||||
<span
|
||||
:class="{
|
||||
heartBeat: heartBeat === true,
|
||||
}"
|
||||
class="text"
|
||||
v-bind="props"
|
||||
@click.stop.prevent="copyText(text, $event)"
|
||||
>
|
||||
{{ text }}
|
||||
</span>
|
||||
</template>
|
||||
<span>{{ tooltip }}</span>
|
||||
</v-tooltip>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.text {
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
border-bottom: 1px dashed;
|
||||
}
|
||||
</style>
|
28
resources/src/components/common/PercentTrend.vue
Normal file
@@ -0,0 +1,28 @@
|
||||
<!--
|
||||
* @Component:
|
||||
* @Maintainer: J.K. Yang
|
||||
* @Description:
|
||||
-->
|
||||
<script setup lang="ts">
|
||||
defineProps({
|
||||
value: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span>
|
||||
<span v-if="value === 0"> {{ value }}% </span>
|
||||
<span v-else-if="value > 0" class="text-success">
|
||||
<v-icon small color="success">mdi-arrow-top-right</v-icon> {{ value }}%
|
||||
</span>
|
||||
<span v-else class="error--text">
|
||||
<v-icon small color="error">mdi-arrow-bottom-right</v-icon>
|
||||
{{ Math.abs(value) }}%
|
||||
</span>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
37
resources/src/components/common/Snackbar.vue
Normal file
@@ -0,0 +1,37 @@
|
||||
<script setup lang="ts">
|
||||
import { useSnackbarStore } from "@/stores/snackbarStore";
|
||||
const snackbarStore = useSnackbarStore();
|
||||
|
||||
const getIcon = (type) => {
|
||||
const icon = {
|
||||
info: "mdi-information",
|
||||
success: "mdi-check-circle",
|
||||
error: "mdi-alert-circle",
|
||||
warning: "mdi-alert",
|
||||
};
|
||||
|
||||
return icon[type];
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<v-snackbar
|
||||
v-model="snackbarStore.isShow"
|
||||
timeout="2000"
|
||||
:color="snackbarStore.type"
|
||||
class="elevation-10"
|
||||
location="top"
|
||||
>
|
||||
<div class="d-flex align-center">
|
||||
<v-icon class="mr-2">{{ getIcon(snackbarStore.type) }}</v-icon>
|
||||
<span> {{ snackbarStore.message }}</span>
|
||||
</div>
|
||||
|
||||
<template v-slot:actions>
|
||||
<v-btn icon variant="text" @click="snackbarStore.isShow = false">
|
||||
<v-icon>mdi-close</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-snackbar>
|
||||
</div>
|
||||
</template>
|
225
resources/src/components/dashboard/ActivityCard.vue
Normal file
@@ -0,0 +1,225 @@
|
||||
<script setup lang="ts">
|
||||
import { Icon } from "@iconify/vue";
|
||||
import { getPublicEventsApi } from "@/api/githubApi";
|
||||
import moment from "moment";
|
||||
const loading = ref(false);
|
||||
const username = ref("yangjiakai");
|
||||
const activityList = ref([
|
||||
{
|
||||
id: "29003260817",
|
||||
type: "PushEvent",
|
||||
user: "yangjiakai",
|
||||
avatar: "https://avatars.githubusercontent.com/u/35951244?",
|
||||
repo: "yangjiakai/lux-admin-vuetify3",
|
||||
content:
|
||||
"<p>Update Dashboard View</p><br/><div><span class='mr-1'>✅</span> Add PieChart1</div>",
|
||||
action: "Commit",
|
||||
created_at: "2023-05-12T14:06:59Z",
|
||||
},
|
||||
]);
|
||||
|
||||
const mockAcitvitys = [
|
||||
{
|
||||
id: "29003260817",
|
||||
type: "PushEvent",
|
||||
user: "yangjiakai",
|
||||
avatar: "https://avatars.githubusercontent.com/u/35951244?",
|
||||
repo: "yangjiakai/lux-admin-vuetify3",
|
||||
content:
|
||||
"<p> Update Dashboard View</p><br/><div><span class='mr-1'>✅</span> Add PieChart1</div><div><span class='mr-1'>✅</span> Add PieChart2</div><div><span class='mr-1'>✅</span> Update ActivityCard,SalesCard,SOurcesCard</div>",
|
||||
action: "Commit",
|
||||
created_at: "2023-05-12T14:06:59Z",
|
||||
},
|
||||
{
|
||||
id: "29003260817",
|
||||
type: "PushEvent",
|
||||
user: "yangjiakai",
|
||||
avatar: "https://avatars.githubusercontent.com/u/35951244?",
|
||||
repo: "yangjiakai/lux-admin-vuetify3",
|
||||
content:
|
||||
"<p>Update ChatBot</p><br/><div><span class='mr-1'>✅</span> Chatbot1 Add Stream</div><div><span class='mr-1'>✅</span> Add ScrollToBottom Common Method</div>",
|
||||
action: "Commit",
|
||||
created_at: "2023-05-11T14:06:59Z",
|
||||
},
|
||||
];
|
||||
|
||||
const getPublicEvent = async () => {
|
||||
loading.value = true;
|
||||
// const response = await getPublicEventsApi(username.value);
|
||||
|
||||
// activityList.value = response.data
|
||||
// .map((activity) => {
|
||||
// return {
|
||||
// id: activity.id,
|
||||
// type: activity.type,
|
||||
// user: activity.actor.display_login,
|
||||
// avatar: activity.actor.avatar_url,
|
||||
// repo: activity.repo?.name,
|
||||
// content: getContent(activity),
|
||||
// action:
|
||||
// activity.type === "IssuesEvent" ? activity.payload.action : "Commit",
|
||||
// created_at: activity.created_at,
|
||||
// };
|
||||
// })
|
||||
|
||||
activityList.value = mockAcitvitys;
|
||||
setTimeout(() => {
|
||||
loading.value = false;
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const getContent = (activity: any) => {
|
||||
if (activity.type === "PushEvent") {
|
||||
return convertToHtml(activity.payload.commits[0].message);
|
||||
} else if (activity.type === "CreateEvent") {
|
||||
return activity.payload.ref_type;
|
||||
} else if (activity.type === "IssuesEvent") {
|
||||
return activity.payload.issue.title;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
const convertToHtml = (text) => {
|
||||
const lines = text.split("\n");
|
||||
let html = "";
|
||||
|
||||
lines.forEach((line) => {
|
||||
if (line.startsWith("- ")) {
|
||||
html += `<div><span class='mr-1'>✅</span> ${line.slice(2)}</div>`;
|
||||
} else if (line.trim() === "") {
|
||||
html += "<br/>";
|
||||
} else {
|
||||
html += `<p>${line}</p>`;
|
||||
}
|
||||
});
|
||||
|
||||
return html;
|
||||
};
|
||||
|
||||
const getTagColor = (activity: any) => {
|
||||
if (activity.type === "PushEvent") {
|
||||
return "green";
|
||||
} else if (activity.type === "IssuesEvent") {
|
||||
return "red";
|
||||
} else {
|
||||
return "blue";
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getPublicEvent();
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<!-- loading spinner -->
|
||||
<div
|
||||
v-if="loading"
|
||||
class="h-full d-flex flex-grow-1 align-center justify-center"
|
||||
>
|
||||
<v-progress-circular indeterminate color="primary"></v-progress-circular>
|
||||
</div>
|
||||
<div v-else>
|
||||
<h6 class="text-h6 pa-5 d-flex align-center">
|
||||
<span class="flex-fill font-weight-bold">Github Activity5</span>
|
||||
<v-menu location="bottom end" transition="slide-x-transition">
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-btn
|
||||
v-bind="props"
|
||||
size="small"
|
||||
variant="text"
|
||||
icon="mdi-dots-vertical"
|
||||
rounded
|
||||
color="primary"
|
||||
class="my-n2"
|
||||
></v-btn>
|
||||
</template>
|
||||
<v-list density="compact">
|
||||
<v-list-item @click="$emit('edit')">
|
||||
<v-list-item-title class="d-inline-flex align-center">
|
||||
<Icon
|
||||
icon="flat-color-icons:refresh"
|
||||
:rotate="2"
|
||||
:horizontalFlip="true"
|
||||
:verticalFlip="true"
|
||||
class="mr-1"
|
||||
/>
|
||||
<span> Refresh</span>
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item @click="$emit('delete')">
|
||||
<v-list-item-title class="d-inline-flex align-center">
|
||||
<Icon
|
||||
icon="icon-park:clear-format"
|
||||
:rotate="2"
|
||||
:horizontalFlip="true"
|
||||
:verticalFlip="true"
|
||||
:inline="true"
|
||||
class="mr-1"
|
||||
/>
|
||||
Clear</v-list-item-title
|
||||
>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</h6>
|
||||
<perfect-scrollbar class="timeline-container">
|
||||
<v-timeline
|
||||
class="time-line text-body-2"
|
||||
density="compact"
|
||||
side="end"
|
||||
truncate-line="start"
|
||||
>
|
||||
<v-timeline-item
|
||||
v-for="activity in activityList"
|
||||
:key="activity.id"
|
||||
size="small"
|
||||
>
|
||||
<template v-slot:icon>
|
||||
<v-avatar>
|
||||
<img :src="activity.avatar" />
|
||||
</v-avatar>
|
||||
</template>
|
||||
<template v-slot:opposite>
|
||||
<span>{{ moment(activity.created_at).format("MM,DD hh:mm") }}</span>
|
||||
</template>
|
||||
<div class="mb-1">
|
||||
<span class="text-h6 font-weight-bold">
|
||||
{{ activity.user }}
|
||||
</span>
|
||||
<span class="ml-2 text-grey">{{
|
||||
moment(activity.created_at).format("MM,DD hh:mm")
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
<v-card max-width="500">
|
||||
<v-card-subtitle class="pt-4">
|
||||
<v-chip
|
||||
:color="getTagColor(activity)"
|
||||
size="small"
|
||||
label
|
||||
class="mr-2 font-weight-bold"
|
||||
>
|
||||
<span>{{ activity.type }}</span>
|
||||
</v-chip>
|
||||
<span class="text-body-2">{{ activity.repo }}</span>
|
||||
</v-card-subtitle>
|
||||
<v-card-text>
|
||||
<div v-html="activity.content"></div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-timeline-item>
|
||||
</v-timeline>
|
||||
</perfect-scrollbar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.timeline-container {
|
||||
height: 360px;
|
||||
overflow: scroll;
|
||||
}
|
||||
.time-line {
|
||||
margin-left: 60px;
|
||||
}
|
||||
</style>
|