init web ui

This commit is contained in:
akrike
2025-08-13 21:39:13 +08:00
parent 3fb8cd4dad
commit 5de8b45b2f
420 changed files with 46414 additions and 0 deletions

View File

@@ -0,0 +1,4 @@
> 1%
last 2 versions
not dead
not ie 11

2
resources/.dockerignore Normal file
View File

@@ -0,0 +1,2 @@
node_modules
npm-debug.log

5
resources/.editorconfig Normal file
View 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
View 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
View File

@@ -0,0 +1 @@
VITE_API_BASE_URL=https://api.example.com

7
resources/.env.template Normal file
View 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
View File

@@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto

32
resources/.gitignore vendored Normal file
View 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
View 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
View 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
View 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
View 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
![Alt](https://repobeats.axiom.co/api/embed/306361b2af1a8556f64a0a828e1726a94bff36f0.svg "Repobeats analytics image")
## 💬 連絡
- 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
View 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
![Alt](https://repobeats.axiom.co/api/embed/306361b2af1a8556f64a0a828e1726a94bff36f0.svg "Repobeats analytics image")
## 💬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
View 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
- 📝 富文本编辑器
- 📇 响应式多平台自适应
## 📈 项目活跃度
![Alt](https://repobeats.axiom.co/api/embed/306361b2af1a8556f64a0a828e1726a94bff36f0.svg "Repobeats analytics image")
## 💬 联络我
- 邮箱 <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
View 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"];
}

View 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
View 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
View File

@@ -0,0 +1,2 @@
[build.environment]
NODE_VERSION = "21.1.0"

68
resources/package.json Normal file
View 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"
}
}

View File

@@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

View File

@@ -0,0 +1 @@
/* /index.html 200

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

55
resources/src/App.vue Normal file
View 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>

View 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,
},
});
};

View 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");
};

View 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 DeepMinds groundbreaking research in WaveNet and Googles 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",
},
}
);
};

View 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");
};

View 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 photos statistics 获取照片的统计数据
export const getPhotoStatisticsApi = (id: string) => {
return instance.get("/photos/" + id + "/statistics");
};
// Get a photos 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 topics 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 users portfolio
export const getUserPortfolioApi = (username: string | string[]) => {
return instance.get("/users/" + username + "/portfolio");
};
// List a users photos
export const getUserPhotosApi = (
username: string | string[],
query?: Query
) => {
return instance.get("/users/" + username + "/photos", { params: query });
};
// List a users liked photos
export const getUserLikesApi = (username: string | string[], query?: Query) => {
return instance.get("/users/" + username + "/likes", { params: query });
};
// List a users collections
export const getUserCollectionsApi = (
username: string | string[],
query?: Query
) => {
return instance.get("/users/" + username + "/collections", { params: query });
};
// Get a users 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 collections photos 获取该图集下所有图片
export const getCollectionPhotosApi = (
id: string | string[],
query?: Query
) => {
return instance.get("/collections/" + id + "/photos", { params: query });
};
// List a collections 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 });
};

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 17 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 520 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View 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

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View 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

View 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

View 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

View 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

View 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

View 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

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 387 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 371 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 685 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 580 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 529 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 294 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 379 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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,
},
},
};

View 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>

View File

@@ -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>

View File

@@ -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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

Some files were not shown because too many files have changed in this diff Show More