Commit 3b877240 authored by hucy's avatar hucy

feat:项目搭建

parents
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
/dist
/src-capacitor
/src-cordova
/.quasar
/node_modules
.eslintrc.js
/src-ssr
/quasar.config.*.temporary.compiled*
module.exports = {
// https://eslint.org/docs/user-guide/configuring#configuration-cascading-and-hierarchy
// This option interrupts the configuration hierarchy at this file
// Remove this if you have an higher level ESLint config file (it usually happens into a monorepos)
root: true,
// https://eslint.vuejs.org/user-guide/#how-to-use-a-custom-parser
// Must use parserOptions instead of "parser" to allow vue-eslint-parser to keep working
// `parser: 'vue-eslint-parser'` is already included with any 'plugin:vue/**' config and should be omitted
parserOptions: {
parser: require.resolve('@typescript-eslint/parser'),
extraFileExtensions: ['.vue'],
},
env: {
browser: true,
es2021: true,
node: true,
'vue/setup-compiler-macros': true,
},
// Rules order is important, please avoid shuffling them
extends: [
// Base ESLint recommended rules
// 'eslint:recommended',
// https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin#usage
// ESLint typescript rules
'plugin:@typescript-eslint/recommended',
// Uncomment any of the lines below to choose desired strictness,
// but leave only one uncommented!
// See https://eslint.vuejs.org/rules/#available-rules
'plugin:vue/vue3-essential', // Priority A: Essential (Error Prevention)
// 'plugin:vue/vue3-strongly-recommended', // Priority B: Strongly Recommended (Improving Readability)
// 'plugin:vue/vue3-recommended', // Priority C: Recommended (Minimizing Arbitrary Choices and Cognitive Overhead)
// https://github.com/prettier/eslint-config-prettier#installation
// usage with Prettier, provided by 'eslint-config-prettier'.
'prettier',
],
plugins: [
// required to apply rules which need type information
'@typescript-eslint',
// https://eslint.vuejs.org/user-guide/#why-doesn-t-it-work-on-vue-files
// required to lint *.vue files
'vue',
// https://github.com/typescript-eslint/typescript-eslint/issues/389#issuecomment-509292674
// Prettier has not been included as plugin to avoid performance impact
// add it as an extension for your IDE
],
globals: {
ga: 'readonly', // Google Analytics
cordova: 'readonly',
__statics: 'readonly',
__QUASAR_SSR__: 'readonly',
__QUASAR_SSR_SERVER__: 'readonly',
__QUASAR_SSR_CLIENT__: 'readonly',
__QUASAR_SSR_PWA__: 'readonly',
process: 'readonly',
Capacitor: 'readonly',
chrome: 'readonly',
},
// add your custom rules here
rules: {
'prefer-promise-reject-errors': 'off',
quotes: ['warn', 'single', { avoidEscape: true }],
// this rule, if on, would require explicit return type on the `render` function
'@typescript-eslint/explicit-function-return-type': 'off',
// in plain CommonJS modules, you can't use `import foo = require('foo')` to pass this rule, so it has to be disabled
'@typescript-eslint/no-var-requires': 'off',
// The core 'no-unused-vars' rules (in the eslint:recommended ruleset)
// does not work with type definitions
'no-unused-vars': 'off',
// allow debugger during development only
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'@typescript-eslint/no-explicit-any': ['off'],
// 防止this变量和局部变量混淆
'@typescript-eslint/no-this-alias': ['off'],
},
};
.DS_Store
.thumbs.db
node_modules
# Quasar core related directories
.quasar
/dist
/quasar.config.*.temporary.compiled*
# Cordova related directories and files
/src-cordova/node_modules
/src-cordova/platforms
/src-cordova/plugins
/src-cordova/www
# Capacitor related directories and files
/src-capacitor/www
/src-capacitor/node_modules
# BEX related directories and files
/src-bex/www
/src-bex/js/core
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
# local .env files
.env.local*
# pnpm-related options
shamefully-hoist=true
strict-peer-dependencies=false
{
"singleQuote": true,
"semi": true
}
{
"recommendations": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"editorconfig.editorconfig",
"vue.volar",
"wayou.vscode-todo-highlight"
],
"unwantedRecommendations": [
"octref.vetur",
"hookyqr.beautify",
"dbaeumer.jshint",
"ms-vscode.vscode-typescript-tslint-plugin"
]
}
\ No newline at end of file
{
"editor.bracketPairColorization.enabled": true,
"editor.guides.bracketPairs": true,
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": [
"source.fixAll.eslint"
],
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"vue"
],
"typescript.tsdk": "node_modules/typescript/lib"
}
\ No newline at end of file
# Quasar App (my-quasar-app02)
A Quasar Project
## Install the dependencies
```bash
yarn
# or
npm install
```
### Start the app in development mode (hot-code reloading, error reporting, etc.)
```bash
quasar dev
```
### Lint the files
```bash
yarn lint
# or
npm run lint
```
### Format the files
```bash
yarn format
# or
npm run format
```
### Build the app for production
```bash
quasar build
```
### Customize the configuration
See [Configuring quasar.config.js](https://v2.quasar.dev/quasar-cli-vite/quasar-config-js).
<!DOCTYPE html>
<html>
<head>
<title><%= productName %></title>
<meta charset="utf-8">
<meta name="description" content="<%= productDescription %>">
<meta name="format-detection" content="telephone=no">
<meta name="msapplication-tap-highlight" content="no">
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width<% if (ctx.mode.cordova || ctx.mode.capacitor) { %>, viewport-fit=cover<% } %>">
<link rel="icon" type="image/png" sizes="128x128" href="icons/favicon-128x128.png">
<link rel="icon" type="image/png" sizes="96x96" href="icons/favicon-96x96.png">
<link rel="icon" type="image/png" sizes="32x32" href="icons/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="icons/favicon-16x16.png">
<link rel="icon" type="image/ico" href="favicon.ico">
</head>
<body>
<!-- quasar:entry-point -->
</body>
</html>
{
"name": "my-quasar-app02",
"version": "0.0.1",
"description": "A Quasar Project",
"productName": "Quasar App",
"author": "hucy <cy.hu@topibd.com>",
"private": true,
"scripts": {
"lint": "eslint --ext .js,.ts,.vue ./",
"format": "prettier --write \"**/*.{js,ts,vue,scss,html,md,json}\" --ignore-path .gitignore",
"test": "echo \"No test specified\" && exit 0",
"dev": "quasar dev",
"build": "quasar build"
},
"dependencies": {
"@quasar/extras": "^1.16.4",
"ag-grid-community": "^30.0.6",
"ag-grid-enterprise": "^30.0.6",
"ag-grid-vue3": "^30.0.6",
"axios": "^1.2.1",
"lodash-es": "^4.17.21",
"pinia": "^2.0.11",
"quasar": "^2.6.0",
"vue": "^3.0.0",
"vue-i18n": "^9.2.2",
"vue-router": "^4.0.0"
},
"devDependencies": {
"@intlify/vite-plugin-vue-i18n": "^3.3.1",
"@quasar/app-vite": "^1.3.0",
"@types/lodash-es": "^4.17.8",
"@types/node": "^12.20.21",
"@typescript-eslint/eslint-plugin": "^5.10.0",
"@typescript-eslint/parser": "^5.10.0",
"autoprefixer": "^10.4.2",
"eslint": "^8.10.0",
"eslint-config-prettier": "^8.1.0",
"eslint-plugin-vue": "^9.0.0",
"prettier": "^2.5.1",
"typescript": "^4.5.4"
},
"engines": {
"node": "^18 || ^16 || ^14.19",
"npm": ">= 6.13.4",
"yarn": ">= 1.21.1"
}
}
/* eslint-disable */
// https://github.com/michael-ciniawsky/postcss-load-config
module.exports = {
plugins: [
// https://github.com/postcss/autoprefixer
require('autoprefixer')({
overrideBrowserslist: [
'last 4 Chrome versions',
'last 4 Firefox versions',
'last 4 Edge versions',
'last 4 Safari versions',
'last 4 Android versions',
'last 4 ChromeAndroid versions',
'last 4 FirefoxAndroid versions',
'last 4 iOS versions'
]
})
// https://github.com/elchininet/postcss-rtlcss
// If you want to support RTL css, then
// 1. yarn/npm install postcss-rtlcss
// 2. optionally set quasar.config.js > framework > lang to an RTL language
// 3. uncomment the following line:
// require('postcss-rtlcss')
]
}
/* eslint-env node */
/*
* This file runs in a Node context (it's NOT transpiled by Babel), so use only
* the ES6 features that are supported by your Node version. https://node.green/
*/
// Configuration for your app
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js
const { configure } = require('quasar/wrappers');
const path = require('path');
module.exports = configure(function (/* ctx */) {
return {
eslint: {
// fix: true,
// include: [],
// exclude: [],
// rawOptions: {},
warnings: true,
errors: true,
},
// https://v2.quasar.dev/quasar-cli-vite/prefetch-feature
// preFetch: true,
// app boot file (/src/boot)
// --> boot files are part of "main.js"
// https://v2.quasar.dev/quasar-cli-vite/boot-files
boot: ['i18n', 'axios'],
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#css
css: ['app.scss'],
// https://github.com/quasarframework/quasar/tree/dev/extras
extras: [
// 'ionicons-v4',
// 'mdi-v5',
'fontawesome-v6',
// 'eva-icons',
// 'themify',
// 'line-awesome',
// 'roboto-font-latin-ext', // this or either 'roboto-font', NEVER both!
'roboto-font', // optional, you are not bound to it
'material-icons', // optional, you are not bound to it
'bootstrap-icons',
],
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#build
build: {
target: {
browser: ['es2019', 'edge88', 'firefox78', 'chrome87', 'safari13.1'],
node: 'node16',
},
vueRouterMode: 'hash', // available values: 'hash', 'history'
// vueRouterBase,
// vueDevtools,
// vueOptionsAPI: false,
// rebuildCache: true, // rebuilds Vite/linter/etc cache on startup
// publicPath: '/',
// analyze: true,
// env: {},
// rawDefine: {}
// ignorePublicFolder: true,
// minify: false,
// polyfillModulePreload: true,
// distDir
// extendViteConf (viteConf) {},
// viteVuePluginOptions: {},
vitePlugins: [
[
'@intlify/vite-plugin-vue-i18n',
{
// if you want to use Vue I18n Legacy API, you need to set `compositionOnly: false`
// compositionOnly: false,
// if you want to use named tokens in your Vue I18n messages, such as 'Hello {name}',
// you need to set `runtimeOnly: false`
// runtimeOnly: false,
// you need to set i18n resource including paths !
include: path.resolve(__dirname, './src/i18n/**'),
},
],
],
},
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#devServer
devServer: {
// https: true
open: true, // opens browser window automatically
},
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#framework
framework: {
config: {},
// iconSet: 'material-icons', // Quasar icon set
lang: 'zh-CN', // Quasar language pack. default en-US
// For special cases outside of where the auto-import strategy can have an impact
// (like functional components as one of the examples),
// you can manually specify Quasar components/directives to be available everywhere:
//
// components: [],
// directives: [],
// Quasar plugins
plugins: ['Notify', 'Dialog'],
},
// animations: 'all', // --- includes all animations
// https://v2.quasar.dev/options/animations
animations: [],
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#sourcefiles
// sourceFiles: {
// rootComponent: 'src/App.vue',
// router: 'src/router/index',
// store: 'src/store/index',
// registerServiceWorker: 'src-pwa/register-service-worker',
// serviceWorker: 'src-pwa/custom-service-worker',
// pwaManifestFile: 'src-pwa/manifest.json',
// electronMain: 'src-electron/electron-main',
// electronPreload: 'src-electron/electron-preload'
// },
// https://v2.quasar.dev/quasar-cli-vite/developing-ssr/configuring-ssr
ssr: {
// ssrPwaHtmlFilename: 'offline.html', // do NOT use index.html as name!
// will mess up SSR
// extendSSRWebserverConf (esbuildConf) {},
// extendPackageJson (json) {},
pwa: false,
// manualStoreHydration: true,
// manualPostHydrationTrigger: true,
prodPort: 3000, // The default port that the production server should use
// (gets superseded if process.env.PORT is specified at runtime)
middlewares: [
'render', // keep this as last one
],
},
// https://v2.quasar.dev/quasar-cli-vite/developing-pwa/configuring-pwa
pwa: {
workboxMode: 'generateSW', // or 'injectManifest'
injectPwaMetaTags: true,
swFilename: 'sw.js',
manifestFilename: 'manifest.json',
useCredentialsForManifestTag: false,
// useFilenameHashes: true,
// extendGenerateSWOptions (cfg) {}
// extendInjectManifestOptions (cfg) {},
// extendManifestJson (json) {}
// extendPWACustomSWConf (esbuildConf) {}
},
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-cordova-apps/configuring-cordova
cordova: {
// noIosLegacyBuildFlag: true, // uncomment only if you know what you are doing
},
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-capacitor-apps/configuring-capacitor
capacitor: {
hideSplashscreen: true,
},
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-electron-apps/configuring-electron
electron: {
// extendElectronMainConf (esbuildConf)
// extendElectronPreloadConf (esbuildConf)
inspectPort: 5858,
bundler: 'packager', // 'packager' or 'builder'
packager: {
// https://github.com/electron-userland/electron-packager/blob/master/docs/api.md#options
// OS X / Mac App Store
// appBundleId: '',
// appCategoryType: '',
// osxSign: '',
// protocol: 'myapp://path',
// Windows only
// win32metadata: { ... }
},
builder: {
// https://www.electron.build/configuration/configuration
appId: 'my-quasar-app02',
},
},
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-browser-extensions/configuring-bex
bex: {
contentScripts: ['my-content-script'],
// extendBexScriptsConf (esbuildConf) {}
// extendBexManifestJson (json) {}
},
};
});
<template>
<router-view />
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import 'src/config/global-import';
export default defineComponent({
name: 'App',
});
</script>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 356 360">
<path
d="M43.4 303.4c0 3.8-2.3 6.3-7.1 6.3h-15v-22h14.4c4.3 0 6.2 2.2 6.2 5.2 0 2.6-1.5 4.4-3.4 5 2.8.4 4.9 2.5 4.9 5.5zm-8-13H24.1v6.9H35c2.1 0 4-1.3 4-3.8 0-2.2-1.3-3.1-3.7-3.1zm5.1 12.6c0-2.3-1.8-3.7-4-3.7H24.2v7.7h11.7c3.4 0 4.6-1.8 4.6-4zm36.3 4v2.7H56v-22h20.6v2.7H58.9v6.8h14.6v2.3H58.9v7.5h17.9zm23-5.8v8.5H97v-8.5l-11-13.4h3.4l8.9 11 8.8-11h3.4l-10.8 13.4zm19.1-1.8V298c0-7.9 5.2-10.7 12.7-10.7 7.5 0 13 2.8 13 10.7v1.4c0 7.9-5.5 10.8-13 10.8s-12.7-3-12.7-10.8zm22.7 0V298c0-5.7-3.9-8-10-8-6 0-9.8 2.3-9.8 8v1.4c0 5.8 3.8 8.1 9.8 8.1 6 0 10-2.3 10-8.1zm37.2-11.6v21.9h-2.9l-15.8-17.9v17.9h-2.8v-22h3l15.6 18v-18h2.9zm37.9 10.2v1.3c0 7.8-5.2 10.4-12.4 10.4H193v-22h11.2c7.2 0 12.4 2.8 12.4 10.3zm-3 0c0-5.3-3.3-7.6-9.4-7.6h-8.4V307h8.4c6 0 9.5-2 9.5-7.7V298zm50.8-7.6h-9.7v19.3h-3v-19.3h-9.7v-2.6h22.4v2.6zm34.4-2.6v21.9h-3v-10.1h-16.8v10h-2.8v-21.8h2.8v9.2H296v-9.2h2.9zm34.9 19.2v2.7h-20.7v-22h20.6v2.7H316v6.8h14.5v2.3H316v7.5h17.8zM24 340.2v7.3h13.9v2.4h-14v9.6H21v-22h20v2.7H24zm41.5 11.4h-9.8v7.9H53v-22h13.3c5.1 0 8 1.9 8 6.8 0 3.7-2 6.3-5.6 7l6 8.2h-3.3l-5.8-8zm-9.8-2.6H66c3.1 0 5.3-1.5 5.3-4.7 0-3.3-2.2-4.1-5.3-4.1H55.7v8.8zm47.9 6.2H89l-2 4.3h-3.2l10.7-22.2H98l10.7 22.2h-3.2l-2-4.3zm-1-2.3l-6.3-13-6 13h12.2zm46.3-15.3v21.9H146v-17.2L135.7 358h-2.1l-10.2-15.6v17h-2.8v-21.8h3l11 16.9 11.3-17h3zm35 19.3v2.6h-20.7v-22h20.6v2.7H166v6.8h14.5v2.3H166v7.6h17.8zm47-19.3l-8.3 22h-3l-7.1-18.6-7 18.6h-3l-8.2-22h3.3L204 356l6.8-18.5h3.4L221 356l6.6-18.5h3.3zm10 11.6v-1.4c0-7.8 5.2-10.7 12.7-10.7 7.6 0 13 2.9 13 10.7v1.4c0 7.9-5.4 10.8-13 10.8-7.5 0-12.7-3-12.7-10.8zm22.8 0v-1.4c0-5.7-4-8-10-8s-9.9 2.3-9.9 8v1.4c0 5.8 3.8 8.2 9.8 8.2 6.1 0 10-2.4 10-8.2zm28.3 2.4h-9.8v7.9h-2.8v-22h13.2c5.2 0 8 1.9 8 6.8 0 3.7-2 6.3-5.6 7l6 8.2h-3.3l-5.8-8zm-9.8-2.6h10.2c3 0 5.2-1.5 5.2-4.7 0-3.3-2.1-4.1-5.2-4.1h-10.2v8.8zm40.3-1.5l-6.8 5.6v6.4h-2.9v-22h2.9v12.3l15.2-12.2h3.7l-9.9 8.1 10.3 13.8h-3.6l-8.9-12z" />
<path fill="#050A14"
d="M188.4 71.7a10.4 10.4 0 01-20.8 0 10.4 10.4 0 1120.8 0zM224.2 45c-2.2-3.9-5-7.5-8.2-10.7l-12 7c-3.7-3.2-8-5.7-12.6-7.3a49.4 49.4 0 00-9.7 13.9 59 59 0 0140.1 14l7.6-4.4a57 57 0 00-5.2-12.5zM178 125.1c4.5 0 9-.6 13.4-1.7v-14a40 40 0 0012.5-7.2 47.7 47.7 0 00-7.1-15.3 59 59 0 01-32.2 27.7v8.7c4.4 1.2 8.9 1.8 13.4 1.8zM131.8 45c-2.3 4-4 8.1-5.2 12.5l12 7a40 40 0 000 14.4c5.7 1.5 11.3 2 16.9 1.5a59 59 0 01-8-41.7l-7.5-4.3c-3.2 3.2-6 6.7-8.2 10.6z" />
<path fill="#00B4FF"
d="M224.2 98.4c2.3-3.9 4-8 5.2-12.4l-12-7a40 40 0 000-14.5c-5.7-1.5-11.3-2-16.9-1.5a59 59 0 018 41.7l7.5 4.4c3.2-3.2 6-6.8 8.2-10.7zm-92.4 0c2.2 4 5 7.5 8.2 10.7l12-7a40 40 0 0012.6 7.3c4-4.1 7.3-8.8 9.7-13.8a59 59 0 01-40-14l-7.7 4.4c1.2 4.3 3 8.5 5.2 12.4zm46.2-80c-4.5 0-9 .5-13.4 1.7V34a40 40 0 00-12.5 7.2c1.5 5.7 4 10.8 7.1 15.4a59 59 0 0132.2-27.7V20a53.3 53.3 0 00-13.4-1.8z" />
<path fill="#00B4FF"
d="M178 9.2a62.6 62.6 0 11-.1 125.2A62.6 62.6 0 01178 9.2m0-9.2a71.7 71.7 0 100 143.5A71.7 71.7 0 00178 0z" />
<path fill="#050A14"
d="M96.6 212v4.3c-9.2-.8-15.4-5.8-15.4-17.8V180h4.6v18.4c0 8.6 4 12.6 10.8 13.5zm16-31.9v18.4c0 8.9-4.3 12.8-10.9 13.5v4.4c9.2-.7 15.5-5.6 15.5-18v-18.3h-4.7zM62.2 199v-2.2c0-12.7-8.8-17.4-21-17.4-12.1 0-20.7 4.7-20.7 17.4v2.2c0 12.8 8.6 17.6 20.7 17.6 1.5 0 3-.1 4.4-.3l11.8 6.2 2-3.3-8.2-4-6.4-3.1a32 32 0 01-3.6.2c-9.8 0-16-3.9-16-13.3v-2.2c0-9.3 6.2-13.1 16-13.1 9.9 0 16.3 3.8 16.3 13.1v2.2c0 5.3-2.1 8.7-5.6 10.8l4.8 2.4c3.4-2.8 5.5-7 5.5-13.2zM168 215.6h5.1L156 179.7h-4.8l17 36zM143 205l7.4-15.7-2.4-5-15.1 31.4h5.1l3.3-7h18.3l-1.8-3.7H143zm133.7 10.7h5.2l-17.3-35.9h-4.8l17 36zm-25-10.7l7.4-15.7-2.4-5-15.1 31.4h5.1l3.3-7h18.3l-1.7-3.7h-14.8zm73.8-2.5c6-1.2 9-5.4 9-11.4 0-8-4.5-10.9-12.9-10.9h-21.4v35.5h4.6v-31.3h16.5c5 0 8.5 1.4 8.5 6.7 0 5.2-3.5 7.7-8.5 7.7h-11.4v4.1h10.7l9.3 12.8h5.5l-9.9-13.2zm-117.4 9.9c-9.7 0-14.7-2.5-18.6-6.3l-2.2 3.8c5.1 5 11 6.7 21 6.7 1.6 0 3.1-.1 4.6-.3l-1.9-4h-3zm18.4-7c0-6.4-4.7-8.6-13.8-9.4l-10.1-1c-6.7-.7-9.3-2.2-9.3-5.6 0-2.5 1.4-4 4.6-5l-1.8-3.8c-4.7 1.4-7.5 4.2-7.5 8.9 0 5.2 3.4 8.7 13 9.6l11.3 1.2c6.4.6 8.9 2 8.9 5.4 0 2.7-2.1 4.7-6 5.8l1.8 3.9c5.3-1.6 8.9-4.7 8.9-10zm-20.3-21.9c7.9 0 13.3 1.8 18.1 5.7l1.8-3.9a30 30 0 00-19.6-5.9c-2 0-4 .1-5.7.3l1.9 4 3.5-.2z" />
<path fill="#00B4FF"
d="M.5 251.9c29.6-.5 59.2-.8 88.8-1l88.7-.3 88.7.3 44.4.4 44.4.6-44.4.6-44.4.4-88.7.3-88.7-.3a7981 7981 0 01-88.8-1z" />
<path fill="none" d="M-565.2 324H-252v15.8h-313.2z" />
</svg>
\ No newline at end of file
import { boot } from 'quasar/wrappers';
import axios, { AxiosInstance } from 'axios';
declare module '@vue/runtime-core' {
interface ComponentCustomProperties {
$axios: AxiosInstance;
$api: AxiosInstance;
}
}
// Be careful when using SSR for cross-request state pollution
// due to creating a Singleton instance here;
// If any client changes this (global) instance, it might be a
// good idea to move this instance creation inside of the
// "export default () => {}" function below (which runs individually
// for each client)
const api = axios.create({ baseURL: 'https://api.example.com' });
export default boot(({ app }) => {
// for use inside Vue files (Options API) through this.$axios and this.$api
app.config.globalProperties.$axios = axios;
// ^ ^ ^ this will allow you to use this.$axios (for Vue Options API form)
// so you won't necessarily have to import axios in each vue file
app.config.globalProperties.$api = api;
// ^ ^ ^ this will allow you to use this.$api (for Vue Options API form)
// so you can easily perform requests against your app's API
});
export { api };
import { boot } from 'quasar/wrappers';
import { createI18n } from 'vue-i18n';
import messages from 'src/i18n';
export type MessageLanguages = keyof typeof messages;
// Type-define 'en-US' as the master schema for the resource
export type MessageSchema = typeof messages['en-US'];
// See https://vue-i18n.intlify.dev/guide/advanced/typescript.html#global-resource-schema-type-definition
/* eslint-disable @typescript-eslint/no-empty-interface */
declare module 'vue-i18n' {
// define the locale messages schema
export interface DefineLocaleMessage extends MessageSchema {}
// define the datetime format schema
export interface DefineDateTimeFormat {}
// define the number format schema
export interface DefineNumberFormat {}
}
/* eslint-enable @typescript-eslint/no-empty-interface */
export default boot(({ app }) => {
const i18n = createI18n({
locale: 'en-US',
legacy: false,
messages,
});
// Set i18n instance on app
app.use(i18n);
});
/**
* 定义公共常数
*/
/**
* The prefix of web storage value, to ideintify the value data type.
* e.g __hcytest_object|{"key": "value"}
*/
// export const STORAGE_VALUE_PREFIX = '__hcytest_';
/**
* The key which save user last selected `Language` or `Local` to
* `SessionStorage` or `LocalStorage`
*/
export const LANG_STORAGE_KEY = 'LANG';
export * from './use-message';
export * from './use-webstorage';
export * from './use-pagestore';
import appConfig from 'src/config/app';
// Typescript高级类型Record
// https://zhuanlan.zhihu.com/p/356662885
type IWebStorageReturnType =
| Date
| RegExp
| number
| boolean
| string
| any[]
| Record<string, any>
| null;
interface IWebStorage {
setItem: (key: string, value: any) => void;
getItem: (key: string) => IWebStorageReturnType;
removeItem: (key: string) => void;
}
const prefixKey = appConfig.storagePrefix;
function getStorage(type: 'local' | 'session'): IWebStorage {
const webStorage =
type === 'local' ? window.localStorage : window.sessionStorage;
return {
setItem: (key: string, value: any) => {
webStorage.setItem(prefixKey + key, value);
},
getItem: (key: string) => {
const item = webStorage.getItem(prefixKey + key);
return item || null;
},
removeItem: (key: string) => {
webStorage.removeItem(prefixKey + key);
},
};
}
export type { IWebStorage, IWebStorageReturnType };
export { getStorage };
import { Notify } from 'quasar';
import ICON from 'src/config/icons';
const creatNotify = function (type: string, icon: string, msg: string) {
Notify.create({
position: 'top',
type,
icon,
textColor: 'white',
message: msg,
});
};
type IMessage = {
success: (msg: string) => void;
error: (msg: string) => void;
warn: (msg: string) => void;
info: (msg: string) => void;
};
const message: IMessage = {
success: (msg: string) => {
creatNotify('positive', ICON.success, msg);
},
error: (msg: string) => {
creatNotify('negative', ICON.error, msg);
},
warn: (msg: string) => {
creatNotify('warning', ICON.warn, msg);
},
info: (msg: string) => {
creatNotify('info', ICON.info, msg);
},
};
function useMessage() {
return message;
}
export { useMessage };
import { defineStore } from 'pinia';
import { useSessionStorage } from './index';
import { some, delArrObj, findIndex } from '../utils';
const PAGE_STORE_KEY = 'MAIN_PAGES';
const session = useSessionStorage();
interface RouterType {
fullPath: string;
path: string;
hash?: string;
href?: string;
matched?: any[];
meta?: any;
name?: string;
params?: any;
query?: any;
redirectedFrom?: any;
[proppName: string]: any;
}
const usePageStore = defineStore(PAGE_STORE_KEY, {
state: () => {
const routersItem = JSON.parse(session.getItem(PAGE_STORE_KEY) as string);
return {
activeRouter: (routersItem?.activeRouter as RouterType) || null,
tabRouterList: (routersItem?.tabRouterList as RouterType[]) || [],
allPageKeys: routersItem?.allPageKeys || [],
};
},
getters: {},
actions: {
addRoute(route: RouterType) {
const item: RouterType = {
fullPath: route.fullPath,
path: route.path,
name: route.name,
meta: route.meta,
params: route.params,
query: route.query,
};
this.activeRouter = item;
const flag = some(this.tabRouterList, (item: RouterType) => {
return item.fullPath === route.fullPath;
});
if (!flag) {
this.tabRouterList.push(item);
if (item.meta.keepalive) {
this.allPageKeys.push(route.name);
}
}
this.saveToSession();
},
removePage(params: any) {
const path = params.link;
const index = findIndex(this.tabRouterList, ['path', path]);
if (path === this.activeRouter.path) {
if (index > 0) {
this.activeRouter = this.tabRouterList[index - 1];
} else {
this.activeRouter = this.tabRouterList[index + 1];
}
}
if (params.keepalive) {
const pageKeyIndex = findIndex(this.allPageKeys, function (o) {
return o === params.name;
});
if (pageKeyIndex !== -1) {
this.allPageKeys.splice(pageKeyIndex, 1);
}
}
delArrObj(this.tabRouterList, { path: path });
this.saveToSession();
},
removeNotCurrentPage(params: any) {
const path = params.link;
if (params.keepalive) {
this.allPageKeys = [params.name];
} else {
this.allPageKeys = [];
}
this.tabRouterList = this.tabRouterList.filter((item) => {
return item.path === path;
});
this.activeRouter = this.tabRouterList[0];
this.saveToSession();
},
removeAllPage() {
this.activeRouter = {} as RouterType;
this.allPageKeys = [];
this.tabRouterList = [];
this.saveToSession();
},
saveToSession() {
session.setItem(PAGE_STORE_KEY, JSON.stringify(this.$state));
},
},
});
export type { RouterType };
export { usePageStore, PAGE_STORE_KEY };
import { getStorage } from './private/webstorage';
import type { IWebStorage, IWebStorageReturnType } from './private/webstorage';
const useLocalStorage = (): IWebStorage => {
return getStorage('local');
};
const useSessionStorage = (): IWebStorage => {
return getStorage('session');
};
export type { IWebStorage, IWebStorageReturnType };
export { useLocalStorage, useSessionStorage };
interface CallbackFun {
<T>(data: T): T;
}
// Color name for component from the Quasar Color Palette
interface CellRenderButtonsType {
hide?: boolean;
label?: string;
icon?: string;
color?: string;
textColor?: string;
tooltip?: string;
loading?: boolean;
disable?: boolean;
dense?: boolean;
noCaps?: boolean;
round?: boolean;
flat?: boolean;
outline?: boolean;
unelevated?: boolean;
rounded?: boolean;
push?: boolean;
square?: boolean;
glossy?: boolean;
fab?: boolean;
callback: CallbackFun;
}
interface MoreButtonsItemType {
hide?: boolean;
label: string;
icon?: string;
color?: string;
callback: CallbackFun;
}
type OmitMoreButtonsType = Omit<CellRenderButtonsType, 'callback'>;
interface MoreButtonsType extends OmitMoreButtonsType {
children?: Array<MoreButtonsItemType>;
}
interface ButtonCellRenderType {
buttons: Array<CellRenderButtonsType>;
moreButtons?: MoreButtonsType;
[proppName: string]: any;
}
interface ColumnDefsType {
field?: string;
headerName?: string;
width?: number;
minWidth?: number;
minHeight?: number;
maxWidth?: number;
maxHeight?: number;
pinned?: string; // 固定在左/右 left right
editable?: boolean; // 可以编辑单元格
flex?: number;
cellEditor?: any;
cellRenderer?: any;
cellRendererParams?: any;
[proppName: string]: any;
}
export type {
ColumnDefsType,
ButtonCellRenderType,
CellRenderButtonsType,
MoreButtonsType,
};
interface DialogLayoutProps {
title?: any;
width?: number;
maxWidth?: number;
minWidth?: number;
height?: number;
maxHeight?: number;
minHeight?: number;
}
export type { DialogLayoutProps };
export interface AnyType {
[proppName: string]: any;
}
export * from './dialog-layout-props';
export * from './ag-grid';
import { AnyType } from 'src/common/types';
function func(data: Array<any>, key: string, value: any) {
return data.filter((item: any) => {
return item[key] == value;
});
}
/**
* 数组对象删除 改变原数组
* @param objArr - 原始数组
* @param valueData - 删除的对象条件{age:18,num:1}
**/
export const delArrObj = function (objArr: Array<any>, valueData: AnyType) {
for (let i = 0; i < objArr.length; i++) {
objArr[i].item_del_id = i;
}
const valueDataArr = [] as any[];
for (const key in valueData) {
valueDataArr.push({
key: key,
value: valueData[key],
});
}
let resArr = [] as any;
for (let i = 0; i < valueDataArr.length; i++) {
if (i == 0) {
const res = func(objArr, valueDataArr[i].key, valueDataArr[i].value);
resArr = res;
} else {
const res = func(resArr, valueDataArr[i].key, valueDataArr[i].value);
resArr = res;
}
}
for (let i = 0; i < objArr.length; i++) {
for (let j = 0; j < resArr.length; j++) {
if (objArr[i].item_del_id == resArr[j].item_del_id) {
objArr.splice(i, 1);
}
}
}
for (let i = 0; i < objArr.length; i++) {
delete objArr[i].item_del_id;
}
};
import { isEmpty } from './isEmpty';
/**
* 删除对象中为空的属性值 返回一个新对象
* @param data - 原始对象
**/
export const delEmptyObjkey = function (data: any) {
const obj = {} as any;
for (const key in data) {
if (!isEmpty(data[key])) {
obj[key] = data[key];
}
}
return obj;
};
import { AnyType } from 'src/common/types';
function func(data: Array<any>, key: string, value: any) {
return data.filter((item: any) => {
return item[key] == value;
});
}
/**
* 数组对象查找 返回一个数组,返回所有符合条件的数据
* @param objArr - 原始数组
* @param valueData - 查找的对象条件{age:18,num:1}
**/
export const findArrObjs = function (objArr: Array<any>, valueData: AnyType) {
const valueDataArr = [] as any[];
for (const key in valueData) {
valueDataArr.push({
key: key,
value: valueData[key],
});
}
let resArr = [] as any;
for (let i = 0; i < valueDataArr.length; i++) {
if (i == 0) {
const res = func(objArr, valueDataArr[i].key, valueDataArr[i].value);
resArr = res;
} else {
const res = func(resArr, valueDataArr[i].key, valueDataArr[i].value);
resArr = res;
}
}
return resArr;
};
/**
* 计算从x1y1到x2y2的直线,与水平线形成的夹角
* 计算规则为顺时针从左侧0°到与该直线形成的夹角
* @param {Object} x1
* @param {Object} y1
* @param {Object} x2
* @param {Object} y2
* @param {Boolean} toAngle 是否转换为角度值,默认false
*/
export const getAngle = function (
x1: number,
y1: number,
x2: number,
y2: number,
toAngle = false
) {
const x = x1 - x2;
const y = y1 - y2;
if (!x && !y) {
return 0;
}
// 弧度 radian = 角度 * Math.PI / 180
// 角度 angle = 弧度 * 180 / Math.PI
let res;
// 角度
const angle = (180 + (Math.atan2(-y, -x) * 180) / Math.PI + 360) % 360;
res = 360 - angle;
if (!toAngle) {
res = (res * Math.PI) / 180;
}
return res;
};
type Types =
| 'Number'
| 'String'
| 'Boolean'
| 'Null'
| 'Undefined'
| 'Array'
| 'Object';
export const getType = function (value: any): Types {
const typeofs = Object.prototype.toString.call(value);
switch (typeofs) {
case '[object Number]':
return 'Number'; // NaN Infinity 也是Number类型
case '[object String]':
return 'String';
case '[object Boolean]':
return 'Boolean';
case '[object Null]':
return 'Null';
case '[object Undefined]':
return 'Undefined';
case '[object Array]':
return 'Array';
case '[object Object]':
return 'Object';
default:
return typeofs as any;
}
};
export * from './del-arr-obj';
export * from './find-arr-objs';
export * from './no-repeat-obj-in-arr';
export * from './obj-del';
export * from './is-obj-equal';
export * from './del-empty-objkey';
export * from './json-str';
export * from './isEmpty';
export * from './maths';
export * from './myCloneDeep';
export * from './get-type';
export * from './is';
export * from './get-angle';
export * from './scale-polygon';
export {
cloneDeep,
orderBy,
find,
findIndex,
size,
every,
some,
maxBy,
minBy,
sumBy,
random,
replace,
repeat,
uniqueId,
omit,
} from 'lodash-es';
/**
* 判断两个对象是否相等
**/
export const isObjEqual = function (obj1: any, obj2: any) {
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
if (keys1.length === keys2.length) {
const sortKeys1 = keys1.sort();
const sortKeys2 = keys2.sort();
const sortStr1 = JSON.stringify(sortKeys1);
const sortStr2 = JSON.stringify(sortKeys2);
if (sortStr1 === sortStr2) {
let flag = true;
for (const key1 in obj1) {
if (obj1[key1] !== obj2[key1]) {
flag = false;
break;
}
}
return flag;
} else {
return false;
}
} else {
return false;
}
};
import { getType } from './get-type';
const typeCheck = function (type: string, val: any): boolean {
if (getType(val) === type) {
return true;
} else {
return false;
}
};
type TypeChecking = {
number: (val: any) => boolean;
string: (val: any) => boolean;
array: (val: any) => boolean;
object: (val: any) => boolean;
};
export const is: TypeChecking = {
number: (val: any) => {
return Number.isFinite(val);
},
string: (val: any) => {
return typeCheck('String', val);
},
array: (val: any) => {
return typeCheck('Array', val);
},
object: (val: any) => {
return typeCheck('Object', val);
},
};
/**
* 是否为空
* @param {any} data
* @return {Boolean}
*/
export const isEmpty = function (data: any): boolean {
if (data === '' || data === null || data === undefined) {
return true;
}
// [] {} 0 false/true
else {
const typeofs = Object.prototype.toString.call(data);
// 数组
if (typeofs === '[object Array]') {
if (data.length > 0) {
return false;
} else {
return true;
}
}
// 对象
else if (typeofs === '[object Object]') {
if (Object.keys(data).length > 0) {
return false;
} else {
return true;
}
} else {
// 不为空
return false;
}
}
};
export const jsonStr = function (json_data: any) {
let cache = [] as any;
const json_str = JSON.stringify(json_data, function (key, value) {
if (typeof value === 'object' && value !== null) {
if (cache.indexOf(value) !== -1) {
return;
}
cache.push(value);
}
return value;
});
cache = null; //释放cache
return json_str;
};
/**
* 返回了一个在指定值之间的随机整数
* @param min 这个值不小于 min (如果 min 不是整数,则不小于 min 的向上取整数)
* @param max 这个值小于(不等于)max
* @returns [min ,max )
*/
export const getRandomInt = function (min: number, max: number) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min; // 不含最大值,含最小值
};
/**
* 对象深拷贝
* @param data - 要拷贝的对象
**/
export const objCloneDeep = function (data: any) {
const newObj: any = {};
for (const key in data) {
const item = data[key];
const typeofs = Object.prototype.toString.call(item);
// console.log(typeofs, item);
// 如果是对象
if (typeofs === '[object Object]') {
newObj[key] = objCloneDeep(item);
}
// 如果是数组
else if (typeofs === '[object Array]') {
newObj[key] = arrCloneDeep(item);
} else {
newObj[key] = item;
}
}
return newObj;
};
/**
* 数组深拷贝
* @param data - 要拷贝的数组
**/
export const arrCloneDeep = function (data: any[]) {
const newArr: any = [];
for (const item of data) {
const typeofs = Object.prototype.toString.call(item);
// 如果是对象
if (typeofs === '[object Object]') {
newArr.push(objCloneDeep(item));
}
// 如果是数组
else if (typeofs === '[object Array]') {
newArr.push(arrCloneDeep(item));
} else {
newArr.push(item);
}
}
return newArr;
};
/**
* 深拷贝
* @param data - 要拷贝的data
**/
export const myCloneDeep = function (data: any) {
const typeofs = Object.prototype.toString.call(data);
// 如果是对象
if (typeofs === '[object Object]') {
return objCloneDeep(data);
}
// 如果是数组
else if (typeofs === '[object Array]') {
return arrCloneDeep(data);
} else {
return data;
}
};
function deduplicatObj(arr: Array<any>) {
const cloneArr = JSON.parse(JSON.stringify(arr));
const res = cloneArr.map((item: any) => {
const types = Object.prototype.toString.call(item);
if (types === '[object Array]') {
return handleArr(item);
} else if (types === '[object Object]') {
return handleObj(item);
} else {
return item;
}
});
return res;
}
function handleArr(data: Array<any>) {
return JSON.parse(JSON.stringify(deduplicatObj(data))).sort();
}
function handleObj(data: any) {
for (const key in data) {
if (Object.prototype.hasOwnProperty.call(data, key)) {
const element = data[key];
const types = Object.prototype.toString.call(element);
if (types === '[object Array]') {
data[key] = handleArr(element);
} else if (types === '[object Object]') {
data[key] = handleObj(element);
} else {
//
}
}
}
// 把对象按照默认顺序排序
const keyslist = Object.keys(data).sort();
const myobj = {} as any;
keyslist.map((item: string) => {
myobj[item] = data[item];
});
return myobj;
}
/**
* 数组对象去重 返回一个新数组
* @param data - 原始数组
**/
export const noRepeatObjInArr = function (data: Array<any>) {
const res = deduplicatObj(data);
const mySet: Set<string> = new Set();
res.map((item: any) => {
const JSONstr = JSON.stringify(item);
mySet.add('JSON_' + JSONstr);
});
const arr = [] as any[];
for (const item of mySet) {
const itemArr = item.split('JSON_', 2);
arr.push(JSON.parse(itemArr[1]));
}
return arr;
};
/**
* 删除对象的某个属性 返回一个删除后的新对象
* @param data - 原始对象
* @param key - 要删除的对象的属性的键名 'name' || ['name','age']
**/
export const objDel = function (data: any, key: string | Array<any>) {
const _data = JSON.parse(JSON.stringify(data));
if (typeof key === 'string') {
_data[key] = undefined;
} else if (Array.isArray(key)) {
for (const item of key) {
_data[item] = undefined;
}
}
const obj = {} as any;
for (const key in _data) {
if (_data[key]) {
obj[key] = _data[key];
}
}
return obj;
};
interface Point {
x: number;
y: number;
[proppName: string]: any;
}
/**
* 缩放多边形坐标
* https://blog.csdn.net/sun_and_breeze/article/details/107517088
* @decoration 需配合顺时针判断方法一起使用
* @param {Point[]} points 点坐标数组 [{x:0,y:0}...]
* @param {number} extra 外延大小。为正: 向外扩; 为负: 向内缩
* @return {Point[]} 扩展或缩小后的多边形点坐标数组
*/
export function scalePolygon(points: Point[], extra: number) {
if (!Array.isArray(points) || points.length < 3) {
console.error('多边形坐标集合不能少于3个');
return;
}
const ps = points;
// 通过顺时针判断取正值还是负值
const extra0 = isClockwise(ps) ? -extra : extra;
const norm = (x: number, y: number) => Math.sqrt(x * x + y * y);
const len = ps.length;
const polygon = [];
for (let i = 0; i < len; i++) {
const point = ps[i];
const point1 = ps[i === 0 ? len - 1 : i - 1];
const point2 = ps[i === len - 1 ? 0 : i + 1];
// 向量PP1
const vectorX1 = point1.x - point.x; // 向量PP1 横坐标
const vectorY1 = point1.y - point.y; // 向量PP1 纵坐标
const n1 = norm(vectorX1, vectorY1); // 向量的平方根 为了对向量PP1做单位化
let vectorUnitX1 = vectorX1 / n1; // 向量单位化 横坐标
let vectorUnitY1 = vectorY1 / n1; // 向量单位化 纵坐标
// 向量PP2
const vectorX2 = point2.x - point.x; // 向量PP2 横坐标
const vectorY2 = point2.y - point.y; // 向量PP2 纵坐标
const n2 = norm(vectorX2, vectorY2); // 向量的平方根 为了对向量PP1做单位化
let vectorUnitX2 = vectorX2 / n2; // 向量单位化 横坐标
let vectorUnitY2 = vectorY2 / n2; // 向量单位化 纵坐标
// PQ距离
const vectorLen =
-extra0 /
Math.sqrt(
(1 - (vectorUnitX1 * vectorUnitX2 + vectorUnitY1 * vectorUnitY2)) / 2
);
// 根据向量的叉乘积来判断角是凹角还是凸角
if (vectorX1 * vectorY2 + -1 * vectorY1 * vectorX2 < 0) {
vectorUnitX2 *= -1;
vectorUnitY2 *= -1;
vectorUnitX1 *= -1;
vectorUnitY1 *= -1;
}
// PQ的方向
const vectorX = vectorUnitX1 + vectorUnitX2;
const vectorY = vectorUnitY1 + vectorUnitY2;
const n = vectorLen / norm(vectorX, vectorY);
const vectorUnitX = vectorX * n;
const vectorUnitY = vectorY * n;
const polygonX = vectorUnitX + point.x;
const polygonY = vectorUnitY + point.y;
polygon[i] = { x: polygonX, y: polygonY };
}
return polygon;
}
/**
* 判断坐标数组是否顺时针(默认为false)
* @param {Point[]} points 点坐标数组 [{x:0,y:0}...]
* @returns {boolean} 是否顺时针
*/
export function isClockwise(points: Point[]) {
// 三个点可以判断矢量是顺时针旋转还是逆时针旋转的,但由于可能存在凹边,所以并不是任意三点都可以正确反映多边形的走向
// 因此需要取多边形中绝对是凸边的点来判断,
// 多边形中的极值点(x最大或x最小或y最大或y最小)它与相邻两点构成的边必然是凸边,因此我们先取出多边形中的极值点,再由极值点和其前后两点去判断矢量的走向,从而判断出多边形的走向。
if (!Array.isArray(points) || points.length < 3) {
console.error('多边形坐标集合不能少于3个');
return false;
}
let coords = JSON.parse(JSON.stringify(points));
if (coords[0] === coords[coords.length - 1]) {
coords = coords.slice(0, coords.length - 1);
}
coords = coords.reverse();
let maxXIndex = 0;
let maxX = parseFloat(coords[maxXIndex].x);
let c1;
let c2;
let c3;
for (let i = 0; i < coords.length; i++) {
if (parseFloat(coords[i].x) > maxX) {
maxX = parseFloat(coords[i].x);
maxXIndex = i;
}
}
if (maxXIndex === 0) {
c1 = coords[coords.length - 1];
c2 = coords[maxXIndex];
c3 = coords[maxXIndex + 1];
} else if (maxXIndex === coords.length - 1) {
c1 = coords[maxXIndex - 1];
c2 = coords[maxXIndex];
c3 = coords[0];
} else {
c1 = coords[maxXIndex - 1];
c2 = coords[maxXIndex];
c3 = coords[maxXIndex + 1];
}
const x1 = parseFloat(c1.x);
const y1 = parseFloat(c1.y);
const x2 = parseFloat(c2.x);
const y2 = parseFloat(c2.y);
const x3 = parseFloat(c3.x);
const y3 = parseFloat(c3.y);
const s = (x1 - x3) * (y2 - y3) - (x2 - x3) * (y1 - y3);
return s < 0;
}
<script setup lang="ts">
import { reactive, computed } from 'vue';
interface IDetailProps {
title?: any;
width?: number;
maxWidth?: number;
minWidth?: number;
height?: number;
maxHeight?: number;
minHeight?: number;
}
const props = withDefaults(defineProps<IDetailProps>(), {
title: '',
width: 1000,
maxWidth: 1000,
minWidth: 500,
height: 600,
maxHeight: 900,
minHeight: 500,
});
const emit = defineEmits<{
(e: 'onOk'): void;
(e: 'onCancel'): void;
}>();
const myCardStyle = computed(() => {
return {
width: props.width + 'px',
maxWidth: props.maxWidth + 'px',
minWidth: props.minWidth + 'px',
height: props.height + 'px',
maxHeight: props.maxHeight + 'px',
minHeight: props.minHeight + 'px',
};
});
const thumbStyle = reactive({
right: '4px',
borderRadius: '5px',
backgroundColor: '#64b3f4',
width: '5px',
opacity: '1',
});
const barStyle = reactive({
right: '2px',
borderRadius: '9px',
backgroundColor: '#027be3',
width: '9px',
opacity: '0.2',
});
function onCancel() {
emit('onCancel');
}
function onSave() {
emit('onOk');
}
</script>
<template>
<q-card class="box" :style="myCardStyle">
<div class="main" :style="myCardStyle">
<div class="card-content">
<div class="title"></div>
<div class="content">
<q-scroll-area
style="height: 100%; max-width: 100%"
:thumb-style="thumbStyle"
:bar-style="barStyle"
>
<slot></slot>
</q-scroll-area>
</div>
<div class="bottom-action">
<q-btn flat style="color: #64b3f4" label="取消" @click="onCancel" />
<q-btn
unelevated
style="background: #64b3f4; color: white"
label="确定"
@click="onSave()"
/>
</div>
</div>
</div>
<div class="title-mask">
<span>{{ title }}</span>
</div>
</q-card>
</template>
<style lang="scss" scoped>
.box {
position: relative;
}
.title-mask {
box-sizing: border-box;
position: absolute;
top: 29.5px;
height: 30.5px;
border-radius: 0 !important;
border-top-right-radius: 15px !important;
border-bottom-right-radius: 15px !important;
background-color: #64b3f4;
padding-left: 20px;
display: flex;
align-items: center;
box-shadow: rgba(0, 0, 0, 0.07) 0px 1px 1px, rgba(0, 0, 0, 0.07) 0px 2px 2px,
rgba(0, 0, 0, 0.07) 0px 4px 4px, rgba(0, 0, 0, 0.07) 0px 8px 8px,
rgba(0, 0, 0, 0.07) 0px 16px 16px;
> span {
color: white;
font-size: 15px;
font-weight: bold;
padding-left: 8px;
padding-right: 17px;
}
}
.main {
position: relative;
padding: 20px;
background-image: linear-gradient(60deg, #c2e59c 0%, #64b3f4 100%);
box-sizing: border-box;
display: flex;
}
.card-content {
width: 100%;
background-color: #fff;
display: flex;
flex-direction: column;
overflow: hidden;
.title {
box-sizing: border-box;
height: 40px;
font-weight: bolder;
display: flex;
align-items: flex-end;
// border: 1px solid red;
}
.content {
flex: 1;
padding: 8px;
position: relative;
}
.bottom-action {
padding: 8px;
display: flex;
flex-direction: row;
column-gap: 4px;
justify-content: flex-end;
align-items: center;
}
}
</style>
import DialogLayout from './dialog-layout/DialogLayout.vue';
export { DialogLayout as ComDialogLayout };
export default {
DialogLayout,
};
// 开发环境app配置
const development = {
name: 'TopPMS',
productName: 'TopPMS',
storagePrefix: '__hcytest_2_',
langs: ['en-US', 'zh-CN', 'zh-TW'],
serverUrl: { base: 'http://139.196.104.13:9000' },
base_url: '',
};
// 生产环境app配置
const production = {
name: 'TopPMS',
productName: 'TopPMS',
storagePrefix: '__hcytest_2_',
langs: ['en-US', 'zh-CN', 'zh-TW'],
// serverUrl: { base: '' },
base_url: '',
};
export default process.env.NODE_ENV === 'production' ? production : development;
/**
* 全局引入文件
*/
/*************** ag grid vue3****************/
import 'ag-grid-enterprise';
import 'src/css/ag-grid.scss';
export default {
success: 'bi-check-circle-fill',
error: 'bi-exclamation-triangle-fill',
warn: 'bi-exclamation-circle-fill',
info: 'bi-info-circle-fill',
takeColor: 'fa-solid fa-palette', // 取色
reset: 'bi-arrow-counterclockwise', // 重置
refresh: 'bi-arrow-repeat', // 刷新
delete: 'bi-trash', // 删除
alarm_clock: 'bi-stopwatch', // 闹钟
export: 'fa-solid fa-file-export', // 导出
upload: 'fa-solid fa-cloud-arrow-up', // 上传
};
export default [
{
title: '主页',
caption: null,
icon: 'home',
link: '/home',
active: false,
},
];
@import 'ag-grid-community/styles/ag-grid.css'; // Core grid CSS, always needed
@import 'ag-grid-community/styles/ag-theme-material.css'; // Optional theme CSS
// 布局
.center {
display: flex;
justify-content: center;
align-items: center;
}
.container-height {
height: calc(100vh - 50px);
}
// 必填
.text-required::before {
content: '*';
display: inline-block;
height: 22px;
font-size: 14px;
color: $negative;
margin-right: 4px;
transform: scale(1.5) translateY(1px);
// transform: translateY(5px);
}
.form-label-padding-bottom {
padding-bottom: 20px;
}
// tooltip样式
.com-tooltip-sty {
border: 1px solid rgba(9, 30, 66, 0.13);
color: $gray-light-text;
background-color: white;
box-shadow: rgba(0, 0, 0, 0.15) 0px 5px 15px 0px;
}
.table-view {
position: relative;
table {
position: relative;
margin: 0 auto;
text-align: center;
background-color: #fafafa;
border: 1px solid #dadada;
border-collapse: collapse; /*表格的边框合并,如果相邻,则共用一个边框*/
border-spacing: 0; /*设置行与单元格边框的间距。当表格边框独立(即border-collapse:separate;)此属性才起作用*/
td {
padding: 4px;
border: 1px solid #dadada;
height: 30px;
min-height: 30px;
min-width: 100px;
}
th {
padding: 4px;
border: 1px solid #dadada;
height: 30px;
min-height: 30px;
}
}
}
/*滚动条整体样式*/
::-webkit-scrollbar {
height: 12px;
width: 14px;
background: transparent;
z-index: 12;
overflow: visible;
}
::scrollbar {
height: 12px;
width: 14px;
background: transparent;
z-index: 12;
overflow: visible;
}
/*滚动条里面小方块*/
::-webkit-scrollbar-thumb {
width: 10px;
background-color: $primary;
border-radius: 10px;
z-index: 12;
border: 4px solid rgba(0, 0, 0, 0);
background-clip: padding-box;
-webkit-transition: background-color 0.28s ease-in-out;
transition: background-color 0.28s ease-in-out;
margin: 4px;
min-height: 32px;
min-width: 32px;
}
::scrollbar-thumb {
width: 10px;
background-color: $primary;
border-radius: 10px;
z-index: 12;
border: 4px solid rgba(0, 0, 0, 0);
background-clip: padding-box;
-webkit-transition: background-color 0.28s ease-in-out;
transition: background-color 0.28s ease-in-out;
margin: 4px;
min-height: 32px;
min-width: 32px;
}
::-webkit-scrollbar-thumb:hover {
background: $primary;
}
::scrollbar-thumb:hover {
background: $primary;
}
// Quasar SCSS (& Sass) Variables
// --------------------------------------------------
// To customize the look and feel of this app, you can override
// the Sass/SCSS variables found in Quasar's source Sass/SCSS files.
// Check documentation for full list of Quasar variables
// Your own variables (that are declared here) and Quasar's own
// ones will be available out of the box in your .vue/.scss/.sass files
// It's highly recommended to change the default colors
// to match your app's branding.
// Tip: Use the "Theme Builder" on Quasar's documentation website.
$space-base: 16px !default;
$padding-xs: ($space-base * 0.25);
$padding-sm: ($space-base * 0.5);
$padding-md: $space-base;
$padding-lg: ($space-base * 1.5);
$padding-xl: ($space-base * 3);
// $primary: #87a44f;
$primary: #87a44f;
$primary-bg-light: rgba(136, 164, 79, 0.125);
$primary-focus-shadow: rgba(136, 164, 79, 0.35);
$secondary: #26a69a;
$accent: #9c27b0;
$dark: #1d1d1d;
$positive: #81c784;
$negative: #dd4a48;
$info: #a696c8;
$warning: #ffb200;
$--color-white: #fff;
$--color-dark: #000;
$primary-10: mix($--color-dark, $primary, 50%);
$primary-9: mix($--color-dark, $primary, 40%);
$primary-8: mix($--color-dark, $primary, 30%);
$primary-7: mix($--color-dark, $primary, 20%);
$primary-6: mix($--color-dark, $primary, 10%);
$primary-5: mix($--color-white, $primary, 5%);
$primary-4: mix($--color-white, $primary, 10%);
$primary-3: mix($--color-white, $primary, 20%);
$primary-2: mix($--color-white, $primary, 30%);
$primary-1: mix($--color-white, $primary, 50%);
$primary-light-1: mix($--color-white, $primary, 60%);
$primary-light-2: mix($--color-white, $primary, 70%);
$primary-light-3: mix($--color-white, $primary, 80%);
$primary-light-4: mix($--color-white, $primary, 90%);
.bg-primary-bg-light {
background: $primary-bg-light;
}
$border-color: rgba(0, 0, 0, 0.12);
$gray-text: rgba(0, 0, 0, 0.871);
$gray-light-text: rgba(0, 0, 0, 0.596);
$header-heigyht: 50px;
/* eslint-disable */
declare namespace NodeJS {
interface ProcessEnv {
NODE_ENV: string;
VUE_ROUTER_MODE: 'hash' | 'history' | 'abstract' | undefined;
VUE_ROUTER_BASE: string | undefined;
}
}
// This is just an example,
// so you can safely delete all default props below
export default {
failed: 'Action failed',
success: 'Action was successful'
};
import enUS from './en-US';
export default {
'en-US': enUS
};
<template>
<q-layout view="lHh Lpr lFf">
<q-header elevated>
<q-toolbar>
<q-btn flat dense round @click="toggleLeftDrawer">
<q-avatar>
<img :src="leftDrawerOpen ? leftShow : leftHide" />
</q-avatar>
</q-btn>
<q-toolbar-title style="min-width: 140px"> 傲来小神仙 </q-toolbar-title>
<q-tabs
v-model="defaultRouter"
inline-label
outside-arrows
no-caps
shrink
class="my-tabs"
>
<q-tab
class="my-tab"
v-for="page in tabsList"
:key="page.link"
:name="page.link"
:label="page.title"
@click.stop="clickTab(page)"
>
<q-btn
v-show="isShowCloseBtn"
dense
flat
round
size="sm"
icon="close"
title="关闭"
@mousedown.stop="doNothing"
@click.stop="closeTab(page)"
>
</q-btn>
<q-menu touch-position context-menu v-if="isShowCloseBtn">
<q-list dense style="min-width: 100px">
<q-item clickable v-close-popup="true">
<q-item-section @click="closeTab(page)"
>关闭当前页面</q-item-section
>
</q-item>
<q-item clickable v-close-popup="true">
<q-item-section @click="closeNotCurrentPage(page)"
>关闭非当前页面</q-item-section
>
</q-item>
<q-item clickable v-close-popup="true">
<q-item-section @click="closeAll">关闭所有</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-tab>
</q-tabs>
<q-space />
<div>Quasar v{{ $q.version }}</div>
</q-toolbar>
</q-header>
<q-drawer v-model="leftDrawerOpen" show-if-above bordered>
<q-scroll-area style="height: 100%">
<q-list>
<q-item-label header style="padding: 0">
<TopLeftText />
</q-item-label>
<EssentialLink
v-for="link in essentialLinks"
:key="link.title"
v-bind="link"
/>
</q-list>
</q-scroll-area>
</q-drawer>
<q-page-container>
<router-view />
</q-page-container>
</q-layout>
</template>
<script lang="ts">
import {
defineComponent,
ref,
onMounted,
reactive,
toRefs,
computed,
} from 'vue';
import { onBeforeRouteUpdate, useRouter } from 'vue-router';
import { usePageStore } from 'src/common/hooks';
import MenuList from 'src/config/menu';
import EssentialLink from './elements/EssentialLink.vue';
import TopLeftText from './elements/TopLeftText.vue';
const leftShow = new URL('./imgs/kk-left-show.jpg', import.meta.url).href;
const leftHide = new URL('./imgs/kk-left-hide.jpg', import.meta.url).href;
export default defineComponent({
name: 'MainLayout',
components: {
EssentialLink,
TopLeftText,
},
setup() {
const pageStore = usePageStore();
const router = useRouter();
const leftDrawerOpen = ref(false);
const defaultRouter = ref('/home');
const linksList = ref<any>([]);
const state = reactive({
tabsList: [] as any,
});
onBeforeRouteUpdate((to, from, next) => {
defaultRouter.value = to.path;
if (to.matched.length == 3) {
pageStore.addRoute(to as any);
getTabsList();
}
next();
});
const isShowCloseBtn = computed(() => {
let flag = true;
const { tabsList } = state;
if (tabsList.length === 1) {
const target = tabsList[0];
if (target.link === '/home') {
flag = false;
}
}
return flag;
});
onMounted(() => {
getLinksList();
const path = router.currentRoute.value.path;
defaultRouter.value = path;
pageStore.addRoute(router.currentRoute.value as any);
getTabsList();
});
const getLinksList = () => {
let lists = MenuList;
for (const item of lists) {
if (item.link === defaultRouter.value) {
item.active = true;
} else {
item.active = false;
}
}
linksList.value = lists;
};
const getTabsList = () => {
state.tabsList = pageStore.tabRouterList.map((item: any) => {
return {
title: item.meta?.title,
link: item.path,
keepalive: item.meta?.keepalive,
permission: item.meta?.permission,
name: item.name,
};
});
};
const clickTab = (page: any) => {
router.push(page.link);
};
const closeTab = (page: any) => {
const { tabsList } = state;
if (tabsList.length === 1) {
router.push('/home');
pageStore.removePage(page);
getTabsList();
} else {
pageStore.removePage(page);
getTabsList();
router.push(pageStore.activeRouter.path);
}
};
const closeNotCurrentPage = (page: any) => {
pageStore.removeNotCurrentPage(page);
getTabsList();
router.push(pageStore.activeRouter.path);
};
const closeAll = () => {
pageStore.removeAllPage();
router.push('/home');
getTabsList();
};
const doNothing = () => {
// to prevent tab close button ripple effect
};
return {
leftShow,
leftHide,
...toRefs(state),
essentialLinks: linksList,
leftDrawerOpen,
toggleLeftDrawer() {
leftDrawerOpen.value = !leftDrawerOpen.value;
},
linksList,
pageStore,
defaultRouter,
clickTab,
closeTab,
closeNotCurrentPage,
closeAll,
doNothing,
isShowCloseBtn,
};
},
});
</script>
<style lang="scss" scoped>
.my-tab {
box-sizing: border-box;
background-color: $primary-4;
margin: 0 2px;
:deep(.q-tab__content) {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
> button {
color: transparent;
}
&:hover > button {
color: white;
}
.q-tab__label {
flex: 1;
}
}
}
.my-tabs {
:deep(.q-tab--active) {
background-color: $primary-2;
}
}
</style>
<template>
<q-expansion-item
v-if="children && children.length > 0"
expand-separator
default-opened
:label="title"
:caption="caption"
switch-toggle-side
>
<q-item
clickable
active-class="bg-primary-bg-light"
v-for="item in children"
@click="expansionClick(item)"
:key="item.link"
:active="pageStore.activeRouter?.path === item.link ? true : false"
>
<q-item-section
v-if="item.icon"
avatar
style="min-width: 30px; padding-right: 0px"
>
<q-icon :name="item.icon" />
</q-item-section>
<q-item-section>
<q-item-label>{{ item.title }}</q-item-label>
<q-item-label caption v-if="item.caption">{{
item.caption
}}</q-item-label>
</q-item-section>
</q-item>
</q-expansion-item>
<q-item
v-else
clickable
@click="onClick"
:active="pageStore.activeRouter?.path === link ? true : false"
active-class="bg-primary-bg-light"
>
<q-item-section
v-if="icon"
avatar
style="min-width: 30px; padding-right: 0px"
>
<q-icon :name="icon" />
</q-item-section>
<q-item-section>
<q-item-label>{{ title }}</q-item-label>
<q-item-label caption v-if="caption">{{ caption }}</q-item-label>
</q-item-section>
</q-item>
</template>
<script lang="ts">
import { defineComponent, PropType, reactive, toRefs } from 'vue';
import { useRouter } from 'vue-router';
import { usePageStore } from 'src/common/hooks';
import { EssentialLinkType } from '../models';
export default defineComponent({
name: 'EssentialLink',
props: {
active: {
type: Boolean,
default: false,
},
title: {
type: String,
required: true,
},
caption: {
type: String,
default: '',
},
link: {
type: String,
default: '#',
},
icon: {
type: String,
default: '',
},
children: {
type: Array as PropType<EssentialLinkType[]>,
default: () => [],
},
},
setup(props) {
const pageStore = usePageStore();
const router = useRouter();
const state = reactive({
//
});
const onClick = () => {
const currentRouter = router.currentRoute.value.path;
if (currentRouter === props.link) return;
router.push(props.link);
};
const expansionClick = (params: EssentialLinkType) => {
const currentRouter = router.currentRoute.value.path;
if (currentRouter === params.link) return;
router.push(params.link as string);
};
return {
...toRefs(state),
onClick,
expansionClick,
pageStore,
};
},
});
</script>
<script setup lang="ts"></script>
<template>
<div class="top-left-box">两眼空空</div>
</template>
<style lang="scss" scoped>
.top-left-box {
height: 50px;
padding: $padding-sm;
line-height: 50px;
text-align: center;
}
</style>
export interface Todo {
id: number;
content: string;
}
export interface Meta {
totalCount: number;
}
export interface EssentialLinkType {
title: string;
caption?: string;
icon?: string;
link?: string;
active?: boolean;
children?: Array<EssentialLinkType>;
}
<!--
* @FileDescription: 主页
* @Date: 2023-08-03
* @LastEditTime: 2023-08-04
-->
<script setup lang="ts">
import { reactive, onMounted } from 'vue';
import { AgGridVue } from 'ag-grid-vue3';
import type { GridOptions } from 'ag-grid-community';
// Ag Grid
const gridOptions: GridOptions<any> = reactive({
rowModelType: 'serverSide',
suppressContextMenu: true, // 阻止“右键单击”上下文菜单
suppressCellFocus: true, // 阻止单元格聚焦,这意味着键盘导航将对网格单元格禁用
defaultColDef: {
suppressMenu: true, // 阻止此列标头菜单显示
},
columnDefs: [
{ headerName: '运动员', field: 'athlete', minWidth: 170 },
{ headerName: '年龄', field: 'age' },
{ headerName: '国家', field: 'country' },
{ headerName: '年份', field: 'year' },
{ headerName: '日期', field: 'date' },
{ headerName: '运动项目', field: 'sport' },
{ headerName: '金牌', field: 'gold' },
{ headerName: '银牌', field: 'silver' },
{ headerName: '铜牌', field: 'bronze' },
{ headerName: '总数', field: 'total' },
],
rowData: [],
onGridReady,
});
onMounted(() => {
//
});
function onGridReady() {
fetch('https://www.ag-grid.com/example-assets/olympic-winners.json')
.then((resp) => resp.json())
.then((data) => {
updateData(data);
});
}
function updateData(data: any) {
const fakeServer = createFakeServer(data);
const datasource = createServerSideDatasource(fakeServer);
// 向网格注册数据源
gridOptions.api?.setServerSideDatasource(datasource);
}
// 用整个数据集设置假服务器
function createFakeServer(allData: any[]) {
return {
getData: (request: any) => {
// 在这个简化的假服务器中,所有行都包含在一个数组中
const requestedRows = allData.slice(request.startRow, request.endRow);
return {
success: true,
rows: requestedRows,
};
},
};
}
// 使用对假服务器的引用创建数据源
function createServerSideDatasource(server: any) {
return {
getRows: (params: any) => {
// console.log('[Datasource] - rows requested by grid: ', params.request);
// 从我们的假服务器获取请求的数据
const response = server.getData(params.request);
// 模拟真实服务器调用,延迟1000ms
setTimeout(function () {
if (response.success) {
// 向网格提供请求块的行
params.success({ rowData: response.rows });
} else {
params.fail();
}
}, 1000);
},
};
}
</script>
<template>
<div class="home-page-home fit">
<div class="fit">
<ag-grid-vue class="ag-theme-material fit" :grid-options="gridOptions">
</ag-grid-vue>
</div>
</div>
</template>
<style lang="scss" scoped></style>
export default [
{
path: 'home',
name: 'HOME',
component: () => import('./PageHome.vue'),
meta: {
title: '主页',
permission: ['*'],
keepalive: true,
},
},
];
<template>
<div class="fullscreen bg-blue text-white text-center q-pa-md flex flex-center">
<div>
<div style="font-size: 30vh">
404
</div>
<div class="text-h2" style="opacity:.4">
Oops. Nothing here...
</div>
<q-btn
class="q-mt-xl"
color="white"
text-color="blue"
unelevated
to="/"
label="Go Home"
no-caps
/>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'ErrorNotFound'
});
</script>
<template>
<q-page class="container-height">
<div class="fit overflow-auto">
<router-view v-slot="{ Component, route }">
<keep-alive :include="keepAliveList">
<component :is="Component" :key="route.path" />
</keep-alive>
</router-view>
</div>
</q-page>
</template>
<script lang="ts">
// v-if="route.meta && route.meta.keepalive"
// <component
// v-if="!(route.meta && route.meta.keepalive)"
// :is="Component"
// />
import { computed } from 'vue';
import { usePageStore } from 'src/common/hooks';
export default {
name: 'LaoutIndexPage',
setup() {
const pageStore = usePageStore();
const keepAliveList = computed(() => {
return pageStore.allPageKeys;
});
return {
keepAliveList,
};
},
};
</script>
<style scoped lang="scss"></style>
/* eslint-disable */
// Forces TS to apply `@quasar/app-vite` augmentations of `quasar` package
// Removing this would break `quasar/wrappers` imports as those typings are declared
// into `@quasar/app-vite`
// As a side effect, since `@quasar/app-vite` reference `quasar` to augment it,
// this declaration also apply `quasar` own
// augmentations (eg. adds `$q` into Vue component context)
/// <reference types="@quasar/app-vite" />
import { route } from 'quasar/wrappers';
import {
createMemoryHistory,
createRouter,
createWebHashHistory,
createWebHistory,
} from 'vue-router';
import routes from './routes';
/*
* If not building with SSR mode, you can
* directly export the Router instantiation;
*
* The function below can be async too; either use
* async/await or return a Promise which resolves
* with the Router instance.
*/
export default route(function (/* { store, ssrContext } */) {
const createHistory = process.env.SERVER
? createMemoryHistory
: (process.env.VUE_ROUTER_MODE === 'history' ? createWebHistory : createWebHashHistory);
const Router = createRouter({
scrollBehavior: () => ({ left: 0, top: 0 }),
routes,
// Leave this as is and make changes in quasar.conf.js instead!
// quasar.conf.js -> build -> vueRouterMode
// quasar.conf.js -> build -> publicPath
history: createHistory(process.env.VUE_ROUTER_BASE),
});
return Router;
});
import { RouteRecordRaw } from 'vue-router';
import HOME from '../modules/home/router';
const routes: RouteRecordRaw[] = [
{
path: '/',
component: () => import('layouts/MainLayout.vue'),
children: [
{
path: '',
name: 'LaoutIndexPage',
component: () => import('pages/IndexPage.vue'),
redirect: '/home',
children: [...HOME],
},
],
},
// Always leave this as last one,
// but you can also remove it
{
path: '/:catchAll(.*)*',
component: () => import('pages/ErrorNotFound.vue'),
},
];
export default routes;
/* eslint-disable */
/// <reference types="vite/client" />
// Mocks all files ending in `.vue` showing them as plain Vue instances
declare module '*.vue' {
import type { DefineComponent } from 'vue';
const component: DefineComponent<{}, {}, any>;
export default component;
}
import { defineStore } from 'pinia';
export const useCounterStore = defineStore('counter', {
state: () => ({
counter: 0,
}),
getters: {
doubleCount: (state) => state.counter * 2,
},
actions: {
increment() {
this.counter++;
},
},
});
import { store } from 'quasar/wrappers'
import { createPinia } from 'pinia'
import { Router } from 'vue-router';
/*
* When adding new properties to stores, you should also
* extend the `PiniaCustomProperties` interface.
* @see https://pinia.vuejs.org/core-concepts/plugins.html#typing-new-store-properties
*/
declare module 'pinia' {
export interface PiniaCustomProperties {
readonly router: Router;
}
}
/*
* If not building with SSR mode, you can
* directly export the Store instantiation;
*
* The function below can be async too; either use
* async/await or return a Promise which resolves
* with the Store instance.
*/
export default store((/* { ssrContext } */) => {
const pinia = createPinia()
// You can add Pinia plugins here
// pinia.use(SomePiniaPlugin)
return pinia
})
/* eslint-disable */
// THIS FEATURE-FLAG FILE IS AUTOGENERATED,
// REMOVAL OR CHANGES WILL CAUSE RELATED TYPES TO STOP WORKING
import "quasar/dist/types/feature-flag";
declare module "quasar/dist/types/feature-flag" {
interface QuasarFeatureFlags {
store: true;
}
}
{
"extends": "@quasar/app-vite/tsconfig-preset",
"compilerOptions": {
"baseUrl": "."
}
}
\ No newline at end of file
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment