fix(项目初始化):前端项目初始化
9
.editorconfig
Normal file
@ -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
|
||||||
8
.eslintignore
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
/dist
|
||||||
|
/src-capacitor
|
||||||
|
/src-cordova
|
||||||
|
/.quasar
|
||||||
|
/node_modules
|
||||||
|
.eslintrc.js
|
||||||
|
/src-ssr
|
||||||
|
/quasar.config.*.temporary.compiled*
|
||||||
90
.eslintrc.cjs
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
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'
|
||||||
|
}
|
||||||
|
}
|
||||||
37
.gitignore
vendored
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
.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*
|
||||||
3
.npmrc
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# pnpm-related options
|
||||||
|
shamefully-hoist=true
|
||||||
|
strict-peer-dependencies=false
|
||||||
4
.prettierrc
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"singleQuote": true,
|
||||||
|
"semi": true
|
||||||
|
}
|
||||||
15
.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"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"
|
||||||
|
]
|
||||||
|
}
|
||||||
16
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"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"
|
||||||
|
}
|
||||||
41
README.md
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
# Data Process By Quasar (quasar-data-process)
|
||||||
|
|
||||||
|
Very New Bee
|
||||||
|
|
||||||
|
## 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).
|
||||||
21
index.html
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<!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>
|
||||||
6301
package-lock.json
generated
Normal file
41
package.json
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"name": "quasar-data-process",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "Very New Bee",
|
||||||
|
"productName": "Data Process By Quasar",
|
||||||
|
"author": "ni ziyi <310925901@qq.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": {
|
||||||
|
"axios": "^1.2.1",
|
||||||
|
"pinia": "^2.0.11",
|
||||||
|
"stylus": "^0.64.0",
|
||||||
|
"@quasar/extras": "^1.16.4",
|
||||||
|
"quasar": "^2.6.0",
|
||||||
|
"vue": "^3.0.0",
|
||||||
|
"vue-router": "^4.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@typescript-eslint/eslint-plugin": "^5.10.0",
|
||||||
|
"@typescript-eslint/parser": "^5.10.0",
|
||||||
|
"eslint": "^8.10.0",
|
||||||
|
"eslint-plugin-vue": "^9.0.0",
|
||||||
|
"eslint-config-prettier": "^8.1.0",
|
||||||
|
"prettier": "^2.5.1",
|
||||||
|
"@types/node": "^12.20.21",
|
||||||
|
"@quasar/app-vite": "^1.3.0",
|
||||||
|
"autoprefixer": "^10.4.2",
|
||||||
|
"typescript": "^4.5.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18 || ^16 || ^14.19",
|
||||||
|
"npm": ">= 6.13.4",
|
||||||
|
"yarn": ">= 1.21.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
27
postcss.config.cjs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/* 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')
|
||||||
|
]
|
||||||
|
}
|
||||||
BIN
public/favicon.ico
Normal file
|
After Width: | Height: | Size: 63 KiB |
BIN
public/icons/favicon-128x128.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
public/icons/favicon-16x16.png
Normal file
|
After Width: | Height: | Size: 859 B |
BIN
public/icons/favicon-32x32.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
public/icons/favicon-96x96.png
Normal file
|
After Width: | Height: | Size: 9.4 KiB |
212
quasar.config.js
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
/* 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');
|
||||||
|
|
||||||
|
|
||||||
|
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: [
|
||||||
|
|
||||||
|
'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
|
||||||
|
],
|
||||||
|
|
||||||
|
// 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: [
|
||||||
|
// [ 'package-name', { ..options.. } ]
|
||||||
|
// ]
|
||||||
|
},
|
||||||
|
|
||||||
|
// 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: 'en-US', // Quasar language pack
|
||||||
|
|
||||||
|
// 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: []
|
||||||
|
},
|
||||||
|
|
||||||
|
// 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: 'quasar-data-process'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 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) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
7
src/App.vue
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<template>
|
||||||
|
<router-view />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
80
src/api/customerMessage.ts
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
import {api} from 'boot/axios';
|
||||||
|
import {Page, XyCustomerMessage} from 'stores/messageStore';
|
||||||
|
|
||||||
|
export const getList = async (): Promise<any> => {
|
||||||
|
try {
|
||||||
|
return await api.get('/message/getAlllist').then(response => {
|
||||||
|
return response
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getAllFinishlist = async (): Promise<any> => {
|
||||||
|
try {
|
||||||
|
return await api.get('/message/getAllFinishlist').then(response => {
|
||||||
|
return response
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getAllUnfinishlist = async (): Promise<any> => {
|
||||||
|
try {
|
||||||
|
return await api.get('/message/getAllUnfinishlist').then(response => {
|
||||||
|
return response
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteById = async (payload: string): Promise<any> => {
|
||||||
|
try {
|
||||||
|
return await api.post('/message/delete/'+ payload).then(response => {
|
||||||
|
return response.data
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const insertMessage = async (payload: XyCustomerMessage): Promise<any> => {
|
||||||
|
try {
|
||||||
|
return await api.post('/message/insert', payload).then(response => {
|
||||||
|
return response.data
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export const finishOne = async (payload: string): Promise<any> => {
|
||||||
|
try {
|
||||||
|
console.log(payload)
|
||||||
|
return await api.post('/message/finishOne/'+ payload).then(response => {
|
||||||
|
return response.data
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const finishList = async (payload: []): Promise<any> => {
|
||||||
|
try {
|
||||||
|
console.log(payload)
|
||||||
|
return await api.post('/message/finishList', payload).then(response => {
|
||||||
|
return response.data
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
};
|
||||||
41
src/api/demo.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import {api} from 'boot/axios';
|
||||||
|
|
||||||
|
export async function getData(): Promise<any> {
|
||||||
|
try {
|
||||||
|
const response = await api.get('/data');
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createData(payload: any): Promise<any> {
|
||||||
|
try {
|
||||||
|
const response = await api.post('/api/data', payload);
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateData(id: string, payload: any): Promise<any> {
|
||||||
|
try {
|
||||||
|
const response = await api.put(`/api/data/${id}`, payload);
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteData(id: string): Promise<any> {
|
||||||
|
try {
|
||||||
|
const response = await api.delete(`/api/data/${id}`);
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
13
src/api/file.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import {api} from 'boot/axios';
|
||||||
|
import {KmArticle} from 'stores/articleStore';
|
||||||
|
|
||||||
|
export const uploadFiles = async (payload:KmArticle): Promise<any> => {
|
||||||
|
try {
|
||||||
|
return await api.post('/',payload).then(response => {
|
||||||
|
return response
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
};
|
||||||
13
src/api/kmArticle.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import {api} from 'boot/axios';
|
||||||
|
import {KmArticle} from 'stores/articleStore';
|
||||||
|
|
||||||
|
export const addArticle = async (payload:KmArticle): Promise<any> => {
|
||||||
|
try {
|
||||||
|
return await api.post('/article/add',payload).then(response => {
|
||||||
|
return response
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
};
|
||||||
55
src/api/login.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import {api} from 'boot/axios';
|
||||||
|
import {DcodeImage, UserLoginReq} from 'stores/userStore';
|
||||||
|
|
||||||
|
export const loginRequest = async (passwordLogin: UserLoginReq): Promise<string> => {
|
||||||
|
let cancel = null;
|
||||||
|
const controller = new AbortController();
|
||||||
|
try {
|
||||||
|
if (cancel) {
|
||||||
|
controller.abort()
|
||||||
|
}
|
||||||
|
cancel = 1;
|
||||||
|
return await api.post('login', passwordLogin, {signal: controller.signal})
|
||||||
|
.then(response => {
|
||||||
|
cancel = null;
|
||||||
|
return response.data
|
||||||
|
}); // 如果响应中有 token 属性则返回它,更具体的属性名称取决于API实现。
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error); // 处理错误信息
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const hello = async (): Promise<string> => {
|
||||||
|
try {
|
||||||
|
const { data } = await api.get('hello' );
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error); // 处理错误信息
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getInfo = async (): Promise<string> => {
|
||||||
|
try {
|
||||||
|
const { data } = await api.get('getInfo' );
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error); // 处理错误信息
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getDcode = async (): Promise<DcodeImage> => {
|
||||||
|
try {
|
||||||
|
return await api.get('login/getDcode' ).then(
|
||||||
|
response => {
|
||||||
|
console.log(response.data)
|
||||||
|
return response.data
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error); // 处理错误信息
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
33
src/assets/login/background.svg
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" baseProfile="full" width="100%" height="100%" viewBox="0 0 1400 800">
|
||||||
|
|
||||||
|
<rect x="1300" y="400" rx="40" ry="40" width="300" height="300" stroke="rgb(129, 201, 149)" fill="rgb(129, 201, 149)">
|
||||||
|
<animateTransform attributeType="XML" attributeName="transform" begin="0s" dur="35s" type="rotate" from="0 1450 550" to="360 1450 550" repeatCount="indefinite"/>
|
||||||
|
</rect>
|
||||||
|
|
||||||
|
<path d="M 100 350 A 150 150 0 1 1 400 350 Q400 370 380 370 L 250 370 L 120 370 Q100 370 100 350" stroke="rgb(253, 214, 99)" fill="rgb(253, 214, 99)">
|
||||||
|
<animateMotion path="M 800 -200 L 800 -300 L 800 -200" dur="20s" begin="0s" repeatCount="indefinite"/>
|
||||||
|
<animateTransform attributeType="XML" attributeName="transform" begin="0s" dur="30s" type="rotate" values="0 210 530 ; -30 210 530 ; 0 210 530" keyTimes="0 ; 0.5 ; 1" repeatCount="indefinite"/>
|
||||||
|
</path>
|
||||||
|
|
||||||
|
<circle cx="200" cy="150" r="20" stroke="#1a73e8" fill="#1a73e8">
|
||||||
|
<animateMotion path="M 0 0 L 40 20 Z" dur="5s" repeatCount="indefinite"/>
|
||||||
|
</circle>
|
||||||
|
|
||||||
|
<!-- 三角形 -->
|
||||||
|
<path d="M 165 580 L 270 580 Q275 578 270 570 L 223 483 Q220 480 217 483 L 165 570 Q160 578 165 580" stroke="rgb(238, 103, 92)" fill="rgb(238, 103, 92)">
|
||||||
|
<animateTransform attributeType="XML" attributeName="transform" begin="0s" dur="35s" type="rotate" from="0 210 530" to="360 210 530" repeatCount="indefinite"/>
|
||||||
|
</path>
|
||||||
|
|
||||||
|
<circle cx="1200" cy="600" r="30" stroke="rgb(241, 243, 244)" fill="rgb(241, 243, 244)">
|
||||||
|
<animateMotion path="M 0 0 L -20 40 Z" dur="9s" repeatCount="indefinite"/>
|
||||||
|
</circle>
|
||||||
|
|
||||||
|
<path d="M 100 350 A 40 40 0 1 1 180 350 L 180 430 A 40 40 0 1 1 100 430 Z" stroke="rgb(241, 243, 244)" fill="rgb(241, 243, 244)">
|
||||||
|
<animateMotion path="M 140 390 L 180 360 L 140 390" dur="20s" begin="0s" repeatCount="indefinite"/>
|
||||||
|
<animateTransform attributeType="XML" attributeName="transform" begin="0s" dur="30s" type="rotate" values="0 140 390; -60 140 390; 0 140 390" keyTimes="0 ; 0.5 ; 1" repeatCount="indefinite"/>
|
||||||
|
</path>
|
||||||
|
|
||||||
|
<rect x="400" y="600" rx="40" ry="40" width="100" height="100" stroke="rgb(129, 201, 149)" fill="rgb(129, 201, 149)">
|
||||||
|
<animateTransform attributeType="XML" attributeName="transform" begin="0s" dur="35s" type="rotate" from="-30 550 750" to="330 550 750" repeatCount="indefinite"/>
|
||||||
|
</rect>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.3 KiB |
15
src/assets/quasar-logo-vertical.svg
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<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>
|
||||||
|
After Width: | Height: | Size: 4.4 KiB |
0
src/boot/.gitkeep
Normal file
90
src/boot/axios.ts
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
import {boot} from 'quasar/wrappers'
|
||||||
|
import axios from 'axios'
|
||||||
|
import QS from 'qs'
|
||||||
|
import router from 'src/router';
|
||||||
|
import {userStore} from 'stores/userStore';
|
||||||
|
import {CookieUtil, tokenKey} from 'src/utils/CookieUtils';
|
||||||
|
import {notifyError} from 'src/utils/NotifyUtils';
|
||||||
|
|
||||||
|
const api = axios.create({baseURL: '/api'});
|
||||||
|
// const api = axios.create({baseURL: 'http://localhost:8090/api'});
|
||||||
|
/**
|
||||||
|
* 拦截器可以有多个,请求拦截器是后进先执行,响应拦截器是先进先执行
|
||||||
|
*/
|
||||||
|
const store = userStore()
|
||||||
|
|
||||||
|
// 添加请求拦截器
|
||||||
|
api.interceptors.request.use(
|
||||||
|
(config) => {
|
||||||
|
if (!config.headers['Content-Type']) {
|
||||||
|
config.headers['Content-Type'] = 'application/json;charset=UTF-8'
|
||||||
|
}
|
||||||
|
config.headers['Access-Control-Allow-Origin'] = '*'
|
||||||
|
config.headers['Access-Control-Allow-Private-Network'] = 'true'
|
||||||
|
config.headers['Access-Control-Allow-Methods']='GET, POST, OPTIONS'
|
||||||
|
const token = CookieUtil.getCookie(tokenKey);
|
||||||
|
if (token) {
|
||||||
|
config.headers[tokenKey] = token
|
||||||
|
}
|
||||||
|
|
||||||
|
config.paramsSerializer = (params): string => QS.stringify(params, {
|
||||||
|
indices: false,
|
||||||
|
skipNulls: true,
|
||||||
|
strictNullHandling: false,
|
||||||
|
// serializeDate: (d:Date):string => formatDateString(d.toDateString())
|
||||||
|
})
|
||||||
|
return config;
|
||||||
|
},
|
||||||
|
(error) => Promise.reject(error),
|
||||||
|
);
|
||||||
|
|
||||||
|
api.interceptors.response.use(
|
||||||
|
(res) => {
|
||||||
|
// 在请求成功后的数据处理
|
||||||
|
if (res.data.errorCode && res.data.errorCode !== '0') {
|
||||||
|
notifyError('错误 ' + res.data.message);
|
||||||
|
return Promise.reject(new Error(res.data.message));
|
||||||
|
} else if (res.headers['content-exception']) {
|
||||||
|
const errorMsg = decodeURI(res.headers['content-exception']);
|
||||||
|
notifyError('错误 ' + errorMsg);
|
||||||
|
return Promise.reject(new Error(errorMsg));
|
||||||
|
}
|
||||||
|
if (res.data && res.status === 200) {
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
console.log(err)
|
||||||
|
// 在请求错误的时候的逻辑处理
|
||||||
|
if (err.response.status === 401) {
|
||||||
|
notifyError('授权过期,请重新登录');
|
||||||
|
store.$reset();
|
||||||
|
CookieUtil.removeCookie(tokenKey);
|
||||||
|
router.push('/manage');
|
||||||
|
return Promise.reject(err);
|
||||||
|
} else {
|
||||||
|
if (err.response.data.message) {
|
||||||
|
notifyError(err.response.data.message)
|
||||||
|
} else if (err.response.headers['content-exception']) {
|
||||||
|
notifyError('错误 ' + decodeURI(err.response.headers['content-exception']))
|
||||||
|
} else {
|
||||||
|
notifyError(err.response.status + ':' + err.response.statusText)
|
||||||
|
}
|
||||||
|
return Promise.reject(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export default boot(({app}) => {
|
||||||
|
// 通过this.$axios和this.$API在Vue文件(Options API)内使用
|
||||||
|
|
||||||
|
app.config.globalProperties.$axios = axios
|
||||||
|
// ^ ^ ^ 这将允许你使用this.$axios(Vue Options API形式)
|
||||||
|
// 所以你不需要在每个vue文件中导入axios
|
||||||
|
|
||||||
|
app.config.globalProperties.$api = api
|
||||||
|
// ^ ^ ^ 这将允许您使用this.$api(Vue Options API形式)
|
||||||
|
// 这样您就可以轻松地根据应用程序的api执行请求
|
||||||
|
})
|
||||||
|
|
||||||
|
export {axios, api}
|
||||||
73
src/components/Editor/WangEditor.vue
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
<template>
|
||||||
|
<!-- {{ valueHtml }}-->
|
||||||
|
<div style="border: 1px solid #ccc">
|
||||||
|
<Toolbar
|
||||||
|
style="border-bottom: 1px solid #ccc"
|
||||||
|
:editor="editorRef"
|
||||||
|
:defaultConfig="toolbarConfig"
|
||||||
|
:mode="mode"
|
||||||
|
/>
|
||||||
|
<Editor
|
||||||
|
style="height: 500px; overflow-y: hidden;"
|
||||||
|
v-model="valueHtml"
|
||||||
|
:defaultConfig="editorConfig"
|
||||||
|
:mode="mode"
|
||||||
|
@onCreated="handleCreated"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
import {Editor, Toolbar} from '@wangeditor/editor-for-vue';
|
||||||
|
import '@wangeditor/editor/dist/css/style.css' // 引入 css
|
||||||
|
import {IEditorConfig, IToolbarConfig} from '@wangeditor/editor'
|
||||||
|
import {onBeforeUnmount, ref, shallowRef, onMounted} from 'vue'
|
||||||
|
import {defineAsyncComponent} from 'vue';
|
||||||
|
|
||||||
|
const editorRef = shallowRef()
|
||||||
|
const mode = ref('default')
|
||||||
|
|
||||||
|
// 内容 HTML
|
||||||
|
const valueHtml = ref('<p></p>')
|
||||||
|
|
||||||
|
// 模拟 ajax 异步获取内容
|
||||||
|
// onMounted(() => {
|
||||||
|
// setTimeout(() => {
|
||||||
|
// valueHtml.value = '<p>模拟 Ajax 异步设置内容</p>'
|
||||||
|
// }, 1500)
|
||||||
|
// })
|
||||||
|
|
||||||
|
const toolbarConfig:Partial<IToolbarConfig> = { // TS 语法
|
||||||
|
// const toolbarConfig = { // JS 语法
|
||||||
|
/* 工具栏配置 */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const editorConfig: Partial<IEditorConfig> = { // TS 语法
|
||||||
|
// const editorConfig = { // JS 语法
|
||||||
|
placeholder:'请输入内容'
|
||||||
|
}
|
||||||
|
|
||||||
|
editorConfig.MENU_CONF['uploadImage'] = {
|
||||||
|
server: '/api/upload',
|
||||||
|
}
|
||||||
|
|
||||||
|
const title = ref('')
|
||||||
|
|
||||||
|
// 组件销毁时,也及时销毁编辑器
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
const editor = editorRef.value
|
||||||
|
if (editor == null) return
|
||||||
|
editor.destroy()
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleCreated = (editor: any) => {
|
||||||
|
editorRef.value = editor // 记录 editor 实例,重要!
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
34
src/components/EssentialLink.vue
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<template>
|
||||||
|
<q-item
|
||||||
|
clickable
|
||||||
|
tag="a"
|
||||||
|
target="_blank"
|
||||||
|
:href="link"
|
||||||
|
>
|
||||||
|
<q-item-section
|
||||||
|
v-if="icon"
|
||||||
|
avatar
|
||||||
|
>
|
||||||
|
<q-icon :name="icon" />
|
||||||
|
</q-item-section>
|
||||||
|
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label>{{ title }}</q-item-label>
|
||||||
|
<q-item-label caption>{{ caption }}</q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
export interface EssentialLinkProps {
|
||||||
|
title: string;
|
||||||
|
caption?: string;
|
||||||
|
link?: string;
|
||||||
|
icon?: string;
|
||||||
|
}
|
||||||
|
withDefaults(defineProps<EssentialLinkProps>(), {
|
||||||
|
caption: '',
|
||||||
|
link: '#',
|
||||||
|
icon: '',
|
||||||
|
});
|
||||||
|
</script>
|
||||||
37
src/components/ExampleComponent.vue
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<p>{{ title }}</p>
|
||||||
|
<ul>
|
||||||
|
<li v-for="todo in todos" :key="todo.id" @click="increment">
|
||||||
|
{{ todo.id }} - {{ todo.content }}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<p>Count: {{ todoCount }} / {{ meta.totalCount }}</p>
|
||||||
|
<p>Active: {{ active ? 'yes' : 'no' }}</p>
|
||||||
|
<p>Clicks on todos: {{ clickCount }}</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, ref } from 'vue';
|
||||||
|
import { Todo, Meta } from './models';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
title: string;
|
||||||
|
todos?: Todo[];
|
||||||
|
meta: Meta;
|
||||||
|
active: boolean;
|
||||||
|
}
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
todos: () => [],
|
||||||
|
});
|
||||||
|
|
||||||
|
const clickCount = ref(0);
|
||||||
|
function increment() {
|
||||||
|
clickCount.value += 1;
|
||||||
|
return clickCount.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const todoCount = computed(() => props.todos.length);
|
||||||
|
|
||||||
|
</script>
|
||||||
184
src/components/GesturePassword/GesturePassword.vue
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
<template>
|
||||||
|
<div style="width: 100%">
|
||||||
|
<canvas ref='myCanvas'></canvas>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default{
|
||||||
|
data(){
|
||||||
|
return{
|
||||||
|
c:'',
|
||||||
|
cPosition:{},
|
||||||
|
cxt:'',
|
||||||
|
CW:600,
|
||||||
|
CH:600,
|
||||||
|
Radius:25,
|
||||||
|
offsetX:60,
|
||||||
|
offsetY:60,
|
||||||
|
password:[0,1,2,3,4],
|
||||||
|
successColor:'#627eed',
|
||||||
|
errorColor:'red',
|
||||||
|
innerColor:'',
|
||||||
|
successInnerColor:'#ffffff',
|
||||||
|
errorInnerColor:'#ffffff',
|
||||||
|
selectColor:'',
|
||||||
|
Re:[],
|
||||||
|
borderWidth:1,
|
||||||
|
pointLine:[],
|
||||||
|
pointWidth:8,//圆心的半径大小
|
||||||
|
lineWidth:3//连线的大小
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted(){
|
||||||
|
this.selectColor=this.successColor;
|
||||||
|
this.innerColor=this.successInnerColor;
|
||||||
|
this.$nextTick( ()=>{
|
||||||
|
this.init();
|
||||||
|
this.initEvent()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
methods:{
|
||||||
|
init(){
|
||||||
|
this.initCas();
|
||||||
|
this.getPointLocationArr();
|
||||||
|
this.draw();
|
||||||
|
},
|
||||||
|
initCas(){//初始化画布
|
||||||
|
this.c=this.$refs.myCanvas;
|
||||||
|
this.CW=document.body.offsetWidth;
|
||||||
|
this.c.width=this.CW;
|
||||||
|
this.c.height=this.CH;
|
||||||
|
this.cPosition=this.c.getBoundingClientRect();
|
||||||
|
this.cxt=this.c.getContext('2d');
|
||||||
|
},
|
||||||
|
getPointLocationArr(){//获取九宫格圆心位置信息
|
||||||
|
//获取圆点之间的间距
|
||||||
|
let diffX=(this.CW-this.offsetX*2-this.Radius*2*3)/2;
|
||||||
|
let diffY=(this.CH-this.offsetY*2-this.Radius*2*3)/2;
|
||||||
|
for(let row=0;row<3;row++){
|
||||||
|
for(let col=0; col<3;col++){
|
||||||
|
let point={
|
||||||
|
x:this.offsetX+col*diffX+(col*2+1)*this.Radius,
|
||||||
|
y:this.offsetY+row*diffY+(row*2+1)*this.Radius
|
||||||
|
}
|
||||||
|
this.Re.push(point);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
drawPoint(touches,touchPoint){//选中的圆画圆心并连接起来
|
||||||
|
let pointLine=this.pointLine;
|
||||||
|
this.cxt.beginPath();
|
||||||
|
for(let i=0; i<pointLine.length;i++){
|
||||||
|
let point=this.Re[pointLine[i]];//根据存储的圆点的下标找到圆点的圆心位置
|
||||||
|
//连接选中的点begin
|
||||||
|
this.cxt.lineTo(point.x,point.y);
|
||||||
|
}
|
||||||
|
this.cxt.strokeStyle=this.selectColor;
|
||||||
|
this.cxt.lineWidth=this.lineWidth;
|
||||||
|
this.cxt.stroke();
|
||||||
|
this.cxt.closePath();
|
||||||
|
if(touchPoint){
|
||||||
|
let lastPoint=this.Re[this.pointLine[this.pointLine.length-1]];
|
||||||
|
this.cxt.beginPath();
|
||||||
|
this.cxt.moveTo(lastPoint.x,lastPoint.y);
|
||||||
|
this.cxt.lineTo(touches.pageX-this.cPosition.left,touches.pageY-this.cPosition.top);
|
||||||
|
this.cxt.strokeStyle=this.selectColor;
|
||||||
|
this.cxt.lineWidth=this.lineWidth;
|
||||||
|
this.cxt.stroke();
|
||||||
|
this.cxt.closePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
isPointSelect(touchs){//圆点是否被选中
|
||||||
|
let Re=this.Re;
|
||||||
|
for(let i=0;i<Re.length;i++){
|
||||||
|
let currentPonit=Re[i];
|
||||||
|
let xdiff=Math.abs(touchs.pageX-currentPonit.x-this.cPosition.left);
|
||||||
|
let ydiff=Math.abs(touchs.pageY-currentPonit.y-this.cPosition.top);
|
||||||
|
let dir=Math.pow(xdiff*xdiff+ydiff*ydiff,0.5);//当前鼠标的位置与圆心之间的距离两边和的开方
|
||||||
|
if(dir<=this.Radius){
|
||||||
|
if(this.pointLine.indexOf(i)==-1){
|
||||||
|
this.pointLine.push(i);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
initEvent(){
|
||||||
|
this.c.addEventListener('touchstart',(e)=>{
|
||||||
|
this.isPointSelect(e.touches[0]);
|
||||||
|
});
|
||||||
|
this.c.addEventListener('touchmove',(e)=>{
|
||||||
|
this.isPointSelect(e.touches[0]);
|
||||||
|
//清空画布
|
||||||
|
this.cxt.clearRect(0,0,this.CW,this.CH);
|
||||||
|
this.draw(e.touches[0],true);
|
||||||
|
|
||||||
|
});
|
||||||
|
this.c.addEventListener('touchend',(e)=>{
|
||||||
|
if(this.pointLine.length<4){
|
||||||
|
alert('密码长度不能小于4!');
|
||||||
|
this.selectColor=this.errorColor;
|
||||||
|
this.innerColor=this.errorInnerColor;
|
||||||
|
}
|
||||||
|
this.checkIsRight();
|
||||||
|
//清空画布
|
||||||
|
this.cxt.clearRect(0,0,this.CW,this.CH);
|
||||||
|
this.draw(e.touches[0],false);
|
||||||
|
this.resetCxt(e.touches[0]);
|
||||||
|
|
||||||
|
});
|
||||||
|
},
|
||||||
|
resetCxt(touches){
|
||||||
|
setTimeout(()=>{
|
||||||
|
this.pointLine=[];
|
||||||
|
this.selectColor=this.successColor;
|
||||||
|
this.innerColor=this.successInnerColor;
|
||||||
|
this.cxt.clearRect(0,0,this.CW,this.CH);
|
||||||
|
this.draw(touches,false);
|
||||||
|
},3000);
|
||||||
|
},
|
||||||
|
checkIsRight(){
|
||||||
|
if(this.password.toString()!=this.pointLine.toString()){
|
||||||
|
this.selectColor=this.errorColor;
|
||||||
|
this.innerColor=this.errorInnerColor;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
draw(touches,touchPonit){
|
||||||
|
//画九宫格
|
||||||
|
if(this.pointLine.length>0)
|
||||||
|
this.drawPoint(touches,touchPonit);
|
||||||
|
let Re=this.Re;
|
||||||
|
for(let i=0; i<Re.length;i++){
|
||||||
|
let point=Re[i];
|
||||||
|
//画外层蓝色的圆
|
||||||
|
this.cxt.fillStyle=this.selectColor;
|
||||||
|
this.cxt.beginPath();
|
||||||
|
this.cxt.arc(point.x,point.y,this.Radius,0,2*Math.PI,true);//圆心x坐标,y坐标,圆的半径,从0 画到360,逆时针画图
|
||||||
|
this.cxt.fill();
|
||||||
|
this.cxt.closePath();
|
||||||
|
//画内部白色的圆
|
||||||
|
this.cxt.fillStyle=this.innerColor;
|
||||||
|
this.cxt.beginPath();
|
||||||
|
this.cxt.arc(point.x,point.y,this.Radius-this.borderWidth,0,2*Math.PI,true);
|
||||||
|
this.cxt.fill();
|
||||||
|
this.cxt.closePath();
|
||||||
|
//画选中圆点的圆心
|
||||||
|
if(this.pointLine.indexOf(i)!=-1){
|
||||||
|
//画选中的点begin
|
||||||
|
this.cxt.fillStyle=this.selectColor;
|
||||||
|
this.cxt.beginPath();
|
||||||
|
this.cxt.arc(point.x,point.y,this.pointWidth,0,2*Math.PI,true);
|
||||||
|
this.cxt.fill();
|
||||||
|
this.cxt.closePath();
|
||||||
|
//画选中的点end
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
8
src/components/models.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
export interface Todo {
|
||||||
|
id: number;
|
||||||
|
content: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Meta {
|
||||||
|
totalCount: number;
|
||||||
|
}
|
||||||
1
src/css/app.scss
Normal file
@ -0,0 +1 @@
|
|||||||
|
// app global css in SCSS form
|
||||||
25
src/css/quasar.variables.scss
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
$primary : #1976D2;
|
||||||
|
$secondary : #36BA98;
|
||||||
|
$accent : #E9C46A;
|
||||||
|
|
||||||
|
$dark : #1D1D1D;
|
||||||
|
$dark-page : #121212;
|
||||||
|
|
||||||
|
$positive : #F4A261;
|
||||||
|
$negative : #E76F51;
|
||||||
|
$info : #31CCEC;
|
||||||
|
$warning : #F2C037;
|
||||||
9
src/env.d.ts
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
|
declare namespace NodeJS {
|
||||||
|
interface ProcessEnv {
|
||||||
|
NODE_ENV: string;
|
||||||
|
VUE_ROUTER_MODE: 'hash' | 'history' | 'abstract' | undefined;
|
||||||
|
VUE_ROUTER_BASE: string | undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
7
src/i18n/en-US/index.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
// This is just an example,
|
||||||
|
// so you can safely delete all default props below
|
||||||
|
|
||||||
|
export default {
|
||||||
|
failed: 'Action failed',
|
||||||
|
success: 'Action was successful'
|
||||||
|
};
|
||||||
5
src/i18n/index.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import enUS from './en-US';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
'en-US': enUS
|
||||||
|
};
|
||||||
93
src/layouts/AdminLayout.vue
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
<template>
|
||||||
|
<q-layout view="hHh lpR fFf">
|
||||||
|
|
||||||
|
<q-header reveal class="bg-secondary text-white" height-hint="98">
|
||||||
|
<q-toolbar>
|
||||||
|
<q-btn dense flat round icon="menu" @click="toggleLeftDrawer"/>
|
||||||
|
|
||||||
|
<q-toolbar-title>
|
||||||
|
<div class="row justify-between q-ma-md">
|
||||||
|
<div class="col-4">
|
||||||
|
<q-avatar>
|
||||||
|
<img src="https://cdn.quasar.dev/logo-v2/svg/logo-mono-white.svg">
|
||||||
|
</q-avatar>
|
||||||
|
后台管理系统
|
||||||
|
</div>
|
||||||
|
<div class="row col-2 justify-end">
|
||||||
|
<!-- <q-btn dense round flat icon="email" @click="routeTo('/admin/order')">-->
|
||||||
|
<!-- <q-badge color="red" floating transparent v-if="message_store.getMessageNumber>0">-->
|
||||||
|
<!-- {{message_store.getMessageNumber}}-->
|
||||||
|
<!-- </q-badge>-->
|
||||||
|
<!-- </q-btn>-->
|
||||||
|
<q-btn flat icon="directions" @click="logOut()" >
|
||||||
|
退出
|
||||||
|
</q-btn>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-toolbar-title>
|
||||||
|
</q-toolbar>
|
||||||
|
|
||||||
|
<q-tabs align="left">
|
||||||
|
<q-route-tab @click="routeTo('/admin')" label="首页"/>
|
||||||
|
<q-route-tab @click="routeTo('/admin/article')" label="文章管理"/>
|
||||||
|
</q-tabs>
|
||||||
|
</q-header>
|
||||||
|
|
||||||
|
<q-drawer v-model="leftDrawerOpen" side="left" bordered>
|
||||||
|
<div class="q-pa-md" style="max-width: 350px">
|
||||||
|
<q-list>
|
||||||
|
<!-- <q-item clickable v-ripple>-->
|
||||||
|
<!-- <q-item-section avatar>-->
|
||||||
|
<!-- <q-icon color="green-5" name="home"/>-->
|
||||||
|
<!-- </q-item-section>-->
|
||||||
|
|
||||||
|
<!-- <q-item-section class="text-bold" @click="routeTo('/admin')">首页</q-item-section>-->
|
||||||
|
<!-- </q-item>-->
|
||||||
|
|
||||||
|
<q-item clickable v-ripple>
|
||||||
|
<q-item-section avatar>
|
||||||
|
<q-icon color="green-5" name="filter_frames"/>
|
||||||
|
</q-item-section>
|
||||||
|
|
||||||
|
<q-item-section class="text-bold" @click="routeTo('/admin/article')">文章管理</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
</q-list>
|
||||||
|
</div>
|
||||||
|
</q-drawer>
|
||||||
|
|
||||||
|
<q-page-container>
|
||||||
|
<router-view/>
|
||||||
|
</q-page-container>
|
||||||
|
|
||||||
|
</q-layout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {onMounted, ref} from 'vue'
|
||||||
|
import router from 'src/router';
|
||||||
|
import {userStore} from 'stores/userStore';
|
||||||
|
import {notifySuccess} from 'src/utils/NotifyUtils';
|
||||||
|
|
||||||
|
const leftDrawerOpen = ref(false)
|
||||||
|
const user_store = userStore();
|
||||||
|
const messageNumber = ref(0)
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
user_store.getInfo()
|
||||||
|
})
|
||||||
|
|
||||||
|
function toggleLeftDrawer() {
|
||||||
|
leftDrawerOpen.value = !leftDrawerOpen.value
|
||||||
|
}
|
||||||
|
function routeTo(path:string){
|
||||||
|
router.push(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
function logOut() {
|
||||||
|
// user_store.logOut();
|
||||||
|
notifySuccess("登出成功")
|
||||||
|
routeTo("/")
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
102
src/layouts/MainLayout.vue
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
<template>
|
||||||
|
<q-layout view="lHh Lpr lFf">
|
||||||
|
<q-header elevated>
|
||||||
|
<q-toolbar>
|
||||||
|
<q-btn
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
round
|
||||||
|
icon="menu"
|
||||||
|
aria-label="Menu"
|
||||||
|
@click="toggleLeftDrawer"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<q-toolbar-title>
|
||||||
|
Quasar App
|
||||||
|
</q-toolbar-title>
|
||||||
|
|
||||||
|
<div>Quasar v{{ $q.version }}</div>
|
||||||
|
</q-toolbar>
|
||||||
|
</q-header>
|
||||||
|
|
||||||
|
<q-drawer
|
||||||
|
v-model="leftDrawerOpen"
|
||||||
|
show-if-above
|
||||||
|
bordered
|
||||||
|
>
|
||||||
|
<q-list>
|
||||||
|
<q-item-label
|
||||||
|
header
|
||||||
|
>
|
||||||
|
Essential Links
|
||||||
|
</q-item-label>
|
||||||
|
|
||||||
|
<EssentialLink
|
||||||
|
v-for="link in essentialLinks"
|
||||||
|
:key="link.title"
|
||||||
|
v-bind="link"
|
||||||
|
/>
|
||||||
|
</q-list>
|
||||||
|
</q-drawer>
|
||||||
|
|
||||||
|
<q-page-container>
|
||||||
|
<router-view />
|
||||||
|
</q-page-container>
|
||||||
|
</q-layout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import EssentialLink, { EssentialLinkProps } from 'components/EssentialLink.vue';
|
||||||
|
|
||||||
|
const essentialLinks: EssentialLinkProps[] = [
|
||||||
|
{
|
||||||
|
title: 'Docs',
|
||||||
|
caption: 'quasar.dev',
|
||||||
|
icon: 'school',
|
||||||
|
link: 'https://quasar.dev'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Github',
|
||||||
|
caption: 'github.com/quasarframework',
|
||||||
|
icon: 'code',
|
||||||
|
link: 'https://github.com/quasarframework'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Discord Chat Channel',
|
||||||
|
caption: 'chat.quasar.dev',
|
||||||
|
icon: 'chat',
|
||||||
|
link: 'https://chat.quasar.dev'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Forum',
|
||||||
|
caption: 'forum.quasar.dev',
|
||||||
|
icon: 'record_voice_over',
|
||||||
|
link: 'https://forum.quasar.dev'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Twitter',
|
||||||
|
caption: '@quasarframework',
|
||||||
|
icon: 'rss_feed',
|
||||||
|
link: 'https://twitter.quasar.dev'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Facebook',
|
||||||
|
caption: '@QuasarFramework',
|
||||||
|
icon: 'public',
|
||||||
|
link: 'https://facebook.quasar.dev'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Quasar Awesome',
|
||||||
|
caption: 'Community Quasar projects',
|
||||||
|
icon: 'favorite',
|
||||||
|
link: 'https://awesome.quasar.dev'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const leftDrawerOpen = ref(false)
|
||||||
|
|
||||||
|
function toggleLeftDrawer() {
|
||||||
|
leftDrawerOpen.value = !leftDrawerOpen.value
|
||||||
|
}
|
||||||
|
</script>
|
||||||
27
src/pages/ErrorNotFound.vue
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<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 setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
42
src/pages/IndexPage.vue
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<template>
|
||||||
|
<q-page class="row items-center justify-evenly">
|
||||||
|
<example-component
|
||||||
|
title="Example component"
|
||||||
|
active
|
||||||
|
:todos="todos"
|
||||||
|
:meta="meta"
|
||||||
|
></example-component>
|
||||||
|
</q-page>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Todo, Meta } from 'components/models';
|
||||||
|
import ExampleComponent from 'components/ExampleComponent.vue';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
const todos = ref<Todo[]>([
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
content: 'ct1'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
content: 'ct2'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
content: 'ct3'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
content: 'ct4'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
content: 'ct5'
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
const meta = ref<Meta>({
|
||||||
|
totalCount: 1200
|
||||||
|
});
|
||||||
|
</script>
|
||||||
225
src/pages/manage/AdminPage.vue
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
<template>
|
||||||
|
<q-page class="bg-grey-2">
|
||||||
|
<div class="q-pa-lg">
|
||||||
|
<div class="text-body1 text-bold">
|
||||||
|
首页
|
||||||
|
</div>
|
||||||
|
<q-separator/>
|
||||||
|
<!-- {{ showWindowFlag }}-->
|
||||||
|
<div class="row justify-around">
|
||||||
|
<baidu-map class="bm-view"
|
||||||
|
ak="Beu0HwnbpN8k7DRtAjZs8MyMk4nqVZFr"
|
||||||
|
type="API"
|
||||||
|
:zoom="zoom"
|
||||||
|
:center="center"
|
||||||
|
@dblclick="dbShow"
|
||||||
|
@ready="whenReady"
|
||||||
|
ref="mapRef"
|
||||||
|
:scroll-wheel-zoom="true"
|
||||||
|
>
|
||||||
|
<bm-point-collection :points="points" color="red" size="BMAP_POINT_SIZE_SMALL" @click="clickHandler"></bm-point-collection>
|
||||||
|
<bm-marker :position="marked" @dblclick="showWindow" ></bm-marker>
|
||||||
|
<bm-info-window :position="position" :show="showWindowFlag" @close="close">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 text-body2">地址:{{lat}},{{lng}}</div>
|
||||||
|
<div class="col-auto text-body2">距离:</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<q-btn color="primary" label="导航到此处" flat size="sm" align="left"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</bm-info-window>
|
||||||
|
<bm-scale anchor="BMAP_ANCHOR_TOP_RIGHT"></bm-scale>
|
||||||
|
<bm-navigation anchor="BMAP_ANCHOR_BOTTOM_RIGHT"></bm-navigation>
|
||||||
|
<bm-geolocation anchor="BMAP_ANCHOR_BOTTOM_LEFT" :showAddressBar="true" :autoLocation="true"></bm-geolocation>
|
||||||
|
<bm-city-list anchor="BMAP_ANCHOR_TOP_LEFT"></bm-city-list>
|
||||||
|
</baidu-map>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row justify-center">
|
||||||
|
<!-- <div class="my-animation"></div>-->
|
||||||
|
<!-- <div class="my-animation2"></div>-->
|
||||||
|
<!-- <q-btn/>-->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</q-page>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {BaiduMap, BmScale, BmNavigation, BmGeolocation, BmCityList, BmControl} from 'vue-baidu-map-3x';
|
||||||
|
import {useGeocoder, usePoint, BmMarker, BmLabel, BmInfoWindow, BmPointCollection} from 'vue-baidu-map-3x';
|
||||||
|
import {ref} from 'vue';
|
||||||
|
|
||||||
|
const center = ref({lng: 116.404, lat: 39.915})
|
||||||
|
const zoom = ref(15)
|
||||||
|
const showLatLng = ref(false);
|
||||||
|
const points = ref([]);
|
||||||
|
const lat = ref(null)
|
||||||
|
const lng = ref(null)
|
||||||
|
const BmapIn = ref(null)
|
||||||
|
const mapIn = ref(null)
|
||||||
|
const mapRef = ref(null)
|
||||||
|
const marked = ref({lng: 0, lat: 0})
|
||||||
|
const position = ref({lng: 0, lat: 0})
|
||||||
|
const showWindowFlag = ref(false)
|
||||||
|
|
||||||
|
|
||||||
|
function showWindow(event) {
|
||||||
|
// 获取鼠标双击位置的像素坐标
|
||||||
|
const point = event.pixel;
|
||||||
|
lat.value = event.point.lat;
|
||||||
|
lng.value = event.point.lng;
|
||||||
|
// 将像素坐标转换为地理坐标(经纬度)
|
||||||
|
showWindowFlag.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function close() {
|
||||||
|
showWindowFlag.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const clickHandler = (event) => {
|
||||||
|
// 获取鼠标双击位置的像素坐标
|
||||||
|
lat.value = event.point.lat;
|
||||||
|
lng.value = event.point.lng;
|
||||||
|
position.value.lat = lat.value;
|
||||||
|
position.value.lng = lng.value;
|
||||||
|
|
||||||
|
// 将像素坐标转换为地理坐标(经纬度)
|
||||||
|
showWindowFlag.value = true
|
||||||
|
};
|
||||||
|
|
||||||
|
function whenReady({BMap, map}) {
|
||||||
|
// 将地图中心设置为昆明市政府的经纬度
|
||||||
|
center.value = {lng: 102.712251, lat: 25.040609};
|
||||||
|
// 获取地图实例
|
||||||
|
mapIn.value = map;
|
||||||
|
BmapIn.value = BMap;
|
||||||
|
|
||||||
|
console.log("Bmap:");
|
||||||
|
console.log(BMap);
|
||||||
|
console.log("map:"+map);
|
||||||
|
console.log(map);
|
||||||
|
|
||||||
|
|
||||||
|
const pointAll = [];
|
||||||
|
for (var i = 0; i < 30; i++) {
|
||||||
|
const position = {lng: Math.random() * 40 + 85, lat: Math.random() * 30 + 21};
|
||||||
|
pointAll.push(position);
|
||||||
|
}
|
||||||
|
points.value = pointAll;
|
||||||
|
|
||||||
|
const sw = new BMap.Point(97.517378, 21.143333); // 西南角(云南的西南角经纬度)
|
||||||
|
const ne = new BMap.Point(106.117187, 29.25284); // 东北角(云南的东北角经纬度)
|
||||||
|
const bounds = new BMap.Bounds(sw, ne);
|
||||||
|
const geo = new BMap.Geolocation();
|
||||||
|
console.log(geo)
|
||||||
|
|
||||||
|
useGeocoder().then((geocoder) => {
|
||||||
|
geocoder.getPoint('昆明市盘龙区低碳中心', (res) => {
|
||||||
|
marked.value.lat = res.lat
|
||||||
|
marked.value.lng = res.lng
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
if (navigator.geolocation) {
|
||||||
|
console.log(navigator.geolocation)
|
||||||
|
geo.enableSDKLocation();
|
||||||
|
geo.getCurrentPosition(function (r) {
|
||||||
|
if (this.getStatus() == BMAP_STATUS_SUCCESS) {
|
||||||
|
var mk = new BMap.Marker(r.point);
|
||||||
|
map.addOverlay(mk);
|
||||||
|
map.panTo(r.point);
|
||||||
|
alert('您的位置:' + r.point.lng + ',' + r.point.lat);
|
||||||
|
} else {
|
||||||
|
alert('failed' + this.getStatus());
|
||||||
|
console.log(this.getStatus())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
alert('浏览器不支持定位')
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.bm-view {
|
||||||
|
width: 100%;
|
||||||
|
height: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes my-animation {
|
||||||
|
0% {background: red; left:0px; top:0px;}
|
||||||
|
25% {background: yellow; left:200px; top:0px;}
|
||||||
|
50% {background: blue; left:200px; top:200px;}
|
||||||
|
75% {background: green; left:0px; top:200px;}
|
||||||
|
100% {background: red; left:0px; top:0px;}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes my-animation2 {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
left: 500px;
|
||||||
|
}
|
||||||
|
25% {
|
||||||
|
transform: rotate(20deg);
|
||||||
|
left: 500px;
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
left: 900px;
|
||||||
|
}
|
||||||
|
55% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
left: 900px;
|
||||||
|
}
|
||||||
|
70% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
left: 900px;
|
||||||
|
background: #1ec7e6;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(-360deg);
|
||||||
|
left: 500px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.my-animation {
|
||||||
|
left: 0;
|
||||||
|
position: absolute;
|
||||||
|
border: 5px black;
|
||||||
|
width: 200px;
|
||||||
|
height: 200px;
|
||||||
|
border-radius: 15px;
|
||||||
|
animation: my-animation 4s infinite;
|
||||||
|
background-color: #F22722;
|
||||||
|
}
|
||||||
|
|
||||||
|
.my-animation2 {
|
||||||
|
position: absolute;
|
||||||
|
left: 500px;
|
||||||
|
border: 5px black;
|
||||||
|
width: 200px;
|
||||||
|
height: 120px;
|
||||||
|
border-radius: 15px;
|
||||||
|
animation: my-animation2 4s infinite;
|
||||||
|
background-color: #F22722;
|
||||||
|
}
|
||||||
|
|
||||||
|
.my-button {
|
||||||
|
color: white;
|
||||||
|
border-radius: 15px;
|
||||||
|
background-color: #E76F51;
|
||||||
|
border: none;
|
||||||
|
padding: 12px 25px;
|
||||||
|
overflow: hidden;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.my-button:hover {
|
||||||
|
transition: background-color ease-out 400ms;
|
||||||
|
background-color: pink;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
137
src/pages/manage/ArticleCommentPage.vue
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
<template>
|
||||||
|
<q-page class="bg-grey-2">
|
||||||
|
<div class="q-pa-lg">
|
||||||
|
|
||||||
|
<div class="text-body1 text-bold">
|
||||||
|
评论管理
|
||||||
|
</div>
|
||||||
|
<q-separator/>
|
||||||
|
|
||||||
|
<div class="text-body1 text-bold row q-py-lg justify-between">
|
||||||
|
<div class="col-2">
|
||||||
|
<q-btn flat size="20px" color="secondary" @click="routeTo('/admin/article')">
|
||||||
|
<q-avatar icon="arrow_back" size="20px"/>
|
||||||
|
文章管理
|
||||||
|
</q-btn>
|
||||||
|
</div>
|
||||||
|
<div class="col-10 bg-white">
|
||||||
|
|
||||||
|
<div class="q-gutter-xs q-pa-md">
|
||||||
|
<div class="row items-center">
|
||||||
|
<q-btn-toggle
|
||||||
|
outline
|
||||||
|
v-model="selectedButton"
|
||||||
|
toggle-color="secondary"
|
||||||
|
:options="[
|
||||||
|
{ label: '文章评论', value: 'article', color: 'accent'},
|
||||||
|
{ label: '博客评论', value: 'blog', color: 'positive' }
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="q-pa-md text-subtitle1">
|
||||||
|
<q-list>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section>
|
||||||
|
<q-list>
|
||||||
|
<q-item v-for="item in 10" :key="item">
|
||||||
|
<q-card style="width: 100%; max-height: 150px">
|
||||||
|
<q-card-section horizontal>
|
||||||
|
|
||||||
|
|
||||||
|
<q-card-section>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card-section>
|
||||||
|
<p>
|
||||||
|
XXX作者的<q-btn
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
no-caps
|
||||||
|
color="accent"
|
||||||
|
to="https://example.com"
|
||||||
|
target="_blank"
|
||||||
|
class="text-link"
|
||||||
|
>
|
||||||
|
XXX文章
|
||||||
|
</q-btn>,添加了评论:
|
||||||
|
</p>
|
||||||
|
<div class="text-subtitle2 text-grey-6">12313</div>
|
||||||
|
<q-separator/>
|
||||||
|
|
||||||
|
<q-card-actions class="float-right">
|
||||||
|
<div class="q-px-lg">
|
||||||
|
2024-11-6 10:20:20
|
||||||
|
</div>
|
||||||
|
<q-btn flat>
|
||||||
|
置顶
|
||||||
|
</q-btn>
|
||||||
|
<q-btn flat>
|
||||||
|
隐藏
|
||||||
|
</q-btn>
|
||||||
|
<q-btn flat>
|
||||||
|
删除
|
||||||
|
</q-btn>
|
||||||
|
</q-card-actions>
|
||||||
|
</q-card>
|
||||||
|
</q-item>
|
||||||
|
</q-list>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
</q-list>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-page>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
import '@wangeditor/editor/dist/css/style.css' // 引入 css
|
||||||
|
|
||||||
|
import {onBeforeUnmount, ref, shallowRef, onMounted} from 'vue'
|
||||||
|
import {Editor, Toolbar} from '@wangeditor/editor-for-vue'
|
||||||
|
import router from 'src/router';
|
||||||
|
|
||||||
|
const editorRef = shallowRef()
|
||||||
|
const mode = ref('default')
|
||||||
|
|
||||||
|
// 内容 HTML
|
||||||
|
const valueHtml = ref('<p></p>')
|
||||||
|
const selectedButton = ref('article')
|
||||||
|
|
||||||
|
// // 模拟 ajax 异步获取内容
|
||||||
|
// onMounted(() => {
|
||||||
|
// setTimeout(() => {
|
||||||
|
// valueHtml.value = '<p>模拟 Ajax 异步设置内容</p>'
|
||||||
|
// }, 1500)
|
||||||
|
// })
|
||||||
|
|
||||||
|
const toolbarConfig = {}
|
||||||
|
const editorConfig = {placeholder: '请输入内容...'}
|
||||||
|
const title = ref('')
|
||||||
|
|
||||||
|
// 组件销毁时,也及时销毁编辑器
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
const editor = editorRef.value
|
||||||
|
if (editor == null) return
|
||||||
|
editor.destroy()
|
||||||
|
})
|
||||||
|
|
||||||
|
function routeTo(path: string) {
|
||||||
|
router.push(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCreated = (editor: any) => {
|
||||||
|
editorRef.value = editor // 记录 editor 实例,重要!
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
.text-link {
|
||||||
|
text-decoration: underline;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
<style scoped>
|
||||||
|
</style>
|
||||||
47
src/pages/manage/ArticleDetailsPage.vue
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<template>
|
||||||
|
<q-page class="bg-grey-2">
|
||||||
|
<div class="q-pa-lg">
|
||||||
|
<div class="text-body1 text-bold row q-pb-lg justify-between">
|
||||||
|
<div class="col-2">
|
||||||
|
<q-btn flat size="20px" color="secondary" @click="routeTo('/admin/article')">
|
||||||
|
<q-avatar icon="arrow_back" size="20px"/>
|
||||||
|
文章管理
|
||||||
|
</q-btn>
|
||||||
|
</div>
|
||||||
|
<div class="col-7">
|
||||||
|
<q-input rounded outlined v-model="title" placeholder="请输入标题" maxlength="50" style="max-width: 1000px"/>
|
||||||
|
</div>
|
||||||
|
<div class="col-2 row justify-end">
|
||||||
|
<q-btn size="15px" color="accent" class="q-mr-sm" style="height: 36px;">
|
||||||
|
保存
|
||||||
|
</q-btn>
|
||||||
|
<q-btn size="15px" color="positive" style="height: 36px;">
|
||||||
|
发表
|
||||||
|
</q-btn>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<q-separator/>
|
||||||
|
<div class="row justify-around">
|
||||||
|
<div class="col-12">
|
||||||
|
<WangEditor/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-page>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
import router from 'src/router';
|
||||||
|
import {ref} from 'vue';
|
||||||
|
import WangEditor from 'components/Editor/WangEditor.vue';
|
||||||
|
|
||||||
|
const title = ref("")
|
||||||
|
function routeTo(path:string){
|
||||||
|
router.push(path)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="sass">
|
||||||
|
|
||||||
|
</style>
|
||||||
113
src/pages/manage/ArticleManagePage.vue
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
<template>
|
||||||
|
<q-page class="bg-grey-2">
|
||||||
|
<div class="q-pa-lg">
|
||||||
|
<div class="text-body1 text-bold">
|
||||||
|
文章管理
|
||||||
|
</div>
|
||||||
|
<q-separator/>
|
||||||
|
<div class="row justify-around">
|
||||||
|
<div class="col-3">
|
||||||
|
<div style="max-width: 350px">
|
||||||
|
<q-list>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section>
|
||||||
|
<q-btn color="secondary" icon="mail" icon-right="send" style="font-size: 25px" label="发布文章"
|
||||||
|
@click="routeTo('/admin/articleDetails')"/>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
|
||||||
|
<!-- <q-item clickable v-ripple>-->
|
||||||
|
<!-- <q-item-section avatar>-->
|
||||||
|
<!-- <q-avatar color="secondary" text-color="white">-->
|
||||||
|
<!-- 文-->
|
||||||
|
<!-- </q-avatar>-->
|
||||||
|
<!-- </q-item-section>-->
|
||||||
|
|
||||||
|
<!-- <q-item-section style="font-size: 20px">管理文章</q-item-section>-->
|
||||||
|
<!-- </q-item>-->
|
||||||
|
<q-item clickable v-ripple @click="routeTo('/admin/articleComment')">
|
||||||
|
<q-item-section avatar>
|
||||||
|
<q-avatar color="secondary" text-color="white">
|
||||||
|
评
|
||||||
|
</q-avatar>
|
||||||
|
</q-item-section>
|
||||||
|
|
||||||
|
<q-item-section style="font-size: 20px">管理评论</q-item-section>
|
||||||
|
|
||||||
|
</q-item>
|
||||||
|
</q-list>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-9 bg-white">
|
||||||
|
<q-list>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section>
|
||||||
|
<q-list>
|
||||||
|
<q-item v-for="item in 10" :key="item">
|
||||||
|
<q-card style="width: 100%; max-height: 150px">
|
||||||
|
<q-card-section horizontal>
|
||||||
|
<q-img
|
||||||
|
style="max-height: 100px"
|
||||||
|
class="col-3"
|
||||||
|
src="https://cdn.quasar.dev/img/parallax1.jpg"
|
||||||
|
/>
|
||||||
|
|
||||||
|
|
||||||
|
<q-card-section class="multi-line-ellipsis">
|
||||||
|
<div class="text-h5">文章标题</div>
|
||||||
|
只是文章简介只是文章简介只是文章简介只是文章简介只是文章简介只是文章简介只是文章简介只是文章简介只是文章简介只是文章简
|
||||||
|
介只是文章简介只是文章简介只是文章简介只是文章简介只是文章简介只是文章简介只是文章简介只是文章简介只是文章简介只是文章简介
|
||||||
|
只是文章简介只是文章简介只是文章简介只是文章简介只是文章简介只是文章简介只是文章简介只是文章简介只是文章简介只是文章简介
|
||||||
|
只是文章简介只是文章简介只是文章简介只是文章简介只是文章简介只是文章简介只是文章简介只是文章简介只是文章简介只是文章简介
|
||||||
|
|
||||||
|
</q-card-section>
|
||||||
|
</q-card-section>
|
||||||
|
|
||||||
|
<q-separator/>
|
||||||
|
|
||||||
|
<q-card-actions class="float-right">
|
||||||
|
<div class="q-px-lg">
|
||||||
|
2024-11-6 10:20:20
|
||||||
|
</div>
|
||||||
|
<q-btn flat>
|
||||||
|
置顶
|
||||||
|
</q-btn>
|
||||||
|
<q-btn flat>
|
||||||
|
隐藏
|
||||||
|
</q-btn>
|
||||||
|
<q-btn flat>
|
||||||
|
编辑
|
||||||
|
</q-btn>
|
||||||
|
<q-btn flat>
|
||||||
|
删除
|
||||||
|
</q-btn>
|
||||||
|
</q-card-actions>
|
||||||
|
</q-card>
|
||||||
|
</q-item>
|
||||||
|
</q-list>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
</q-list>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-page>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import router from 'src/router';
|
||||||
|
|
||||||
|
|
||||||
|
function routeTo(path: string) {
|
||||||
|
router.push(path)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.multi-line-ellipsis {
|
||||||
|
white-space: nowrap; /* 防止换行 */
|
||||||
|
overflow: hidden; /* 超出部分隐藏 */
|
||||||
|
text-overflow: ellipsis; /* 显示省略号 */
|
||||||
|
width: 700px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
217
src/pages/manage/LoginPage.vue
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
<template>
|
||||||
|
<div class="login-container row items-center justify-center">
|
||||||
|
<q-form class="layui-form" @submit="onSubmit">
|
||||||
|
<div class="cc-login-form-item">
|
||||||
|
<!-- <img class="logo" src="icons/logo/cc-admin-logo.svg"/>-->
|
||||||
|
<div class="title">小小数据处理站</div>
|
||||||
|
<div class="desc">
|
||||||
|
奥里给!!!
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="cc-login-form-item">
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model.trim="username"
|
||||||
|
dense
|
||||||
|
debounce="500"
|
||||||
|
lazy-rules
|
||||||
|
placeholder="用户名:"
|
||||||
|
hide-bottom-space
|
||||||
|
bg-color="white"
|
||||||
|
square
|
||||||
|
:rules="[(val: string) => (val && val.length > 0) || '请输入用户名']"
|
||||||
|
>
|
||||||
|
</q-input>
|
||||||
|
</div>
|
||||||
|
<div class="cc-login-form-item">
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
clear-icon="cancel"
|
||||||
|
type='password'
|
||||||
|
v-model.trim="password"
|
||||||
|
dense
|
||||||
|
bg-color="white"
|
||||||
|
placeholder="密码:"
|
||||||
|
debounce="500"
|
||||||
|
hide-bottom-space
|
||||||
|
lazy-rules
|
||||||
|
square
|
||||||
|
:rules="[(val: string) => (val && val.length > 0) || '请输入密码']"
|
||||||
|
>
|
||||||
|
</q-input>
|
||||||
|
</div>
|
||||||
|
<div class="cc-login-form-item row justify-between">
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
clear-icon="cancel"
|
||||||
|
type='text'
|
||||||
|
v-model.trim="dcode"
|
||||||
|
dense
|
||||||
|
style="width: 300px"
|
||||||
|
bg-color="white"
|
||||||
|
placeholder="验证码 单击图片刷新"
|
||||||
|
debounce="500"
|
||||||
|
hide-bottom-space
|
||||||
|
lazy-rules
|
||||||
|
square
|
||||||
|
:rules="[(val: string) => (val && val.length > 0) || '请输入右侧动态码']"
|
||||||
|
/>
|
||||||
|
<q-img width="190px" height="40px" :src="user_store.dcodeImage.code" @click="user_store.getDcode()">
|
||||||
|
</q-img>
|
||||||
|
</div>
|
||||||
|
<div class="cc-login-form-item">
|
||||||
|
<q-btn
|
||||||
|
dense
|
||||||
|
unelevated
|
||||||
|
label="登 录"
|
||||||
|
size="17px"
|
||||||
|
color="green-5"
|
||||||
|
class="full-width no-border-radius q-pa-none"
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
<template v-slot:loading>
|
||||||
|
<q-spinner-ios class="on-left"/>
|
||||||
|
登录...
|
||||||
|
</template>
|
||||||
|
</q-btn>
|
||||||
|
</div>
|
||||||
|
</q-form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {onMounted, ref} from 'vue';
|
||||||
|
import {userStore} from 'stores/userStore';
|
||||||
|
import {notifySuccess} from 'src/utils/NotifyUtils';
|
||||||
|
import {useRouter} from 'vue-router';
|
||||||
|
|
||||||
|
|
||||||
|
const username = ref('')
|
||||||
|
const password = ref('')
|
||||||
|
const dcode = ref('')
|
||||||
|
const loggingIn = ref(false);
|
||||||
|
const user_store = userStore();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
function loginSuccess(): void {
|
||||||
|
notifySuccess('欢迎登录')
|
||||||
|
router.push('/admin');
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
user_store.getDcode()
|
||||||
|
})
|
||||||
|
|
||||||
|
async function onSubmit(): Promise<void> {
|
||||||
|
if (loggingIn.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
loggingIn.value = true;
|
||||||
|
|
||||||
|
const token = await user_store.loginByPassword(username.value, password.value,dcode.value);
|
||||||
|
if (token) {
|
||||||
|
loginSuccess()
|
||||||
|
}
|
||||||
|
loggingIn.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus">
|
||||||
|
.login-container
|
||||||
|
width 100%
|
||||||
|
height 100vh
|
||||||
|
background-image url("src/assets/login/background.svg")
|
||||||
|
background-repeat no-repeat
|
||||||
|
background-size 100% 100%
|
||||||
|
position relative
|
||||||
|
|
||||||
|
.layui-form {
|
||||||
|
width: 500px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layui-form button {
|
||||||
|
width: 100%;
|
||||||
|
height: 44px;
|
||||||
|
line-height: 44px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 550;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layui-tab-content {
|
||||||
|
margin-top: 15px;
|
||||||
|
padding-left: 0px;
|
||||||
|
padding-right: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-login-form-item {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-login-form-item .q-btn__wrapper {
|
||||||
|
padding: 0em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layui-input {
|
||||||
|
height: 44px;
|
||||||
|
line-height: 44px;
|
||||||
|
padding-left: 15px;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layui-input:focus {
|
||||||
|
box-shadow: 0px 0px 2px 1px #5FB878;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layui-form-danger:focus {
|
||||||
|
box-shadow: 0px 0px 2px 1px #f56c6c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
width: 60px;
|
||||||
|
margin-left: 20px;
|
||||||
|
transform: translateY(-6000px);
|
||||||
|
filter: drop-shadow(#FF9933 0 6000px)
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 30px;
|
||||||
|
font-weight: 550;
|
||||||
|
text-align: center;
|
||||||
|
color: #5FB878;
|
||||||
|
height: 60px;
|
||||||
|
line-height: 60px;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.desc {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
color: gray;
|
||||||
|
height: 60px;
|
||||||
|
line-height: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-color: whitesmoke;
|
||||||
|
background-size: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code {
|
||||||
|
float: left;
|
||||||
|
margin-right: 13px;
|
||||||
|
margin: 0px;
|
||||||
|
border: #e6e6e6 1px solid;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codeImage {
|
||||||
|
float: right;
|
||||||
|
width: 160px;
|
||||||
|
height: 42px;
|
||||||
|
border: #781a1a 2px solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
9
src/quasar.d.ts
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
/* 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" />
|
||||||
27
src/router/index.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const createHistory = process.env.SERVER
|
||||||
|
? createMemoryHistory
|
||||||
|
: (process.env.VUE_ROUTER_MODE === 'history' ? createWebHistory : createWebHashHistory);
|
||||||
|
|
||||||
|
const router = createRouter({
|
||||||
|
history: createHistory(process.env.VUE_ROUTER_BASE),
|
||||||
|
routes,
|
||||||
|
});
|
||||||
|
export default router;
|
||||||
30
src/router/routes.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { RouteRecordRaw } from 'vue-router';
|
||||||
|
|
||||||
|
const routes: RouteRecordRaw[] = [
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
component: () => import('pages/manage/LoginPage.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/manage',
|
||||||
|
component: () => import('pages/manage/LoginPage.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/admin',
|
||||||
|
component: () => import('layouts/AdminLayout.vue'),
|
||||||
|
children: [{path: '', component: () => import('pages/manage/AdminPage.vue')},
|
||||||
|
{path: 'article', component: () => import('pages/manage/ArticleManagePage.vue')},
|
||||||
|
{path: 'articleDetails', component: () => import('pages/manage/ArticleDetailsPage.vue')},
|
||||||
|
{path: 'articleComment', component: () => import('pages/manage/ArticleCommentPage.vue')},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
// Always leave this as last one,
|
||||||
|
// but you can also remove it
|
||||||
|
{
|
||||||
|
path: '/:catchAll(.*)*',
|
||||||
|
component: () => import('pages/ErrorNotFound.vue'),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default routes;
|
||||||
10
src/shims-vue.d.ts
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
/* 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;
|
||||||
|
}
|
||||||
53
src/stores/articleStore.ts
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import { defineStore } from 'pinia';
|
||||||
|
import {addArticle} from 'src/api/kmArticle';
|
||||||
|
|
||||||
|
export interface KmArticle {
|
||||||
|
articleId: string,
|
||||||
|
articleTitle: string,
|
||||||
|
articleIntroduce: string,
|
||||||
|
articleAuthor: string,
|
||||||
|
articlePicture: string,
|
||||||
|
articleText: string,
|
||||||
|
// 0为置顶,1为普通
|
||||||
|
isFirst: number,
|
||||||
|
// 0为隐藏,1为普通
|
||||||
|
isHide: number,
|
||||||
|
regionalClassification: string,
|
||||||
|
foodClassification: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
kmArticle: KmArticle;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useCounterStore = defineStore('article', {
|
||||||
|
state: (): State => {
|
||||||
|
return {
|
||||||
|
kmArticle : {
|
||||||
|
articleId : '',
|
||||||
|
articleTitle:'',
|
||||||
|
articleIntroduce:'',
|
||||||
|
articleAuthor:'',
|
||||||
|
articlePicture:'',
|
||||||
|
articleText:'',
|
||||||
|
isFirst:1,
|
||||||
|
isHide:1,
|
||||||
|
regionalClassification:'五华区',
|
||||||
|
foodClassification:'昆明菜'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
getters: {
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
async addArticle(data:KmArticle){
|
||||||
|
const result = await addArticle(data);
|
||||||
|
console.log(result)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
15
src/stores/example-store.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { defineStore } from 'pinia';
|
||||||
|
|
||||||
|
export const useCounterStore = defineStore('counter', {
|
||||||
|
state: () => ({
|
||||||
|
counter: 0,
|
||||||
|
}),
|
||||||
|
getters: {
|
||||||
|
doubleCount: (state) => state.counter * 2,
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
increment() {
|
||||||
|
this.counter++;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
32
src/stores/index.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
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
|
||||||
|
})
|
||||||
107
src/stores/messageStore.ts
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
import { defineStore } from 'pinia';
|
||||||
|
import {
|
||||||
|
deleteById, finishList,
|
||||||
|
finishOne,
|
||||||
|
getAllFinishlist,
|
||||||
|
getAllUnfinishlist,
|
||||||
|
getList,
|
||||||
|
insertMessage
|
||||||
|
} from 'src/api/customerMessage';
|
||||||
|
import {api} from 'boot/axios';
|
||||||
|
import {hello} from 'src/api/login';
|
||||||
|
|
||||||
|
export interface XyCustomerMessage {
|
||||||
|
id : string,
|
||||||
|
name: string,
|
||||||
|
contactMethod : string,
|
||||||
|
contactInfo : string,
|
||||||
|
translateType : string,
|
||||||
|
sourceLanguage : string,
|
||||||
|
targetLanguage : string,
|
||||||
|
deliveryRequest : string,
|
||||||
|
createdTime : Date,
|
||||||
|
createdBy : string,
|
||||||
|
updatedTime : Date,
|
||||||
|
updatedBy : string,
|
||||||
|
processed : number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Page{
|
||||||
|
page : number,
|
||||||
|
size : number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
xyCustomerMessage: XyCustomerMessage;
|
||||||
|
page: Page;
|
||||||
|
unfinishedList:XyCustomerMessage[]
|
||||||
|
messageList:XyCustomerMessage[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const messageStore = defineStore('message', {
|
||||||
|
state: (): State => {
|
||||||
|
return {
|
||||||
|
xyCustomerMessage : {
|
||||||
|
id:'',
|
||||||
|
name:'',
|
||||||
|
contactMethod:'',
|
||||||
|
contactInfo:'',
|
||||||
|
translateType:'',
|
||||||
|
targetLanguage:'',
|
||||||
|
sourceLanguage:'',
|
||||||
|
deliveryRequest:'',
|
||||||
|
createdBy:'',
|
||||||
|
createdTime : new Date(),
|
||||||
|
updatedBy : '',
|
||||||
|
updatedTime : new Date(),
|
||||||
|
processed: 1
|
||||||
|
},
|
||||||
|
page:{
|
||||||
|
page:1,
|
||||||
|
size:8
|
||||||
|
},
|
||||||
|
unfinishedList:[],
|
||||||
|
messageList:[]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getters: {
|
||||||
|
getMessageNumber(state){
|
||||||
|
console.log(this.messageList)
|
||||||
|
return state.unfinishedList.length
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
async insertCustomerMessage(name:string,contactMethod:string,contactInfo:string,translateType:string,targetLanguage:string,sourceLanguage:string,deliveryRequest:string) {
|
||||||
|
this.xyCustomerMessage.name = name
|
||||||
|
this.xyCustomerMessage.contactMethod = contactMethod
|
||||||
|
this.xyCustomerMessage.contactInfo = contactInfo
|
||||||
|
this.xyCustomerMessage.translateType = translateType
|
||||||
|
this.xyCustomerMessage.targetLanguage = targetLanguage
|
||||||
|
this.xyCustomerMessage.sourceLanguage = sourceLanguage
|
||||||
|
this.xyCustomerMessage.deliveryRequest = deliveryRequest
|
||||||
|
await insertMessage(this.xyCustomerMessage)
|
||||||
|
},
|
||||||
|
async getCustomerMessages(){
|
||||||
|
this.messageList = await getList();
|
||||||
|
},
|
||||||
|
async getUnfinishedCustomerMessages(){
|
||||||
|
this.messageList = await getAllUnfinishlist();
|
||||||
|
this.unfinishedList = this.messageList
|
||||||
|
},
|
||||||
|
async getFinishedCustomerMessages(){
|
||||||
|
this.messageList = await getAllFinishlist();
|
||||||
|
},
|
||||||
|
async finishOne(id:string){
|
||||||
|
await finishOne(id);
|
||||||
|
},
|
||||||
|
async deleteOne(id:string){
|
||||||
|
await deleteById(id)
|
||||||
|
},
|
||||||
|
async finishList(ids:[]){
|
||||||
|
console.log(ids)
|
||||||
|
await finishList(ids)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
10
src/stores/store-flag.d.ts
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
91
src/stores/userStore.ts
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
import { defineStore } from 'pinia';
|
||||||
|
import {getDcode, getInfo, hello, loginRequest} from 'src/api/login';
|
||||||
|
import {CookieUtil, tokenKey} from 'src/utils/CookieUtils';
|
||||||
|
|
||||||
|
export interface UserLoginReq {
|
||||||
|
userName:string;
|
||||||
|
password:string;
|
||||||
|
dcodeId:string;
|
||||||
|
dcode:string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DcodeImage {
|
||||||
|
uuid:string;
|
||||||
|
code:string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UserStore {
|
||||||
|
id: string;
|
||||||
|
username: string;
|
||||||
|
token: string;
|
||||||
|
password: string;
|
||||||
|
imagePath: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
user: UserStore;
|
||||||
|
loginReq: UserLoginReq;
|
||||||
|
dcodeImage:DcodeImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const userStore = defineStore('userInfo', {
|
||||||
|
state: (): State => {
|
||||||
|
return {
|
||||||
|
user: {
|
||||||
|
id: '',
|
||||||
|
username: '',
|
||||||
|
password: '',
|
||||||
|
imagePath: '',
|
||||||
|
token: '',
|
||||||
|
},
|
||||||
|
loginReq: {
|
||||||
|
userName: '',
|
||||||
|
password: '',
|
||||||
|
dcodeId:'',
|
||||||
|
dcode:'',
|
||||||
|
},
|
||||||
|
dcodeImage: {
|
||||||
|
uuid:'',
|
||||||
|
code:''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getters: {
|
||||||
|
getUserInfo(state){
|
||||||
|
return state.user;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
async loginByPassword(username: string, password: string,dcode:string) {
|
||||||
|
this.loginReq.userName = username;
|
||||||
|
this.loginReq.password = password;
|
||||||
|
this.loginReq.dcode = dcode;
|
||||||
|
this.loginReq.dcodeId = this.dcodeImage.uuid;
|
||||||
|
const token = await loginRequest(this.loginReq);
|
||||||
|
if (token){
|
||||||
|
CookieUtil.setCookie(tokenKey, token);
|
||||||
|
this.user.token = token
|
||||||
|
return true;
|
||||||
|
}else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async logOut(){
|
||||||
|
this.user.token = ''
|
||||||
|
CookieUtil.removeCookie(tokenKey)
|
||||||
|
},
|
||||||
|
async hello(){
|
||||||
|
const var1 = await hello();
|
||||||
|
},
|
||||||
|
async getInfo(){
|
||||||
|
const info = await getInfo();
|
||||||
|
},
|
||||||
|
async getDcode(){
|
||||||
|
const var1 = await getDcode()
|
||||||
|
this.dcodeImage.uuid = var1.uuid
|
||||||
|
this.dcodeImage.code = var1.code
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
19
src/utils/CookieUtils.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { Cookies } from 'quasar'
|
||||||
|
const tokenKey = 'x-user-token'
|
||||||
|
|
||||||
|
class CookieUtil {
|
||||||
|
static setCookie(key: string, value: string, expires?: number): void {
|
||||||
|
const options = expires ? { expires } : undefined
|
||||||
|
Cookies.set(key, value, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
static getCookie(key: string): string | null {
|
||||||
|
return Cookies.get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
static removeCookie(key: string): void {
|
||||||
|
Cookies.remove(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export{ tokenKey,CookieUtil}
|
||||||
10
src/utils/DateUtils.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { date } from 'quasar'
|
||||||
|
|
||||||
|
export function formatDate (d:Date, fmt:string):string {
|
||||||
|
return date.formatDate(d, fmt)
|
||||||
|
}
|
||||||
|
export function formatDateString ( dsting:string | Date):string {
|
||||||
|
// 空的时候直接返回 '' 字符串
|
||||||
|
if (dsting === null || dsting === '' || typeof dsting === 'undefined') return ''
|
||||||
|
return formatDate(new Date(dsting), 'YYYY-MM-DD HH:mm:ss')
|
||||||
|
}
|
||||||
22
src/utils/NotifyUtils.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { Notify } from 'quasar'
|
||||||
|
export function notifySuccess (msg:string):void {
|
||||||
|
Notify.create({
|
||||||
|
color: 'white',
|
||||||
|
textColor: 'green-5',
|
||||||
|
icon: 'check_circle',
|
||||||
|
message: msg,
|
||||||
|
timeout: 3000,
|
||||||
|
position: 'top'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function notifyError (msg:string):void {
|
||||||
|
Notify.create({
|
||||||
|
message: msg,
|
||||||
|
type: 'negative',
|
||||||
|
color: 'red',
|
||||||
|
icon: 'error',
|
||||||
|
timeout:3000,
|
||||||
|
position: 'top'
|
||||||
|
})
|
||||||
|
}
|
||||||
6
tsconfig.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"extends": "@quasar/app-vite/tsconfig-preset",
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": "."
|
||||||
|
}
|
||||||
|
}
|
||||||