Compare commits
38 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5af4215562 | |||
| 4f94fcf541 | |||
| 22afbab8ff | |||
| ef22f1d20b | |||
| 99f401bac0 | |||
| 3d751f746b | |||
| 2df4ccf13c | |||
| 0786113589 | |||
| 7499f34dc7 | |||
| a65fea33a9 | |||
| 90ba4b448a | |||
| dfb3a96f84 | |||
| 58eecd80ae | |||
| ec29b2fbc7 | |||
| b8a7b39b62 | |||
| 4e041645a8 | |||
| 5fe5a64140 | |||
| 746f4d98ec | |||
| 4e8254e2d7 | |||
| 7043581335 | |||
| a0d8020787 | |||
| 6144b60541 | |||
| 42f5a37e4d | |||
| a9c59953f4 | |||
| 93badd5ecf | |||
| 2b4f9bb90c | |||
| 63d0b4d79a | |||
| d7675c0b6a | |||
| 1fc202c785 | |||
| 804d95d493 | |||
| 8925bbe343 | |||
| a64f0d93da | |||
| ac3b303b2e | |||
| 8a13967797 | |||
| 9544edd65a | |||
| 4cbb90feda | |||
| 730143f816 | |||
| 28b4a6ec2d |
@@ -0,0 +1,115 @@
|
||||
aliases:
|
||||
- &restore_cache
|
||||
name: Restore Yarn Package Cache
|
||||
keys:
|
||||
- yarn-packages-{{ checksum "yarn.lock" }}
|
||||
|
||||
- &install_dependencies
|
||||
name: Install Dependencies
|
||||
command: yarn install --frozen-lockfile --cache-folder ~/.cache/yarn
|
||||
|
||||
- &save_cache
|
||||
name: Save Yarn Package Cache
|
||||
key: yarn-packages-{{ checksum "yarn.lock" }}
|
||||
paths:
|
||||
- ~/.cache/yarn
|
||||
|
||||
docker: &docker
|
||||
docker:
|
||||
- image: cimg/node:16.16.0
|
||||
|
||||
defaults: &defaults
|
||||
<<: *docker
|
||||
parallelism: 1
|
||||
working_directory: ~/repo
|
||||
|
||||
version: 2
|
||||
jobs:
|
||||
test:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache: *restore_cache
|
||||
- run: *install_dependencies
|
||||
- save_cache: *save_cache
|
||||
- run: yarn test:unit
|
||||
|
||||
deploy-staging:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Add variables
|
||||
command:
|
||||
echo "VUE_APP_INCLUDE_DEMOS=true" >> .env.production.local &&
|
||||
echo "VUE_APP_BUILD_VERSION=true" >> .env.production.local &&
|
||||
echo "VUE_APP_GTM_ENABLED=false" >> .env.production.local
|
||||
- restore_cache: *restore_cache
|
||||
- run: *install_dependencies
|
||||
- save_cache: *save_cache
|
||||
- run: yarn build:ci
|
||||
- add_ssh_keys:
|
||||
fingerprints:
|
||||
- "f8:c0:94:79:68:a2:5e:33:9f:78:bc:ad:ed:86:c6:3c"
|
||||
- run:
|
||||
name: Add $SERVER_IP to known_hosts
|
||||
command: ssh-keyscan -H $SERVER_IP >> ~/.ssh/known_hosts
|
||||
- run:
|
||||
name: Deploy to hosting
|
||||
command: |
|
||||
tar -czf dist.tar.gz ~/repo/dist -C ~/repo/dist .
|
||||
scp ~/repo/dist.tar.gz $SERVER_USER@$SERVER_IP:~/tmp
|
||||
ssh $SERVER_USER@$SERVER_IP /bin/bash << EOF
|
||||
rm -rf $DEPLOY_PATH/*
|
||||
tar -xzf ~/tmp/dist.tar.gz -C $DEPLOY_PATH
|
||||
rm -f ~/tmp/dist.tar.gz
|
||||
EOF
|
||||
|
||||
deploy-production:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Add variables
|
||||
command:
|
||||
echo "VUE_APP_YANDEX_METRICS_KEY=$YANDEX_METRICS_KEY" >> .env.production.local &&
|
||||
echo "VUE_APP_DRIFT_KEY=$DRIFT_KEY" >> .env.production.local &&
|
||||
echo "VUE_APP_GTM_KEY=$GTM_KEY" >> .env.production.local &&
|
||||
echo "VUE_APP_GTM_ENABLED=true" >> .env.production.local &&
|
||||
echo "VUE_APP_ROUTER_MODE_HISTORY=$ROUTER_MODE_HISTORY" >> .env.production.local
|
||||
- restore_cache: *restore_cache
|
||||
- run: *install_dependencies
|
||||
- save_cache: *save_cache
|
||||
- run: yarn build:ci
|
||||
- run:
|
||||
name: Deploy to hosting
|
||||
command: |
|
||||
tar -czf dist.tar.gz ~/repo/dist -C ~/repo/dist .
|
||||
ssh-keyscan -t rsa $SERVER_IP >> ~/.ssh/known_hosts
|
||||
scp ~/repo/dist.tar.gz $SERVER_USERNAME@$SERVER_IP:~/
|
||||
ssh $SERVER_USERNAME@$SERVER_IP << EOF
|
||||
mkdir -p tmp
|
||||
rm -rf $PRODUCTION_DEPLOY_PATH/*
|
||||
tar -xzf ~/dist.tar.gz -C $PRODUCTION_DEPLOY_PATH
|
||||
rm -rf ~/dist.tar.gz ~/tmp
|
||||
EOF
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
build-and-deploy:
|
||||
jobs:
|
||||
# - test
|
||||
- deploy-staging:
|
||||
# requires:
|
||||
# - test
|
||||
context: vuestic-admin-staging
|
||||
filters:
|
||||
branches:
|
||||
only: develop
|
||||
- deploy-production:
|
||||
# requires:
|
||||
# - test
|
||||
context: vuestic-production
|
||||
filters:
|
||||
branches:
|
||||
only: master
|
||||
@@ -0,0 +1,7 @@
|
||||
node_modules/
|
||||
dist/
|
||||
.dockerignore
|
||||
.gitattributes
|
||||
.gitignore
|
||||
Dockerfile
|
||||
README.md
|
||||
@@ -0,0 +1,9 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
@@ -0,0 +1,7 @@
|
||||
VITE_APP_GTM_KEY=
|
||||
|
||||
VITE_APP_INCLUDE_DEMOS=
|
||||
|
||||
VITE_APP_ROUTER_MODE_HISTORY=
|
||||
|
||||
VITE_APP_BUILD_VERSION=
|
||||
@@ -1,28 +0,0 @@
|
||||
module.exports = {
|
||||
env: {
|
||||
browser: true,
|
||||
es2021: true,
|
||||
// 新增 fix module export
|
||||
node: true,
|
||||
},
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:vue/vue3-essential',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
// 新增,必须放在最后面
|
||||
// 'airbnb',
|
||||
// 'prettier',
|
||||
'plugin:prettier/recommended',
|
||||
],
|
||||
overrides: [],
|
||||
// 新增
|
||||
parser: 'vue-eslint-parser',
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
// 新增
|
||||
parser: '@typescript-eslint/parser',
|
||||
sourceType: 'module',
|
||||
},
|
||||
plugins: ['vue', '@typescript-eslint'],
|
||||
rules: {},
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
browser: true,
|
||||
es2021: true,
|
||||
node: true,
|
||||
'vue/setup-compiler-macros': true,
|
||||
},
|
||||
plugins: ['@typescript-eslint'],
|
||||
parser: 'vue-eslint-parser',
|
||||
parserOptions: {
|
||||
parser: '@typescript-eslint/parser',
|
||||
sourceType: 'module',
|
||||
ecmaVersion: 2021,
|
||||
},
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:vue/vue3-recommended',
|
||||
'@vue/typescript/recommended',
|
||||
'@vue/prettier',
|
||||
],
|
||||
|
||||
rules: {
|
||||
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||
'prettier/prettier': ['warn', {}, { usePrettierrc: true }],
|
||||
'@typescript-eslint/no-non-null-assertion': 0,
|
||||
'@typescript-eslint/no-explicit-any': 0, // allow explicit any's because of the legacy code and ts-less deps, but still prohibit IMplicit any's
|
||||
'vue/multi-word-component-names': 0,
|
||||
'vue/no-lone-template': 0,
|
||||
},
|
||||
}
|
||||
@@ -22,3 +22,7 @@ dist-ssr
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
# Local Netlify folder
|
||||
.netlify
|
||||
netlify.toml
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
_
|
||||
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
npx lint-staged
|
||||
@@ -0,0 +1,18 @@
|
||||
# Ignore everything recursively...
|
||||
*
|
||||
|
||||
# But not the .{ts,js,html,css,scss,vue,json,md} files
|
||||
!*.ts
|
||||
!*.js
|
||||
!*.html
|
||||
!*.css
|
||||
!*.scss
|
||||
!*.vue
|
||||
!*.json
|
||||
!*.md
|
||||
|
||||
# But still ignore the dist folder
|
||||
dist/**
|
||||
|
||||
# Check subdirectories too
|
||||
!*/
|
||||
@@ -1,9 +1,10 @@
|
||||
{
|
||||
"printWidth": 80,
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"singleQuote": true,
|
||||
"semi": false,
|
||||
"trailingComma": "all",
|
||||
"bracketSpacing": true
|
||||
"tabWidth": 2,
|
||||
"semi": false,
|
||||
"singleQuote": true,
|
||||
"quoteProps": "as-needed",
|
||||
"trailingComma": "all",
|
||||
"bracketSpacing": true,
|
||||
"printWidth": 120,
|
||||
"vueIndentScriptAndStyle": true
|
||||
}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||
level of experience, education, socio-economic status, nationality, personal
|
||||
appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
- Using welcoming and inclusive language
|
||||
- Being respectful of differing viewpoints and experiences
|
||||
- Gracefully accepting constructive criticism
|
||||
- Focusing on what is best for the community
|
||||
- Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
- The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
- Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
- Public or private harassment
|
||||
- Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
- Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at [INSERT EMAIL ADDRESS]. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
@@ -0,0 +1,13 @@
|
||||
FROM node:14
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY . /app
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
RUN npm install http-server -g
|
||||
|
||||
RUN npm install --no-optional && npm run build
|
||||
|
||||
CMD http-server ./dist
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Epicmax LLC
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,146 @@
|
||||
<p align="center">
|
||||
<a href="./README.md"> English </a> | <a href="./README.zh-CN.md"> 简体中文 </a> | 日本語
|
||||
</p>
|
||||
|
||||
<h1 align="center"> Vuestic Admin </h1>
|
||||
|
||||
<p align="center">
|
||||
38+のカスタムUIコンポーネントと美しいデザインのVue.js無料管理画面テンプレート</br>
|
||||
開発者 <a href="https://epicmax.co">Epicmax</a>.
|
||||
デザイン <a href="#">Vasili Savitski</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://vuestic.epicmax.co"> デモ </a> | <a href="[THEME_URL]"> 新しい白いテーマのデモ </a> | <a href="https://github.com/epicmaxco/vuestic-admin/wiki"> 説明書 </a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://vuestic.epicmax.co" target="_blank">
|
||||
<img src="https://i.imgur.com/RMchKNW.jpg" align="center" width="888px"/>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://circleci.com/gh/epicmaxco/vuestic-admin">
|
||||
<img src="https://img.shields.io/circleci/build/github/epicmaxco/vuestic-admin/master" alt="Circleci">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<h2>
|
||||
<a href="[THEME_URL]" target="_blank">
|
||||
新しい白いテーマのデモ
|
||||
</a>
|
||||
</h2>
|
||||
|
||||
<p align="center">
|
||||
<a href="[THEME_URL]" target="_blank">
|
||||
<img src="https://i.imgur.com/DnFcrjD.png" align="center" width="888px"/>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
## インストール方法
|
||||
|
||||
まず初めに、事前に必要なソフトが全てインストールされていることを確認してください。
|
||||
|
||||
- [Node.js](https://nodejs.org/en/) (>=8.9)
|
||||
- [npm](https://www.npmjs.com/get-npm) バージョン 3+ (あるいは[yarn](https://yarnpkg.com/lang/en/docs/install/#mac-stable)バージョン 1.16+) と[Git](https://git-scm.com/).
|
||||
|
||||
必要なソフトウェアがインストールされていることを確認できたら、次に示す簡単なステップに沿って Vuestic Admin をインストールして、ご使用ください。
|
||||
|
||||
```
|
||||
# repoをクローン
|
||||
$ git clone https://github.com/epicmaxco/vuestic-admin.git myproject
|
||||
|
||||
# appディレクトリに移動し、dependenciesをインストール
|
||||
$ cd myproject
|
||||
|
||||
```
|
||||
|
||||
npm をご使用の方:
|
||||
|
||||
```
|
||||
$ npm install
|
||||
|
||||
# localhost:8080(デフォルト)でホットリロードを使ってサーブ
|
||||
$ npm run dev
|
||||
|
||||
# プロダクション用にビルド
|
||||
$ npm run build
|
||||
|
||||
# プロダクション用にビルドして、バンドルアナライザーレポートを閲覧
|
||||
$ npm run build --report
|
||||
```
|
||||
|
||||
yarn をご使用の方:
|
||||
|
||||
```
|
||||
$ yarn install
|
||||
|
||||
# localhost:8080(デフォルト)でホットリロードを使ってサーブ
|
||||
$ yarn dev
|
||||
|
||||
# プロダクション用にビルド
|
||||
$ yarn build
|
||||
|
||||
# プロダクション用にビルドして、バンドルアナライザーレポートを閲覧
|
||||
$ yarn build --report
|
||||
```
|
||||
|
||||
## 機能
|
||||
|
||||
[レスポンシブレイアウト](https://vuestic.epicmax.co/#/admin/dashboard) |
|
||||
[チャート (Chart.js)](https://vuestic.epicmax.co/#/admin/statistics/charts) |
|
||||
[プログレスバー](https://vuestic.epicmax.co/#/admin/statistics/progress-bars) |
|
||||
[フォーム](https://vuestic.epicmax.co/#/admin/forms/form-elements) |
|
||||
[セレクト](https://vuestic.epicmax.co/#/admin/forms/form-elements) |
|
||||
[デートピッカー](https://vuestic.epicmax.co/#/admin/forms/form-elements) |
|
||||
[チェックボックス・ラジオ](https://vuestic.epicmax.co/#/admin/forms/form-elements) |
|
||||
[スタティックテーブル・データテーブル](https://vuestic.epicmax.co/#/admin/tables/data) |
|
||||
[medium エディタ](https://vuestic.epicmax.co/#/admin/forms/medium-editor) |
|
||||
[スムーズタイポグラフィ](https://vuestic.epicmax.co/#/admin/ui/typography) |
|
||||
[ボタン](https://vuestic.epicmax.co/#/admin/ui/buttons) |
|
||||
[折りたたみ](https://vuestic.epicmax.co/#/admin/ui/collapses) |
|
||||
[カラーピッカー](https://vuestic.epicmax.co/#/admin/ui/color-pickers) |
|
||||
[タイムライン](https://vuestic.epicmax.co/#/admin/ui/timelines) |
|
||||
[トースト](https://vuestic.epicmax.co/#/admin/ui/notifications) |
|
||||
[ツールチップ](https://vuestic.epicmax.co/#/admin/ui/popovers) |
|
||||
[ポップオーバー](https://vuestic.epicmax.co/#/admin/ui/popovers) |
|
||||
[アイコン](https://vuestic.epicmax.co/#/admin/ui/icons/) |
|
||||
[スピナー](https://vuestic.epicmax.co/#/admin/ui/spinners) |
|
||||
[モーダル](https://vuestic.epicmax.co/#/admin/ui/modals) |
|
||||
[ファイルアップロード](https://vuestic.epicmax.co/#/admin/ui/file-upload) |
|
||||
[チップ](https://vuestic.epicmax.co/#/admin/ui/chips) |
|
||||
[ツリー](https://vuestic.epicmax.co/#/admin/ui/tree-view) |
|
||||
[カード ](https://vuestic.epicmax.co/#/admin/ui/cards) |
|
||||
[レーティング](https://vuestic.epicmax.co/#/admin/ui/rating) |
|
||||
[スライダー](https://vuestic.epicmax.co/#/admin/ui/sliders) |
|
||||
[チャット](https://vuestic.epicmax.co/#/admin/ui/chat) |
|
||||
[マップ (MapLibre, Yandex, Leaflet, amMap)](https://vuestic.epicmax.co/#/admin/maps/maplibre-maps) |
|
||||
[ログイン/登録ページテンプレート](https://vuestic.epicmax.co/#/auth/login) |
|
||||
[404 ページテンプレート](https://vuestic.epicmax.co/#/admin/pages/404-pages) |
|
||||
[i18n](https://vuestic.epicmax.co/#/admin/dashboard)
|
||||
|
||||
## ブラウザサポート
|
||||
|
||||
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>IE / Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/opera/opera_48x48.png" alt="Opera" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Opera |
|
||||
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| IE11, Edge | last 2 versions | last 2 versions | last 2 versions | last 2 versions |
|
||||
|
||||
## 支援メンバー
|
||||
|
||||
素晴らしい PR、課題、アイデアをご提供いただきいつもありがとうございます。[参加](https://github.com/epicmaxco/vuestic-admin/blob/master/.github/CONTRIBUTING.md)いただける方を常に受け付けています!
|
||||
|
||||
[](https://sourcerer.io/fame/smartapant/epicmaxco/vuestic-admin/links/0)[](https://sourcerer.io/fame/smartapant/epicmaxco/vuestic-admin/links/1)[](https://sourcerer.io/fame/smartapant/epicmaxco/vuestic-admin/links/2)[](https://sourcerer.io/fame/smartapant/epicmaxco/vuestic-admin/links/3)[](https://sourcerer.io/fame/smartapant/epicmaxco/vuestic-admin/links/4)[](https://sourcerer.io/fame/smartapant/epicmaxco/vuestic-admin/links/5)[](https://sourcerer.io/fame/smartapant/epicmaxco/vuestic-admin/links/6)[](https://sourcerer.io/fame/smartapant/epicmaxco/vuestic-admin/links/7)
|
||||
|
||||
## あなた方に仕事を依頼することはできますか?
|
||||
|
||||
はい!一声おかけください:[hello@epicmax.co](mailto:hello@epicmax.co) </br>
|
||||
ぜひ一緒に仕事をしましょう!その他これまでやってきた仕事 - [epicmax.co](https://epicmax.co)
|
||||
|
||||
## ソーシャルネットワーク
|
||||
|
||||
Vuestic の最新ニュースを受け取ろうや [Facebook](https://facebook.com/epicmaxco)でフォローしてください。
|
||||
|
||||
## ライセンス
|
||||
|
||||
[MIT](https://github.com/epicmaxco/vuestic-admin/blob/master/LICENSE) license
|
||||
@@ -1,16 +1,189 @@
|
||||
# Vue 3 + TypeScript + Vite
|
||||
<p align="center">
|
||||
<a href="https://vuestic.dev" target="_blank">
|
||||
<img alt="Vuestic UI Logo" width="500" src="./public/vuestic-admin-logo.png">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
|
||||
<p align="center">
|
||||
Free and beautiful Vue.js admin template with 44+ custom UI components.</br>
|
||||
Developed by <a href="https://epicmax.co">Epicmax</a>.
|
||||
Designed by <a href="#">Vasili Savitski</a>
|
||||
<br>
|
||||
Based on <a href="https://github.com/epicmaxco/vuestic-ui">Vuestic UI</a> library.
|
||||
</p>
|
||||
|
||||
## Recommended IDE Setup
|
||||
<p align="center">
|
||||
<a href="https://vuestic.epicmax.co/admin/dashboard?theme=light"> Light theme demo </a> | <a href="https://vuestic.epicmax.co/admin/dashboard?theme=semi-dark">Semi-dark theme demo </a> | <a href="https://vuestic.epicmax.co/admin/dashboard?theme=original">Original theme demo </a> | <a href="https://vuestic.dev"> Vuestic UI documentation </a>
|
||||
</p>
|
||||
|
||||
- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar)
|
||||
> Vuestic Admin is built with Vuestic UI. See our
|
||||
> <a href="https://github.com/epicmaxco/vuestic-ui/issues">issues</a>,
|
||||
> <a href="https://vuestic.dev/en/contribution/guide">contributing guide</a> and join discussions on our
|
||||
> <a href="https://discord.gg/jTKTjj2weV">discord server</a> to help us improve Vuestic Admin & Vuestic UI experience.
|
||||
|
||||
## Type Support For `.vue` Imports in TS
|
||||
<p align="center">
|
||||
<a href="https://vuestic.epicmax.co" target="_blank">
|
||||
<img src="./public/vuestic-admin-image.jpg" align="center" width="888px"/>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
Since TypeScript cannot handle type information for `.vue` imports, they are shimmed to be a generic Vue component type by default. In most cases this is fine if you don't really care about component prop types outside of templates. However, if you wish to get actual prop types in `.vue` imports (for example to get props validation when using manual `h(...)` calls), you can enable Volar's Take Over mode by following these steps:
|
||||
<p align="center">
|
||||
<a href="https://circleci.com/gh/epicmaxco/vuestic-admin">
|
||||
<img src="https://img.shields.io/circleci/build/github/epicmaxco/vuestic-admin/master" alt="Circleci">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
1. Run `Extensions: Show Built-in Extensions` from VS Code's command palette, look for `TypeScript and JavaScript Language Features`, then right click and select `Disable (Workspace)`. By default, Take Over mode will enable itself if the default TypeScript extension is disabled.
|
||||
2. Reload the VS Code window by running `Developer: Reload Window` from the command palette.
|
||||
### Documentation
|
||||
|
||||
You can learn more about Take Over mode [here](https://github.com/johnsoncodehk/volar/discussions/471).
|
||||
Documentation, guides and tutorials are available
|
||||
on <a href="https://vuestic.dev">vuestic.dev</a>
|
||||
|
||||
### Official Discord Server
|
||||
|
||||
Ask questions at the official
|
||||
community [discord server](https://discord.gg/jTKTjj2weV)
|
||||
|
||||
### Installation
|
||||
|
||||
First of all, make sure you have all prerequisites installed:
|
||||
|
||||
- [Node.js](https://nodejs.org/en/) ( >=14.\*)
|
||||
- [npm](https://www.npmjs.com/get-npm) version 3+ (
|
||||
or [yarn](https://yarnpkg.com/lang/en/docs/install) version 1.16+)
|
||||
and [Git](https://git-scm.com).
|
||||
|
||||
After checking the prerequisites, follow these simple steps to install and use
|
||||
Vuestic Admin:
|
||||
|
||||
```
|
||||
# clone the repo
|
||||
$ git clone https://github.com/epicmaxco/vuestic-admin.git myproject
|
||||
|
||||
# go into app's directory and install dependencies:
|
||||
$ cd myproject
|
||||
|
||||
```
|
||||
|
||||
Then, if you use npm:
|
||||
|
||||
```
|
||||
$ npm install
|
||||
|
||||
# serve with hot reload at localhost:8080 by default.
|
||||
$ npm run dev
|
||||
|
||||
# build for production
|
||||
$ npm run build
|
||||
|
||||
# build for production and view the bundle analyzer report.
|
||||
$ npm run build --report
|
||||
```
|
||||
|
||||
If you use yarn:
|
||||
|
||||
```
|
||||
$ yarn install
|
||||
|
||||
# serve with hot reload at localhost:8080 by default.
|
||||
$ yarn dev
|
||||
|
||||
# build for production
|
||||
$ yarn build
|
||||
|
||||
# build for production and view the bundle analyzer report.
|
||||
$ yarn build --report
|
||||
```
|
||||
|
||||
### Features
|
||||
|
||||
[Responsive layout](https://vuestic.epicmax.co/admin/dashboard) |
|
||||
[charts (Chart.js)](https://vuestic.epicmax.co/admin/statistics/charts) |
|
||||
[progress bars](https://vuestic.epicmax.co/admin/statistics/progress-bars) |
|
||||
[forms](https://vuestic.epicmax.co/admin/forms/form-elements) |
|
||||
[selects](https://vuestic.epicmax.co/admin/forms/form-elements) |
|
||||
[date pickers](https://vuestic.epicmax.co/admin/forms/form-elements) |
|
||||
[checkboxes and radios](https://vuestic.epicmax.co/admin/forms/form-elements)
|
||||
|
|
||||
[static tables and data tables](https://vuestic.epicmax.co/admin/tables/data)
|
||||
|
|
||||
[medium editor](https://vuestic.epicmax.co/admin/forms/medium-editor) |
|
||||
[smooth typography](https://vuestic.epicmax.co/admin/ui/typography) |
|
||||
[buttons](https://vuestic.epicmax.co/admin/ui/buttons) |
|
||||
[collapses](https://vuestic.epicmax.co/admin/ui/collapses) |
|
||||
[color pickers](https://vuestic.epicmax.co/admin/ui/color-pickers) |
|
||||
[timelines](https://vuestic.epicmax.co/admin/ui/timelines) |
|
||||
[toasts](https://vuestic.epicmax.co/admin/ui/notifications) |
|
||||
[tooltips](https://vuestic.epicmax.co/admin/ui/popovers) |
|
||||
[popovers](https://vuestic.epicmax.co/admin/ui/popovers) |
|
||||
[icons](https://vuestic.epicmax.co/admin/ui/icons/) |
|
||||
[spinners](https://vuestic.epicmax.co/admin/ui/spinners) |
|
||||
[modals](https://vuestic.epicmax.co/admin/ui/modals) |
|
||||
[file upload](https://vuestic.epicmax.co/admin/ui/file-upload) |
|
||||
[chips](https://vuestic.epicmax.co/admin/ui/chips) |
|
||||
[trees](https://vuestic.epicmax.co/admin/ui/tree-view) |
|
||||
[cards](https://vuestic.epicmax.co/admin/ui/cards) |
|
||||
[ratings](https://vuestic.epicmax.co/admin/ui/rating) |
|
||||
[sliders](https://vuestic.epicmax.co/admin/ui/sliders) |
|
||||
[chat](https://vuestic.epicmax.co/admin/ui/chat) |
|
||||
[maps (MapLibre, Yandex, Leaflet, amMap)](https://vuestic.epicmax.co/admin/maps/maplibre-maps)
|
||||
|
|
||||
[login/signup pages templates](https://vuestic.epicmax.co/auth/login) |
|
||||
[404 pages templates](https://vuestic.epicmax.co/admin/pages/404-pages) |
|
||||
[i18n](https://vuestic.epicmax.co/admin/dashboard)
|
||||
|
||||
### Browser Support
|
||||
|
||||
We design Vuestic Admin to support the latest modern web browsers.
|
||||
|
||||
| <img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="Edge" width="24px" height="24px" /><br>Edge | <img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" /><br>Firefox | <img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" /><br>Chrome | <img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" /><br>Safari | <img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/opera/opera_48x48.png" alt="Opera" width="24px" height="24px" /><br>Opera |
|
||||
| ------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Edge | last 2 versions | last 2 versions | last 2 versions | last 2 versions |
|
||||
|
||||
### Contributing
|
||||
|
||||
Thanks for all your wonderful PRs, issues and ideas.
|
||||
<a href="https://github.com/epicmaxco/vuestic-admin/graphs/contributors">
|
||||
<img src="https://opencollective.com/vuestic-admin/contributors.svg?width=890&button=false" />
|
||||
</a>
|
||||
<br>
|
||||
You’re always welcome to join: check out
|
||||
our <a href="https://vuestic.dev/en/contribution/guide">
|
||||
contribution guides</a>
|
||||
, [open issues](https://github.com/epicmaxco/vuestic-ui/issues)
|
||||
and [discord server](https://discord.gg/jTKTjj2weV)
|
||||
|
||||
### Partners & Sponsors ❤️
|
||||
|
||||
<div>
|
||||
<a href="https://epicmax.co/" target="_blank" title="Epicmax - Top Vue.js Development Company"><img src="./.github/assets/epicmax.svg" loading="lazy" vspace="10" alt=""></a>
|
||||
<a href="https://www.ag-grid.com/" target="_blank" title=" Data Grid: AG Grid: High-Performance React Grid, Angular Grid, JavaScript Grid"><img hspace="15" vspace="10" src="./.github/assets/aggrid.svg" loading="lazy" alt=""></a>
|
||||
<a href="https://vuejobs.com/" target="_blank" title="Vue.js jobs – Browse through dozens of Vue.js openings"><img src="./.github/assets/vuejobs.svg" loading="lazy" vspace="10" alt=""></a>
|
||||
<a href="https://flatlogic.com/" target="_blank" title="Flatlogic - The best way to create React, Angular and Vue full-stack web applications"><img hspace="15" vspace="10" src="./.github/assets/flatlogic.svg" loading="lazy" alt=""></a>
|
||||
</div>
|
||||
<br >
|
||||
|
||||
Become a partner: [hello@epicmax.co](mailto:hello@epicmax.co)
|
||||
|
||||
## Can I hire you guys?
|
||||
|
||||
Yes! Say hi: [hello@epicmax.co](mailto:hello@epicmax.co) </br>
|
||||
We will be happy to work with you! Other [work we’ve done](https://epicmax.co)
|
||||
|
||||
### Awards
|
||||
|
||||
<a href="https://flatlogic.com/templates/vuestic-vue-free-admin" target="_blank">
|
||||
<img src="https://i.imgur.com/ZeQPZ3Q.png" align="center" width="150px"/>
|
||||
</a>
|
||||
<p>
|
||||
By <a href="https://flatlogic.com/templates/vuestic-vue-free-admin" target="_blank">@flatlogic</a> marketplace
|
||||
</p>
|
||||
|
||||
### Follow us
|
||||
|
||||
Stay up to date with the latest Vuestic news! Follow us
|
||||
on [Twitter](https://twitter.com/epicmaxco)
|
||||
or [Facebook](https://facebook.com/epicmaxco)
|
||||
|
||||
### License
|
||||
|
||||
[MIT](https://github.com/epicmaxco/vuestic-admin/blob/master/LICENSE) license.
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
<p align="center">
|
||||
<a href="./README.md"> English </a> | 简体中文 | <a href="./README.ja-JP.md"> 日本語 </a>
|
||||
</p>
|
||||
|
||||
<h1 align="center"> Vuestic Admin </h1>
|
||||
|
||||
<p align="center">
|
||||
免费与美妙Vue.js管理模板包括38以上个定制用户界面组件</br>
|
||||
由<a href="https://epicmax.co">Epicmax</a>开发。
|
||||
由<a href="#">Vasili Savitski</a>设计
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://vuestic.epicmax.co"> 演示版 </a> | <a href="[THEME_URL]"> 新白色主题演示 </a> | <a href="https://github.com/epicmaxco/vuestic-admin/wiki"> 文档 </a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://vuestic.epicmax.co" target="_blank">
|
||||
<img src="https://i.imgur.com/hVxoYQM.jpg" align="center" width="888px"/>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://circleci.com/gh/epicmaxco/vuestic-admin">
|
||||
<img src="https://img.shields.io/circleci/build/github/epicmaxco/vuestic-admin/master" alt="Circleci">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<h2>
|
||||
<a href="[THEME_URL]" target="_blank">
|
||||
新白色主题演示
|
||||
</a>
|
||||
</h2>
|
||||
|
||||
<p align="center">
|
||||
<a href="[THEME_URL]" target="_blank">
|
||||
<img src="https://i.imgur.com/DnFcrjD.png" align="center" width="888px"/>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
## 安装
|
||||
|
||||
首先确定 ni 安装所有的先决条件:
|
||||
|
||||
- [Node.js](https://nodejs.org/en/) (>=8.9)
|
||||
- [npm](https://www.npmjs.com/get-npm) 3+版本 (或许[yarn](https://yarnpkg.com/lang/en/docs/install/#mac-stable) 1.16+版本) 和[Git](https://git-scm.com/)。
|
||||
|
||||
检查先决条件之后,用简单指令之下安装与应用 Vuestic 管理台:
|
||||
|
||||
```
|
||||
#克隆存储库
|
||||
$ git clone https://github.com/epicmaxco/vuestic-admin.git myproject
|
||||
|
||||
#进入应用目录与安装依赖
|
||||
$ cd myproject
|
||||
|
||||
```
|
||||
|
||||
之后,如果您用 npm:
|
||||
|
||||
```
|
||||
$ npm install
|
||||
|
||||
#默认情况下在localhost:8080用热重载
|
||||
$ npm run dev
|
||||
|
||||
#构建生产
|
||||
$ npm run build
|
||||
|
||||
#构建生产与查看包分析报表
|
||||
$ npm run build --report
|
||||
```
|
||||
|
||||
如果您用 yarn:
|
||||
|
||||
```
|
||||
$ yarn install
|
||||
|
||||
#默认情况下在localhost:8080用热重载
|
||||
$ yarn dev
|
||||
|
||||
#构建生产
|
||||
$ yarn build
|
||||
|
||||
#构建生产与查看包分析报表
|
||||
$ yarn build --report
|
||||
```
|
||||
|
||||
## 功能
|
||||
|
||||
[响应布局](https://vuestic.epicmax.co/#/admin/dashboard) |
|
||||
[图表(Charts.js)](https://vuestic.epicmax.co/#/admin/statistics/charts) |
|
||||
[进度表](https://vuestic.epicmax.co/#/admin/statistics/progress-bars) |
|
||||
[表格](https://vuestic.epicmax.co/#/admin/forms/form-elements) |
|
||||
[选辑](https://vuestic.epicmax.co/#/admin/forms/form-elements) |
|
||||
[日期选择器](https://vuestic.epicmax.co/#/admin/forms/form-elements) |
|
||||
[复选框和单选框](https://vuestic.epicmax.co/#/admin/forms/form-elements) |
|
||||
[静态表与数据表](https://vuestic.epicmax.co/#/admin/tables/data) |
|
||||
[medium editor](https://vuestic.epicmax.co/#/admin/forms/medium-editor) |
|
||||
[平滑设计字体](https://vuestic.epicmax.co/#/admin/ui/typography) |
|
||||
[按钮](https://vuestic.epicmax.co/#/admin/ui/buttons) |
|
||||
[塌缩](https://vuestic.epicmax.co/#/admin/ui/collapses) |
|
||||
[颜色选择器](https://vuestic.epicmax.co/#/admin/ui/color-pickers) |
|
||||
[时间线](https://vuestic.epicmax.co/#/admin/ui/timelines) |
|
||||
[土司通知](https://vuestic.epicmax.co/#/admin/ui/notifications) |
|
||||
[工具提示](https://vuestic.epicmax.co/#/admin/ui/popovers) |
|
||||
[弹窗](https://vuestic.epicmax.co/#/admin/ui/popovers) |
|
||||
[图标](https://vuestic.epicmax.co/#/admin/ui/icons/) |
|
||||
[自旋体](https://vuestic.epicmax.co/#/admin/ui/spinners) |
|
||||
[模式](https://vuestic.epicmax.co/#/admin/ui/modals) |
|
||||
[文件上传](https://vuestic.epicmax.co/#/admin/ui/file-upload) |
|
||||
[厚切薯条通知](https://vuestic.epicmax.co/#/admin/ui/chips) |
|
||||
[树](https://vuestic.epicmax.co/#/admin/ui/tree-view) |
|
||||
[卡片](https://vuestic.epicmax.co/#/admin/ui/cards) |
|
||||
[等级](https://vuestic.epicmax.co/#/admin/ui/rating) |
|
||||
[滑动器](https://vuestic.epicmax.co/#/admin/ui/sliders) |
|
||||
[聊天系统](https://vuestic.epicmax.co/#/admin/ui/chat) |
|
||||
[地图(MapLibre, Yandex, Leaflet, amMap)](https://vuestic.epicmax.co/#/admin/maps/maplibre-maps) |
|
||||
[登录/注册页模板](https://vuestic.epicmax.co/#/auth/login) |
|
||||
[404 页模板](https://vuestic.epicmax.co/#/admin/pages/404-pages) |
|
||||
[i18n](https://vuestic.epicmax.co/#/admin/dashboard)
|
||||
|
||||
## 支持的浏览器
|
||||
|
||||
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>IE / Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/opera/opera_48x48.png" alt="Opera" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Opera |
|
||||
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| IE11, Edge | last 2 versions | last 2 versions | last 2 versions | last 2 versions |
|
||||
|
||||
## 参与贡献
|
||||
|
||||
多谢您的出色公关、问题与注意。随时欢迎您的[加入](https://github.com/epicmaxco/vuestic-admin/blob/master/.github/CONTRIBUTING.md)!
|
||||
|
||||
[](https://sourcerer.io/fame/smartapant/epicmaxco/vuestic-admin/links/0)[](https://sourcerer.io/fame/smartapant/epicmaxco/vuestic-admin/links/1)[](https://sourcerer.io/fame/smartapant/epicmaxco/vuestic-admin/links/2)[](https://sourcerer.io/fame/smartapant/epicmaxco/vuestic-admin/links/3)[](https://sourcerer.io/fame/smartapant/epicmaxco/vuestic-admin/links/4)[](https://sourcerer.io/fame/smartapant/epicmaxco/vuestic-admin/links/5)[](https://sourcerer.io/fame/smartapant/epicmaxco/vuestic-admin/links/6)[](https://sourcerer.io/fame/smartapant/epicmaxco/vuestic-admin/links/7)
|
||||
|
||||
## 我可以雇佣你们吗?
|
||||
|
||||
当然!给[hello@epicmax.co](mailto:hello@epicmax.co)发电子邮件 </br>
|
||||
和您一起工作我们将很高兴!查看我们其它项目 - [epicmax.co](https://epicmax.co)
|
||||
|
||||
## 关注我们
|
||||
|
||||
敬请期待 Vuestic 最新消息!关注我们[Twitter](https://twitter.com/epicmaxco)与[Facebook](https://facebook.com/epicmaxco)
|
||||
|
||||
## 许可证
|
||||
|
||||
[MIT](https://github.com/epicmaxco/vuestic-admin/blob/master/LICENSE)许可证.
|
||||
@@ -0,0 +1,16 @@
|
||||
### 2022.12.30
|
||||
|
||||
- file system
|
||||
|
||||
### 2022.12.06
|
||||
|
||||
- e-chart
|
||||
- mqtt
|
||||
- new page data-analysis
|
||||
|
||||
### 2022.11.30
|
||||
|
||||
- setup environment
|
||||
- vite config add path
|
||||
- import windicss
|
||||
- add vue-shim.d.ts file (TS2307)
|
||||
@@ -0,0 +1,7 @@
|
||||
'use strict'
|
||||
const merge = require('webpack-merge')
|
||||
const prodEnv = require('./prod.env')
|
||||
|
||||
module.exports = merge(prodEnv, {
|
||||
NODE_ENV: '"development"',
|
||||
})
|
||||
@@ -0,0 +1,39 @@
|
||||
'use strict'
|
||||
|
||||
const path = require('path')
|
||||
|
||||
module.exports = {
|
||||
build: {
|
||||
env: require('./prod.env'),
|
||||
index: path.resolve(__dirname, '../dist/index.html'),
|
||||
assetsRoot: path.resolve(__dirname, '../dist'),
|
||||
assetsSubDirectory: 'static',
|
||||
assetsPublicPath: '/',
|
||||
productionSourceMap: true,
|
||||
// Gzip off by default as many popular static hosts such as
|
||||
// Surge or Netlify already gzip all static assets for you.
|
||||
// Before setting to `true`, make sure to:
|
||||
// npm install --save-dev compression-webpack-plugin
|
||||
productionGzip: false,
|
||||
productionGzipExtensions: ['js', 'css'],
|
||||
// Run the build command with an extra argument to
|
||||
// View the bundle analyzer report after build finishes:
|
||||
// `npm run build --report`
|
||||
// Set to `true` or `false` to always turn it on or off
|
||||
bundleAnalyzerReport: process.env.npm_config_report,
|
||||
},
|
||||
dev: {
|
||||
env: require('./dev.env'),
|
||||
port: process.env.PORT || 8080,
|
||||
autoOpenBrowser: true,
|
||||
assetsSubDirectory: 'static',
|
||||
assetsPublicPath: '/',
|
||||
proxyTable: {},
|
||||
// CSS Sourcemaps off by default because relative paths are "buggy"
|
||||
// with this option, according to the CSS-Loader README
|
||||
// (https://github.com/webpack/css-loader#sourcemaps)
|
||||
// In our experience, they generally work as expected,
|
||||
// just be aware of this issue when enabling this option.
|
||||
cssSourceMap: false,
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
'use strict'
|
||||
module.exports = {
|
||||
NODE_ENV: '"production"',
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
# Pre-production
|
||||
|
||||
## SEO
|
||||
|
||||
We have a boierplate prepared with some analytics ingrained. This includes:
|
||||
|
||||
- [Yandex Metrica](https://metrica.yandex.com/about)
|
||||
|
||||
To get these running - just provide keys to respective apis. You're advised to copy `.env.example` with rename to `.env` then modify it.
|
||||
|
||||
Notice, that if you are about to use Google Maps then you also have to provide it with your personal API key. The key must be defined under the `VUE_APP_GOOGLE_MAPS_API_KEY` environment-variable (more on them below) and can be obtained [here](https://developers.google.com/maps/documentation/javascript/get-api-key).
|
||||
|
||||
## Deploy
|
||||
|
||||
We use [circleci](https://circleci.com) to deploy vuestic version you're able to see on demo.
|
||||
|
||||
If you want to save some time and use our config, do notice that circleci will need the following keys, that you have to set in **Build Settings -> Environment Variables**.
|
||||
|
||||
- `DEPLOY_PASSWORD` ssh password.
|
||||
- `DEPLOY_PATH_PRODUCTION` production build will be loaded to this folder.
|
||||
- `DEPLOY_PATH_STAGING` staging build will be loaded to this folder.
|
||||
- `DEPLOY_URL` ssh url.
|
||||
- `DEPLOY_USER` ssh password.
|
||||
|
||||
You can modify [config](../.circleci/config.yml) if our solution doesn't suit your needs exactly.
|
||||
|
||||
Couple of things to note:
|
||||
|
||||
- in `.env` file keys should look like this `VUE_APP_DRIFT_KEY`. Which will correspond to circleci key `DRIFT_KEY`. You essentially have two ways to pass config into build process.
|
||||
- Circleci will run tests before both staging and production.
|
||||
|
||||
## Demos
|
||||
|
||||
You can enable demos in build by:
|
||||
|
||||
```
|
||||
VUE_APP_INCLUDE_DEMOS=true
|
||||
```
|
||||
|
||||
Demos are included in staging build by default. They're not present in production because of significant impact on bundle size.
|
||||
|
||||
## Build Version
|
||||
|
||||
You can enable build version, hash commit, and timestamp by build to the main page footer:
|
||||
|
||||
```
|
||||
VUE_APP_BUILD_VERSION=true
|
||||
```
|
||||
|
||||
This information are excluded by default.
|
||||
@@ -1,18 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html lang="en" class="bg-red-500">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite + Vue + TS</title>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Source+Sans+Pro:ital,wght@0,400;1,700&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/icon?family=Material+Icons"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,700" rel="stylesheet" />
|
||||
<link href="https://fonts.googleapis.com/css?family=Source+Code+Pro:400" rel="stylesheet" />
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
|
||||
<title>Vuestic Admin</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
@@ -1,36 +1,72 @@
|
||||
{
|
||||
"name": "vite-project",
|
||||
"name": "vuestic-admin",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"version": "3.1.0",
|
||||
"scripts": {
|
||||
"prepare": "husky install",
|
||||
"dev": "vite",
|
||||
"build": "vue-tsc && vite build",
|
||||
"preview": "vite preview",
|
||||
"lint": "eslint . --ext .vue,.js,.ts,.jsx,.tsx --fix",
|
||||
"format": "prettier --write \"./**/*.{html,vue,ts,js,json,md}\""
|
||||
"build": "npm run lint && vue-tsc --noEmit && vite build",
|
||||
"build:ci": "vite build",
|
||||
"prelint": "npm run format",
|
||||
"lint": "eslint --fix \"./src/**/*.{ts,js,vue}\"",
|
||||
"format": "prettier --write .",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"lint-staged": {
|
||||
"./src/**/*.{ts,js,vue}": [
|
||||
"npm run lint"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"pinia": "^2.0.27",
|
||||
"vite-plugin-windicss": "^1.8.8",
|
||||
"vue": "^3.2.41",
|
||||
"vue-router": "^4.1.6",
|
||||
"vuestic-ui": "^1.5.2",
|
||||
"windicss": "^3.5.6"
|
||||
"@amcharts/amcharts5": "^5.2.19",
|
||||
"@amcharts/amcharts5-geodata": "^5.0.3",
|
||||
"@fortawesome/fontawesome-free": "^6.1.1",
|
||||
"@gtm-support/vue-gtm": "^1.6.0",
|
||||
"axios": "^0.27.2",
|
||||
"chart.js": "^3.8.0",
|
||||
"epic-spinners": "^2.0.0",
|
||||
"flag-icons": "^6.6.4",
|
||||
"ionicons": "^4.6.3",
|
||||
"leaflet": "^1.8.0",
|
||||
"leaflet-map": "^0.2.1",
|
||||
"maplibre-gl": "^2.1.9",
|
||||
"medium-editor": "^5.23.3",
|
||||
"pinia": "^2.0.16",
|
||||
"register-service-worker": "^1.7.1",
|
||||
"sass": "^1.53.0",
|
||||
"vue": "^3.2.25",
|
||||
"vue-chartjs": "^4.1.1",
|
||||
"vue-i18n": "^9.1.10",
|
||||
"vue-router": "^4.0.14",
|
||||
"vue-yandex-maps": "^0.12.3",
|
||||
"vuestic-ui": "^1.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.11.11",
|
||||
"@typescript-eslint/eslint-plugin": "^5.45.1",
|
||||
"@typescript-eslint/parser": "^5.45.1",
|
||||
"@vitejs/plugin-vue": "^3.2.0",
|
||||
"eslint": "^8.29.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-vue": "^9.8.0",
|
||||
"prettier": "^2.8.0",
|
||||
"typescript": "*",
|
||||
"vite": "^3.2.3",
|
||||
"vue-tsc": "^1.0.9"
|
||||
"@intlify/vite-plugin-vue-i18n": "^5.0.1",
|
||||
"@types/leaflet": "^1.7.11",
|
||||
"@types/medium-editor": "^5.0.5",
|
||||
"@types/node": "^17.0.25",
|
||||
"@typescript-eslint/eslint-plugin": "^5.20.0",
|
||||
"@typescript-eslint/parser": "^5.20.0",
|
||||
"@vitejs/plugin-vue": "^2.3.3",
|
||||
"@vue/eslint-config-prettier": "^7.0.0",
|
||||
"@vue/eslint-config-typescript": "^10.0.0",
|
||||
"autoprefixer": "^10.4.4",
|
||||
"echarts": "^5.4.0",
|
||||
"eslint": "^8.13.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"eslint-plugin-vue": "^8.7.0",
|
||||
"husky": "^8.0.1",
|
||||
"lint-staged": "^13.0.1",
|
||||
"mqtt": "^4.3.7",
|
||||
"postcss": "^8.4.12",
|
||||
"prettier": "^2.6.2",
|
||||
"typescript": "^4.5.4",
|
||||
"vite": "^2.9.9",
|
||||
"vite-plugin-windicss": "^1.8.8",
|
||||
"vue-echarts": "^6.2.3",
|
||||
"vue-eslint-parser": "^8.3.0",
|
||||
"vue-tsc": "^1.0.10",
|
||||
"windicss": "^3.5.6"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 618 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 5.6 KiB |
@@ -1,19 +1,18 @@
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<template>
|
||||
<div><router-view></router-view></div>
|
||||
<router-view />
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.logo {
|
||||
height: 6em;
|
||||
padding: 1.5em;
|
||||
will-change: filter;
|
||||
}
|
||||
.logo:hover {
|
||||
filter: drop-shadow(0 0 2em #646cffaa);
|
||||
}
|
||||
.logo.vue:hover {
|
||||
filter: drop-shadow(0 0 2em #42b883aa);
|
||||
}
|
||||
<style lang="scss">
|
||||
@import 'scss/main.scss';
|
||||
|
||||
#app {
|
||||
font-family: 'Source Sans Pro', Avenir, Helvetica, Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>
|
||||
|
Before Width: | Height: | Size: 496 B |
@@ -1,38 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
|
||||
defineProps<{ msg: string }>()
|
||||
|
||||
const count = ref(0)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1>{{ msg }}</h1>
|
||||
|
||||
<div class="card">
|
||||
<button type="button" @click="count++">count is {{ count }}</button>
|
||||
<p>
|
||||
Edit
|
||||
<code>components/HelloWorld.vue</code> to test HMR
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
Check out
|
||||
<a href="https://vuejs.org/guide/quick-start.html#local" target="_blank"
|
||||
>create-vue</a
|
||||
>, the official Vue + Vite starter
|
||||
</p>
|
||||
<p>
|
||||
Install
|
||||
<a href="https://github.com/johnsoncodehk/volar" target="_blank">Volar</a>
|
||||
in your IDE for a better DX
|
||||
</p>
|
||||
<p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.read-the-docs {
|
||||
color: #888;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<svg class="va-icon-vuestic" :height="height" viewBox="0 0 478 57" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M18.2761 56.1386L0 13.3794H10.5519L18.8968 33.3107L27.2417 13.3794H37.7936L19.5175 56.1386H18.2761ZM62.4836 40.8281V13.3794H72.5527V39.3108C72.5527 44.8281 75.1734 46.7592 78.6907 46.7592C82.5528 46.7592 84.9666 44.8281 84.9666 39.3108V13.3794H95.1047V40.8281C95.1047 50.6213 87.7942 56.1386 78.6907 56.1386C69.2423 56.1386 62.4836 50.3454 62.4836 40.8281ZM122.415 54.7593V13.3794H149.795V22.3451H132.553V29.7935H148.485V38.6211H132.553V46.0695H150.554V54.7593H122.415ZM175.933 42.6212H185.795C185.795 45.035 187.727 46.9661 190.071 46.9661C192.623 46.9661 194.209 45.5178 194.209 43.3798C194.209 40.2763 190.14 39.3798 186.761 38.2074C179.795 35.7246 175.933 32.4831 175.933 25.3796C175.933 18.0692 182.209 12.0001 190.002 12.0001C199.175 12.0001 203.52 17.8623 204.072 25.5175H194.554C194.554 23.1727 193.106 21.2416 190.278 21.2416C188.071 21.2416 185.933 22.552 185.933 25.2417C185.933 28.3452 189.727 28.8969 193.244 29.9314C200.623 32.1383 204.072 36.2763 204.072 42.7591C204.072 50.0696 197.865 56.1386 190.071 56.1386C181.037 56.1386 175.933 50.0696 175.933 42.6212ZM236.442 54.7593V22.3451H227.2V13.3794H256.028V22.3451H246.649V54.7593H236.442ZM281.408 54.7593V13.3794H291.546V54.7593H281.408ZM317.502 34.0694C317.502 21.7244 326.812 12.0001 339.916 12.0001C347.778 12.0001 353.226 14.9657 357.64 19.5864L350.675 26.2072C347.778 23.3796 344.054 21.5865 339.916 21.5865C332.536 21.5865 327.778 27.1038 327.778 34.0694C327.778 41.035 332.536 46.5523 339.916 46.5523C344.054 46.5523 347.778 44.7592 350.675 41.9315L357.433 48.5523C353.295 52.8972 347.571 56.1386 339.916 56.1386C326.812 56.1386 317.502 46.4143 317.502 34.0694Z"
|
||||
fill="url(#paint0_linear)"
|
||||
/>
|
||||
<path
|
||||
d="M388.671 23.5893L388.364 24.844H391.71L391.403 23.5893C391.18 22.7157 390.957 21.7677 390.734 20.7454C390.511 19.7231 390.288 18.7565 390.065 17.8457H389.953C389.749 18.7751 389.535 19.751 389.312 20.7733C389.107 21.777 388.894 22.7157 388.671 23.5893ZM381.7 32.3999L387.221 14.2769H393.02L398.541 32.3999H393.466L392.574 28.5523H387.5L386.607 32.3999H381.7Z"
|
||||
:fill="colorsComputed.start"
|
||||
/>
|
||||
<path
|
||||
d="M403.987 32.3999V14.2769H409.34C410.715 14.2769 411.97 14.4441 413.104 14.7787C414.256 15.1133 415.241 15.6431 416.059 16.368C416.877 17.0929 417.509 18.0223 417.955 19.1561C418.42 20.29 418.652 21.6562 418.652 23.2548C418.652 24.8533 418.429 26.2288 417.983 27.3812C417.537 28.5337 416.905 29.4817 416.087 30.2252C415.288 30.9687 414.331 31.517 413.215 31.8702C412.119 32.2234 410.92 32.3999 409.619 32.3999H403.987ZM408.782 28.5523H409.061C409.712 28.5523 410.325 28.4779 410.901 28.3292C411.477 28.1619 411.97 27.8831 412.379 27.4928C412.806 27.1024 413.141 26.5634 413.383 25.8756C413.624 25.1879 413.745 24.3143 413.745 23.2548C413.745 22.1953 413.624 21.3309 413.383 20.6618C413.141 19.9926 412.806 19.4721 412.379 19.1004C411.97 18.7286 411.477 18.4777 410.901 18.3476C410.325 18.1989 409.712 18.1245 409.061 18.1245H408.782V28.5523Z"
|
||||
:fill="colorsComputed.start"
|
||||
/>
|
||||
<path
|
||||
d="M424.664 32.3999V14.2769H429.794L432.526 21.6934C432.694 22.1953 432.852 22.7157 433 23.2548C433.149 23.7938 433.307 24.3328 433.474 24.8719H433.586C433.753 24.3328 433.911 23.7938 434.06 23.2548C434.208 22.7157 434.366 22.1953 434.534 21.6934L437.155 14.2769H442.285V32.3999H437.935V27.0467C437.935 26.582 437.954 26.0708 437.991 25.5132C438.047 24.9555 438.103 24.3886 438.158 23.8124C438.214 23.2176 438.279 22.6414 438.354 22.0837C438.428 21.5261 438.493 21.0242 438.549 20.5781H438.437L436.932 24.9277L434.701 30.9222H432.136L429.905 24.9277L428.456 20.5781H428.344C428.4 21.0242 428.465 21.5261 428.539 22.0837C428.614 22.6414 428.679 23.2176 428.734 23.8124C428.79 24.3886 428.837 24.9555 428.874 25.5132C428.93 26.0708 428.957 26.582 428.957 27.0467V32.3999H424.664Z"
|
||||
:fill="colorsComputed.start"
|
||||
/>
|
||||
<path d="M450.028 32.3999V14.2769H454.823V32.3999H450.028Z" :fill="colorsComputed.start" />
|
||||
<path
|
||||
d="M462.567 32.3999V14.2769H467.474L471.489 22.3625L473.218 26.4333H473.329C473.274 25.95 473.209 25.4202 473.134 24.844C473.06 24.2678 472.995 23.6823 472.939 23.0875C472.883 22.4741 472.828 21.87 472.772 21.2752C472.735 20.6618 472.716 20.0762 472.716 19.5186V14.2769H477.289V32.3999H472.381L468.366 24.2864L466.638 20.2435H466.526C466.638 21.2845 466.768 22.4183 466.917 23.6451C467.065 24.8719 467.14 26.0429 467.14 27.1582V32.3999H462.567Z"
|
||||
:fill="colorsComputed.start"
|
||||
/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear" x1="0" y1="56.1386" x2="357.64" y2="56.1386" gradientUnits="userSpaceOnUse">
|
||||
<stop :stop-color="colorsComputed.end" />
|
||||
<stop offset="1" :stop-color="colorsComputed.start" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useColors } from 'vuestic-ui'
|
||||
const { getColor, shiftHSLAColor } = useColors()
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
height?: number | string
|
||||
color?: string
|
||||
}>(),
|
||||
{
|
||||
height: 16,
|
||||
color: 'primary',
|
||||
},
|
||||
)
|
||||
|
||||
const colorsComputed = computed(() => {
|
||||
const color = getColor(props.color, 'primary')
|
||||
return { start: color, end: shiftHSLAColor(color, { l: -20 }) }
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,96 @@
|
||||
<template>
|
||||
<div>
|
||||
<va-card class="h-full">
|
||||
<va-card-content>
|
||||
<va-select
|
||||
v-model="simpleSelectModel"
|
||||
:label="'Filter Mode'"
|
||||
text-by="description"
|
||||
track-by="id"
|
||||
:options="simpleOptions"
|
||||
/>
|
||||
<va-card-content>
|
||||
<div v-if="simpleSelectModel.id === 1">
|
||||
<va-slider v-model="VTModeModel" label="percentage(%)" track-label-visible :min="0" :max="50" />
|
||||
</div>
|
||||
<div v-else-if="simpleSelectModel.id === 2">
|
||||
<va-slider v-model="slopModeModel" label="1D window" track-label-visible :min="1" :max="50" />
|
||||
</div>
|
||||
<div v-else></div>
|
||||
<div>
|
||||
<p>{{ channel.xAxis }}</p>
|
||||
<p>{{ channel.yAxis }}</p>
|
||||
<p>{{ fileView.filesSelected }}</p>
|
||||
</div>
|
||||
</va-card-content>
|
||||
|
||||
<va-button class="mt-2" @click="filterData">Start</va-button>
|
||||
<!-- <div v-if="simpleSelectModel.id === 1">
|
||||
<va-button>test</va-button>
|
||||
<va-input></va-input>
|
||||
</div> -->
|
||||
</va-card-content>
|
||||
</va-card>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { useChannelStore } from '@/stores/data-analysis/channel'
|
||||
import { useFileViewStore } from '@/stores/data-analysis/file-view'
|
||||
import { Ref, ref, defineEmits, defineExpose } from 'vue'
|
||||
|
||||
// define emit
|
||||
// const emit = defineEmits({
|
||||
// 'filter-data': ({ mode, data_id, data_channel, data }) => {
|
||||
// return { mode, data_id, data_channel, data }
|
||||
// },
|
||||
// })
|
||||
const emit = defineEmits(['filter-data'])
|
||||
|
||||
const channel = useChannelStore()
|
||||
const fileView = useFileViewStore()
|
||||
const VTModeModel = ref(10)
|
||||
const slopModeModel = ref(20)
|
||||
const simpleSelectModel: Ref<{ id: number; description: string } | Record<string, never>> = ref({})
|
||||
const simpleOptions = ref([
|
||||
{
|
||||
id: 1,
|
||||
description: 'V-T Mode',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
description: '1st Order Differential',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
description: 'Third option',
|
||||
},
|
||||
])
|
||||
const filterData = function () {
|
||||
console.log(VTModeModel.value)
|
||||
const a = 0
|
||||
console.log(a)
|
||||
// switch(simpleSelectModel.value.id) {
|
||||
// case 1:
|
||||
// const return_data = { mode: 1, data_id: 166, data: { persentage: 0 } };
|
||||
// case 2:
|
||||
// const return_data = { mode: 2, data_id: 166, data: { persentage: 0 } };
|
||||
// default:
|
||||
// }
|
||||
// console.log(return_data)
|
||||
emit('filter-data', {
|
||||
mode: simpleSelectModel.value.id,
|
||||
data_id: fileView.filesSelected,
|
||||
data_channel: [channel.xAxis.id, channel.yAxis.id],
|
||||
data: { persentage: VTModeModel.value, window: slopModeModel.value },
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
simpleSelectModel,
|
||||
simpleOptions,
|
||||
VTModeModel,
|
||||
slopModeModel,
|
||||
filterData,
|
||||
})
|
||||
</script>
|
||||
<style lang=""></style>
|
||||
@@ -0,0 +1,31 @@
|
||||
<template>
|
||||
<va-card class="">
|
||||
<div class="h-200px">
|
||||
<va-button @click="showModal">FILE</va-button>
|
||||
<p>Meta: {{ fileView.filesSelected }}</p>
|
||||
<p>X: {{ channel.xAxis }}</p>
|
||||
<p>Y: {{ channel.yAxis }}</p>
|
||||
<Suspense>
|
||||
<FileModal ref="file_modal_ref"></FileModal>
|
||||
</Suspense>
|
||||
</div>
|
||||
</va-card>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { useChannelStore } from '@/stores/data-analysis/channel'
|
||||
import { useFileViewStore } from '@/stores/data-analysis/file-view'
|
||||
import FileModal from '@/components/data-analysis/FileModal.vue'
|
||||
|
||||
const channel = useChannelStore()
|
||||
const fileView = useFileViewStore()
|
||||
|
||||
/* File Modal Handling */
|
||||
let file_modal_ref = ref<InstanceType<typeof FileModal> | null>(null)
|
||||
const showModal = function () {
|
||||
file_modal_ref.value?.showModal()
|
||||
}
|
||||
|
||||
defineExpose({ file_modal_ref, showModal })
|
||||
</script>
|
||||
@@ -0,0 +1,212 @@
|
||||
<template>
|
||||
<div>
|
||||
<va-modal v-model="show" :fixed-layout="true" size="large" @before-open="initLoad">
|
||||
<!-- File Container-->
|
||||
<div class="h-[75vh] w-[50vw]">
|
||||
<!-- Control Bar -->
|
||||
<div class="d-flex align-center">
|
||||
<va-input v-model="filter" placeholder="Filter..." class="mr-3" style="flex: 0 200px" clearable />
|
||||
<va-checkbox v-model="isFilterCaseSensitive" class="mr-3" label="Case sensitive" />
|
||||
<va-button-group class="mr-3">
|
||||
<va-button @click="changeFileView('time')">Time</va-button>
|
||||
<va-button @click="changeFileView('folder')">Folder</va-button>
|
||||
<va-button @click="changeFileView('project')">Project</va-button>
|
||||
</va-button-group>
|
||||
<va-button-group>
|
||||
<va-button @click="expandAll(true)">Expand</va-button>
|
||||
<va-button @click="expandAll(false)">Close</va-button>
|
||||
</va-button-group>
|
||||
</div>
|
||||
<!-- File Window-->
|
||||
<div class="grid grid-cols-4 gap-6">
|
||||
<!-- File View-->
|
||||
<div class="h-full col-span-3">
|
||||
<va-tree-view
|
||||
v-model:checked="selectedNodes"
|
||||
v-model:expanded="expanedNodes"
|
||||
:nodes="treeNodes"
|
||||
class="customizable-content"
|
||||
:filter="filter"
|
||||
:filter-method="customFilterMethod"
|
||||
:track-by="'idType'"
|
||||
:value-by="'idType'"
|
||||
selectable
|
||||
>
|
||||
<template #content="node">
|
||||
<div class="align-center">
|
||||
<va-icon v-if="node.type === 'Folder' && node.expanded === false" class="fas fa-folder mr-2" />
|
||||
<va-icon v-if="node.type === 'Folder' && node.expanded === true" class="fas fa-folder-open mr-2" />
|
||||
<va-icon v-if="node.type === 'MetaFile'" class="fas fa-file mr-2" />
|
||||
<template v-if="node.type !== 'MetaFile'">{{ node.name }}</template>
|
||||
<template v-if="node.type === 'MetaFile'">
|
||||
{{ node.name }}
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</va-tree-view>
|
||||
</div>
|
||||
<!-- Channel & Group setting-->
|
||||
<div class="h-full col-span-1">
|
||||
<div>
|
||||
X-Axis
|
||||
<va-select v-model="channel.xAxis" :text-by="'name'" :options="options"></va-select>
|
||||
</div>
|
||||
<div>
|
||||
Y-Axis
|
||||
<va-select v-model="channel.yAxis" :text-by="'name'" :options="options"></va-select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</va-modal>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { Ref, ref, computed, watch } from 'vue'
|
||||
import { FileView, Project, MetaFile, Folder, Time } from '@/utils/file'
|
||||
import configTable from '@/data/config-table/index'
|
||||
import { useChannelStore } from '@/stores/data-analysis/channel'
|
||||
import { useFileViewStore } from '@/stores/data-analysis/file-view'
|
||||
|
||||
// emit
|
||||
const emit = defineEmits({
|
||||
filesSelected: ({ metas, channel }) => {
|
||||
return { metas, channel }
|
||||
},
|
||||
})
|
||||
|
||||
function emitFilesSelected(meta: string[], channel: { x: object; y: object }) {
|
||||
emit('filesSelected', { meta, channel })
|
||||
}
|
||||
|
||||
// filter function
|
||||
const customFilterMethod = computed(() => {
|
||||
return (node: Project | MetaFile, filterText: string) => {
|
||||
// console.log('customFilterMethod', node, filterText, key, node.name.includes(filterText))
|
||||
return node.name.includes(filterText)
|
||||
}
|
||||
})
|
||||
|
||||
// handling show modal
|
||||
let show = ref(false)
|
||||
const showModal = function () {
|
||||
show.value = !show.value
|
||||
}
|
||||
|
||||
// handling fileView
|
||||
const fileViewStore = useFileViewStore()
|
||||
const fileView = new FileView()
|
||||
|
||||
// handling tree-view
|
||||
const filter = ref('')
|
||||
const isFilterCaseSensitive = ref(false)
|
||||
const selectedNodes: Ref<(never | string)[]> = ref([])
|
||||
const expanedNodes: Ref<(never | string)[]> = ref([])
|
||||
const treeNodes: Ref<(never | Project | Folder | Time | MetaFile)[]> = ref([])
|
||||
|
||||
const initLoad = async function () {
|
||||
if (treeNodes.value.length === 0) {
|
||||
await initLoadFileView()
|
||||
initLoadNodes()
|
||||
}
|
||||
}
|
||||
|
||||
const initLoadFileView = async function () {
|
||||
await fileView.appendChildren(1)
|
||||
}
|
||||
|
||||
const initLoadNodes = function () {
|
||||
const files = fileView.getChildren()
|
||||
if (files.length > 0) {
|
||||
treeNodes.value.push(...files)
|
||||
expanedNodes.value.push(treeNodes.value[0].idType)
|
||||
}
|
||||
}
|
||||
|
||||
const reset = function () {
|
||||
resetFileView()
|
||||
resetNodes()
|
||||
}
|
||||
|
||||
const resetFileView = function () {
|
||||
fileView.reset()
|
||||
}
|
||||
|
||||
const resetNodes = function () {
|
||||
selectedNodes.value.length = 0
|
||||
expanedNodes.value.length = 0
|
||||
treeNodes.value.length = 0
|
||||
}
|
||||
|
||||
// handling modal header
|
||||
const changeFileView = async function (view: string) {
|
||||
// reset fileView
|
||||
fileView.changeView(view)
|
||||
await fileView.appendChildren(1)
|
||||
|
||||
// reset treeNodes & expandedNodes & selectedNodes
|
||||
treeNodes.value.length = 0
|
||||
treeNodes.value.push(...fileView.getChildren())
|
||||
|
||||
expanedNodes.value.length = 0
|
||||
expanedNodes.value.push(treeNodes.value[0].idType)
|
||||
|
||||
// selectedNodes.value.length = 0
|
||||
}
|
||||
|
||||
const expandAll = function (expandOrNot: boolean) {
|
||||
expanedNodes.value.length = 0
|
||||
if (!expandOrNot) return
|
||||
for (const node of treeNodes.value) {
|
||||
expanedNodes.value.push(node.idType)
|
||||
}
|
||||
}
|
||||
|
||||
// handling channel options
|
||||
const channel = useChannelStore()
|
||||
let options: any[] = []
|
||||
|
||||
// watch selectedNodes to decide axis options
|
||||
watch(selectedNodes, (newSelectedNodes, oldSelectedNodes) => {
|
||||
// update fileViewStore.filesSelected
|
||||
fileViewStore.filesSelected.length = 0
|
||||
fileViewStore.filesSelected.push(...newSelectedNodes.filter((x) => x.includes('MetaFile')))
|
||||
// 新的要filter掉舊的沒有的
|
||||
const newNodes = newSelectedNodes.filter((x) => x.includes('MetaFile') && !oldSelectedNodes.includes(x))
|
||||
const MetFileList = []
|
||||
for (const node of newNodes) {
|
||||
const meta = fileView.getMetaFile(node)
|
||||
MetFileList.push(meta)
|
||||
}
|
||||
|
||||
console.log('MetFileList', MetFileList)
|
||||
if (MetFileList[0]) {
|
||||
// console.log(MetFileList[0].device.library_name, MetFileList[0].parameter.MODE)
|
||||
// console.log(configTable.getConfig(MetFileList[0].device.library_name))
|
||||
|
||||
const config: any = configTable.getModeConfig(MetFileList[0].device.library_name, MetFileList[0].parameter.MODE)
|
||||
const channel = Object.values(config.channels)
|
||||
const channelOptions = channel.map((v: any, idx) => {
|
||||
v.id = idx
|
||||
return v
|
||||
})
|
||||
console.log('channelOptions', channelOptions)
|
||||
options.length = 0
|
||||
options.push(...channelOptions)
|
||||
}
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
emitFilesSelected,
|
||||
show,
|
||||
showModal,
|
||||
changeFileView,
|
||||
expandAll,
|
||||
reset,
|
||||
initLoad,
|
||||
treeNodes,
|
||||
expanedNodes,
|
||||
selectedNodes,
|
||||
})
|
||||
</script>
|
||||
<style lang="scss"></style>
|
||||
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<div>
|
||||
<va-card class="h-full">
|
||||
<va-card-content class="h-full w-full">
|
||||
<EChart ref="chart_ref" :option="option" :autoresize="true" :loading="loading" />
|
||||
</va-card-content>
|
||||
</va-card>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import LineChart from '@/data/echart/composables/LineChart'
|
||||
import { EChartsCoreOption } from 'echarts'
|
||||
import { ref } from 'vue'
|
||||
|
||||
const loading = ref(false)
|
||||
const { chart_ref, option } = LineChart()
|
||||
|
||||
const setOption = function (option: EChartsCoreOption) {
|
||||
chart_ref.value?.setOption(option)
|
||||
}
|
||||
|
||||
const appendData = function (seriesIndex: number, data: Array<number | string>) {
|
||||
chart_ref.value?.appendData({
|
||||
seriesIndex: seriesIndex,
|
||||
data: data,
|
||||
})
|
||||
chart_ref.value?.resize()
|
||||
}
|
||||
|
||||
const markLine = function (
|
||||
seriesIndex: number,
|
||||
markLineIndex: number,
|
||||
header: string,
|
||||
value: number | Array<number> | string,
|
||||
) {
|
||||
chart_ref.value?.setOption({
|
||||
series: [
|
||||
{
|
||||
markLine: {
|
||||
data: [{ name: header, yAxis: value }],
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
const startLoading = function () {
|
||||
loading.value = true
|
||||
}
|
||||
|
||||
const stopLoading = function () {
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
startLoading,
|
||||
stopLoading,
|
||||
setOption,
|
||||
appendData,
|
||||
markLine,
|
||||
})
|
||||
</script>
|
||||
<style lang=""></style>
|
||||
@@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<svg class="va-icon-clean-code" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 56.02 50.34">
|
||||
<defs />
|
||||
<title>overview_icon_4</title>
|
||||
<g id="Layer_2" data-name="Layer 2">
|
||||
<g id="Layer_1-2" data-name="Layer 1">
|
||||
<path class="cls-1" d="M38.23,16.17a10,10,0,1,0-17.67,6.42V47.5l7.33-5,8,5V22.58A10,10,0,0,0,38.23,16.17Z" />
|
||||
<path
|
||||
class="cls-2"
|
||||
d="M28.23,0a13.15,13.15,0,0,0-9.17,22.6V50.34l8.87-6,9.46,5.92V22.6A13.15,13.15,0,0,0,28.23,0ZM34.4,44.79l-6.54-4.08-5.8,4V24.79a13.11,13.11,0,0,0,12.33,0ZM28.23,23.33A10.17,10.17,0,1,1,38.4,13.17,10.18,10.18,0,0,1,28.23,23.33Z"
|
||||
/>
|
||||
<path
|
||||
class="cls-2"
|
||||
d="M28.23,5.67a7.5,7.5,0,1,0,7.5,7.5A7.51,7.51,0,0,0,28.23,5.67Zm0,12a4.5,4.5,0,1,1,4.5-4.5A4.5,4.5,0,0,1,28.23,17.67Z"
|
||||
/>
|
||||
<polygon class="cls-2" points="9.51 15.11 0 24.61 9.51 34.12 11.63 32 4.24 24.61 11.63 17.23 9.51 15.11" />
|
||||
<polygon
|
||||
class="cls-2"
|
||||
points="46.52 15.11 44.39 17.23 51.78 24.61 44.39 32 46.52 34.12 56.02 24.61 46.52 15.11"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.va-icon-clean-code {
|
||||
display: inline-block;
|
||||
width: 56px;
|
||||
height: 50px;
|
||||
|
||||
.cls-1 {
|
||||
fill: #4ae387;
|
||||
}
|
||||
|
||||
.cls-2 {
|
||||
fill: #34495e;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<svg
|
||||
class="va-icon-color"
|
||||
:fill="color"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path fill="none" d="M0 0h24v24H0V0z" />
|
||||
<path
|
||||
d="M12 22C6.49 22 2 17.51 2 12S6.49 2 12 2s10 4.04 10 9c0 3.31-2.69 6-6 6h-1.77c-.28 0-.5.22-.5.5 0 .12.05.23.13.33.41.47.64 1.06.64 1.67 0 1.38-1.12 2.5-2.5 2.5zm0-18c-4.41 0-8 3.59-8 8s3.59 8 8 8c.28 0 .5-.22.5-.5 0-.16-.08-.28-.14-.35-.41-.46-.63-1.05-.63-1.65 0-1.38 1.12-2.5 2.5-2.5H16c2.21 0 4-1.79 4-4 0-3.86-3.59-7-8-7z"
|
||||
/>
|
||||
<circle cx="6.5" cy="11.5" r="1.5" />
|
||||
<circle cx="9.5" cy="7.5" r="1.5" />
|
||||
<circle cx="14.5" cy="7.5" r="1.5" />
|
||||
<circle cx="17.5" cy="11.5" r="1.5" />
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
color?: string
|
||||
}>(),
|
||||
{
|
||||
color: 'inherit',
|
||||
},
|
||||
)
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.va-icon-color {
|
||||
display: inline-block;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,33 @@
|
||||
<template>
|
||||
<svg
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
class="va-icon-faster"
|
||||
viewBox="0 0 24 24"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<!-- Generator: sketchtool 48.2 (47327) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>62EBC3B8-A55C-4B01-95A2-52FB8EDD4150</title>
|
||||
<defs />
|
||||
<g id="symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="icon-faster" fill="#34495E">
|
||||
<g>
|
||||
<path
|
||||
id="A"
|
||||
d="M17.748,19 L16.956,16.3 L12.942,16.3 L12.168,19 L8.928,19 L13.302,6.13 L16.614,6.13 L20.988,19 L17.748,19 Z M14.976,9.064 L14.94,9.064 C14.94,9.064 14.652,10.468 14.418,11.278 L13.68,13.78 L16.218,13.78 L15.498,11.278 C15.264,10.468 14.976,9.064 14.976,9.064 Z"
|
||||
/>
|
||||
<rect id="Rectangle-4" x="3" y="11" width="5" height="2" rx="1" />
|
||||
<rect id="Rectangle-4-Copy" x="4" y="7" width="6" height="2" rx="1" />
|
||||
<rect id="Rectangle-4" x="2" y="15" width="4" height="2" rx="1" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.va-icon-faster {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<svg class="va-icon-free" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 44.99 51.04">
|
||||
<defs />
|
||||
<title>overview_icon_2</title>
|
||||
<g id="Layer_2" data-name="Layer 2">
|
||||
<g id="Layer_1-2" data-name="Layer 1">
|
||||
<path
|
||||
class="cls-1"
|
||||
d="M1.08,28.21C1.08,13.62,8.38,6.29,19,6.29S37,13.69,37,28.21,29.66,50.54,19,50.54,1.08,42.8,1.08,28.21Zm23.56,0c0-11.3-2.58-16.9-5.62-16.9s-5.62,5.6-5.62,16.9S16,41.66,19,41.66,24.65,39.51,24.65,28.21Z"
|
||||
/>
|
||||
<line class="cls-2" x1="39.83" y1="47.62" x2="39.83" y2="50.96" />
|
||||
<path
|
||||
class="cls-3"
|
||||
d="M18.73,9.64c-4.9,0-6.9,4.54-6.9,15.66,0,11.29,2.06,16.1,6.9,16.1s6.9-4.81,6.9-16.1C25.63,14.17,23.63,9.64,18.73,9.64Zm0,28.76c-1.07,0-3.9,0-3.9-13.1,0-12.66,2.64-12.66,3.9-12.66s3.9,0,3.9,12.66C22.63,38.4,19.8,38.4,18.73,38.4Z"
|
||||
/>
|
||||
<path
|
||||
class="cls-3"
|
||||
d="M42.9,43.74A3.76,3.76,0,0,1,40.17,45c-1.95,0-3.24-1.57-3.24-4.4s1.53-4.35,3.29-4.35a3.67,3.67,0,0,1,2.5,1.11l2.08-2.55A6.8,6.8,0,0,0,41.33,33V31h-3v2.12a7.09,7.09,0,0,0-1.64.63,43.71,43.71,0,0,0,.77-8.41c0-15.84-7-25.3-18.73-25.3S0,9.46,0,25.3,7.18,51,18.73,51A16.4,16.4,0,0,0,33.12,43.1,6.77,6.77,0,0,0,40,48.46a6.35,6.35,0,0,0,5-2.22ZM18.73,48C8.88,48,3,39.54,3,25.3S8.73,3,18.73,3s15.73,8.13,15.73,22.3S28.58,48,18.73,48Z"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.va-icon-free {
|
||||
display: inline-block;
|
||||
width: 55px;
|
||||
height: 47.8px;
|
||||
|
||||
.cls-1 {
|
||||
fill: #4ae387;
|
||||
}
|
||||
|
||||
.cls-2 {
|
||||
fill: none;
|
||||
stroke: #34495e;
|
||||
stroke-miterlimit: 10;
|
||||
stroke-width: 3px;
|
||||
}
|
||||
|
||||
.cls-3 {
|
||||
fill: #34495e;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<svg class="va-icon-fresh" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50.98 47.66">
|
||||
<defs />
|
||||
<title>overview_icon_5</title>
|
||||
<g id="Layer_2" data-name="Layer 2">
|
||||
<g id="Layer_1-2" data-name="Layer 1">
|
||||
<path
|
||||
class="cls-1"
|
||||
d="M6,19C11,12.66,26.33,3,46.33,6c-3.67,17-8.67,26-8.67,26s-7,14-19.67-3.67C5.67,26.33,9,22.33,6,19Z"
|
||||
/>
|
||||
<path
|
||||
class="cls-2"
|
||||
d="M48.33.49l-.77,0c-11.22-1.88-30.21,1.46-39,9-3.38,2.89-5,6.11-4.69,9.59a11.06,11.06,0,0,0,4.77,8,11,11,0,0,0,6.24,1.82q.53,0,1.09,0A55.51,55.51,0,0,0,13.2,39.21C9.48,33.07,2.35,30.83,0,30.83v3c.12,0,12.18,1.95,12.5,13.54h0c0,.1,0,.19,0,.29h3a50.57,50.57,0,0,1,3.12-17.21c2.48,5.09,6.36,8,10.91,8.13,5.3.07,10.1-3.85,11.91-9.81.94-3.12,1.88-6.37,2.78-9.51C46.32,11.9,48.31,5,49.62,2.76L51,.49ZM10.28,24.61a8.06,8.06,0,0,1-3.45-5.73c-.19-2.47,1-4.85,3.65-7.08,5.9-5,17.25-8.12,27-8.7-8.35,4-15.7,12.31-20.23,22.51l-.38.1h0A8.82,8.82,0,0,1,10.28,24.61Zm31-6.18c-.9,3.13-1.82,6.37-2.76,9.47-1.4,4.62-4.95,7.69-8.86,7.69h-.13c-4-.07-7.45-3.43-9.33-9.09C25.83,14.27,35.67,4.94,45.92,3.65,44.6,7,43.06,12.39,41.33,18.42Z"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.va-icon-fresh {
|
||||
display: inline-block;
|
||||
width: 51px;
|
||||
height: 48px;
|
||||
|
||||
.cls-1 {
|
||||
fill: #4ae387;
|
||||
}
|
||||
|
||||
.cls-2 {
|
||||
fill: #34495e;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,30 @@
|
||||
<template>
|
||||
<svg class="va-icon-menu" xmlns="http://www.w3.org/2000/svg" width="23" height="18" viewBox="0 0 24 18">
|
||||
<g fill="none" fill-rule="nonzero" transform="translate(1 -3)">
|
||||
<path d="M0 0h24v24H0z" />
|
||||
<rect width="20" height="2" x="2" y="3" :fill="color" rx="1" />
|
||||
<path :fill="color" d="M11 11h10a1 1 0 0 1 0 2H11a1 1 0 0 1 0-2zM1 11h5a1 1 0 0 1 0 2H1a1 1 0 0 1 0-2z" />
|
||||
<rect width="20" height="2" x="2" y="19" :fill="color" rx="1" />
|
||||
<path :stroke="color" stroke-width="2" d="M4 9l-3 3 3 3" />
|
||||
</g>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
color?: string
|
||||
}>(),
|
||||
{
|
||||
color: 'inherit',
|
||||
},
|
||||
)
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.va-icon-menu {
|
||||
display: inline-block;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<svg class="va-icon-menu-collapsed" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<g fill="none" fill-rule="nonzero">
|
||||
<path d="M0 0h24v24H0z" />
|
||||
<rect width="20" height="2" x="2" y="3" :fill="color" rx="1" />
|
||||
<path
|
||||
:fill="color"
|
||||
d="M3 11h10a1 1 0 0 1 0 2H3a1 1 0 0 1 0-2zM20.993 11l-2.7-2.7-1.414 1.414L18.164 11H16a1 1 0 0 0 0 2h2.179l-1.3 1.3 1.414 1.414L21.007 13A1 1 0 0 0 21 11h-.007z"
|
||||
/>
|
||||
<rect width="20" height="2" x="2" y="19" :fill="color" rx="1" />
|
||||
</g>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
color?: string
|
||||
}>(),
|
||||
{
|
||||
color: 'inherit',
|
||||
},
|
||||
)
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.va-icon-menu-collapsed {
|
||||
display: inline-block;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="16" viewBox="0 0 20 16" :fill="color">
|
||||
<path
|
||||
fill-rule="nonzero"
|
||||
d="M20 2c0-1.1-.9-2-2-2H2C.9 0 0 .9 0 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V2zm-2 0l-8 5-8-5h16zm0 12H2V4l8 5 8-5v10z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
color?: string
|
||||
}>(),
|
||||
{
|
||||
color: 'inherit',
|
||||
},
|
||||
)
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.va-icon-message {
|
||||
display: inline-block;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<svg class="va-icon-notification" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
|
||||
<path
|
||||
:fill="color"
|
||||
d="M10 20c1.1 0 2-.9 2-2H8c0 1.1.9 2 2 2zm6-6V9c0-3.07-1.63-5.64-4.5-6.32V2c0-.83-.67-1.5-1.5-1.5S8.5 1.17 8.5 2v.68C5.64 3.36 4 5.92 4 9v5l-2 2v1h16v-1l-2-2zm-2 1H6V9c0-2.48 1.51-4.5 4-4.5s4 2.02 4 4.5v6zM5.58 2.08L4.15.65C1.75 2.48.17 5.3.03 8.5h2a8.445 8.445 0 0 1 3.55-6.42zM17.97 8.5h2c-.15-3.2-1.73-6.02-4.12-7.85l-1.42 1.43a8.495 8.495 0 0 1 3.54 6.42z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
color?: string
|
||||
}>(),
|
||||
{
|
||||
color: 'inherit',
|
||||
},
|
||||
)
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.va-icon-notification {
|
||||
display: inline-block;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<svg class="va-icon-responsive" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 47.5 49">
|
||||
<defs />
|
||||
<title>overview_icon_3</title>
|
||||
<g id="Layer_2" data-name="Layer 2">
|
||||
<g id="Layer_1-2" data-name="Layer 1">
|
||||
<polygon class="cls-1" points="37 26 37 7 11 7 11 18 3 18 3 46 11 46 15 46 30 46 37 46 45 46 45 26 37 26" />
|
||||
<path class="cls-2" d="M40,19V0H8V11H0V49H47.5V19ZM3,46V14H8V46Zm34,0H11V3H37Zm7.5,0H40V22h4.5Z" />
|
||||
<circle class="cls-2" cx="24" cy="41" r="2.67" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.va-icon-responsive {
|
||||
display: inline-block;
|
||||
width: 47.5px;
|
||||
height: 49px;
|
||||
|
||||
.cls-1 {
|
||||
fill: #4ae387;
|
||||
}
|
||||
|
||||
.cls-2 {
|
||||
fill: #34495e;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<svg class="va-icon-rich" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 56.99 55">
|
||||
<defs />
|
||||
<title>overview_icon_6</title>
|
||||
<g id="Layer_2" data-name="Layer 2">
|
||||
<g id="Layer_1-2" data-name="Layer 1">
|
||||
<rect class="cls-1" x="10.31" y="30.5" width="37.33" height="23" />
|
||||
<path
|
||||
class="cls-2"
|
||||
d="M57,41.18l-7.85-16V24H8.81v1.11L0,41.11l2.63,1.45L8.81,31.33V55H49.15V32L54.3,42.5ZM46.15,52H11.81V27H46.15Z"
|
||||
/>
|
||||
<polygon class="cls-2" points="35.3 1.8 32.9 0 28.12 6.39 26.16 4.63 24.16 6.87 28.56 10.8 35.3 1.8" />
|
||||
<polygon
|
||||
class="cls-2"
|
||||
points="22.3 12.46 19.9 10.67 15.12 17.05 13.16 15.3 11.16 17.54 15.56 21.47 22.3 12.46"
|
||||
/>
|
||||
<polygon
|
||||
class="cls-2"
|
||||
points="38.89 21.14 45.64 12.13 43.23 10.33 38.45 16.72 36.49 14.97 34.49 17.2 38.89 21.14"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.va-icon-rich {
|
||||
display: inline-block;
|
||||
width: 57px;
|
||||
height: 55px;
|
||||
|
||||
.cls-1 {
|
||||
fill: #4ae387;
|
||||
}
|
||||
|
||||
.cls-2 {
|
||||
fill: #34495e;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,33 @@
|
||||
<template>
|
||||
<svg
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
class="va-icon-slower"
|
||||
viewBox="0 0 24 24"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<!-- Generator: sketchtool 48.2 (47327) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>67046716-A590-445C-AC65-1EEF69089C00</title>
|
||||
<defs />
|
||||
<g id="symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="icon-slower" fill="#34495E">
|
||||
<g>
|
||||
<path
|
||||
id="A"
|
||||
d="M16.82,18.87 L16.028,16.17 L12.014,16.17 L11.24,18.87 L8,18.87 L12.374,6 L15.686,6 L20.06,18.87 L16.82,18.87 Z M14.048,8.934 L14.012,8.934 C14.012,8.934 13.724,10.338 13.49,11.148 L12.752,13.65 L15.29,13.65 L14.57,11.148 C14.336,10.338 14.048,8.934 14.048,8.934 Z"
|
||||
/>
|
||||
<rect id="Rectangle-4" x="5" y="11" width="2" height="2" rx="1" />
|
||||
<rect id="Rectangle-4-Copy" x="6" y="7" width="3" height="2" rx="1" />
|
||||
<rect id="Rectangle-4" x="4" y="15" width="2" height="2" rx="1" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.va-icon-slower {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<svg class="va-icon-vue" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 55.05 47.8">
|
||||
<defs />
|
||||
<title>overview_icon_1</title>
|
||||
<g id="Layer_2" data-name="Layer 2">
|
||||
<g id="Layer_1-2" data-name="Layer 1">
|
||||
<polygon
|
||||
class="cls-1"
|
||||
points="27.75 21.19 18.48 6.18 4.78 6.18 27.75 42.92 50.89 6.18 36.13 6.18 27.75 21.19"
|
||||
/>
|
||||
<path
|
||||
class="cls-2"
|
||||
d="M33.08,0,27.44,9.76,21.84,0H0L27.43,47.8,55,0ZM27.43,15.77,34.81,3h4.6l-12,20.72L15.55,3h4.55ZM5.18,3h6.91L27.43,29.73,42.88,3h7L27.44,41.78Z"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.va-icon-vue {
|
||||
display: inline-block;
|
||||
width: 55px;
|
||||
height: 47.8px;
|
||||
|
||||
.cls-1 {
|
||||
fill: #4ae387;
|
||||
}
|
||||
|
||||
.cls-2 {
|
||||
fill: #34495e;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="va-icon-vuestic" width="304" height="31" viewBox="0 0 304 31">
|
||||
<defs>
|
||||
<linearGradient :id="'ORIGINAL'" x1="0%" y1="50%" y2="50%">
|
||||
<stop offset="0%" stop-color="#4AE387" />
|
||||
<stop offset="100%" stop-color="#C8EA13" />
|
||||
</linearGradient>
|
||||
<linearGradient :id="'CORPORATE'" x1="0%" y1="50%" y2="50%">
|
||||
<stop offset="0%" stop-color="#74BBFF" />
|
||||
<stop offset="100%" stop-color="#6E85E8" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g fill="none" fill-rule="evenodd" transform="scale(2)">
|
||||
<path
|
||||
:fill="`url(#${themeGradientId})`"
|
||||
fill-rule="nonzero"
|
||||
d="M6.36,15.36 L-7.10542736e-15,0.48 L3.672,0.48 L6.576,7.416 L9.48,0.48 L13.152,0.48 L6.792,15.36 L6.36,15.36 Z M21.744,10.032 L21.744,0.48 L25.248,0.48 L25.248,9.504 C25.248,11.424 26.16,12.096 27.384,12.096 C28.728,12.096 29.568,11.424 29.568,9.504 L29.568,0.48 L33.096,0.48 L33.096,10.032 C33.096,13.44 30.552,15.36 27.384,15.36 C24.096,15.36 21.744,13.344 21.744,10.032 Z M42.6,14.88 L42.6,0.48 L52.128,0.48 L52.128,3.6 L46.128,3.6 L46.128,6.192 L51.672,6.192 L51.672,9.264 L46.128,9.264 L46.128,11.856 L52.392,11.856 L52.392,14.88 L42.6,14.88 Z M61.224,10.656 L64.656,10.656 C64.656,11.496 65.328,12.168 66.144,12.168 C67.032,12.168 67.584,11.664 67.584,10.92 C67.584,9.84 66.168,9.528 64.992,9.12 C62.568,8.256 61.224,7.128 61.224,4.656 C61.224,2.112 63.408,7.10542736e-15 66.12,7.10542736e-15 C69.312,7.10542736e-15 70.824,2.04 71.016,4.704 L67.704,4.704 C67.704,3.888 67.2,3.216 66.216,3.216 C65.448,3.216 64.704,3.672 64.704,4.608 C64.704,5.688 66.024,5.88 67.248,6.24 C69.816,7.008 71.016,8.448 71.016,10.704 C71.016,13.248 68.856,15.36 66.144,15.36 C63,15.36 61.224,13.248 61.224,10.656 Z M82.896,14.88 L82.896,3.6 L79.68,3.6 L79.68,0.48 L89.712,0.48 L89.712,3.6 L86.448,3.6 L86.448,14.88 L82.896,14.88 Z M98.544,14.88 L98.544,0.48 L102.072,0.48 L102.072,14.88 L98.544,14.88 Z M111.72,7.68 C111.72,3.384 114.96,7.10542736e-15 119.52,7.10542736e-15 C122.256,7.10542736e-15 124.152,1.032 125.688,2.64 L123.264,4.944 C122.256,3.96 120.96,3.336 119.52,3.336 C116.952,3.336 115.296,5.256 115.296,7.68 C115.296,10.104 116.952,12.024 119.52,12.024 C120.96,12.024 122.256,11.4 123.264,10.416 L125.616,12.72 C124.176,14.232 122.184,15.36 119.52,15.36 C114.96,15.36 111.72,11.976 111.72,7.68 Z"
|
||||
/>
|
||||
<path
|
||||
:fill="textColor"
|
||||
d="M139.712,7.88 L139.712,6.152 L138.44,6.152 C138.272,6.152 138.066,6.162 137.822,6.182 C137.578,6.202 137.36,6.224 137.168,6.248 C137.424,5.984 137.682,5.704 137.942,5.408 C138.202,5.112 138.436,4.808 138.644,4.496 C138.852,4.184 139.022,3.87 139.154,3.554 C139.286,3.238 139.352,2.928 139.352,2.624 C139.352,2.248 139.288,1.906 139.16,1.598 C139.032,1.29 138.852,1.028 138.62,0.812 C138.388,0.596 138.112,0.428 137.792,0.308 C137.472,0.188 137.12,0.128 136.736,0.128 C136.456,0.128 136.198,0.152 135.962,0.2 C135.726,0.248 135.502,0.322 135.29,0.422 C135.078,0.522 134.876,0.65 134.684,0.806 C134.492,0.962 134.296,1.144 134.096,1.352 L134.096,1.352 L135.2,2.456 C135.384,2.272 135.574,2.106 135.77,1.958 C135.966,1.81 136.192,1.736 136.448,1.736 C136.728,1.736 136.954,1.818 137.126,1.982 C137.298,2.146 137.384,2.4 137.384,2.744 C137.384,3 137.302,3.274 137.138,3.566 C136.974,3.858 136.748,4.166 136.46,4.49 C136.172,4.814 135.834,5.16 135.446,5.528 C135.058,5.896 134.64,6.288 134.192,6.704 L134.192,6.704 L134.192,7.88 L139.712,7.88 Z M142.56,8.024 C142.912,8.024 143.2,7.904 143.424,7.664 C143.648,7.424 143.76,7.128 143.76,6.776 C143.76,6.424 143.648,6.128 143.424,5.888 C143.2,5.648 142.912,5.528 142.56,5.528 C142.208,5.528 141.92,5.648 141.696,5.888 C141.472,6.128 141.36,6.424 141.36,6.776 C141.36,7.128 141.472,7.424 141.696,7.664 C141.92,7.904 142.208,8.024 142.56,8.024 Z M150.736,7.88 L150.736,6.224 L149.368,6.224 L149.368,0.272 L147.856,0.272 C147.568,0.456 147.272,0.604 146.968,0.716 C146.664,0.828 146.296,0.928 145.864,1.016 L145.864,1.016 L145.864,2.288 L147.304,2.288 L147.304,6.224 L145.672,6.224 L145.672,7.88 L150.736,7.88 Z"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'VaIconVuestic',
|
||||
inject: ['contextConfig'],
|
||||
computed: {
|
||||
themeGradientId() {
|
||||
return this.contextConfig.invertedColor ? 'CORPORATE' : 'ORIGINAL'
|
||||
},
|
||||
textColor() {
|
||||
return this.contextConfig.invertedColor ? '#6E85E8' : '#E4FF32'
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.va-icon-vuestic {
|
||||
.st0 {
|
||||
fill: #4ae387;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,279 @@
|
||||
<template>
|
||||
<div ref="mapRef" class="line-map" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, toRef, computed, onMounted, onBeforeUnmount, watch, shallowRef, onUpdated } from 'vue'
|
||||
import * as am5 from '@amcharts/amcharts5'
|
||||
import * as am5map from '@amcharts/amcharts5/map'
|
||||
import am5geodata_worldLow from '@amcharts/amcharts5-geodata/worldLow'
|
||||
import am5themes_Animated from '@amcharts/amcharts5/themes/Animated'
|
||||
import { useColors } from 'vuestic-ui'
|
||||
|
||||
import {
|
||||
planeSVG,
|
||||
useMapData,
|
||||
CityItem,
|
||||
getGeoBounds,
|
||||
generateLineSeriesData,
|
||||
compareStrings,
|
||||
} from '../../data/maps/lineMapData'
|
||||
|
||||
const generateLabelText = (city?: string) => (city ? `Flights from ${city}` : '')
|
||||
|
||||
const generateButtonText = (city: string) => `Show flights from ${city}`
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
mapData: CityItem[]
|
||||
homeCity: string
|
||||
modelValue: string
|
||||
}>(),
|
||||
{
|
||||
homeCity: 'New York',
|
||||
modelValue: 'New York',
|
||||
},
|
||||
)
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', value: string): void
|
||||
}>()
|
||||
|
||||
const { colors } = useColors()
|
||||
const mapRef = ref()
|
||||
const mapRoot = shallowRef()
|
||||
const mapChart = shallowRef()
|
||||
const mapPolygonSeries = shallowRef()
|
||||
const mapPointSeries = shallowRef()
|
||||
const mapLineSeries = shallowRef()
|
||||
const mapZoomControl = shallowRef()
|
||||
const mapLabelText = shallowRef()
|
||||
const mapLabelSvg = shallowRef()
|
||||
const mapHomeCityButton = shallowRef()
|
||||
|
||||
const mainCity = computed({
|
||||
get() {
|
||||
return props.modelValue
|
||||
},
|
||||
set(value) {
|
||||
emit('update:modelValue', value)
|
||||
},
|
||||
})
|
||||
|
||||
const mapPointSeriesData = useMapData(toRef(props, 'mapData'))
|
||||
|
||||
const mapLineSeriesData = computed(() => {
|
||||
const item = getItemByMainCityTitle()
|
||||
return generateLineSeriesData(item)
|
||||
})
|
||||
|
||||
const getItemByMainCityTitle = () =>
|
||||
mapPointSeriesData.value.find(({ title }) => compareStrings(title, mainCity.value))
|
||||
|
||||
const zoomToGeoBounds = () => {
|
||||
const item = getItemByMainCityTitle()
|
||||
const geoBounds = getGeoBounds(item)
|
||||
|
||||
if (geoBounds) {
|
||||
mapChart.value.zoomToGeoBounds(geoBounds)
|
||||
}
|
||||
}
|
||||
|
||||
const createMap = () => {
|
||||
const root = am5.Root.new(mapRef.value)
|
||||
|
||||
root.setThemes([am5themes_Animated.new(root)])
|
||||
|
||||
const chart = root.container.children.push(
|
||||
am5map.MapChart.new(root, {
|
||||
minZoomLevel: 1,
|
||||
maxZoomLevel: 10,
|
||||
}),
|
||||
)
|
||||
|
||||
const zoomControl = chart.set('zoomControl', am5map.ZoomControl.new(root, {}))
|
||||
|
||||
// polygon series
|
||||
const polygonSeries = chart.series.push(
|
||||
am5map.MapPolygonSeries.new(root, {
|
||||
geoJSON: am5geodata_worldLow,
|
||||
exclude: ['AQ'],
|
||||
}),
|
||||
)
|
||||
|
||||
polygonSeries.mapPolygons.template.setAll({
|
||||
fill: am5.color(colors.secondary),
|
||||
fillOpacity: 0.4,
|
||||
strokeWidth: 0.5,
|
||||
})
|
||||
|
||||
polygonSeries.events.on('datavalidated', zoomToGeoBounds)
|
||||
|
||||
// point series
|
||||
const pointSeries = chart.series.push(
|
||||
am5map.MapPointSeries.new(root, {
|
||||
latitudeField: 'latitude',
|
||||
longitudeField: 'longitude',
|
||||
}),
|
||||
)
|
||||
|
||||
// point series bullets
|
||||
const bulletTemplate = am5.Template.new({}) as am5.Template<am5.Graphics>
|
||||
bulletTemplate.events.on('click', (ev) => {
|
||||
mainCity.value = (ev as any).target._dataItem.dataContext.title
|
||||
})
|
||||
|
||||
pointSeries.bullets.push((root, series, dataItem) => {
|
||||
const itemData = dataItem.dataContext as CityItem
|
||||
const isMainCity = compareStrings(itemData.title, mainCity.value)
|
||||
|
||||
return am5.Bullet.new(root, {
|
||||
sprite: am5.Graphics.new(
|
||||
root,
|
||||
{
|
||||
svgPath: itemData.svgPath,
|
||||
x: am5.percent(50),
|
||||
y: am5.percent(50),
|
||||
centerX: am5.percent(50),
|
||||
centerY: am5.percent(50),
|
||||
fill: am5.color(isMainCity ? colors.primary : itemData.color),
|
||||
scale: isMainCity ? 1.5 : 1,
|
||||
tooltipText: '{title}',
|
||||
},
|
||||
bulletTemplate,
|
||||
),
|
||||
})
|
||||
})
|
||||
|
||||
// line series
|
||||
const lineSeries = chart.series.push(am5map.MapLineSeries.new(root, {}))
|
||||
|
||||
lineSeries.mapLines.template.setAll({
|
||||
stroke: am5.color(colors.primary),
|
||||
strokeWidth: 2,
|
||||
strokeOpacity: 0.5,
|
||||
})
|
||||
|
||||
// set map data
|
||||
pointSeries.data.setAll(mapPointSeriesData.value)
|
||||
lineSeries.data.setAll(mapLineSeriesData.value as unknown[])
|
||||
|
||||
// label 'Flights from ...'
|
||||
const labelContainer = chart.children.push(
|
||||
am5.Container.new(root, {
|
||||
x: 15,
|
||||
y: 15,
|
||||
layout: root.horizontalLayout,
|
||||
}),
|
||||
)
|
||||
|
||||
const labelSvg = labelContainer.children.push(
|
||||
am5.Graphics.new(root, {
|
||||
svgPath: planeSVG,
|
||||
fill: am5.color(colors.info),
|
||||
}),
|
||||
)
|
||||
|
||||
const labelText = labelContainer.children.push(
|
||||
am5.Label.new(root, {
|
||||
text: generateLabelText(getItemByMainCityTitle()?.title),
|
||||
fill: am5.color(colors.info),
|
||||
fontSize: 22,
|
||||
lineHeight: am5.p100,
|
||||
}),
|
||||
)
|
||||
|
||||
// button 'Show flights from homeCity'
|
||||
const homeCityButton = chart.children.push(
|
||||
am5.Button.new(root, {
|
||||
x: 15,
|
||||
y: 45,
|
||||
label: am5.Label.new(root, {
|
||||
text: generateButtonText(props.homeCity),
|
||||
paddingTop: 0,
|
||||
marginRight: 0,
|
||||
paddingBottom: 0,
|
||||
marginLeft: 0,
|
||||
}),
|
||||
visible: false,
|
||||
}),
|
||||
)
|
||||
|
||||
homeCityButton.events.on('click', () => {
|
||||
mainCity.value = props.homeCity
|
||||
homeCityButton.hide()
|
||||
})
|
||||
|
||||
// assign objects to refs
|
||||
mapRoot.value = root
|
||||
mapChart.value = chart
|
||||
mapZoomControl.value = zoomControl
|
||||
mapPointSeries.value = pointSeries
|
||||
mapLineSeries.value = lineSeries
|
||||
mapPolygonSeries.value = polygonSeries
|
||||
mapLabelSvg.value = labelSvg
|
||||
mapLabelText.value = labelText
|
||||
mapHomeCityButton.value = homeCityButton
|
||||
}
|
||||
|
||||
const setPointSeriesData = () => {
|
||||
mapPointSeries.value.data.setAll(mapPointSeriesData.value)
|
||||
}
|
||||
|
||||
const setLineSeriesData = () => {
|
||||
mapLineSeries.value.data.setAll(mapLineSeriesData.value ?? [])
|
||||
}
|
||||
|
||||
const updateInteractiveElements = () => {
|
||||
mapLabelText.value.set('text', generateLabelText(getItemByMainCityTitle()?.title))
|
||||
mapHomeCityButton.value[compareStrings(props.homeCity, mainCity.value) ? 'hide' : 'show']?.()
|
||||
}
|
||||
|
||||
const updateChartDataOnChangeTheme = () => {
|
||||
if (mapRoot.value) {
|
||||
mapPolygonSeries.value.mapPolygons.template.setAll({
|
||||
fill: am5.color(colors.secondary),
|
||||
})
|
||||
|
||||
mapLineSeries.value.mapLines.template.setAll({
|
||||
stroke: am5.color(colors.primary),
|
||||
})
|
||||
|
||||
mapLabelText.value.set('fill', am5.color(colors.info))
|
||||
mapLabelSvg.value.set('fill', am5.color(colors.info))
|
||||
|
||||
setPointSeriesData()
|
||||
}
|
||||
}
|
||||
|
||||
const updateChartDataOnUpdateProps = () => {
|
||||
if (mapRoot.value) {
|
||||
setPointSeriesData()
|
||||
setLineSeriesData()
|
||||
updateInteractiveElements()
|
||||
zoomToGeoBounds()
|
||||
}
|
||||
}
|
||||
|
||||
const disposeMap = () => {
|
||||
if (mapRoot.value) {
|
||||
mapRoot.value.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(createMap)
|
||||
onUpdated(updateChartDataOnUpdateProps)
|
||||
watch(colors, updateChartDataOnChangeTheme)
|
||||
onBeforeUnmount(disposeMap)
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.line-map {
|
||||
border-radius: inherit;
|
||||
|
||||
:deep(div),
|
||||
:deep(canvas) {
|
||||
border-radius: inherit;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,114 @@
|
||||
<template>
|
||||
<va-navbar class="app-layout-navbar">
|
||||
<template #left>
|
||||
<div class="left">
|
||||
<va-icon-menu-collapsed
|
||||
:class="{ 'x-flip': isSidebarMinimized }"
|
||||
class="va-navbar__item"
|
||||
:color="colors.primary"
|
||||
@click="isSidebarMinimized = !isSidebarMinimized"
|
||||
/>
|
||||
<router-link to="/">
|
||||
<vuestic-logo class="logo" />
|
||||
</router-link>
|
||||
</div>
|
||||
</template>
|
||||
<template #center>
|
||||
<div class="app-navbar-center">
|
||||
<span class="app-navbar-center__text mr-2">{{ t('navbar.messageUs') }}</span>
|
||||
<a
|
||||
class="app-navbar-center__mail mr-2"
|
||||
href="mailto:hello@epicmax.co"
|
||||
target="_blank"
|
||||
:style="{ color: colors.primary }"
|
||||
>
|
||||
hello@epicmax.co
|
||||
</a>
|
||||
<va-button
|
||||
href="https://github.com/epicmaxco/vuestic-admin"
|
||||
color="#000000"
|
||||
class="app-navbar-center__github-button"
|
||||
icon="github"
|
||||
target="_blank"
|
||||
>
|
||||
{{ t('navbar.repository') }}
|
||||
</va-button>
|
||||
</div>
|
||||
</template>
|
||||
<template #right>
|
||||
<app-navbar-actions class="app-navbar__actions md5 lg4" :user-name="userName" />
|
||||
</template>
|
||||
</va-navbar>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useGlobalStore } from '../../stores/global-store'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useColors } from 'vuestic-ui'
|
||||
import VuesticLogo from '../VuesticLogo.vue'
|
||||
import VaIconMenuCollapsed from '../icons/VaIconMenuCollapsed.vue'
|
||||
import AppNavbarActions from './components/AppNavbarActions.vue'
|
||||
|
||||
const GlobalStore = useGlobalStore()
|
||||
const { t } = useI18n()
|
||||
|
||||
const { isSidebarMinimized, userName } = storeToRefs(GlobalStore)
|
||||
|
||||
const { getColors } = useColors()
|
||||
const colors = computed(() => getColors())
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.va-navbar {
|
||||
box-shadow: var(--va-box-shadow);
|
||||
z-index: 2;
|
||||
|
||||
@media screen and (max-width: 950px) {
|
||||
.left {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.app-navbar__actions {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
& > * {
|
||||
margin-right: 1.5rem;
|
||||
}
|
||||
|
||||
& > *:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.x-flip {
|
||||
transform: scaleX(-100%);
|
||||
}
|
||||
|
||||
.app-navbar-center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@media screen and (max-width: 1200px) {
|
||||
&__github-button {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 950px) {
|
||||
&__text {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,97 @@
|
||||
<template>
|
||||
<div class="app-navbar-actions">
|
||||
<color-dropdown class="app-navbar-actions__item" />
|
||||
<message-dropdown class="app-navbar-actions__item" />
|
||||
<notification-dropdown class="app-navbar-actions__item" />
|
||||
<!-- <settings-dropdown class="app-navbar-actions__item" /> -->
|
||||
<language-dropdown class="app-navbar-actions__item" />
|
||||
<profile-dropdown class="app-navbar-actions__item app-navbar-actions__item--profile">
|
||||
<span>{{ userName }}</span>
|
||||
</profile-dropdown>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import LanguageDropdown from './dropdowns/LanguageDropdown.vue'
|
||||
import ProfileDropdown from './dropdowns/ProfileDropdown.vue'
|
||||
import NotificationDropdown from './dropdowns/NotificationDropdown.vue'
|
||||
import MessageDropdown from './dropdowns/MessageDropdown.vue'
|
||||
import ColorDropdown from './dropdowns/ColorDropdown.vue'
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
userName?: string
|
||||
isTopBar?: boolean
|
||||
}>(),
|
||||
{
|
||||
userName: '',
|
||||
isTopBar: false,
|
||||
},
|
||||
)
|
||||
|
||||
defineEmits<{
|
||||
(e: 'update:isTopBar', isTopBar: boolean): void
|
||||
}>()
|
||||
|
||||
// const isTopBarProxy = computed({
|
||||
// get() {
|
||||
// return props.isTopBar
|
||||
// },
|
||||
//
|
||||
// set(isTopBar: boolean) {
|
||||
// emit('update:isTopBar', isTopBar)
|
||||
// },
|
||||
// })
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.app-navbar-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.va-dropdown__anchor {
|
||||
color: var(--va-primary);
|
||||
fill: var(--va-primary);
|
||||
}
|
||||
|
||||
&__item {
|
||||
padding: 0;
|
||||
margin-left: 1.25rem;
|
||||
margin-right: 1.25rem;
|
||||
|
||||
svg {
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
&:last-of-type {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
&--profile {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: auto 0 auto 1.25rem;
|
||||
}
|
||||
|
||||
.va-dropdown-content {
|
||||
background-color: var(--va-white);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 640px) {
|
||||
margin-right: 0;
|
||||
|
||||
&:first-of-type {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
&--profile {
|
||||
position: absolute;
|
||||
right: 0.75rem;
|
||||
top: 1.25rem;
|
||||
height: fit-content;
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<va-dropdown class="color-dropdown" :offset="[13, 0]" position="bottom">
|
||||
<template #anchor>
|
||||
<va-icon-color />
|
||||
</template>
|
||||
|
||||
<va-dropdown-content class="color-dropdown__content pl-4 pr-4 pt-2 pb-2">
|
||||
<va-button-toggle
|
||||
v-model="currentTheme"
|
||||
class="color-dropdown__toggle"
|
||||
:options="themeOptions"
|
||||
outline
|
||||
round
|
||||
grow
|
||||
size="small"
|
||||
/>
|
||||
|
||||
<table style="width: 100%">
|
||||
<color-dropdown-item
|
||||
v-for="colorName in colorNames"
|
||||
:key="colorName"
|
||||
class="color-picker-dropdown"
|
||||
:color-name="colorName"
|
||||
/>
|
||||
</table>
|
||||
</va-dropdown-content>
|
||||
</va-dropdown>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import VaIconColor from '../../../icons/VaIconColor.vue'
|
||||
import ColorDropdownItem from './ColorDropdownItem.vue'
|
||||
import { useColors } from 'vuestic-ui'
|
||||
import { ref, watchEffect } from 'vue'
|
||||
|
||||
const { presets, applyPreset, colors } = useColors()
|
||||
|
||||
const currentTheme = ref('light')
|
||||
|
||||
watchEffect(() => {
|
||||
applyPreset(currentTheme.value)
|
||||
})
|
||||
|
||||
const themeOptions = Object.keys(presets.value).map((themeName) => ({
|
||||
value: themeName,
|
||||
label: themeName,
|
||||
}))
|
||||
const colorNames = Object.keys(colors)
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.color-dropdown {
|
||||
cursor: pointer;
|
||||
|
||||
&__icon {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.va-dropdown__anchor {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
&__toggle {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: stretch;
|
||||
}
|
||||
}
|
||||
|
||||
.button-restore {
|
||||
display: flex;
|
||||
margin: 0.375rem auto;
|
||||
}
|
||||
|
||||
table {
|
||||
margin: 1rem 0;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<tr>
|
||||
<td class="color">
|
||||
<va-color-indicator :color="theme[colorName]" />
|
||||
</td>
|
||||
<td>
|
||||
{{ colorName }}
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useColors } from 'vuestic-ui'
|
||||
const { getColors } = useColors()
|
||||
|
||||
defineProps<{
|
||||
colorName: string
|
||||
}>()
|
||||
|
||||
const theme = computed(() => {
|
||||
return getColors()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.color {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
&-bordered {
|
||||
border: 1px solid var(--va-gray);
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
td {
|
||||
color: var(--va-gray);
|
||||
padding: 0.25rem 0.5rem;
|
||||
box-sizing: border-box;
|
||||
vertical-align: middle;
|
||||
&:first-child {
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,106 @@
|
||||
<template>
|
||||
<va-dropdown class="language-dropdown" fixed position="bottom" :offset="[13, 0]">
|
||||
<template #anchor>
|
||||
<va-icon :name="getFlagIcon(locale, 'large')" />
|
||||
</template>
|
||||
|
||||
<va-dropdown-content class="language-dropdown__content pl-4 pr-4 pt-2 pb-2">
|
||||
<div
|
||||
v-for="(option, id) in options"
|
||||
:key="id"
|
||||
class="language-dropdown__item row align--center pt-1 pb-1 mt-2 mb-2"
|
||||
:class="{ active: option.code === locale }"
|
||||
@click="locale = option.code"
|
||||
>
|
||||
<va-icon :name="getFlagIcon(option.code, 'small')" />
|
||||
<span class="dropdown-item__text">
|
||||
{{ t(`language.${option.name}`) }}
|
||||
</span>
|
||||
</div>
|
||||
</va-dropdown-content>
|
||||
</va-dropdown>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const { t, locale } = useI18n()
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
options?: { code: string; name: string }[]
|
||||
}>(),
|
||||
{
|
||||
options: () => [
|
||||
{
|
||||
code: 'gb',
|
||||
name: 'english',
|
||||
},
|
||||
{
|
||||
code: 'es',
|
||||
name: 'spanish',
|
||||
},
|
||||
{
|
||||
code: 'br',
|
||||
name: 'brazilian_portuguese',
|
||||
},
|
||||
{
|
||||
code: 'cn',
|
||||
name: 'simplified_chinese',
|
||||
},
|
||||
{
|
||||
code: 'ir',
|
||||
name: 'persian',
|
||||
},
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
function getFlagIcon(code: string, size: string) {
|
||||
return `flag-icon-${code} ${size}`
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import 'flag-icons/css/flag-icons.css';
|
||||
|
||||
.language-dropdown {
|
||||
cursor: pointer;
|
||||
|
||||
&__content {
|
||||
.fi-size-small {
|
||||
min-width: 1.5rem;
|
||||
min-height: 1.5rem;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
&__item {
|
||||
padding-bottom: 0.625rem;
|
||||
cursor: pointer;
|
||||
flex-wrap: nowrap;
|
||||
|
||||
&:last-of-type {
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--va-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.fi::before {
|
||||
content: '';
|
||||
}
|
||||
|
||||
.fi-size-large {
|
||||
display: block;
|
||||
width: 32px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.va-dropdown__anchor {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,157 @@
|
||||
<template>
|
||||
<va-dropdown class="message-dropdown" position="bottom" :offset="[13, 0]">
|
||||
<template #anchor>
|
||||
<va-icon-message class="message-dropdown__icon" :class="{ 'message-dropdown__icon--unread': !allRead }" />
|
||||
</template>
|
||||
|
||||
<va-dropdown-content class="message-dropdown__content pl-4 pr-4 pt-2 pb-2">
|
||||
<div
|
||||
v-for="message in messagesProxy"
|
||||
:key="message.id"
|
||||
class="message-dropdown__item row pt-1 pb-1 mt-2 mb-2"
|
||||
:class="{ 'message-dropdown__item--unread': message.unread }"
|
||||
@click="message.unread = false"
|
||||
>
|
||||
<img :src="message.details.avatar" class="message-dropdown__item__avatar mr-2" />
|
||||
<span class="ellipsis" style="max-width: 85%">{{
|
||||
t(`messages.${message.name}`, { name: message.details.name })
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="row justify--space-between mt-1">
|
||||
<va-button class="md6 mr-2" size="small">{{ t('messages.all') }}</va-button>
|
||||
<va-button
|
||||
class="md6"
|
||||
size="small"
|
||||
preset="outline"
|
||||
border-color="primary"
|
||||
:disabled="allRead"
|
||||
@click="markAllAsRead"
|
||||
>{{ t('messages.mark_as_read') }}</va-button
|
||||
>
|
||||
</div>
|
||||
</va-dropdown-content>
|
||||
</va-dropdown>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import VaIconMessage from '../../../icons/VaIconMessage.vue'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
interface IMessage {
|
||||
name: string
|
||||
details: {
|
||||
name: string
|
||||
avatar: string
|
||||
}
|
||||
unread: boolean
|
||||
id: number
|
||||
}
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
messages?: IMessage[]
|
||||
}>(),
|
||||
{
|
||||
messages: () => [
|
||||
{
|
||||
name: 'new',
|
||||
details: {
|
||||
name: 'Oleg M',
|
||||
avatar: 'https://picsum.photos/24?image=1083',
|
||||
},
|
||||
unread: true,
|
||||
id: 1,
|
||||
},
|
||||
{
|
||||
name: 'new',
|
||||
details: {
|
||||
name: 'Andrei H',
|
||||
avatar: 'https://picsum.photos/24?image=1025',
|
||||
},
|
||||
unread: true,
|
||||
id: 2,
|
||||
},
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
const messagesProxy = ref<IMessage[]>([...props.messages])
|
||||
|
||||
const allRead = computed(() => {
|
||||
return messagesProxy.value.every((message) => !message.unread)
|
||||
})
|
||||
|
||||
function markAllAsRead() {
|
||||
messagesProxy.value = messagesProxy.value.map((message) => ({
|
||||
...message,
|
||||
unread: false,
|
||||
}))
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.message-dropdown {
|
||||
cursor: pointer;
|
||||
|
||||
.message-dropdown__icon {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&--unread::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 0;
|
||||
left: 0;
|
||||
top: -0.5rem;
|
||||
background-color: var(--va-danger);
|
||||
height: 0.375rem;
|
||||
width: 0.375rem;
|
||||
margin: 0 auto;
|
||||
border-radius: 0.187rem;
|
||||
}
|
||||
}
|
||||
|
||||
&__item {
|
||||
cursor: pointer;
|
||||
margin-bottom: 0.75rem;
|
||||
position: relative;
|
||||
flex-wrap: nowrap;
|
||||
|
||||
&--unread {
|
||||
color: var(--va-gray);
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
height: 0.375rem;
|
||||
width: 0.375rem;
|
||||
background-color: var(--va-danger);
|
||||
margin: auto;
|
||||
border-radius: 0.187rem;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--va-primary);
|
||||
}
|
||||
|
||||
&__avatar {
|
||||
border-radius: 50%;
|
||||
min-width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.va-dropdown__anchor {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,176 @@
|
||||
<template>
|
||||
<va-dropdown class="notification-dropdown" position="bottom" :offset="[13, 0]">
|
||||
<template #anchor>
|
||||
<va-icon-notification
|
||||
class="notification-dropdown__icon"
|
||||
:class="{ 'notification-dropdown__icon--unread': !allRead }"
|
||||
/>
|
||||
</template>
|
||||
<va-dropdown-content class="notification-dropdown__content pl-3 pr-3 pt-2 pb-2">
|
||||
<div
|
||||
v-for="notification in notificationsProxy"
|
||||
:key="notification.id"
|
||||
class="notification-dropdown__item row pt-1 pb-1 mt-2 mb-2"
|
||||
:class="{ 'notification-dropdown__item--unread': notification.unread }"
|
||||
@click="notification.unread = false"
|
||||
>
|
||||
<img
|
||||
v-if="notification.details.avatar"
|
||||
class="mr-2 notification-dropdown__item__avatar"
|
||||
:src="notification.details.avatar"
|
||||
/>
|
||||
<span class="ellipsis" style="max-width: 85%">
|
||||
<span v-if="notification.details.name" class="text--bold">{{ notification.details.name }}</span>
|
||||
{{ t(`notifications.${notification.name}`, { type: notification.details.type }) }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="row justify--space-between mt-1">
|
||||
<va-button class="md6 mr-2" size="small">{{ t('notifications.all') }}</va-button>
|
||||
<va-button
|
||||
class="md6"
|
||||
size="small"
|
||||
preset="outline"
|
||||
border-color="primary"
|
||||
:disabled="allRead"
|
||||
@click="markAllAsRead"
|
||||
>{{ t('notifications.mark_as_read') }}</va-button
|
||||
>
|
||||
</div>
|
||||
</va-dropdown-content>
|
||||
</va-dropdown>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import VaIconNotification from '../../../icons/VaIconNotification.vue'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
interface INotification {
|
||||
name: string
|
||||
details: {
|
||||
name: string
|
||||
avatar: string
|
||||
type?: string
|
||||
}
|
||||
unread: boolean
|
||||
id: number
|
||||
}
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
notifications?: INotification[]
|
||||
}>(),
|
||||
{
|
||||
notifications: () => [
|
||||
{
|
||||
name: 'sentMessage',
|
||||
details: { name: 'Vasily S', avatar: 'https://picsum.photos/123' },
|
||||
unread: true,
|
||||
id: 1,
|
||||
},
|
||||
{
|
||||
name: 'uploadedZip',
|
||||
details: {
|
||||
name: 'Oleg M',
|
||||
avatar: 'https://picsum.photos/100',
|
||||
type: 'typography component',
|
||||
},
|
||||
unread: true,
|
||||
id: 2,
|
||||
},
|
||||
{
|
||||
name: 'startedTopic',
|
||||
details: { name: 'Andrei H', avatar: 'https://picsum.photos/24' },
|
||||
unread: true,
|
||||
id: 3,
|
||||
},
|
||||
],
|
||||
},
|
||||
)
|
||||
const notificationsProxy = ref<INotification[]>([...props.notifications])
|
||||
|
||||
const allRead = computed(() => {
|
||||
return notificationsProxy.value.every((notification) => !notification.unread)
|
||||
})
|
||||
|
||||
function markAllAsRead() {
|
||||
notificationsProxy.value = notificationsProxy.value.map((notification) => ({
|
||||
...notification,
|
||||
unread: false,
|
||||
}))
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.row {
|
||||
flex-wrap: nowrap !important;
|
||||
}
|
||||
.md6 {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.notification-dropdown {
|
||||
cursor: pointer;
|
||||
|
||||
.notification-dropdown__icon {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&--unread::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 0;
|
||||
left: 0;
|
||||
top: -0.5rem;
|
||||
// background-color: $brand-danger;
|
||||
height: 0.375rem;
|
||||
width: 0.375rem;
|
||||
margin: 0 auto;
|
||||
border-radius: 0.187rem;
|
||||
}
|
||||
}
|
||||
|
||||
&__content {
|
||||
max-width: 20rem;
|
||||
}
|
||||
|
||||
&__item {
|
||||
cursor: pointer;
|
||||
margin-bottom: 0.75rem;
|
||||
flex-wrap: nowrap;
|
||||
position: relative;
|
||||
|
||||
&--unread {
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
height: 0.375rem;
|
||||
width: 0.375rem;
|
||||
margin: auto;
|
||||
border-radius: 0.187rem;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--va-primary);
|
||||
}
|
||||
|
||||
&__avatar {
|
||||
border-radius: 50%;
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
min-width: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.va-dropdown__anchor {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<div class="profile-dropdown-wrapper">
|
||||
<va-dropdown v-model="isShown" class="profile-dropdown" stick-to-edges placement="bottom" :offset="[13, 0]">
|
||||
<template #anchor>
|
||||
<span class="profile-dropdown__anchor">
|
||||
<slot />
|
||||
<va-icon class="px-2" :name="isShown ? 'angle_up' : 'angle_down'" :color="colors.primary" />
|
||||
</span>
|
||||
</template>
|
||||
<va-dropdown-content class="profile-dropdown__content">
|
||||
<va-list-item v-for="option in options" :key="option.name" class="pa-2">
|
||||
<router-link :to="{ name: option.redirectTo }" class="profile-dropdown__item">
|
||||
{{ t(`user.${option.name}`) }}
|
||||
</router-link>
|
||||
</va-list-item>
|
||||
</va-dropdown-content>
|
||||
</va-dropdown>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useColors } from 'vuestic-ui'
|
||||
|
||||
const { t } = useI18n()
|
||||
const { colors } = useColors()
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
options?: { name: string; redirectTo: string }[]
|
||||
}>(),
|
||||
{
|
||||
options: () => [
|
||||
{
|
||||
name: 'profile',
|
||||
redirectTo: '',
|
||||
},
|
||||
{
|
||||
name: 'logout',
|
||||
redirectTo: 'login',
|
||||
},
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
const isShown = ref(false)
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.profile-dropdown {
|
||||
cursor: pointer;
|
||||
|
||||
&__anchor {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
&__item {
|
||||
display: block;
|
||||
color: var(--va-gray);
|
||||
|
||||
&:hover,
|
||||
&:active {
|
||||
color: var(--va-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,83 @@
|
||||
<template>
|
||||
<va-dropdown class="settings-dropdown" position="bottom" boundary-body :offset="[13, 0]">
|
||||
<template #anchor>
|
||||
<va-icon
|
||||
name="vuestic-iconset-settings"
|
||||
style="font-size: 1.4rem; display: flex"
|
||||
class="settings-dropdown__icon"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<va-dropdown-content class="settings-dropdown__content pl-4 pr-4 pt-2 pb-2">
|
||||
<div class="settings-dropdown__content-label mt-2 mb-3" :style="{ color: colors.primary }">
|
||||
{{ t('dashboard.navigationLayout') }}
|
||||
</div>
|
||||
<va-button-toggle
|
||||
v-model="isTopBarProxy"
|
||||
outline
|
||||
:options="options"
|
||||
class="settings-dropdown__control mb-2"
|
||||
size="small"
|
||||
/>
|
||||
</va-dropdown-content>
|
||||
</va-dropdown>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useColors } from 'vuestic-ui'
|
||||
|
||||
const { colors } = useColors()
|
||||
const { t } = useI18n()
|
||||
|
||||
const props = defineProps<{
|
||||
isTopBar: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:isTopBar', value: boolean): void
|
||||
}>()
|
||||
|
||||
const options = ref([
|
||||
{ label: t('dashboard.sideBarButton'), value: String(false) }, // NOTE: boolean is unsupported for va-dropdown
|
||||
{ label: t('dashboard.topBarButton'), value: String(true) },
|
||||
])
|
||||
|
||||
const isTopBarProxy = computed({
|
||||
get() {
|
||||
return String(props.isTopBar)
|
||||
},
|
||||
set() {
|
||||
emit('update:isTopBar', props.isTopBar)
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.settings-dropdown {
|
||||
cursor: pointer;
|
||||
|
||||
&__icon {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__content {
|
||||
&-label {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
&__control {
|
||||
.va-button-group {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.va-dropdown__anchor {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,231 @@
|
||||
export interface INavigationRoute {
|
||||
name: string
|
||||
displayName: string
|
||||
meta: { icon: string }
|
||||
children?: INavigationRoute[]
|
||||
}
|
||||
|
||||
export default {
|
||||
root: {
|
||||
name: '/',
|
||||
displayName: 'navigationRoutes.home',
|
||||
},
|
||||
routes: [
|
||||
{
|
||||
name: 'dashboard',
|
||||
displayName: 'menu.dashboard',
|
||||
meta: {
|
||||
icon: 'vuestic-iconset-dashboard',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'statistics',
|
||||
displayName: 'menu.statistics',
|
||||
meta: {
|
||||
icon: 'vuestic-iconset-statistics',
|
||||
},
|
||||
disabled: true,
|
||||
children: [
|
||||
{
|
||||
name: 'charts',
|
||||
displayName: 'menu.charts',
|
||||
},
|
||||
{
|
||||
name: 'progress-bars',
|
||||
displayName: 'menu.progressBars',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'forms',
|
||||
displayName: 'menu.forms',
|
||||
meta: {
|
||||
icon: 'vuestic-iconset-forms',
|
||||
},
|
||||
disabled: true,
|
||||
children: [
|
||||
{
|
||||
name: 'form-elements',
|
||||
displayName: 'menu.formElements',
|
||||
},
|
||||
{
|
||||
name: 'medium-editor',
|
||||
displayName: 'menu.mediumEditor',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'tables',
|
||||
displayName: 'menu.tables',
|
||||
meta: {
|
||||
icon: 'vuestic-iconset-tables',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
name: 'markup',
|
||||
displayName: 'menu.markupTables',
|
||||
},
|
||||
// {
|
||||
// name: 'data',
|
||||
// displayName: 'menu.dataTables',
|
||||
// },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'ui',
|
||||
displayName: 'menu.uiElements',
|
||||
meta: {
|
||||
icon: 'vuestic-iconset-ui-elements',
|
||||
},
|
||||
disabled: true,
|
||||
children: [
|
||||
{
|
||||
name: 'buttons',
|
||||
displayName: 'menu.buttons',
|
||||
},
|
||||
{
|
||||
name: 'cards',
|
||||
displayName: 'menu.cards',
|
||||
},
|
||||
{
|
||||
name: 'chat',
|
||||
displayName: 'menu.chat',
|
||||
},
|
||||
{
|
||||
name: 'chips',
|
||||
displayName: 'menu.chips',
|
||||
},
|
||||
{
|
||||
name: 'collapses',
|
||||
displayName: 'menu.collapses',
|
||||
},
|
||||
{
|
||||
name: 'colors',
|
||||
displayName: 'menu.colors',
|
||||
},
|
||||
// {
|
||||
// name: 'color-pickers',
|
||||
// displayName: 'menu.colorPickers',
|
||||
// },
|
||||
{
|
||||
name: 'file-upload',
|
||||
displayName: 'menu.fileUpload',
|
||||
},
|
||||
{
|
||||
name: 'grid',
|
||||
displayName: 'menu.grid',
|
||||
},
|
||||
{
|
||||
name: 'icon-sets',
|
||||
displayName: 'menu.icons',
|
||||
children: [
|
||||
{
|
||||
displayName: 'concrete',
|
||||
name: 'icon-set',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'lists',
|
||||
displayName: 'menu.lists',
|
||||
},
|
||||
{
|
||||
name: 'modals',
|
||||
displayName: 'menu.modals',
|
||||
},
|
||||
{
|
||||
name: 'notifications',
|
||||
displayName: 'menu.notifications',
|
||||
},
|
||||
{
|
||||
name: 'popovers',
|
||||
displayName: 'menu.popovers',
|
||||
},
|
||||
{
|
||||
name: 'rating',
|
||||
displayName: 'menu.rating',
|
||||
},
|
||||
{
|
||||
name: 'sliders',
|
||||
displayName: 'menu.sliders',
|
||||
},
|
||||
{
|
||||
name: 'spacing',
|
||||
displayName: 'menu.spacing',
|
||||
},
|
||||
{
|
||||
name: 'spinners',
|
||||
displayName: 'menu.spinners',
|
||||
},
|
||||
{
|
||||
name: 'tabs',
|
||||
displayName: 'menu.tabs',
|
||||
},
|
||||
// {
|
||||
// name: "timelines",
|
||||
// displayName: "menu.timelines",
|
||||
// },
|
||||
{
|
||||
name: 'tree-view',
|
||||
displayName: 'menu.treeView',
|
||||
},
|
||||
{
|
||||
name: 'typography',
|
||||
displayName: 'menu.typography',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'maps',
|
||||
displayName: 'menu.maps',
|
||||
meta: {
|
||||
icon: 'vuestic-iconset-maps',
|
||||
},
|
||||
disabled: true,
|
||||
children: [
|
||||
{
|
||||
name: 'maplibre-maps',
|
||||
displayName: 'menu.maplibre-maps',
|
||||
},
|
||||
{
|
||||
name: 'yandex-maps',
|
||||
displayName: 'menu.yandex-maps',
|
||||
},
|
||||
{
|
||||
name: 'leaflet-maps',
|
||||
displayName: 'menu.leaflet-maps',
|
||||
},
|
||||
{
|
||||
name: 'bubble-maps',
|
||||
displayName: 'menu.bubble-maps',
|
||||
},
|
||||
{
|
||||
name: 'line-maps',
|
||||
displayName: 'menu.line-maps',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'pages',
|
||||
displayName: 'menu.pages',
|
||||
meta: {
|
||||
icon: 'vuestic-iconset-files',
|
||||
},
|
||||
disabled: true,
|
||||
children: [
|
||||
{
|
||||
name: 'login',
|
||||
displayName: 'menu.login-singup',
|
||||
},
|
||||
{
|
||||
name: '404-pages',
|
||||
displayName: 'menu.404-pages',
|
||||
},
|
||||
{
|
||||
name: 'faq',
|
||||
displayName: 'menu.faq',
|
||||
},
|
||||
],
|
||||
},
|
||||
] as INavigationRoute[],
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<va-sidebar :width="width" :minimized="minimized" :minimized-width="minimizedWidth" :animated="animated">
|
||||
<menu-minimized v-if="minimized" :items="items" />
|
||||
<menu-accordion v-else :items="items" />
|
||||
</va-sidebar>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import NavigationRoutes from './NavigationRoutes'
|
||||
import MenuAccordion from './menu/MenuAccordion.vue'
|
||||
import MenuMinimized from './menu/MenuMinimized.vue'
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
width?: string
|
||||
color?: string
|
||||
animated?: boolean
|
||||
minimized?: boolean
|
||||
minimizedWidth?: string
|
||||
}>(),
|
||||
{
|
||||
width: '16rem',
|
||||
color: 'secondary',
|
||||
animated: true,
|
||||
minimized: true,
|
||||
minimizedWidth: undefined,
|
||||
},
|
||||
)
|
||||
|
||||
const items = ref(NavigationRoutes.routes)
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.va-sidebar {
|
||||
&__menu {
|
||||
padding: 2rem 0;
|
||||
}
|
||||
|
||||
&-item {
|
||||
&__icon {
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<va-accordion v-model="accordionValue" class="sidebar-accordion va-sidebar__menu__inner" multiple>
|
||||
<va-collapse v-for="(route, idx) in items" :key="idx">
|
||||
<template #header>
|
||||
<va-sidebar-item :active="isRouteActive(route)" :to="route.children ? undefined : { name: route.name }">
|
||||
<va-sidebar-item-content>
|
||||
<va-icon :name="route.meta.icon" class="va-sidebar-item__icon" />
|
||||
|
||||
<va-sidebar-item-title>
|
||||
{{ t(route.displayName) }}
|
||||
</va-sidebar-item-title>
|
||||
|
||||
<va-icon v-if="route.children" :name="accordionValue[idx] ? 'expand_less' : 'expand_more'" />
|
||||
</va-sidebar-item-content>
|
||||
</va-sidebar-item>
|
||||
</template>
|
||||
<template v-for="(child, index) in route.children" :key="index">
|
||||
<va-sidebar-item :active="isRouteActive(child)" :to="{ name: child.name }">
|
||||
<va-sidebar-item-content>
|
||||
<div class="va-sidebar-item__icon" />
|
||||
|
||||
<va-sidebar-item-title>
|
||||
{{ t(child.displayName) }}
|
||||
</va-sidebar-item-title>
|
||||
</va-sidebar-item-content>
|
||||
</va-sidebar-item>
|
||||
</template>
|
||||
</va-collapse>
|
||||
</va-accordion>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { INavigationRoute } from '../NavigationRoutes'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
const { t } = useI18n()
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
items?: INavigationRoute[]
|
||||
}>(),
|
||||
{
|
||||
items: () => [],
|
||||
},
|
||||
)
|
||||
|
||||
const accordionValue = ref<boolean[]>([])
|
||||
|
||||
onMounted(() => {
|
||||
accordionValue.value = props.items.map((item) => isItemExpanded(item))
|
||||
})
|
||||
|
||||
// function isGroup(item: INavigationRoute) {
|
||||
// return !!item.children
|
||||
// }
|
||||
|
||||
function isRouteActive(item: INavigationRoute) {
|
||||
return item.name === useRoute().name
|
||||
}
|
||||
|
||||
function isItemExpanded(item: INavigationRoute): boolean {
|
||||
if (!item.children) {
|
||||
return false
|
||||
}
|
||||
|
||||
const isCurrentItemActive = isRouteActive(item)
|
||||
const isChildActive = !!item.children.find((child) =>
|
||||
child.children ? isItemExpanded(child) : isRouteActive(child),
|
||||
)
|
||||
|
||||
return isCurrentItemActive || isChildActive
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,107 @@
|
||||
<template>
|
||||
<va-dropdown
|
||||
v-for="(route, idx) in items"
|
||||
:key="idx"
|
||||
v-model="dropdownsValue[idx]"
|
||||
placement="right-start"
|
||||
prevent-overflow
|
||||
:offset="[1, 0]"
|
||||
>
|
||||
<template #anchor>
|
||||
<va-sidebar-item :active="isItemChildsActive(route)" :to="route.children ? undefined : { name: route.name }">
|
||||
<va-sidebar-item-content>
|
||||
<va-icon :name="route.meta.icon" class="va-sidebar-item__icon" />
|
||||
<va-icon
|
||||
v-if="route.children"
|
||||
class="more_icon"
|
||||
:name="dropdownsValue[idx] ? 'chevron_left' : 'chevron_right'"
|
||||
/>
|
||||
</va-sidebar-item-content>
|
||||
</va-sidebar-item>
|
||||
</template>
|
||||
<div class="sidebar-item__children">
|
||||
<template v-for="(child, index) in route.children" :key="index">
|
||||
<va-sidebar-item :active="isRouteActive(child)" :to="{ name: child.name }">
|
||||
<va-sidebar-item-content>
|
||||
<va-sidebar-item-title>
|
||||
{{ t(child.displayName) }}
|
||||
</va-sidebar-item-title>
|
||||
</va-sidebar-item-content>
|
||||
</va-sidebar-item>
|
||||
</template>
|
||||
</div>
|
||||
</va-dropdown>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { INavigationRoute } from '../NavigationRoutes'
|
||||
import { ref } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
const { t } = useI18n()
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
items?: INavigationRoute[]
|
||||
}>(),
|
||||
{
|
||||
items: () => [],
|
||||
},
|
||||
)
|
||||
|
||||
const dropdownsValue = ref([])
|
||||
|
||||
// function isGroup(item: INavigationRoute) {
|
||||
// return !!item.children
|
||||
// }
|
||||
|
||||
function isRouteActive(item: INavigationRoute) {
|
||||
return item.name === useRoute().name
|
||||
}
|
||||
|
||||
function isItemChildsActive(item: INavigationRoute): boolean {
|
||||
if (!item.children) {
|
||||
return false
|
||||
}
|
||||
|
||||
const isCurrentItemActive = isRouteActive(item)
|
||||
|
||||
let isChildActive = false
|
||||
if (item.children) {
|
||||
isChildActive = !!item.children.find((child) =>
|
||||
child.children ? isItemChildsActive(child) : isRouteActive(child),
|
||||
)
|
||||
}
|
||||
|
||||
return isCurrentItemActive || isChildActive
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.sidebar-item {
|
||||
&__children {
|
||||
max-height: 60vh;
|
||||
overflow-y: auto;
|
||||
overflow-x: visible;
|
||||
width: 16rem;
|
||||
color: var(--va-gray);
|
||||
background: var(--va-white, #fff);
|
||||
box-shadow: var(--va-box-shadow);
|
||||
}
|
||||
}
|
||||
|
||||
.va-sidebar-item {
|
||||
&-content {
|
||||
position: relative;
|
||||
|
||||
.more_icon {
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
bottom: 0.5rem;
|
||||
top: 50%;
|
||||
right: 0;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<component :is="chartComponent" ref="chart" class="va-chart" :chart-options="chartOptions" :chart-data="data" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue'
|
||||
import type { TChartOptions } from 'vue-chartjs/dist/types'
|
||||
import { defaultConfig, chartTypesMap } from './vaChartConfigs'
|
||||
import { TChartData } from '../../data/types'
|
||||
|
||||
const props = defineProps<{
|
||||
data: TChartData
|
||||
options?: TChartOptions<'line' | 'bar' | 'bubble' | 'doughnut' | 'pie'>
|
||||
type: keyof typeof chartTypesMap
|
||||
}>()
|
||||
|
||||
const chart = ref()
|
||||
|
||||
const chartComponent = computed(() => chartTypesMap[props.type])
|
||||
|
||||
const chartOptions = computed(() => ({
|
||||
...defaultConfig,
|
||||
...props.options,
|
||||
}))
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.va-chart {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
> * {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
canvas {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
min-height: 320px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,17 @@
|
||||
<template>
|
||||
<Bar :chart-options="chartOptions" :chart-data="props.chartData" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Bar } from 'vue-chartjs'
|
||||
import type { TChartOptions } from 'vue-chartjs/dist/types'
|
||||
import { Chart as ChartJS, Title, Tooltip, Legend, BarElement, LinearScale, CategoryScale } from 'chart.js'
|
||||
import { TBarChartData } from '../../../data/types'
|
||||
|
||||
ChartJS.register(Title, Tooltip, Legend, BarElement, LinearScale, CategoryScale)
|
||||
|
||||
const props = defineProps<{
|
||||
chartData: TBarChartData
|
||||
chartOptions?: TChartOptions<'bar'>
|
||||
}>()
|
||||
</script>
|
||||
@@ -0,0 +1,17 @@
|
||||
<template>
|
||||
<Bubble :chart-options="chartOptions" :chart-data="props.chartData" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Bubble } from 'vue-chartjs'
|
||||
import type { TChartOptions } from 'vue-chartjs/dist/types'
|
||||
import { Chart as ChartJS, Title, Tooltip, Legend, PointElement, LinearScale } from 'chart.js'
|
||||
import { TBubbleChartData } from '../../../data/types'
|
||||
|
||||
ChartJS.register(Title, Tooltip, Legend, PointElement, LinearScale)
|
||||
|
||||
const props = defineProps<{
|
||||
chartData: TBubbleChartData
|
||||
chartOptions?: TChartOptions<'bubble'>
|
||||
}>()
|
||||
</script>
|
||||
@@ -0,0 +1,17 @@
|
||||
<template>
|
||||
<Doughnut :chart-options="chartOptions" :chart-data="props.chartData" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Doughnut } from 'vue-chartjs'
|
||||
import type { TChartOptions } from 'vue-chartjs/dist/types'
|
||||
import { Chart as ChartJS, Title, Tooltip, Legend, ArcElement, CategoryScale } from 'chart.js'
|
||||
import { TDoughnutChartData } from '../../../data/types'
|
||||
|
||||
ChartJS.register(Title, Tooltip, Legend, ArcElement, CategoryScale)
|
||||
|
||||
const props = defineProps<{
|
||||
chartData: TDoughnutChartData
|
||||
chartOptions?: TChartOptions<'doughnut'>
|
||||
}>()
|
||||
</script>
|
||||
@@ -0,0 +1,26 @@
|
||||
<template>
|
||||
<Bar :chart-options="{ ...chartOptions, ...horizontalBarOptions }" :chart-data="props.chartData" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Bar } from 'vue-chartjs'
|
||||
import type { TChartOptions } from 'vue-chartjs/dist/types'
|
||||
import { Chart as ChartJS, Title, Tooltip, Legend, BarElement, LinearScale, CategoryScale } from 'chart.js'
|
||||
import { TBarChartData } from '../../../data/types'
|
||||
|
||||
ChartJS.register(Title, Tooltip, Legend, BarElement, LinearScale, CategoryScale)
|
||||
|
||||
const horizontalBarOptions = {
|
||||
indexAxis: 'y' as 'x' | 'y',
|
||||
elements: {
|
||||
bar: {
|
||||
borderWidth: 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
chartData: TBarChartData
|
||||
chartOptions?: TChartOptions<'bar'>
|
||||
}>()
|
||||
</script>
|
||||
@@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<Line :chart-options="chartOptions" :chart-data="props.chartData" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Line } from 'vue-chartjs'
|
||||
import type { TChartOptions } from 'vue-chartjs/dist/types'
|
||||
import {
|
||||
Chart as ChartJS,
|
||||
Title,
|
||||
Tooltip,
|
||||
Legend,
|
||||
LineElement,
|
||||
LinearScale,
|
||||
PointElement,
|
||||
CategoryScale,
|
||||
Filler,
|
||||
} from 'chart.js'
|
||||
import { TLineChartData } from '../../../data/types'
|
||||
|
||||
ChartJS.register(Title, Tooltip, Legend, LineElement, LinearScale, PointElement, CategoryScale, Filler)
|
||||
|
||||
const props = defineProps<{
|
||||
chartData: TLineChartData
|
||||
chartOptions?: TChartOptions<'line'>
|
||||
}>()
|
||||
</script>
|
||||
@@ -0,0 +1,17 @@
|
||||
<template>
|
||||
<Pie :chart-options="chartOptions" :chart-data="props.chartData" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Pie } from 'vue-chartjs'
|
||||
import type { TChartOptions } from 'vue-chartjs/dist/types'
|
||||
import { Chart as ChartJS, Title, Tooltip, Legend, ArcElement, CategoryScale } from 'chart.js'
|
||||
import { TPieChartData } from '../../../data/types'
|
||||
|
||||
ChartJS.register(Title, Tooltip, Legend, ArcElement, CategoryScale)
|
||||
|
||||
const props = defineProps<{
|
||||
chartData: TPieChartData
|
||||
chartOptions?: TChartOptions<'pie'>
|
||||
}>()
|
||||
</script>
|
||||
@@ -0,0 +1,48 @@
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
export const defaultConfig = {
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'bottom',
|
||||
labels: {
|
||||
font: {
|
||||
color: '#34495e',
|
||||
family: 'sans-serif',
|
||||
size: 14,
|
||||
},
|
||||
usePointStyle: true,
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
bodyFont: {
|
||||
size: 14,
|
||||
family: 'sans-serif',
|
||||
},
|
||||
boxPadding: 4,
|
||||
},
|
||||
},
|
||||
datasets: {
|
||||
line: {
|
||||
fill: 'origin',
|
||||
tension: 0.3,
|
||||
borderColor: 'transparent',
|
||||
},
|
||||
bubble: {
|
||||
borderColor: 'transparent',
|
||||
},
|
||||
bar: {
|
||||
borderColor: 'transparent',
|
||||
},
|
||||
},
|
||||
maintainAspectRatio: false,
|
||||
animation: true,
|
||||
}
|
||||
|
||||
export const chartTypesMap = {
|
||||
pie: defineAsyncComponent(() => import('./chart-types/PieChart.vue')),
|
||||
doughnut: defineAsyncComponent(() => import('./chart-types/DoughnutChart.vue')),
|
||||
bubble: defineAsyncComponent(() => import('./chart-types/BubbleChart.vue')),
|
||||
line: defineAsyncComponent(() => import('./chart-types/LineChart.vue')),
|
||||
bar: defineAsyncComponent(() => import('./chart-types/BarChart.vue')),
|
||||
'horizontal-bar': defineAsyncComponent(() => import('./chart-types/HorizontalBarChart.vue')),
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
<template>
|
||||
<div ref="editorElement" class="va-medium-editor content">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, Ref, onMounted, onBeforeUnmount } from 'vue'
|
||||
import MediumEditor from 'medium-editor'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
editorOptions?: {
|
||||
buttonLabels: string
|
||||
autoLink: boolean
|
||||
toolbar: {
|
||||
buttons: string[]
|
||||
}
|
||||
}
|
||||
}>(),
|
||||
{
|
||||
editorOptions: () => ({
|
||||
buttonLabels: 'fontawesome',
|
||||
autoLink: true,
|
||||
toolbar: {
|
||||
buttons: ['bold', 'italic', 'underline', 'anchor', 'h1', 'h2', 'h3'],
|
||||
},
|
||||
}),
|
||||
},
|
||||
)
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'initialized', editor: typeof MediumEditor): void
|
||||
}>()
|
||||
|
||||
const editorElement: Ref<null | HTMLElement> = ref(null)
|
||||
let editor: typeof MediumEditor | null = null
|
||||
|
||||
onMounted(() => {
|
||||
if (!editorElement.value) {
|
||||
return
|
||||
}
|
||||
|
||||
editor = new MediumEditor(editorElement.value, props.editorOptions)
|
||||
emit('initialized', editor)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (editor) {
|
||||
editor.destroy()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import 'medium-editor/src/sass/medium-editor';
|
||||
@import 'variables';
|
||||
|
||||
$medium-editor-shadow: var(--va-box-shadow);
|
||||
$medium-editor-background-color: var(--va-divider);
|
||||
$medium-editor-text-color: var(--va-dark);
|
||||
$medium-editor-active-background-color: var(--va-primary);
|
||||
$medium-editor-active-text-color: var(--va-white);
|
||||
|
||||
.va-medium-editor {
|
||||
margin-bottom: var(--va-medium-editor-margin-bottom);
|
||||
min-width: var(--va-medium-editor-min-width);
|
||||
max-width: var(--va-medium-editor-max-width);
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&.content {
|
||||
i {
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// isn't a part of the .va-medium-editor, so can't be places inside it
|
||||
.medium-editor-toolbar,
|
||||
.medium-editor-toolbar-form,
|
||||
.medium-editor-toolbar-actions,
|
||||
.medium-editor-toolbar-anchor-preview {
|
||||
box-shadow: $medium-editor-shadow;
|
||||
background-color: $medium-editor-background-color;
|
||||
border-radius: 1.5rem;
|
||||
height: 44px;
|
||||
line-height: 42px;
|
||||
}
|
||||
|
||||
.medium-editor-toolbar-anchor-preview {
|
||||
a {
|
||||
padding: 0 2rem;
|
||||
margin: 0;
|
||||
line-height: 44px;
|
||||
}
|
||||
}
|
||||
|
||||
.medium-editor-toolbar {
|
||||
box-shadow: $medium-editor-shadow;
|
||||
|
||||
.medium-editor-toolbar-actions {
|
||||
overflow: hidden;
|
||||
height: 44px;
|
||||
}
|
||||
|
||||
.medium-editor-action {
|
||||
margin: 0;
|
||||
border: 0;
|
||||
padding: 0.375rem 1rem;
|
||||
height: 44px;
|
||||
background-color: $medium-editor-background-color;
|
||||
box-shadow: none;
|
||||
border-radius: 0;
|
||||
|
||||
i {
|
||||
color: $medium-editor-text-color;
|
||||
}
|
||||
|
||||
&.medium-editor-button-active {
|
||||
background-color: $medium-editor-active-background-color;
|
||||
color: $medium-editor-active-text-color;
|
||||
i {
|
||||
color: $medium-editor-active-text-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
& > .medium-editor-action:not(:last-child) {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
& > .medium-editor-action + .medium-editor-action {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
border-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.medium-editor-toolbar-form {
|
||||
color: $medium-editor-text-color;
|
||||
overflow: hidden;
|
||||
|
||||
a {
|
||||
color: $medium-editor-text-color;
|
||||
transform: translateY(1px);
|
||||
}
|
||||
|
||||
input {
|
||||
margin-left: 4px !important;
|
||||
transform: translateY(-2px);
|
||||
border-radius: 13px;
|
||||
}
|
||||
|
||||
.medium-editor-toolbar-close {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.medium-toolbar-arrow-under::after {
|
||||
border-color: $medium-editor-background-color transparent transparent transparent;
|
||||
top: 100%;
|
||||
}
|
||||
|
||||
.medium-toolbar-arrow-over::before {
|
||||
border-color: transparent transparent var(--va-primary) transparent;
|
||||
}
|
||||
|
||||
.medium-editor-toolbar-anchor-preview {
|
||||
// @include va-button($btn-padding-y-nrm, $btn-padding-x-nrm, $btn-font-size-nrm, $btn-line-height-nrm, $btn-border-radius-nrm);
|
||||
|
||||
.medium-editor-toolbar-anchor-preview {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.medium-editor-anchor-preview {
|
||||
max-width: 50%;
|
||||
|
||||
a {
|
||||
color: $medium-editor-text-color;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,9 @@
|
||||
:root {
|
||||
--va-medium-editor-margin-bottom: 2.25rem;
|
||||
--va-medium-editor-min-width: 6rem;
|
||||
--va-medium-editor-max-width: 600px;
|
||||
|
||||
/* Toolbar */
|
||||
--va-medium-editor-toolbar-max-width: 90%;
|
||||
--va-medium-editor-toolbar-box-shadow: none;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import MQTT from '@/utils/mqtt'
|
||||
import type { OnMessageCallback } from 'mqtt'
|
||||
import { onUnmounted, ref } from 'vue'
|
||||
|
||||
export default function useMqtt() {
|
||||
const PublicMqtt = ref<MQTT | null>(null)
|
||||
|
||||
const startMqtt = (val: string, callback: OnMessageCallback) => {
|
||||
// set mqtt topic
|
||||
PublicMqtt.value = new MQTT(val)
|
||||
// init mqtt
|
||||
PublicMqtt.value.init()
|
||||
// link mqtt
|
||||
PublicMqtt.value.link()
|
||||
getMessage(callback)
|
||||
}
|
||||
|
||||
const publish = (topic: string, message: string) => {
|
||||
PublicMqtt.value?.publish(topic, message)
|
||||
}
|
||||
|
||||
const getMessage = (callback: OnMessageCallback) => {
|
||||
// PublicMqtt.value?.client.on('message', callback);
|
||||
PublicMqtt.value?.get(callback)
|
||||
}
|
||||
onUnmounted(() => {
|
||||
if (PublicMqtt.value) {
|
||||
PublicMqtt.value.unsubscribes()
|
||||
PublicMqtt.value.over()
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
startMqtt,
|
||||
publish,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
export default [
|
||||
'Afghanistan',
|
||||
'Albania',
|
||||
'Algeria',
|
||||
'American Samoa',
|
||||
'Andorra',
|
||||
'Angola',
|
||||
'Anguilla',
|
||||
'Antarctica',
|
||||
'Antigua and Barbuda',
|
||||
'Argentina',
|
||||
'Armenia',
|
||||
'Aruba',
|
||||
'Australia',
|
||||
'Austria',
|
||||
'Azerbaijan',
|
||||
'Bahamas',
|
||||
'Bahrain',
|
||||
'Bangladesh',
|
||||
'Barbados',
|
||||
'Belarus',
|
||||
'Belgium',
|
||||
'Belize',
|
||||
'Benin',
|
||||
'Bermuda',
|
||||
'Bhutan',
|
||||
'Bolivia',
|
||||
'Bosnia and Herzegowina',
|
||||
'Botswana',
|
||||
'Bouvet Island',
|
||||
'Brazil',
|
||||
'British Indian Ocean Territory',
|
||||
'Brunei Darussalam',
|
||||
'Bulgaria',
|
||||
'Burkina Faso',
|
||||
'Burundi',
|
||||
'Cambodia',
|
||||
'Cameroon',
|
||||
'Canada',
|
||||
'Cape Verde',
|
||||
'Cayman Islands',
|
||||
'Central African Republic',
|
||||
'Chad',
|
||||
'Chile',
|
||||
'China',
|
||||
'Christmas Island',
|
||||
'Cocos (Keeling) Islands',
|
||||
'Colombia',
|
||||
'Comoros',
|
||||
'Congo',
|
||||
'Congo, the Democratic Republic of the',
|
||||
'Cook Islands',
|
||||
'Costa Rica',
|
||||
"Cote d'Ivoire",
|
||||
'Croatia (Hrvatska)',
|
||||
'Cuba',
|
||||
'Cyprus',
|
||||
'Czech Republic',
|
||||
'Denmark',
|
||||
'Djibouti',
|
||||
'Dominica',
|
||||
'Dominican Republic',
|
||||
'East Timor',
|
||||
'Ecuador',
|
||||
'Egypt',
|
||||
'El Salvador',
|
||||
'Equatorial Guinea',
|
||||
'Eritrea',
|
||||
'Estonia',
|
||||
'Ethiopia',
|
||||
'Falkland Islands (Malvinas)',
|
||||
'Faroe Islands',
|
||||
'Fiji',
|
||||
'Finland',
|
||||
'France',
|
||||
'France Metropolitan',
|
||||
'French Guiana',
|
||||
'French Polynesia',
|
||||
'French Southern Territories',
|
||||
'Gabon',
|
||||
'Gambia',
|
||||
'Georgia',
|
||||
'Germany',
|
||||
'Ghana',
|
||||
'Gibraltar',
|
||||
'Greece',
|
||||
'Greenland',
|
||||
'Grenada',
|
||||
'Guadeloupe',
|
||||
'Guam',
|
||||
'Guatemala',
|
||||
'Guinea',
|
||||
'Guinea-Bissau',
|
||||
'Guyana',
|
||||
'Haiti',
|
||||
'Heard and Mc Donald Islands',
|
||||
'Holy See (Vatican City State)',
|
||||
'Honduras',
|
||||
'Hong Kong',
|
||||
'Hungary',
|
||||
'Iceland',
|
||||
'India',
|
||||
'Indonesia',
|
||||
'Iran (Islamic Republic of)',
|
||||
'Iraq',
|
||||
'Ireland',
|
||||
'Israel',
|
||||
'Italy',
|
||||
'Jamaica',
|
||||
'Japan',
|
||||
'Jordan',
|
||||
'Kazakhstan',
|
||||
'Kenya',
|
||||
'Kiribati',
|
||||
"Korea, Democratic People's Republic of",
|
||||
'Korea, Republic of',
|
||||
'Kuwait',
|
||||
'Kyrgyzstan',
|
||||
"Lao, People's Democratic Republic",
|
||||
'Latvia',
|
||||
'Lebanon',
|
||||
'Lesotho',
|
||||
'Liberia',
|
||||
'Libyan Arab Jamahiriya',
|
||||
'Liechtenstein',
|
||||
'Lithuania',
|
||||
'Luxembourg',
|
||||
'Macau',
|
||||
'Macedonia, The Former Yugoslav Republic of',
|
||||
'Madagascar',
|
||||
'Malawi',
|
||||
'Malaysia',
|
||||
'Maldives',
|
||||
'Mali',
|
||||
'Malta',
|
||||
'Marshall Islands',
|
||||
'Martinique',
|
||||
'Mauritania',
|
||||
'Mauritius',
|
||||
'Mayotte',
|
||||
'Mexico',
|
||||
'Micronesia, Federated States of',
|
||||
'Moldova, Republic of',
|
||||
'Monaco',
|
||||
'Mongolia',
|
||||
'Montserrat',
|
||||
'Morocco',
|
||||
'Mozambique',
|
||||
'Myanmar',
|
||||
'Namibia',
|
||||
'Nauru',
|
||||
'Nepal',
|
||||
'Netherlands',
|
||||
'Netherlands Antilles',
|
||||
'New Caledonia',
|
||||
'New Zealand',
|
||||
'Nicaragua',
|
||||
'Niger',
|
||||
'Nigeria',
|
||||
'Niue',
|
||||
'Norfolk Island',
|
||||
'Northern Mariana Islands',
|
||||
'Norway',
|
||||
'Oman',
|
||||
'Pakistan',
|
||||
'Palau',
|
||||
'Panama',
|
||||
'Papua New Guinea',
|
||||
'Paraguay',
|
||||
'Peru',
|
||||
'Philippines',
|
||||
'Pitcairn',
|
||||
'Poland',
|
||||
'Portugal',
|
||||
'Puerto Rico',
|
||||
'Qatar',
|
||||
'Reunion',
|
||||
'Romania',
|
||||
'Russian Federation',
|
||||
'Rwanda',
|
||||
'Saint Kitts and Nevis',
|
||||
'Saint Lucia',
|
||||
'Saint Vincent and the Grenadines',
|
||||
'Samoa',
|
||||
'San Marino',
|
||||
'Sao Tome and Principe',
|
||||
'Saudi Arabia',
|
||||
'Senegal',
|
||||
'Serbia',
|
||||
'Seychelles',
|
||||
'Sierra Leone',
|
||||
'Singapore',
|
||||
'Slovakia (Slovak Republic)',
|
||||
'Slovenia',
|
||||
'Solomon Islands',
|
||||
'Somalia',
|
||||
'South Africa',
|
||||
'South Georgia and the South Sandwich Islands',
|
||||
'Spain',
|
||||
'Sri Lanka',
|
||||
'St. Helena',
|
||||
'St. Pierre and Miquelon',
|
||||
'Sudan',
|
||||
'Suriname',
|
||||
'Svalbard and Jan Mayen Islands',
|
||||
'Swaziland',
|
||||
'Sweden',
|
||||
'Switzerland',
|
||||
'Syrian Arab Republic',
|
||||
'Taiwan, Province of China',
|
||||
'Tajikistan',
|
||||
'Tanzania, United Republic of',
|
||||
'United States of America',
|
||||
'Thailand',
|
||||
'Togo',
|
||||
'Tokelau',
|
||||
'Tonga',
|
||||
'Trinidad and Tobago',
|
||||
'Tunisia',
|
||||
'Turkey',
|
||||
'Turkmenistan',
|
||||
'Turks and Caicos Islands',
|
||||
'Tuvalu',
|
||||
'Uganda',
|
||||
'Ukraine',
|
||||
'United Arab Emirates',
|
||||
'United Kingdom',
|
||||
'United States',
|
||||
'United States Minor Outlying Islands',
|
||||
'Uruguay',
|
||||
'Uzbekistan',
|
||||
'Vanuatu',
|
||||
'Venezuela',
|
||||
'Vietnam',
|
||||
'Virgin Islands (British)',
|
||||
'Virgin Islands (U.S.)',
|
||||
'Wallis and Futuna Islands',
|
||||
'Western Sahara',
|
||||
'Yemen',
|
||||
'Yugoslavia',
|
||||
'Zambia',
|
||||
'Zimbabwe',
|
||||
]
|
||||
@@ -0,0 +1,13 @@
|
||||
import axios, { Axios } from 'axios'
|
||||
|
||||
export const setBaseURL = function (baseURL: string) {
|
||||
if (baseURL) {
|
||||
axios.defaults.baseURL = `http://${baseURL}:3000`
|
||||
}
|
||||
}
|
||||
|
||||
setBaseURL(location.href.split('/')[2].split(':')[0])
|
||||
|
||||
export const getAxios = function (): Axios {
|
||||
return axios
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import { TBarChartData } from '../types'
|
||||
|
||||
export const barChartData: TBarChartData = {
|
||||
labels: [
|
||||
'January',
|
||||
'February',
|
||||
'March',
|
||||
'April',
|
||||
'May',
|
||||
'June',
|
||||
'July',
|
||||
'August',
|
||||
'September',
|
||||
'October',
|
||||
'November',
|
||||
'December',
|
||||
],
|
||||
datasets: [
|
||||
{
|
||||
label: 'Last year',
|
||||
backgroundColor: 'primary',
|
||||
data: [50, 20, 12, 39, 10, 40, 39, 80, 40, 20, 12, 11],
|
||||
},
|
||||
{
|
||||
label: 'Current year',
|
||||
backgroundColor: 'info',
|
||||
data: [50, 10, 22, 39, 15, 20, 85, 32, 60, 50, 20, 30],
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
import { TBubbleChartData } from '../types'
|
||||
|
||||
export const bubbleChartData: TBubbleChartData = {
|
||||
datasets: [
|
||||
{
|
||||
label: 'USA',
|
||||
backgroundColor: 'danger',
|
||||
data: [
|
||||
{
|
||||
x: 23,
|
||||
y: 25,
|
||||
r: 15,
|
||||
},
|
||||
{
|
||||
x: 40,
|
||||
y: 10,
|
||||
r: 10,
|
||||
},
|
||||
{
|
||||
x: 30,
|
||||
y: 22,
|
||||
r: 30,
|
||||
},
|
||||
{
|
||||
x: 7,
|
||||
y: 43,
|
||||
r: 40,
|
||||
},
|
||||
{
|
||||
x: 23,
|
||||
y: 27,
|
||||
r: 12,
|
||||
},
|
||||
{
|
||||
x: 20,
|
||||
y: 15,
|
||||
r: 11,
|
||||
},
|
||||
{
|
||||
x: 7,
|
||||
y: 10,
|
||||
r: 35,
|
||||
},
|
||||
{
|
||||
x: 10,
|
||||
y: 20,
|
||||
r: 40,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Russia',
|
||||
backgroundColor: 'primary',
|
||||
data: [
|
||||
{
|
||||
x: 0,
|
||||
y: 30,
|
||||
r: 15,
|
||||
},
|
||||
{
|
||||
x: 20,
|
||||
y: 20,
|
||||
r: 20,
|
||||
},
|
||||
{
|
||||
x: 15,
|
||||
y: 15,
|
||||
r: 50,
|
||||
},
|
||||
{
|
||||
x: 31,
|
||||
y: 46,
|
||||
r: 30,
|
||||
},
|
||||
{
|
||||
x: 20,
|
||||
y: 14,
|
||||
r: 25,
|
||||
},
|
||||
{
|
||||
x: 34,
|
||||
y: 17,
|
||||
r: 30,
|
||||
},
|
||||
{
|
||||
x: 44,
|
||||
y: 44,
|
||||
r: 10,
|
||||
},
|
||||
{
|
||||
x: 39,
|
||||
y: 25,
|
||||
r: 35,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Canada',
|
||||
backgroundColor: 'warning',
|
||||
data: [
|
||||
{
|
||||
x: 10,
|
||||
y: 30,
|
||||
r: 45,
|
||||
},
|
||||
{
|
||||
x: 10,
|
||||
y: 50,
|
||||
r: 20,
|
||||
},
|
||||
{
|
||||
x: 5,
|
||||
y: 5,
|
||||
r: 30,
|
||||
},
|
||||
{
|
||||
x: 40,
|
||||
y: 30,
|
||||
r: 20,
|
||||
},
|
||||
{
|
||||
x: 33,
|
||||
y: 15,
|
||||
r: 18,
|
||||
},
|
||||
{
|
||||
x: 40,
|
||||
y: 20,
|
||||
r: 40,
|
||||
},
|
||||
{
|
||||
x: 33,
|
||||
y: 33,
|
||||
r: 40,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Belarus',
|
||||
backgroundColor: 'info',
|
||||
data: [
|
||||
{
|
||||
x: 35,
|
||||
y: 30,
|
||||
r: 45,
|
||||
},
|
||||
{
|
||||
x: 25,
|
||||
y: 40,
|
||||
r: 35,
|
||||
},
|
||||
{
|
||||
x: 5,
|
||||
y: 5,
|
||||
r: 30,
|
||||
},
|
||||
{
|
||||
x: 5,
|
||||
y: 20,
|
||||
r: 40,
|
||||
},
|
||||
{
|
||||
x: 10,
|
||||
y: 40,
|
||||
r: 15,
|
||||
},
|
||||
{
|
||||
x: 3,
|
||||
y: 10,
|
||||
r: 10,
|
||||
},
|
||||
{
|
||||
x: 15,
|
||||
y: 40,
|
||||
r: 40,
|
||||
},
|
||||
{
|
||||
x: 7,
|
||||
y: 15,
|
||||
r: 10,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Ukraine',
|
||||
backgroundColor: 'success',
|
||||
data: [
|
||||
{
|
||||
x: 25,
|
||||
y: 10,
|
||||
r: 40,
|
||||
},
|
||||
{
|
||||
x: 17,
|
||||
y: 40,
|
||||
r: 40,
|
||||
},
|
||||
{
|
||||
x: 35,
|
||||
y: 10,
|
||||
r: 20,
|
||||
},
|
||||
{
|
||||
x: 3,
|
||||
y: 40,
|
||||
r: 10,
|
||||
},
|
||||
{
|
||||
x: 40,
|
||||
y: 40,
|
||||
r: 40,
|
||||
},
|
||||
{
|
||||
x: 20,
|
||||
y: 10,
|
||||
r: 10,
|
||||
},
|
||||
{
|
||||
x: 10,
|
||||
y: 27,
|
||||
r: 35,
|
||||
},
|
||||
{
|
||||
x: 7,
|
||||
y: 26,
|
||||
r: 40,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { computed, ref } from '@vue/reactivity'
|
||||
import { watch } from 'vue'
|
||||
import { useColors, useGlobalConfig } from 'vuestic-ui'
|
||||
|
||||
type chartColors = string | string[]
|
||||
|
||||
export function useChartColors(chartColors = [] as chartColors, alfa = 0.6) {
|
||||
const { getGlobalConfig } = useGlobalConfig()
|
||||
const { setHSLAColor, getColor } = useColors()
|
||||
|
||||
const generateHSLAColors = (colors: chartColors) =>
|
||||
typeof colors === 'string'
|
||||
? setHSLAColor(getColor(colors), { a: alfa })
|
||||
: colors.map((color) => setHSLAColor(getColor(color), { a: alfa }))
|
||||
|
||||
const generateColors = (colors: chartColors) =>
|
||||
typeof colors === 'string' ? getColor(colors) : colors.map((color) => getColor(color))
|
||||
|
||||
const generatedHSLAColors = ref(generateHSLAColors(chartColors))
|
||||
const generatedColors = ref(generateColors(chartColors))
|
||||
|
||||
const theme = computed(() => getGlobalConfig().colors!)
|
||||
|
||||
watch(theme, () => {
|
||||
generatedHSLAColors.value = generateHSLAColors(chartColors)
|
||||
generatedColors.value = generateColors(chartColors)
|
||||
})
|
||||
|
||||
return {
|
||||
generateHSLAColors,
|
||||
generateColors,
|
||||
generatedColors,
|
||||
generatedHSLAColors,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { computed, ComputedRef } from '@vue/reactivity'
|
||||
import { useChartColors } from './useChartColors'
|
||||
import { TChartData } from '../../types'
|
||||
|
||||
export function useChartData<T extends TChartData>(data: T, alfa?: number): ComputedRef<T> {
|
||||
const datasetsColors = data.datasets.map((dataset) => dataset.backgroundColor as string)
|
||||
|
||||
const datasetsThemesColors = datasetsColors.map(
|
||||
(colors) => useChartColors(colors, alfa)[alfa ? 'generatedHSLAColors' : 'generatedColors'],
|
||||
)
|
||||
|
||||
return computed(() => {
|
||||
const datasets = data.datasets.map((dataset, idx) => ({
|
||||
...dataset,
|
||||
backgroundColor: datasetsThemesColors[idx].value,
|
||||
}))
|
||||
|
||||
return { ...data, datasets } as T
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { TDoughnutChartData } from '../types'
|
||||
|
||||
export const doughnutChartData: TDoughnutChartData = {
|
||||
labels: ['North America', 'South America', 'Australia'],
|
||||
datasets: [
|
||||
{
|
||||
label: 'Population (millions)',
|
||||
backgroundColor: ['danger', 'info', 'primary'],
|
||||
data: [2478, 5267, 734],
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import { TBarChartData } from '../types'
|
||||
|
||||
export const horizontalBarChartData: TBarChartData = {
|
||||
labels: [
|
||||
'January',
|
||||
'February',
|
||||
'March',
|
||||
'April',
|
||||
'May',
|
||||
'June',
|
||||
'July',
|
||||
'August',
|
||||
'September',
|
||||
'October',
|
||||
'November',
|
||||
'December',
|
||||
],
|
||||
datasets: [
|
||||
{
|
||||
label: 'Vuestic Satisfaction Score',
|
||||
backgroundColor: 'primary',
|
||||
data: [80, 90, 50, 70, 60, 90, 50, 90, 80, 40, 72, 93],
|
||||
},
|
||||
{
|
||||
label: 'Bulma Satisfaction Score',
|
||||
backgroundColor: 'danger',
|
||||
data: [20, 30, 20, 40, 50, 40, 15, 60, 30, 20, 42, 53],
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export { bubbleChartData } from './bubbleChartData'
|
||||
export { doughnutChartData } from './doughnutChartData'
|
||||
export { barChartData } from './barChartData'
|
||||
export { horizontalBarChartData } from './horizontalBarChartData'
|
||||
export { lineChartData } from './lineChartData'
|
||||
export { pieChartData } from './pieChartData'
|
||||
@@ -0,0 +1,44 @@
|
||||
import { TLineChartData } from '../types'
|
||||
|
||||
const months = [
|
||||
'January',
|
||||
'February',
|
||||
'March',
|
||||
'April',
|
||||
'May',
|
||||
'June',
|
||||
'July',
|
||||
'August',
|
||||
'September',
|
||||
'October',
|
||||
'November',
|
||||
'December',
|
||||
]
|
||||
|
||||
const getSize = (minSize = 5) => Math.max(minSize, new Date().getMonth())
|
||||
const size = getSize()
|
||||
|
||||
const generateValue = () => Math.floor(Math.random() * 100)
|
||||
const generateArray = (length: number) => Array.from(Array(length), generateValue)
|
||||
|
||||
const generateYLabels = () => {
|
||||
const flip = !!Math.floor(Math.random() * 2)
|
||||
return flip ? ['Debit', 'Credit'] : ['Credit', 'Debit']
|
||||
}
|
||||
const yLabels = generateYLabels()
|
||||
|
||||
export const lineChartData: TLineChartData = {
|
||||
labels: months.slice(0, size),
|
||||
datasets: [
|
||||
{
|
||||
label: yLabels[0],
|
||||
backgroundColor: 'primary',
|
||||
data: generateArray(size),
|
||||
},
|
||||
{
|
||||
label: yLabels[1],
|
||||
backgroundColor: 'secondary',
|
||||
data: generateArray(size),
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { TLineChartData } from '../types'
|
||||
|
||||
export const pieChartData: TLineChartData = {
|
||||
labels: ['Africa', 'Asia', 'Europe'],
|
||||
datasets: [
|
||||
{
|
||||
label: 'Population (millions)',
|
||||
backgroundColor: ['primary', 'warning', 'danger'],
|
||||
data: [2478, 5267, 734],
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
export default {
|
||||
name: 'Idle',
|
||||
parameter: [], // 這個mode用到的參數
|
||||
showParameter: [], // 有要秀給user看的參數
|
||||
headerParameter: () => [], // export header的參數
|
||||
valScales: {
|
||||
linear: {
|
||||
func: (val) => {
|
||||
return val
|
||||
},
|
||||
},
|
||||
log: {
|
||||
func: (val) => {
|
||||
return Math.log10(Math.abs(val))
|
||||
},
|
||||
},
|
||||
},
|
||||
channels: {},
|
||||
charts: {},
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
export default {
|
||||
name: 'Cali Mode - test',
|
||||
parameter: ['ADC_DAC_CHANNEL_15', 'ADC_LEVEL_I_15', 'ADC_LEVEL_V_IN_15', 'DAC_LEVEL_V_OUT_15', 'DAC_VOLT_BUTTON'], // 這個mode用到的參數
|
||||
showParameter: ['ADC_DAC_CHANNEL_15', 'ADC_LEVEL_I_15', 'ADC_LEVEL_V_IN_15', 'DAC_LEVEL_V_OUT_15', 'DAC_VOLT_BUTTON'], // 有要秀給user看的參數
|
||||
headerParameter: () => [
|
||||
'ADC_DAC_CHANNEL_15',
|
||||
'ADC_LEVEL_I_15',
|
||||
'ADC_LEVEL_V_IN_15',
|
||||
'DAC_LEVEL_V_OUT_15',
|
||||
'DAC_VOLT_BUTTON',
|
||||
], // export header的參數
|
||||
valScales: {
|
||||
linear: {
|
||||
func: (val) => {
|
||||
return val
|
||||
},
|
||||
},
|
||||
log: {
|
||||
func: (val) => {
|
||||
return Math.log10(Math.abs(val))
|
||||
},
|
||||
},
|
||||
},
|
||||
channels: {
|
||||
time: {
|
||||
name: 'Time',
|
||||
unit: {
|
||||
us: 1,
|
||||
ms: 1e3,
|
||||
s: 1e6,
|
||||
m: 60 * 1e6,
|
||||
h: 60 * 60 * 1e6,
|
||||
auto: 1,
|
||||
},
|
||||
defaultUnit: 'us',
|
||||
},
|
||||
0: {
|
||||
name: 'I_in',
|
||||
unit: {
|
||||
nA: 1,
|
||||
},
|
||||
defaultUnit: 'nA',
|
||||
},
|
||||
1: {
|
||||
name: 'V_in/V_out',
|
||||
unit: {
|
||||
uV: 1,
|
||||
},
|
||||
defaultUnit: 'uV',
|
||||
},
|
||||
2: {
|
||||
name: 'Gain',
|
||||
unit: {
|
||||
default: 1,
|
||||
},
|
||||
defaultUnit: 'default',
|
||||
},
|
||||
},
|
||||
charts: {
|
||||
default: [
|
||||
{
|
||||
name: 'Cali Mode - test',
|
||||
description: '',
|
||||
subplot: [
|
||||
{
|
||||
x1: {
|
||||
type: 'time',
|
||||
valScale: 'linear',
|
||||
min: 'dataMin',
|
||||
max: 'dataMax',
|
||||
},
|
||||
y1: {
|
||||
type: 'value',
|
||||
valScale: 'linear',
|
||||
min: 'dataMin',
|
||||
max: 'dataMax',
|
||||
},
|
||||
data: [
|
||||
{
|
||||
legend: 'ADC',
|
||||
x1: {
|
||||
channel: 'time',
|
||||
},
|
||||
y1: {
|
||||
channel: 0,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
export default {
|
||||
name: 'Cali DAC - test',
|
||||
parameter: ['CTRL_HIGH_Z_15', 'DAC_VOLT'], // 這個mode用到的參數
|
||||
showParameter: ['CTRL_HIGH_Z_15', 'DAC_VOLT'], // 有要秀給user看的參數
|
||||
valScales: {
|
||||
linear: {
|
||||
func: (val) => {
|
||||
return val
|
||||
},
|
||||
},
|
||||
log: {
|
||||
func: (val) => {
|
||||
return Math.log10(Math.abs(val))
|
||||
},
|
||||
},
|
||||
},
|
||||
channels: {
|
||||
time: {
|
||||
name: 'Time',
|
||||
unit: {
|
||||
us: 1,
|
||||
ms: 1e3,
|
||||
s: 1e6,
|
||||
m: 60 * 1e6,
|
||||
h: 60 * 60 * 1e6,
|
||||
auto: 1,
|
||||
},
|
||||
defaultUnit: 'us',
|
||||
},
|
||||
0: {
|
||||
name: 'I_in',
|
||||
unit: {
|
||||
nA: 1,
|
||||
},
|
||||
defaultUnit: 'nA',
|
||||
},
|
||||
1: {
|
||||
name: 'V_in/V_out',
|
||||
unit: {
|
||||
uV: 1,
|
||||
},
|
||||
defaultUnit: 'uV',
|
||||
},
|
||||
2: {
|
||||
name: 'Gain',
|
||||
unit: {
|
||||
default: 1,
|
||||
},
|
||||
defaultUnit: 'default',
|
||||
},
|
||||
},
|
||||
charts: {
|
||||
default: [
|
||||
{
|
||||
name: 'Cali DAC - test',
|
||||
description: '',
|
||||
subplot: [
|
||||
{
|
||||
x1: {
|
||||
type: 'time',
|
||||
valScale: 'linear',
|
||||
min: 'dataMin',
|
||||
max: 'dataMax',
|
||||
},
|
||||
y1: {
|
||||
type: 'value',
|
||||
valScale: 'linear',
|
||||
min: 'dataMin',
|
||||
max: 'dataMax',
|
||||
},
|
||||
data: [
|
||||
{
|
||||
legend: 'DAC',
|
||||
x1: {
|
||||
channel: 'time',
|
||||
},
|
||||
y1: {
|
||||
channel: 0,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
export default {
|
||||
name: 'Chronoamperometry',
|
||||
parameter: ['CA_VOLT', 'SAMPLE_RATE', 'ADC_LEVEL_I_15', 'ADC_LEVEL_V_IN_15', 'CTRL_HIGH_Z_15', 'TIME_DURATION'], // 這個mode用到的參數
|
||||
showParameter: ['ADC_LEVEL_I_15', 'ADC_LEVEL_V_IN_15', 'CTRL_HIGH_Z_15', 'CA_VOLT', 'SAMPLE_RATE', 'TIME_DURATION'], // 有要秀給user看的參數
|
||||
headerParameter: () => ['CA_VOLT', 'SAMPLE_RATE', 'ADC_LEVEL_I_15', 'ADC_LEVEL_V_IN_15', 'TIME_DURATION'], // 有要秀給user看的參數
|
||||
valScales: {
|
||||
linear: {
|
||||
func: (val) => {
|
||||
return val
|
||||
},
|
||||
},
|
||||
log: {
|
||||
func: (val) => {
|
||||
return Math.log10(Math.abs(val))
|
||||
},
|
||||
},
|
||||
},
|
||||
channels: {
|
||||
time: {
|
||||
name: 'Time',
|
||||
unit: {
|
||||
us: 1,
|
||||
ms: 1e3,
|
||||
s: 1e6,
|
||||
minute: 60 * 1e6,
|
||||
hour: 60 * 60 * 1e6,
|
||||
},
|
||||
defaultUnit: 'ms',
|
||||
downloadUnit: 'ms',
|
||||
},
|
||||
0: {
|
||||
name: 'I_in',
|
||||
unit: {
|
||||
nA: 1,
|
||||
uA: 1e3,
|
||||
mA: 1e6,
|
||||
},
|
||||
defaultUnit: 'mA',
|
||||
downloadUnit: 'mA',
|
||||
},
|
||||
1: {
|
||||
name: 'Potential',
|
||||
unit: {
|
||||
uV: 1,
|
||||
mV: 1e3,
|
||||
V: 1e6,
|
||||
},
|
||||
defaultUnit: 'mV',
|
||||
downloadUnit: 'mV',
|
||||
},
|
||||
2: {
|
||||
name: 'V_out',
|
||||
unit: {
|
||||
uV: 1,
|
||||
mV: 1e3,
|
||||
V: 1e6,
|
||||
},
|
||||
defaultUnit: 'mV',
|
||||
downloadUnit: 'mV',
|
||||
},
|
||||
},
|
||||
charts: {
|
||||
default: [
|
||||
{
|
||||
name: 'Chronoamperometric',
|
||||
description: '',
|
||||
subplot: [
|
||||
{
|
||||
x1: {
|
||||
type: 'time',
|
||||
valScale: 'linear',
|
||||
min: 'dataMin',
|
||||
max: 'dataMax',
|
||||
},
|
||||
y1: {
|
||||
type: 'value',
|
||||
valScale: 'linear',
|
||||
min: 'dataMin',
|
||||
max: 'dataMax',
|
||||
},
|
||||
data: [
|
||||
{
|
||||
legend: 'chrono',
|
||||
x1: {
|
||||
channel: 'time',
|
||||
},
|
||||
y1: {
|
||||
channel: 0,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
export default {
|
||||
name: 'Chronopotentiometry',
|
||||
parameter: [
|
||||
'Charge',
|
||||
'Const_Current_value',
|
||||
'SAMPLE_RATE',
|
||||
'VOLTSTOP_MAX',
|
||||
'VOLTSTOP_MIN',
|
||||
'CC_CP_SPEED',
|
||||
'ADC_LEVEL_I_15',
|
||||
'ADC_LEVEL_V_IN_15',
|
||||
'TIME_DURATION',
|
||||
], // 這個mode用到的參數
|
||||
showParameter: [
|
||||
'ADC_LEVEL_I_15',
|
||||
'ADC_LEVEL_V_IN_15',
|
||||
'CTRL_HIGH_Z_15',
|
||||
'SAMPLE_RATE',
|
||||
'Charge',
|
||||
'Const_Current_value',
|
||||
'VOLTSTOP_MAX',
|
||||
'VOLTSTOP_MIN',
|
||||
'TIME_DURATION',
|
||||
], // 有要秀給user看的參數
|
||||
headerParameter: () => [
|
||||
'Charge',
|
||||
'Const_Current_value',
|
||||
'SAMPLE_RATE',
|
||||
'VOLTSTOP_MAX',
|
||||
'VOLTSTOP_MIN',
|
||||
'CC_CP_SPEED',
|
||||
'ADC_LEVEL_I_15',
|
||||
'ADC_LEVEL_V_IN_15',
|
||||
'TIME_DURATION',
|
||||
], // 有要秀給user看的參數
|
||||
valScales: {
|
||||
linear: {
|
||||
func: (val) => {
|
||||
return val
|
||||
},
|
||||
},
|
||||
log: {
|
||||
func: (val) => {
|
||||
return Math.log10(Math.abs(val))
|
||||
},
|
||||
},
|
||||
},
|
||||
channels: {
|
||||
time: {
|
||||
name: 'Time',
|
||||
unit: {
|
||||
us: 1,
|
||||
ms: 1e3,
|
||||
s: 1e6,
|
||||
minute: 60 * 1e6,
|
||||
hour: 60 * 60 * 1e6,
|
||||
},
|
||||
defaultUnit: 'ms',
|
||||
downloadUnit: 'ms',
|
||||
},
|
||||
0: {
|
||||
name: 'I_in',
|
||||
unit: {
|
||||
nA: 1,
|
||||
uA: 1e3,
|
||||
mA: 1e6,
|
||||
},
|
||||
defaultUnit: 'mA',
|
||||
downloadUnit: 'mA',
|
||||
},
|
||||
1: {
|
||||
name: 'Potential',
|
||||
unit: {
|
||||
uV: 1,
|
||||
mV: 1e3,
|
||||
V: 1e6,
|
||||
},
|
||||
defaultUnit: 'mV',
|
||||
downloadUnit: 'mV',
|
||||
},
|
||||
2: {
|
||||
name: 'V_out',
|
||||
unit: {
|
||||
uV: 1,
|
||||
mV: 1e3,
|
||||
V: 1e6,
|
||||
},
|
||||
defaultUnit: 'mV',
|
||||
downloadUnit: 'mV',
|
||||
},
|
||||
3: {
|
||||
name: 'sum_cnt',
|
||||
unit: {
|
||||
uV: 1,
|
||||
mV: 1e3,
|
||||
V: 1e6,
|
||||
},
|
||||
defaultUnit: 'mV',
|
||||
downloadUnit: 'uV',
|
||||
},
|
||||
4: {
|
||||
name: 'sum_adc_delta_Iin',
|
||||
unit: {
|
||||
uV: 1,
|
||||
mV: 1e3,
|
||||
V: 1e6,
|
||||
},
|
||||
defaultUnit: 'mV',
|
||||
downloadUnit: 'uV',
|
||||
},
|
||||
5: {
|
||||
name: 'sum_adc_delta_Voutin',
|
||||
unit: {
|
||||
uV: 1,
|
||||
mV: 1e3,
|
||||
V: 1e6,
|
||||
},
|
||||
defaultUnit: 'mV',
|
||||
downloadUnit: 'uV',
|
||||
},
|
||||
6: {
|
||||
name: 'resis',
|
||||
unit: {
|
||||
uV: 1,
|
||||
mV: 1e3,
|
||||
V: 1e6,
|
||||
},
|
||||
defaultUnit: 'mV',
|
||||
downloadUnit: 'uV',
|
||||
},
|
||||
},
|
||||
charts: {
|
||||
default: [
|
||||
{
|
||||
name: 'Chronopotentiometry',
|
||||
description: '',
|
||||
subplot: [
|
||||
{
|
||||
x1: {
|
||||
type: 'time',
|
||||
valScale: 'linear',
|
||||
min: 'dataMin',
|
||||
max: 'dataMax',
|
||||
},
|
||||
y1: {
|
||||
type: 'value',
|
||||
valScale: 'linear',
|
||||
min: 'dataMin',
|
||||
max: 'dataMax',
|
||||
},
|
||||
data: [
|
||||
{
|
||||
legend: 'CP',
|
||||
x1: {
|
||||
channel: 'time',
|
||||
},
|
||||
y1: {
|
||||
channel: 1,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
export default {
|
||||
name: 'Constant Current',
|
||||
parameter: [
|
||||
'Charge',
|
||||
'Const_Current_value',
|
||||
'SAMPLE_RATE',
|
||||
'VOLTSTOP_MAX',
|
||||
'VOLTSTOP_MIN',
|
||||
'CC_CP_SPEED',
|
||||
'ADC_LEVEL_I_15',
|
||||
'ADC_LEVEL_V_IN_15',
|
||||
'TIME_DURATION',
|
||||
], // 這個mode用到的參數
|
||||
showParameter: [
|
||||
'ADC_LEVEL_I_15',
|
||||
'ADC_LEVEL_V_IN_15',
|
||||
'CTRL_HIGH_Z_15',
|
||||
'SAMPLE_RATE',
|
||||
'Charge',
|
||||
'Const_Current_value',
|
||||
'VOLTSTOP_MAX',
|
||||
'VOLTSTOP_MIN',
|
||||
'TIME_DURATION',
|
||||
], // 有要秀給user看的參數
|
||||
headerParameter: () => [
|
||||
'Charge',
|
||||
'Const_Current_value',
|
||||
'SAMPLE_RATE',
|
||||
'VOLTSTOP_MAX',
|
||||
'VOLTSTOP_MIN',
|
||||
'CC_CP_SPEED',
|
||||
'ADC_LEVEL_I_15',
|
||||
'ADC_LEVEL_V_IN_15',
|
||||
'TIME_DURATION',
|
||||
], // 有要秀給user看的參數
|
||||
valScales: {
|
||||
linear: {
|
||||
func: (val) => {
|
||||
return val
|
||||
},
|
||||
},
|
||||
log: {
|
||||
func: (val) => {
|
||||
return Math.log10(Math.abs(val))
|
||||
},
|
||||
},
|
||||
},
|
||||
channels: {
|
||||
time: {
|
||||
name: 'Time',
|
||||
unit: {
|
||||
us: 1,
|
||||
ms: 1e3,
|
||||
s: 1e6,
|
||||
minute: 60 * 1e6,
|
||||
hour: 60 * 60 * 1e6,
|
||||
},
|
||||
defaultUnit: 'ms',
|
||||
downloadUnit: 'ms',
|
||||
},
|
||||
0: {
|
||||
name: 'I_in',
|
||||
unit: {
|
||||
nA: 1,
|
||||
uA: 1e3,
|
||||
mA: 1e6,
|
||||
},
|
||||
defaultUnit: 'mA',
|
||||
downloadUnit: 'mA',
|
||||
},
|
||||
1: {
|
||||
name: 'V_in',
|
||||
unit: {
|
||||
uV: 1,
|
||||
mV: 1e3,
|
||||
V: 1e6,
|
||||
},
|
||||
defaultUnit: 'mV',
|
||||
downloadUnit: 'mV',
|
||||
},
|
||||
2: {
|
||||
name: 'V_out',
|
||||
unit: {
|
||||
uV: 1,
|
||||
mV: 1e3,
|
||||
V: 1e6,
|
||||
},
|
||||
defaultUnit: 'mV',
|
||||
downloadUnit: 'mV',
|
||||
},
|
||||
},
|
||||
charts: {
|
||||
default: [
|
||||
{
|
||||
name: 'Constant Current',
|
||||
description: '',
|
||||
subplot: [
|
||||
{
|
||||
x1: {
|
||||
type: 'time',
|
||||
valScale: 'linear',
|
||||
min: 'dataMin',
|
||||
max: 'dataMax',
|
||||
},
|
||||
y1: {
|
||||
type: 'value',
|
||||
valScale: 'linear',
|
||||
min: 'dataMin',
|
||||
max: 'dataMax',
|
||||
},
|
||||
data: [
|
||||
{
|
||||
legend: 'CC',
|
||||
x1: {
|
||||
channel: 'time',
|
||||
},
|
||||
y1: {
|
||||
channel: 1,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
export default {
|
||||
name: 'Cycle I-V',
|
||||
parameter: [
|
||||
'VOLT_ORIGIN',
|
||||
'VOLT_FINAL',
|
||||
'VOLT_STEP',
|
||||
'STEP_TIME',
|
||||
'SAMPLE_RATE',
|
||||
'CYCLE_NUMBER',
|
||||
'ADC_LEVEL_I_15',
|
||||
'ADC_LEVEL_V_IN_15',
|
||||
'CTRL_HIGH_Z_15',
|
||||
], // 這個mode用到的參數
|
||||
showParameter: [
|
||||
'ADC_LEVEL_I_15',
|
||||
'ADC_LEVEL_V_IN_15',
|
||||
'CTRL_HIGH_Z_15',
|
||||
'VOLT_ORIGIN',
|
||||
'VOLT_FINAL',
|
||||
'VOLT_STEP',
|
||||
'STEP_TIME',
|
||||
'SAMPLE_RATE',
|
||||
'CYCLE_NUMBER',
|
||||
], // 有要秀給user看的參數
|
||||
headerParameter: () => [
|
||||
'VOLT_ORIGIN',
|
||||
'VOLT_FINAL',
|
||||
'VOLT_STEP',
|
||||
'STEP_TIME',
|
||||
'SAMPLE_RATE',
|
||||
'CYCLE_NUMBER',
|
||||
'ADC_LEVEL_I_15',
|
||||
'ADC_LEVEL_V_IN_15',
|
||||
], // export header的參數
|
||||
valScales: {
|
||||
linear: {
|
||||
func: (val) => {
|
||||
return val
|
||||
},
|
||||
},
|
||||
log: {
|
||||
func: (val) => {
|
||||
return Math.log10(Math.abs(val))
|
||||
},
|
||||
},
|
||||
},
|
||||
channels: {
|
||||
time: {
|
||||
name: 'Time',
|
||||
unit: {
|
||||
us: 1,
|
||||
ms: 1e3,
|
||||
s: 1e6,
|
||||
minute: 60 * 1e6,
|
||||
hour: 60 * 60 * 1e6,
|
||||
},
|
||||
defaultUnit: 'ms',
|
||||
downloadUnit: 'ms',
|
||||
},
|
||||
0: {
|
||||
name: 'I_in',
|
||||
unit: {
|
||||
nA: 1,
|
||||
uA: 1e3,
|
||||
mA: 1e6,
|
||||
},
|
||||
defaultUnit: 'mA',
|
||||
downloadUnit: 'mA',
|
||||
},
|
||||
1: {
|
||||
name: 'V_out',
|
||||
unit: {
|
||||
uV: 1,
|
||||
mV: 1e3,
|
||||
V: 1e6,
|
||||
},
|
||||
defaultUnit: 'mV',
|
||||
downloadUnit: 'mV',
|
||||
},
|
||||
2: {
|
||||
name: 'V_in',
|
||||
unit: {
|
||||
uV: 1,
|
||||
mV: 1e3,
|
||||
V: 1e6,
|
||||
},
|
||||
defaultUnit: 'mV',
|
||||
downloadUnit: 'mV',
|
||||
},
|
||||
3: {
|
||||
name: 'Cycle number',
|
||||
unit: {
|
||||
cycle: 1,
|
||||
},
|
||||
defaultUnit: 'cycle',
|
||||
downloadUnit: null,
|
||||
},
|
||||
},
|
||||
charts: {
|
||||
default: [
|
||||
{
|
||||
name: 'Cycle I-V',
|
||||
description: '',
|
||||
subplot: [
|
||||
{
|
||||
x1: {
|
||||
type: 'value',
|
||||
valScale: 'linear',
|
||||
min: 'dataMin',
|
||||
max: 'dataMax',
|
||||
},
|
||||
y1: {
|
||||
type: 'value',
|
||||
valScale: 'linear',
|
||||
min: 'dataMin',
|
||||
max: 'dataMax',
|
||||
},
|
||||
data: [
|
||||
{
|
||||
legend: 'CycleIV',
|
||||
x1: {
|
||||
channel: 1,
|
||||
},
|
||||
y1: {
|
||||
channel: 0,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||