fix(项目初始化):前端项目初始化

This commit is contained in:
ni ziyi 2025-04-04 22:16:25 +08:00
commit e581375fd5
62 changed files with 8999 additions and 0 deletions

9
.editorconfig Normal file
View 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
View 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
View 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
View 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
View File

@ -0,0 +1,3 @@
# pnpm-related options
shamefully-hoist=true
strict-peer-dependencies=false

4
.prettierrc Normal file
View File

@ -0,0 +1,4 @@
{
"singleQuote": true,
"semi": true
}

15
.vscode/extensions.json vendored Normal file
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

41
package.json Normal file
View 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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 859 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

212
quasar.config.js Normal file
View 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
View File

@ -0,0 +1,7 @@
<template>
<router-view />
</template>
<script setup lang="ts">
</script>

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

View 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

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

90
src/boot/axios.ts Normal file
View 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.$axiosVue Options API形式
// 所以你不需要在每个vue文件中导入axios
app.config.globalProperties.$api = api
// ^ ^ ^ 这将允许您使用this.$apiVue Options API形式
// 这样您就可以轻松地根据应用程序的api执行请求
})
export {axios, api}

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

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

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

View 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);//xy0 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
View File

@ -0,0 +1,8 @@
export interface Todo {
id: number;
content: string;
}
export interface Meta {
totalCount: number;
}

1
src/css/app.scss Normal file
View File

@ -0,0 +1 @@
// app global css in SCSS form

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

@ -0,0 +1,5 @@
import enUS from './en-US';
export default {
'en-US': enUS
};

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

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

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

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

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

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

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

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

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

@ -0,0 +1,6 @@
{
"extends": "@quasar/app-vite/tsconfig-preset",
"compilerOptions": {
"baseUrl": "."
}
}