remake project
This commit is contained in:
parent
3863a7bd51
commit
cd78b47dcb
@ -1,5 +0,0 @@
|
||||
[*.{js,jsx,ts,tsx,vue}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
15
.eslintrc.cjs
Normal file
15
.eslintrc.cjs
Normal file
@ -0,0 +1,15 @@
|
||||
/* eslint-env node */
|
||||
require('@rushstack/eslint-patch/modern-module-resolution')
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
'extends': [
|
||||
'plugin:vue/vue3-essential',
|
||||
'eslint:recommended',
|
||||
'@vue/eslint-config-typescript',
|
||||
'@vue/eslint-config-prettier/skip-formatting'
|
||||
],
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest'
|
||||
}
|
||||
}
|
29
.gitignore
vendored
29
.gitignore
vendored
@ -1,23 +1,30 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
/dist
|
||||
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Log files
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
||||
|
||||
/cypress/videos/
|
||||
/cypress/screenshots/
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
*.tsbuildinfo
|
||||
|
8
.prettierrc.json
Normal file
8
.prettierrc.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/prettierrc",
|
||||
"semi": true,
|
||||
"tabWidth": 2,
|
||||
"singleQuote": true,
|
||||
"printWidth": 100,
|
||||
"trailingComma": "es5"
|
||||
}
|
8
.vscode/extensions.json
vendored
Normal file
8
.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"Vue.volar",
|
||||
"Vue.vscode-typescript-vue-plugin",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode"
|
||||
]
|
||||
}
|
9
LICENSE
9
LICENSE
@ -1,9 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 zhangyuheng
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
48
README.md
48
README.md
@ -1,24 +1,46 @@
|
||||
# QuickExam-UI
|
||||
# quick-exam-ui
|
||||
|
||||
## Project setup
|
||||
```
|
||||
This template should help get you started developing with Vue 3 in Vite.
|
||||
|
||||
## Recommended IDE Setup
|
||||
|
||||
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
|
||||
|
||||
## Type Support for `.vue` Imports in TS
|
||||
|
||||
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) to make the TypeScript language service aware of `.vue` types.
|
||||
|
||||
If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps:
|
||||
|
||||
1. Disable the built-in TypeScript Extension
|
||||
1) Run `Extensions: Show Built-in Extensions` from VSCode's command palette
|
||||
2) Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)`
|
||||
2. Reload the VSCode window by running `Developer: Reload Window` from the command palette.
|
||||
|
||||
## Customize configuration
|
||||
|
||||
See [Vite Configuration Reference](https://vitejs.dev/config/).
|
||||
|
||||
## Project Setup
|
||||
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compiles and hot-reloads for development
|
||||
```
|
||||
npm run serve
|
||||
### Compile and Hot-Reload for Development
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Compiles and minifies for production
|
||||
```
|
||||
### Type-Check, Compile and Minify for Production
|
||||
|
||||
```sh
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Lints and fixes files
|
||||
```
|
||||
### Lint with [ESLint](https://eslint.org/)
|
||||
|
||||
```sh
|
||||
npm run lint
|
||||
```
|
||||
|
||||
### Customize configuration
|
||||
See [Configuration Reference](https://cli.vuejs.org/config/).
|
||||
|
@ -1,5 +0,0 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@vue/cli-plugin-babel/preset'
|
||||
]
|
||||
}
|
13
index.html
Normal file
13
index.html
Normal file
@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Vite App</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
22235
package-lock.json
generated
22235
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
84
package.json
84
package.json
@ -1,61 +1,39 @@
|
||||
{
|
||||
"name": "quickexam-ui",
|
||||
"version": "0.1.0",
|
||||
"name": "quick-exam-ui",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
"lint": "vue-cli-service lint"
|
||||
"dev": "vite --host 0.0.0.0",
|
||||
"build": "run-p type-check \"build-only {@}\" --",
|
||||
"preview": "vite preview",
|
||||
"build-only": "vite build",
|
||||
"type-check": "vue-tsc --build --force",
|
||||
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
|
||||
"format": "prettier --write src/"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.6.2",
|
||||
"core-js": "^3.8.3",
|
||||
"vue": "^3.2.13",
|
||||
"vue-class-component": "^8.0.0-0",
|
||||
"vue-router": "^4.0.3"
|
||||
"axios": "^1.6.3",
|
||||
"element-plus": "^2.4.4",
|
||||
"pinia": "^2.1.7",
|
||||
"vue": "^3.3.11",
|
||||
"vue-router": "^4.2.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
||||
"@typescript-eslint/parser": "^5.4.0",
|
||||
"@vue/cli-plugin-babel": "~5.0.0",
|
||||
"@vue/cli-plugin-eslint": "~5.0.0",
|
||||
"@vue/cli-plugin-router": "~5.0.0",
|
||||
"@vue/cli-plugin-typescript": "~5.0.0",
|
||||
"@vue/cli-service": "~5.0.0",
|
||||
"@vue/eslint-config-standard": "^6.1.0",
|
||||
"@vue/eslint-config-typescript": "^9.1.0",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-plugin-import": "^2.25.3",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^5.1.0",
|
||||
"eslint-plugin-vue": "^8.0.3",
|
||||
"naive-ui": "^2.36.0",
|
||||
"typescript": "~4.5.5"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
"env": {
|
||||
"node": true
|
||||
},
|
||||
"extends": [
|
||||
"plugin:vue/vue3-essential",
|
||||
"@vue/standard",
|
||||
"@vue/typescript/recommended"
|
||||
],
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2020
|
||||
},
|
||||
"rules": {
|
||||
"semi": [
|
||||
"warn",
|
||||
"always"
|
||||
]
|
||||
}
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
"last 2 versions",
|
||||
"not dead",
|
||||
"not ie 11"
|
||||
]
|
||||
"@rushstack/eslint-patch": "^1.3.3",
|
||||
"@tsconfig/node18": "^18.2.2",
|
||||
"@types/node": "^18.19.3",
|
||||
"@vitejs/plugin-vue": "^4.5.2",
|
||||
"@vitejs/plugin-vue-jsx": "^3.1.0",
|
||||
"@vue/eslint-config-prettier": "^8.0.0",
|
||||
"@vue/eslint-config-typescript": "^12.0.0",
|
||||
"@vue/tsconfig": "^0.5.0",
|
||||
"eslint": "^8.49.0",
|
||||
"eslint-plugin-vue": "^9.17.0",
|
||||
"npm-run-all2": "^6.1.1",
|
||||
"prettier": "^3.0.3",
|
||||
"typescript": "~5.3.0",
|
||||
"vite": "^5.0.10",
|
||||
"vue-tsc": "^1.8.25"
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||
</noscript>
|
||||
<div id="app"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
</html>
|
95
src/App.vue
95
src/App.vue
@ -1,32 +1,85 @@
|
||||
<script setup lang="ts">
|
||||
import { RouterLink, RouterView } from 'vue-router'
|
||||
import HelloWorld from './components/HelloWorld.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-notification-provider :max="3" :placement="'top'">
|
||||
<nav>
|
||||
<router-link to="/">Home</router-link> |
|
||||
<router-link to="/about">About</router-link>
|
||||
</nav>
|
||||
<router-view />
|
||||
</n-notification-provider>
|
||||
<header>
|
||||
<img alt="Vue logo" class="logo" src="@/assets/logo.svg" width="125" height="125" />
|
||||
|
||||
<div class="wrapper">
|
||||
<HelloWorld msg="You did it!" />
|
||||
|
||||
<nav>
|
||||
<RouterLink to="/">Home</RouterLink>
|
||||
<RouterLink to="/about">About</RouterLink>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<RouterView />
|
||||
</template>
|
||||
|
||||
<style>
|
||||
#app {
|
||||
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
text-align: center;
|
||||
color: #2c3e50;
|
||||
<style scoped>
|
||||
header {
|
||||
line-height: 1.5;
|
||||
max-height: 100vh;
|
||||
}
|
||||
|
||||
.logo {
|
||||
display: block;
|
||||
margin: 0 auto 2rem;
|
||||
}
|
||||
|
||||
nav {
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
nav a {
|
||||
font-weight: bold;
|
||||
color: #2c3e50;
|
||||
width: 100%;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
nav a.router-link-exact-active {
|
||||
color: #42b983;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
nav a.router-link-exact-active:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
nav a {
|
||||
display: inline-block;
|
||||
padding: 0 1rem;
|
||||
border-left: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
nav a:first-of-type {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
header {
|
||||
display: flex;
|
||||
place-items: center;
|
||||
padding-right: calc(var(--section-gap) / 2);
|
||||
}
|
||||
|
||||
.logo {
|
||||
margin: 0 2rem 0 0;
|
||||
}
|
||||
|
||||
header .wrapper {
|
||||
display: flex;
|
||||
place-items: flex-start;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
nav {
|
||||
text-align: left;
|
||||
margin-left: -1rem;
|
||||
font-size: 1rem;
|
||||
|
||||
padding: 1rem 0;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -1,121 +1,124 @@
|
||||
import { Question } from '@/interfaces/question';
|
||||
import axiosInstance from '@/common/axios';
|
||||
import { ORDER_TYPE, Page } from '@/interfaces/page';
|
||||
import { QuestionQueryCondition } from '@/interfaces/question';
|
||||
import type { Question } from '@/interfaces/question';
|
||||
import axiosInstance from '@/commons/axios';
|
||||
import { ORDER_TYPE, type Page } from '@/interfaces/page';
|
||||
import type { QuestionQueryCondition, QuestionPage } from '@/interfaces/question';
|
||||
|
||||
const API = {
|
||||
CREATE: '/question/create',
|
||||
ADD_TAG: '/question/add/tag',
|
||||
UPDATE: '/question/update',
|
||||
DELETE: '/question/delete',
|
||||
DELETE_TAG: '/question/delete/tag',
|
||||
GET: '/question/get',
|
||||
GET_BY_PAGE: '/question/get/by/conditions',
|
||||
CREATE_SUBQUESTION: '/question/create/sub_question',
|
||||
}
|
||||
CREATE: '/question/create',
|
||||
ADD_TAG: '/question/add/tag',
|
||||
UPDATE: '/question/update',
|
||||
DELETE: '/question/delete',
|
||||
DELETE_TAG: '/question/delete/tag',
|
||||
GET: '/question/get',
|
||||
GET_BY_PAGE: '/question/get/by/conditions',
|
||||
CREATE_SUBQUESTION: '/question/create/sub_question',
|
||||
};
|
||||
|
||||
export async function createNewQuestion(question: Question): Promise<Question> {
|
||||
try {
|
||||
const response = await axiosInstance.post(API.CREATE, question);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error creating question:', error);
|
||||
throw error;
|
||||
}
|
||||
try {
|
||||
const response = await axiosInstance.post(API.CREATE, question);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error creating question:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function addTagToQuestion(questionId: number, tagId: number): Promise<Question> {
|
||||
try {
|
||||
const response = await axiosInstance.post(API.ADD_TAG, {
|
||||
params: {
|
||||
question_id: questionId,
|
||||
tag_id: tagId,
|
||||
},
|
||||
});
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error adding tag:', error);
|
||||
throw error;
|
||||
}
|
||||
try {
|
||||
const response = await axiosInstance.post(API.ADD_TAG, {
|
||||
params: {
|
||||
question_id: questionId,
|
||||
tag_id: tagId,
|
||||
},
|
||||
});
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error adding tag:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function updateQuestionBasicInfo(question: Question): Promise<Question> {
|
||||
try {
|
||||
const response = await axiosInstance.put(API.UPDATE, question);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error updating question:', error);
|
||||
throw error;
|
||||
}
|
||||
try {
|
||||
const response = await axiosInstance.put(API.UPDATE, question);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error updating question:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteQuestion(questionId: number): Promise<Question> {
|
||||
try {
|
||||
const response = await axiosInstance.delete(API.DELETE, {
|
||||
params: {
|
||||
question_id: questionId,
|
||||
},
|
||||
});
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error deleting question:', error);
|
||||
throw error;
|
||||
}
|
||||
try {
|
||||
const response = await axiosInstance.delete(API.DELETE, {
|
||||
params: {
|
||||
question_id: questionId,
|
||||
},
|
||||
});
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error deleting question:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteTagFromQuestion(questionId: number, tagId: number): Promise<Question> {
|
||||
try {
|
||||
const response = await axiosInstance.delete(API.DELETE_TAG, {
|
||||
params: {
|
||||
question_id: questionId,
|
||||
tag_id: tagId,
|
||||
},
|
||||
});
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error deleting tag:', error);
|
||||
throw error;
|
||||
}
|
||||
try {
|
||||
const response = await axiosInstance.delete(API.DELETE_TAG, {
|
||||
params: {
|
||||
question_id: questionId,
|
||||
tag_id: tagId,
|
||||
},
|
||||
});
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error deleting tag:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getQuestionDetail(questionId: number): Promise<Question> {
|
||||
try {
|
||||
const response = await axiosInstance.get(API.GET, {
|
||||
params: {
|
||||
question_id: questionId,
|
||||
},
|
||||
});
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error getting question:', error);
|
||||
throw error;
|
||||
}
|
||||
try {
|
||||
const response = await axiosInstance.get(API.GET, {
|
||||
params: {
|
||||
question_id: questionId,
|
||||
},
|
||||
});
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error getting question:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getQuestionsByPage(condition: QuestionQueryCondition): Promise<Page> {
|
||||
try {
|
||||
if (condition.sort_order !== ORDER_TYPE.ASC && condition.sort_order !== ORDER_TYPE.DESC) {
|
||||
condition.sort_order = ORDER_TYPE.ASC;
|
||||
}
|
||||
const response = await axiosInstance.post(API.GET_BY_PAGE, condition);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error getting questions:', error);
|
||||
throw error;
|
||||
export async function getQuestionsByPage(condition: QuestionQueryCondition): Promise<QuestionPage> {
|
||||
try {
|
||||
if (condition.sort_order !== ORDER_TYPE.ASC && condition.sort_order !== ORDER_TYPE.DESC) {
|
||||
condition.sort_order = ORDER_TYPE.ASC;
|
||||
}
|
||||
const response = await axiosInstance.post(API.GET_BY_PAGE, condition);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error getting questions:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function createSubQuestion(parentQuestionId: number, subQuestion: Question): Promise<Question> {
|
||||
try {
|
||||
subQuestion.is_sub_question = true;
|
||||
const response = await axiosInstance.post(API.CREATE_SUBQUESTION, subQuestion, {
|
||||
params: {
|
||||
parent_question_id: parentQuestionId,
|
||||
},
|
||||
});
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error creating sub question:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
export async function createSubQuestion(
|
||||
parentQuestionId: number,
|
||||
subQuestion: Question
|
||||
): Promise<Question> {
|
||||
try {
|
||||
subQuestion.parent = parentQuestionId;
|
||||
const response = await axiosInstance.post(API.CREATE_SUBQUESTION, subQuestion, {
|
||||
params: {
|
||||
parent_question_id: parentQuestionId,
|
||||
},
|
||||
});
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error creating sub question:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
86
src/assets/base.css
Normal file
86
src/assets/base.css
Normal file
@ -0,0 +1,86 @@
|
||||
/* color palette from <https://github.com/vuejs/theme> */
|
||||
:root {
|
||||
--vt-c-white: #ffffff;
|
||||
--vt-c-white-soft: #f8f8f8;
|
||||
--vt-c-white-mute: #f2f2f2;
|
||||
|
||||
--vt-c-black: #181818;
|
||||
--vt-c-black-soft: #222222;
|
||||
--vt-c-black-mute: #282828;
|
||||
|
||||
--vt-c-indigo: #2c3e50;
|
||||
|
||||
--vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
|
||||
--vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
|
||||
--vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
|
||||
--vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
|
||||
|
||||
--vt-c-text-light-1: var(--vt-c-indigo);
|
||||
--vt-c-text-light-2: rgba(60, 60, 60, 0.66);
|
||||
--vt-c-text-dark-1: var(--vt-c-white);
|
||||
--vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
|
||||
}
|
||||
|
||||
/* semantic color variables for this project */
|
||||
:root {
|
||||
--color-background: var(--vt-c-white);
|
||||
--color-background-soft: var(--vt-c-white-soft);
|
||||
--color-background-mute: var(--vt-c-white-mute);
|
||||
|
||||
--color-border: var(--vt-c-divider-light-2);
|
||||
--color-border-hover: var(--vt-c-divider-light-1);
|
||||
|
||||
--color-heading: var(--vt-c-text-light-1);
|
||||
--color-text: var(--vt-c-text-light-1);
|
||||
|
||||
--section-gap: 160px;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--color-background: var(--vt-c-black);
|
||||
--color-background-soft: var(--vt-c-black-soft);
|
||||
--color-background-mute: var(--vt-c-black-mute);
|
||||
|
||||
--color-border: var(--vt-c-divider-dark-2);
|
||||
--color-border-hover: var(--vt-c-divider-dark-1);
|
||||
|
||||
--color-heading: var(--vt-c-text-dark-1);
|
||||
--color-text: var(--vt-c-text-dark-2);
|
||||
}
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
body {
|
||||
min-height: 100vh;
|
||||
color: var(--color-text);
|
||||
background: var(--color-background);
|
||||
transition:
|
||||
color 0.5s,
|
||||
background-color 0.5s;
|
||||
line-height: 1.6;
|
||||
font-family:
|
||||
Inter,
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
'Segoe UI',
|
||||
Roboto,
|
||||
Oxygen,
|
||||
Ubuntu,
|
||||
Cantarell,
|
||||
'Fira Sans',
|
||||
'Droid Sans',
|
||||
'Helvetica Neue',
|
||||
sans-serif;
|
||||
font-size: 15px;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 6.7 KiB |
1
src/assets/logo.svg
Normal file
1
src/assets/logo.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>
|
After Width: | Height: | Size: 276 B |
35
src/assets/main.css
Normal file
35
src/assets/main.css
Normal file
@ -0,0 +1,35 @@
|
||||
@import './base.css';
|
||||
|
||||
#app {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
a,
|
||||
.green {
|
||||
text-decoration: none;
|
||||
color: hsla(160, 100%, 37%, 1);
|
||||
transition: 0.4s;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
a:hover {
|
||||
background-color: hsla(160, 100%, 37%, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
body {
|
||||
display: flex;
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
#app {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
padding: 0 2rem;
|
||||
}
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
|
||||
import axios from 'axios';
|
||||
import { useNotification } from 'naive-ui';
|
||||
|
||||
// const BASE_URL = window.location.origin + '/api';
|
||||
const BASE_URL = 'http://pix.lunadeer.cn:8280/api';
|
||||
|
||||
const axiosInstance = axios.create({
|
||||
baseURL: BASE_URL,
|
||||
timeout: 10000,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
interface ResponseData {
|
||||
code: number;
|
||||
message: string;
|
||||
data: any;
|
||||
}
|
||||
|
||||
const notification = useNotification();
|
||||
|
||||
// on every response, check self-defined status code
|
||||
axiosInstance.interceptors.response.use(
|
||||
response => {
|
||||
if (response.status === 200) {
|
||||
const responseData: ResponseData = response.data;
|
||||
if (responseData.code === 200) {
|
||||
return Promise.resolve(responseData.data);
|
||||
} else {
|
||||
console.log(responseData.message);
|
||||
notification.warning({
|
||||
title: 'Opps!',
|
||||
content: responseData.message,
|
||||
duration: 2000,
|
||||
keepAliveOnHover: true,
|
||||
});
|
||||
return Promise.reject(responseData.message);
|
||||
}
|
||||
} else {
|
||||
console.log(response.data);
|
||||
notification.error({
|
||||
title: 'Crap!',
|
||||
content: response.data,
|
||||
duration: 2000,
|
||||
keepAliveOnHover: true,
|
||||
});
|
||||
return Promise.reject(response.data);
|
||||
}
|
||||
},
|
||||
error => {
|
||||
console.log(error);
|
||||
notification.error({
|
||||
title: 'Damn!',
|
||||
content: error,
|
||||
duration: 2000,
|
||||
keepAliveOnHover: true,
|
||||
});
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
export default axiosInstance;
|
46
src/commons/axios.ts
Normal file
46
src/commons/axios.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import axios, { type AxiosInstance, type AxiosResponse } from 'axios';
|
||||
import { ElNotification } from 'element-plus';
|
||||
import { h } from 'vue';
|
||||
|
||||
interface Response {
|
||||
code: number;
|
||||
message: string;
|
||||
data: any;
|
||||
}
|
||||
|
||||
// const BASE_URL = window.location.origin + '/api';
|
||||
const BASE_URL = 'http://pix.lunadeer.cn:8280/api';
|
||||
|
||||
const axiosInstance: AxiosInstance = axios.create({
|
||||
baseURL: BASE_URL,
|
||||
timeout: 1000,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
axiosInstance.interceptors.response.use(
|
||||
function (response: AxiosResponse<any, any>) {
|
||||
const responseJson: Response = response.data;
|
||||
if (responseJson.code === 200) {
|
||||
return response.data;
|
||||
} else {
|
||||
ElNotification({
|
||||
title: 'Oops!',
|
||||
message: responseJson.message,
|
||||
type: 'warning',
|
||||
});
|
||||
return Promise.reject(responseJson.message);
|
||||
}
|
||||
},
|
||||
function (error: any) {
|
||||
ElNotification({
|
||||
title: 'Crash!',
|
||||
message: error.message,
|
||||
type: 'error',
|
||||
});
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
export default axiosInstance;
|
@ -1,64 +1,41 @@
|
||||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
msg: string
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="hello">
|
||||
<h1>{{ msg }}</h1>
|
||||
<p>
|
||||
For a guide and recipes on how to configure / customize this project,<br>
|
||||
check out the
|
||||
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
|
||||
</p>
|
||||
<h3>Installed CLI Plugins</h3>
|
||||
<ul>
|
||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
|
||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-router" target="_blank" rel="noopener">router</a></li>
|
||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
|
||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-typescript" target="_blank" rel="noopener">typescript</a></li>
|
||||
</ul>
|
||||
<h3>Essential Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
|
||||
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
|
||||
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
|
||||
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
|
||||
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
|
||||
</ul>
|
||||
<h3>Ecosystem</h3>
|
||||
<ul>
|
||||
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
|
||||
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
|
||||
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
|
||||
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
|
||||
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
|
||||
</ul>
|
||||
<div class="greetings">
|
||||
<h1 class="green">{{ msg }}</h1>
|
||||
<h3>
|
||||
You’ve successfully created a project with
|
||||
<a href="https://vitejs.dev/" target="_blank" rel="noopener">Vite</a> +
|
||||
<a href="https://vuejs.org/" target="_blank" rel="noopener">Vue 3</a>. What's next?
|
||||
</h3>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Options, Vue } from 'vue-class-component';
|
||||
|
||||
@Options({
|
||||
props: {
|
||||
msg: String
|
||||
}
|
||||
})
|
||||
export default class HelloWorld extends Vue {
|
||||
msg!: string
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
h1 {
|
||||
font-weight: 500;
|
||||
font-size: 2.6rem;
|
||||
position: relative;
|
||||
top: -10px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 40px 0 0;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
|
||||
.greetings h1,
|
||||
.greetings h3 {
|
||||
text-align: center;
|
||||
}
|
||||
li {
|
||||
display: inline-block;
|
||||
margin: 0 10px;
|
||||
}
|
||||
a {
|
||||
color: #42b983;
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.greetings h1,
|
||||
.greetings h3 {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
181
src/components/TableQuestions.vue
Normal file
181
src/components/TableQuestions.vue
Normal file
@ -0,0 +1,181 @@
|
||||
<template>
|
||||
<!-- Condition Filter From -->
|
||||
<el-form :inline="true" :model="query_condition" class="demo-form-inline">
|
||||
<el-form-item label="发布状态">
|
||||
<el-select v-model="query_condition.is_published" placeholder="请选择">
|
||||
<el-option label="全部" value="-1" />
|
||||
<el-option label="已发布" value="1" />
|
||||
<el-option label="编辑中" value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="题型">
|
||||
<el-select v-model="query_condition.types" multiple placeholder="请选择">
|
||||
<el-option v-for="id in type_ids" :key="id" :label="getQuestionTypeText(id)" :value="id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="标签">
|
||||
<el-select v-model="query_condition.tag_ids" multiple placeholder="请选择">
|
||||
<el-option v-for="tag in tag_list" :key="tag.id" :label="tag.name" :value="tag.id">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="关键字">
|
||||
<el-input v-model="query_condition.search" placeholder="请输入关键字" />
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间">
|
||||
<el-date-picker v-model="timerange_create" type="datarange" range-separator="至" value-format="x"
|
||||
start-placeholder="开始日期" end-placeholder="结束日期" @change="createTimeRangeChange" />
|
||||
</el-form-item>
|
||||
<el-form-item label="更新时间">
|
||||
<el-date-picker v-model="timerange_update" type="datarange" range-separator="至" value-format="x"
|
||||
start-placeholder="开始日期" end-placeholder="结束日期" @change="updateTimeRangeChange" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" @click="fetchQuestionList">搜索</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<!-- Table -->
|
||||
<el-table :data="page_data.items" :table-layout="'auto'" :default-sort="{ prop: 'id', order: 'ascending' }"
|
||||
:border="true" :disabled="disabled" @sort-change="handleSortChange">
|
||||
<el-table-column prop="id" label="ID" :sortable="'custom'" />
|
||||
<el-table-column prop="title" label="标题" :sortable="'custom'" />
|
||||
<el-table-column prop="type" label="题型" :sortable="'custom'">
|
||||
<template #default="scope">
|
||||
{{ getQuestionTypeText(scope.row.type) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="正确率" :sortable="'custom'">
|
||||
<template #default="scope">
|
||||
{{ scope.row.correct_count }} / {{ scope.row.reference_count }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="reference_count" label="引用次数" :sortable="'custom'" />
|
||||
<el-table-column prop="is_published" label="发布状态" :sortable="'custom'">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.is_published" type="success">已发布</el-tag>
|
||||
<el-tag v-else type="info">编辑中</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" :sortable="'custom'">
|
||||
<template #default="scope">
|
||||
{{ scope.row.created_at }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="更新时间" :sortable="'custom'">
|
||||
<template #default="scope">
|
||||
{{ scope.row.updated_at }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- edit -->
|
||||
<el-table-column align="right">
|
||||
<template #header>
|
||||
操作
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<el-button size="small" @click="handleEdit(scope.$index, scope.row)">编辑</el-button>
|
||||
<el-button size="small" type="danger" @click="handleDelete(scope.$index, scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- Pagination -->
|
||||
<el-pagination v-model:current-page="page_data.page" v-model:page-size="page_data.page_size" :page-sizes="[10, 50, 100]"
|
||||
:disabled="disabled" :background="true" layout="prev, pager, next, total, jumper, sizes"
|
||||
:total="page_data.total_count" @current-change="handlePageChange" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { defineComponent, ref } from 'vue';
|
||||
import type { QuestionPage, QuestionQueryCondition } from '@/interfaces/question';
|
||||
import { ORDER_TYPE } from '@/interfaces/page';
|
||||
import { getQuestionTypeText, QuestionType } from '@/interfaces/question';
|
||||
import type { Tag } from '@/interfaces/tag';
|
||||
import { isNumber } from 'element-plus/es/utils/types.mjs';
|
||||
import { getQuestionsByPage } from '@/apis/question';
|
||||
|
||||
const timerange_create = ref<number[]>([])
|
||||
const timerange_update = ref<number[]>([])
|
||||
const createTimeRangeChange = (val: number[]) => {
|
||||
query_condition.value.after_create_time = val[0]
|
||||
query_condition.value.before_create_time = val[1]
|
||||
}
|
||||
const updateTimeRangeChange = (val: number[]) => {
|
||||
query_condition.value.after_update_time = val[0]
|
||||
query_condition.value.before_update_time = val[1]
|
||||
}
|
||||
|
||||
const type_ids = ref<number[]>([])
|
||||
|
||||
const disabled = ref<boolean>(false) // before data loaded, disable pagination
|
||||
|
||||
const page_data = ref<QuestionPage>({
|
||||
page: 0,
|
||||
page_size: 0,
|
||||
total_count: 0,
|
||||
total_page: 0,
|
||||
items: []
|
||||
})
|
||||
|
||||
const tag_list = ref<Tag[]>([])
|
||||
|
||||
const query_condition = ref<QuestionQueryCondition>({
|
||||
page: 1,
|
||||
page_size: 10,
|
||||
sort_by: 'id',
|
||||
sort_order: ORDER_TYPE.ASC,
|
||||
search: '',
|
||||
is_published: -1,
|
||||
tag_ids: [],
|
||||
types: [],
|
||||
after_create_time: 0,
|
||||
before_create_time: 0,
|
||||
after_update_time: 0,
|
||||
before_update_time: 0
|
||||
})
|
||||
|
||||
const handlePageChange = (val: number) => {
|
||||
query_condition.value.page = val
|
||||
fetchQuestionList()
|
||||
query_condition.value.page = page_data.value.page
|
||||
query_condition.value.page_size = page_data.value.page_size
|
||||
}
|
||||
|
||||
const handleSortChange = (val: any) => {
|
||||
query_condition.value.sort_by = val.prop
|
||||
query_condition.value.sort_order = val.order === 'ascending' ? ORDER_TYPE.ASC : ORDER_TYPE.DESC
|
||||
fetchQuestionList()
|
||||
}
|
||||
|
||||
const handleEdit = (index: number, row: any) => {
|
||||
// todo: go to edit question view
|
||||
}
|
||||
|
||||
const handleDelete = (index: number, row: any) => {
|
||||
// todo: pop up a warning
|
||||
}
|
||||
|
||||
const fetchQuestionList = () => {
|
||||
disabled.value = true
|
||||
getQuestionsByPage(query_condition.value).then((res) => {
|
||||
page_data.value = res
|
||||
}).catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
disabled.value = false
|
||||
}
|
||||
|
||||
// on mounted
|
||||
const mounted = () => {
|
||||
for (let key in QuestionType) {
|
||||
if (isNumber(key)) {
|
||||
type_ids.value.push(Number(key))
|
||||
}
|
||||
}
|
||||
// todo get tag list
|
||||
fetchQuestionList()
|
||||
|
||||
disabled.value = false
|
||||
}
|
||||
|
||||
</script>
|
||||
|
9
src/components/TheWelcome.vue
Normal file
9
src/components/TheWelcome.vue
Normal file
@ -0,0 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import TableQuestions from './TableQuestions.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main>
|
||||
<TableQuestions />
|
||||
</main>
|
||||
</template>
|
87
src/components/WelcomeItem.vue
Normal file
87
src/components/WelcomeItem.vue
Normal file
@ -0,0 +1,87 @@
|
||||
<template>
|
||||
<div class="item">
|
||||
<i>
|
||||
<slot name="icon"></slot>
|
||||
</i>
|
||||
<div class="details">
|
||||
<h3>
|
||||
<slot name="heading"></slot>
|
||||
</h3>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.item {
|
||||
margin-top: 2rem;
|
||||
display: flex;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.details {
|
||||
flex: 1;
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
i {
|
||||
display: flex;
|
||||
place-items: center;
|
||||
place-content: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.2rem;
|
||||
font-weight: 500;
|
||||
margin-bottom: 0.4rem;
|
||||
color: var(--color-heading);
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.item {
|
||||
margin-top: 0;
|
||||
padding: 0.4rem 0 1rem calc(var(--section-gap) / 2);
|
||||
}
|
||||
|
||||
i {
|
||||
top: calc(50% - 25px);
|
||||
left: -26px;
|
||||
position: absolute;
|
||||
border: 1px solid var(--color-border);
|
||||
background: var(--color-background);
|
||||
border-radius: 8px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.item:before {
|
||||
content: ' ';
|
||||
border-left: 1px solid var(--color-border);
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: calc(50% + 25px);
|
||||
height: calc(50% - 25px);
|
||||
}
|
||||
|
||||
.item:after {
|
||||
content: ' ';
|
||||
border-left: 1px solid var(--color-border);
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: calc(50% + 25px);
|
||||
height: calc(50% - 25px);
|
||||
}
|
||||
|
||||
.item:first-of-type:before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.item:last-of-type:after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
7
src/components/icons/IconCommunity.vue
Normal file
7
src/components/icons/IconCommunity.vue
Normal file
@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
|
||||
<path
|
||||
d="M15 4a1 1 0 1 0 0 2V4zm0 11v-1a1 1 0 0 0-1 1h1zm0 4l-.707.707A1 1 0 0 0 16 19h-1zm-4-4l.707-.707A1 1 0 0 0 11 14v1zm-4.707-1.293a1 1 0 0 0-1.414 1.414l1.414-1.414zm-.707.707l-.707-.707.707.707zM9 11v-1a1 1 0 0 0-.707.293L9 11zm-4 0h1a1 1 0 0 0-1-1v1zm0 4H4a1 1 0 0 0 1.707.707L5 15zm10-9h2V4h-2v2zm2 0a1 1 0 0 1 1 1h2a3 3 0 0 0-3-3v2zm1 1v6h2V7h-2zm0 6a1 1 0 0 1-1 1v2a3 3 0 0 0 3-3h-2zm-1 1h-2v2h2v-2zm-3 1v4h2v-4h-2zm1.707 3.293l-4-4-1.414 1.414 4 4 1.414-1.414zM11 14H7v2h4v-2zm-4 0c-.276 0-.525-.111-.707-.293l-1.414 1.414C5.42 15.663 6.172 16 7 16v-2zm-.707 1.121l3.414-3.414-1.414-1.414-3.414 3.414 1.414 1.414zM9 12h4v-2H9v2zm4 0a3 3 0 0 0 3-3h-2a1 1 0 0 1-1 1v2zm3-3V3h-2v6h2zm0-6a3 3 0 0 0-3-3v2a1 1 0 0 1 1 1h2zm-3-3H3v2h10V0zM3 0a3 3 0 0 0-3 3h2a1 1 0 0 1 1-1V0zM0 3v6h2V3H0zm0 6a3 3 0 0 0 3 3v-2a1 1 0 0 1-1-1H0zm3 3h2v-2H3v2zm1-1v4h2v-4H4zm1.707 4.707l.586-.586-1.414-1.414-.586.586 1.414 1.414z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
7
src/components/icons/IconDocumentation.vue
Normal file
7
src/components/icons/IconDocumentation.vue
Normal file
@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="17" fill="currentColor">
|
||||
<path
|
||||
d="M11 2.253a1 1 0 1 0-2 0h2zm-2 13a1 1 0 1 0 2 0H9zm.447-12.167a1 1 0 1 0 1.107-1.666L9.447 3.086zM1 2.253L.447 1.42A1 1 0 0 0 0 2.253h1zm0 13H0a1 1 0 0 0 1.553.833L1 15.253zm8.447.833a1 1 0 1 0 1.107-1.666l-1.107 1.666zm0-14.666a1 1 0 1 0 1.107 1.666L9.447 1.42zM19 2.253h1a1 1 0 0 0-.447-.833L19 2.253zm0 13l-.553.833A1 1 0 0 0 20 15.253h-1zm-9.553-.833a1 1 0 1 0 1.107 1.666L9.447 14.42zM9 2.253v13h2v-13H9zm1.553-.833C9.203.523 7.42 0 5.5 0v2c1.572 0 2.961.431 3.947 1.086l1.107-1.666zM5.5 0C3.58 0 1.797.523.447 1.42l1.107 1.666C2.539 2.431 3.928 2 5.5 2V0zM0 2.253v13h2v-13H0zm1.553 13.833C2.539 15.431 3.928 15 5.5 15v-2c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM5.5 15c1.572 0 2.961.431 3.947 1.086l1.107-1.666C9.203 13.523 7.42 13 5.5 13v2zm5.053-11.914C11.539 2.431 12.928 2 14.5 2V0c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM14.5 2c1.573 0 2.961.431 3.947 1.086l1.107-1.666C18.203.523 16.421 0 14.5 0v2zm3.5.253v13h2v-13h-2zm1.553 12.167C18.203 13.523 16.421 13 14.5 13v2c1.573 0 2.961.431 3.947 1.086l1.107-1.666zM14.5 13c-1.92 0-3.703.523-5.053 1.42l1.107 1.666C11.539 15.431 12.928 15 14.5 15v-2z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
7
src/components/icons/IconEcosystem.vue
Normal file
7
src/components/icons/IconEcosystem.vue
Normal file
@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="20" fill="currentColor">
|
||||
<path
|
||||
d="M11.447 8.894a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm0 1.789a1 1 0 1 0 .894-1.789l-.894 1.789zM7.447 7.106a1 1 0 1 0-.894 1.789l.894-1.789zM10 9a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0H8zm9.447-5.606a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm2 .789a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zM18 5a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0h-2zm-5.447-4.606a1 1 0 1 0 .894-1.789l-.894 1.789zM9 1l.447-.894a1 1 0 0 0-.894 0L9 1zm-2.447.106a1 1 0 1 0 .894 1.789l-.894-1.789zm-6 3a1 1 0 1 0 .894 1.789L.553 4.106zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zm-2-.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 2.789a1 1 0 1 0 .894-1.789l-.894 1.789zM2 5a1 1 0 1 0-2 0h2zM0 7.5a1 1 0 1 0 2 0H0zm8.553 12.394a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 1a1 1 0 1 0 .894 1.789l-.894-1.789zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zM8 19a1 1 0 1 0 2 0H8zm2-2.5a1 1 0 1 0-2 0h2zm-7.447.394a1 1 0 1 0 .894-1.789l-.894 1.789zM1 15H0a1 1 0 0 0 .553.894L1 15zm1-2.5a1 1 0 1 0-2 0h2zm12.553 2.606a1 1 0 1 0 .894 1.789l-.894-1.789zM17 15l.447.894A1 1 0 0 0 18 15h-1zm1-2.5a1 1 0 1 0-2 0h2zm-7.447-5.394l-2 1 .894 1.789 2-1-.894-1.789zm-1.106 1l-2-1-.894 1.789 2 1 .894-1.789zM8 9v2.5h2V9H8zm8.553-4.894l-2 1 .894 1.789 2-1-.894-1.789zm.894 0l-2-1-.894 1.789 2 1 .894-1.789zM16 5v2.5h2V5h-2zm-4.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zm-2.894-1l-2 1 .894 1.789 2-1L8.553.106zM1.447 5.894l2-1-.894-1.789-2 1 .894 1.789zm-.894 0l2 1 .894-1.789-2-1-.894 1.789zM0 5v2.5h2V5H0zm9.447 13.106l-2-1-.894 1.789 2 1 .894-1.789zm0 1.789l2-1-.894-1.789-2 1 .894 1.789zM10 19v-2.5H8V19h2zm-6.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zM2 15v-2.5H0V15h2zm13.447 1.894l2-1-.894-1.789-2 1 .894 1.789zM18 15v-2.5h-2V15h2z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
7
src/components/icons/IconSupport.vue
Normal file
7
src/components/icons/IconSupport.vue
Normal file
@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
|
||||
<path
|
||||
d="M10 3.22l-.61-.6a5.5 5.5 0 0 0-7.666.105 5.5 5.5 0 0 0-.114 7.665L10 18.78l8.39-8.4a5.5 5.5 0 0 0-.114-7.665 5.5 5.5 0 0 0-7.666-.105l-.61.61z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
19
src/components/icons/IconTooling.vue
Normal file
19
src/components/icons/IconTooling.vue
Normal file
@ -0,0 +1,19 @@
|
||||
<!-- This icon is from <https://github.com/Templarian/MaterialDesign>, distributed under Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0) license-->
|
||||
<template>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
aria-hidden="true"
|
||||
role="img"
|
||||
class="iconify iconify--mdi"
|
||||
width="24"
|
||||
height="24"
|
||||
preserveAspectRatio="xMidYMid meet"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
d="M20 18v-4h-3v1h-2v-1H9v1H7v-1H4v4h16M6.33 8l-1.74 4H7v-1h2v1h6v-1h2v1h2.41l-1.74-4H6.33M9 5v1h6V5H9m12.84 7.61c.1.22.16.48.16.8V18c0 .53-.21 1-.6 1.41c-.4.4-.85.59-1.4.59H4c-.55 0-1-.19-1.4-.59C2.21 19 2 18.53 2 18v-4.59c0-.32.06-.58.16-.8L4.5 7.22C4.84 6.41 5.45 6 6.33 6H7V5c0-.55.18-1 .57-1.41C7.96 3.2 8.44 3 9 3h6c.56 0 1.04.2 1.43.59c.39.41.57.86.57 1.41v1h.67c.88 0 1.49.41 1.83 1.22l2.34 5.39z"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
</svg>
|
||||
</template>
|
@ -1,9 +1,8 @@
|
||||
|
||||
import { AnswerContent } from "./content";
|
||||
import type { AnswerContent } from './content';
|
||||
|
||||
export interface Answer {
|
||||
id: number;
|
||||
question_id: number;
|
||||
is_correct: boolean;
|
||||
answer_contents: AnswerContent[];
|
||||
}
|
||||
id: number;
|
||||
question_id: number;
|
||||
is_correct: boolean;
|
||||
answer_contents: AnswerContent[];
|
||||
}
|
||||
|
@ -1,22 +1,21 @@
|
||||
|
||||
export enum ContentType {
|
||||
TEXT = 0,
|
||||
IMAGE_BASE64 = 1,
|
||||
AUDIO_URL = 2,
|
||||
VIDEO_URL = 3,
|
||||
TEXT = 0,
|
||||
IMAGE_BASE64 = 1,
|
||||
AUDIO_URL = 2,
|
||||
VIDEO_URL = 3,
|
||||
}
|
||||
|
||||
interface Content {
|
||||
id: number;
|
||||
content_type: ContentType;
|
||||
content: string;
|
||||
content_index: number;
|
||||
id: number;
|
||||
content_type: ContentType;
|
||||
content: string;
|
||||
content_index: number;
|
||||
}
|
||||
|
||||
export interface QuestionContent extends Content {
|
||||
question_id: number;
|
||||
question_id: number;
|
||||
}
|
||||
|
||||
export interface AnswerContent extends Content {
|
||||
answer_id: number;
|
||||
}
|
||||
answer_id: number;
|
||||
}
|
||||
|
@ -1,7 +0,0 @@
|
||||
|
||||
export interface OperateTime {
|
||||
id: number;
|
||||
belong_id: number;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
@ -1,21 +1,24 @@
|
||||
|
||||
export const ORDER_TYPE = {
|
||||
ASC: 'ASC',
|
||||
DESC: 'DESC',
|
||||
ASC: 'ASC',
|
||||
DESC: 'DESC',
|
||||
};
|
||||
|
||||
export interface Page {
|
||||
page: number;
|
||||
page_size: number;
|
||||
total_count: number;
|
||||
total_page: number;
|
||||
items: any[];
|
||||
export interface Page<T> {
|
||||
page: number;
|
||||
page_size: number;
|
||||
total_count: number;
|
||||
total_page: number;
|
||||
items: T[];
|
||||
}
|
||||
|
||||
export interface QueryCondition {
|
||||
page: number;
|
||||
page_size: number;
|
||||
sort_by: string;
|
||||
sort_order: string;
|
||||
search: string;
|
||||
}
|
||||
page: number;
|
||||
page_size: number;
|
||||
sort_by: string;
|
||||
sort_order: string;
|
||||
search: string;
|
||||
after_create_time: number;
|
||||
before_create_time: number;
|
||||
after_update_time: number;
|
||||
before_update_time: number;
|
||||
}
|
||||
|
@ -1,40 +1,63 @@
|
||||
import { OperateTime } from "./operateTime";
|
||||
import { Tag } from "./tag";
|
||||
import { Answer } from "./answer";
|
||||
import { QuestionContent } from "./content";
|
||||
import { QueryCondition } from "./page";
|
||||
import type { Tag } from './tag';
|
||||
import type { Answer } from './answer';
|
||||
import type { QuestionContent } from './content';
|
||||
import type { QueryCondition } from './page';
|
||||
import type { Page } from './page';
|
||||
|
||||
export enum QuestionType {
|
||||
NONE = -1,
|
||||
SINGLE_CHOICE = 0,
|
||||
MULTIPLE_CHOICE = 1,
|
||||
JUDGEMENT = 2,
|
||||
SHORT_ANSWER = 3,
|
||||
LONG_ANSWER = 4,
|
||||
FILE_UPLOAD = 5,
|
||||
NONE = -1,
|
||||
SINGLE_CHOICE = 0,
|
||||
MULTIPLE_CHOICE = 1,
|
||||
JUDGEMENT = 2,
|
||||
SHORT_ANSWER = 3,
|
||||
LONG_ANSWER = 4,
|
||||
FILE_UPLOAD = 5,
|
||||
}
|
||||
|
||||
export function getQuestionTypeText(type: QuestionType): string {
|
||||
switch (type) {
|
||||
case QuestionType.SINGLE_CHOICE:
|
||||
return '单选题';
|
||||
case QuestionType.MULTIPLE_CHOICE:
|
||||
return '多选题';
|
||||
case QuestionType.JUDGEMENT:
|
||||
return '判断题';
|
||||
case QuestionType.SHORT_ANSWER:
|
||||
return '简答题';
|
||||
case QuestionType.LONG_ANSWER:
|
||||
return '论述题';
|
||||
case QuestionType.FILE_UPLOAD:
|
||||
return '文件上传题';
|
||||
default:
|
||||
return '未知';
|
||||
}
|
||||
}
|
||||
|
||||
export interface Question {
|
||||
id: number | null;
|
||||
is_sub_question: boolean;
|
||||
title: string;
|
||||
type: QuestionType;
|
||||
estimated_time_sec: number;
|
||||
score: number;
|
||||
is_published: boolean;
|
||||
reference_count: number;
|
||||
correct_count: number;
|
||||
operate_time: OperateTime | null;
|
||||
id: number | null;
|
||||
parent: number;
|
||||
title: string;
|
||||
type: QuestionType;
|
||||
estimated_time_sec: number;
|
||||
score: number;
|
||||
is_published: boolean;
|
||||
reference_count: number;
|
||||
correct_count: number;
|
||||
create_at: string;
|
||||
update_at: string;
|
||||
}
|
||||
|
||||
export interface QuestionDetail extends Question {
|
||||
tags: Tag[];
|
||||
answers: Answer[];
|
||||
question_contents: QuestionContent[];
|
||||
sub_questions: QuestionDetail[];
|
||||
tags: Tag[];
|
||||
answers: Answer[];
|
||||
question_contents: QuestionContent[];
|
||||
sub_questions: QuestionDetail[];
|
||||
}
|
||||
|
||||
export interface QuestionQueryCondition extends QueryCondition {
|
||||
tag_ids: number[];
|
||||
types: QuestionType[];
|
||||
}
|
||||
tag_ids: number[];
|
||||
types: QuestionType[];
|
||||
is_published: number;
|
||||
}
|
||||
|
||||
export interface QuestionPage extends Page<Question> {}
|
||||
|
@ -1,11 +1,10 @@
|
||||
|
||||
export interface Tag {
|
||||
id: number;
|
||||
name: string;
|
||||
color: number;
|
||||
id: number;
|
||||
name: string;
|
||||
color: number;
|
||||
}
|
||||
|
||||
export function getHexColor(tag: Tag) {
|
||||
const color = tag.color.toString(16);
|
||||
return '#' + color;
|
||||
}
|
||||
const color = tag.color.toString(16);
|
||||
return '#' + color;
|
||||
}
|
||||
|
15
src/main.ts
15
src/main.ts
@ -1,5 +1,18 @@
|
||||
import './assets/main.css';
|
||||
|
||||
import { createApp } from 'vue';
|
||||
import { createPinia } from 'pinia';
|
||||
|
||||
import App from './App.vue';
|
||||
import router from './router';
|
||||
|
||||
createApp(App).use(router).mount('#app');
|
||||
import ElementPlus from 'element-plus';
|
||||
import 'element-plus/dist/index.css';
|
||||
|
||||
const app = createApp(App);
|
||||
app.use(ElementPlus);
|
||||
|
||||
app.use(createPinia());
|
||||
app.use(router);
|
||||
|
||||
app.mount('#app');
|
||||
|
@ -1,25 +1,23 @@
|
||||
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router';
|
||||
import HomeView from '../views/HomeView.vue';
|
||||
|
||||
const routes: Array<RouteRecordRaw> = [
|
||||
{
|
||||
path: '/',
|
||||
name: 'home',
|
||||
component: HomeView
|
||||
},
|
||||
{
|
||||
path: '/about',
|
||||
name: 'about',
|
||||
// route level code-splitting
|
||||
// this generates a separate chunk (about.[hash].js) for this route
|
||||
// which is lazy-loaded when the route is visited.
|
||||
component: () => import(/* webpackChunkName: "about" */ '../views/AboutView.vue')
|
||||
}
|
||||
];
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import HomeView from '../views/HomeView.vue'
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes
|
||||
});
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
name: 'home',
|
||||
component: HomeView
|
||||
},
|
||||
{
|
||||
path: '/about',
|
||||
name: 'about',
|
||||
// route level code-splitting
|
||||
// this generates a separate chunk (About.[hash].js) for this route
|
||||
// which is lazy-loaded when the route is visited.
|
||||
component: () => import('../views/AboutView.vue')
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
export default router;
|
||||
export default router
|
||||
|
6
src/shims-vue.d.ts
vendored
6
src/shims-vue.d.ts
vendored
@ -1,6 +0,0 @@
|
||||
/* eslint-disable */
|
||||
declare module '*.vue' {
|
||||
import type { DefineComponent } from 'vue'
|
||||
const component: DefineComponent<{}, {}, any>
|
||||
export default component
|
||||
}
|
12
src/stores/counter.ts
Normal file
12
src/stores/counter.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { ref, computed } from 'vue'
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
export const useCounterStore = defineStore('counter', () => {
|
||||
const count = ref(0)
|
||||
const doubleCount = computed(() => count.value * 2)
|
||||
function increment() {
|
||||
count.value++
|
||||
}
|
||||
|
||||
return { count, doubleCount, increment }
|
||||
})
|
@ -3,3 +3,13 @@
|
||||
<h1>This is an about page</h1>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
@media (min-width: 1024px) {
|
||||
.about {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -1,39 +1,30 @@
|
||||
<template>
|
||||
<div class="home">
|
||||
<img alt="Vue logo" src="../assets/logo.png">
|
||||
<HelloWorld msg="Welcome to Your Vue.js + TypeScript App" />
|
||||
</div>
|
||||
<main>
|
||||
<TheWelcome />
|
||||
<el-button plain @click="createTest"> create test </el-button>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Options, Vue } from 'vue-class-component';
|
||||
import HelloWorld from '@/components/HelloWorld.vue'; // @ is an alias to /src
|
||||
<script setup lang="ts">
|
||||
import TheWelcome from '../components/TheWelcome.vue'
|
||||
import { createNewQuestion } from '@/apis/question';
|
||||
import { Question, QuestionType } from '@/interfaces/question';
|
||||
import type { Question } from '@/interfaces/question';
|
||||
import { QuestionType } from '@/interfaces/question';
|
||||
|
||||
@Options({
|
||||
components: {
|
||||
HelloWorld
|
||||
}
|
||||
})
|
||||
|
||||
export default class HomeView extends Vue {
|
||||
// createNewQuestion for testing
|
||||
async mounted() {
|
||||
const req: Question = {
|
||||
title: 'test',
|
||||
score: 0,
|
||||
is_sub_question: false,
|
||||
id: null,
|
||||
type: QuestionType.SINGLE_CHOICE,
|
||||
estimated_time_sec: 0,
|
||||
is_published: false,
|
||||
reference_count: 0,
|
||||
correct_count: 0,
|
||||
operate_time: null,
|
||||
};
|
||||
const question = await createNewQuestion(req);
|
||||
console.log(question);
|
||||
const createTest = () => {
|
||||
let question: Question = {
|
||||
id: null,
|
||||
parent: -1,
|
||||
title: 'Test 0123',
|
||||
type: QuestionType.SINGLE_CHOICE,
|
||||
estimated_time_sec: 20,
|
||||
score: 1,
|
||||
is_published: false,
|
||||
reference_count: 0,
|
||||
correct_count: 0,
|
||||
create_at: '',
|
||||
update_at: ''
|
||||
}
|
||||
createNewQuestion(question)
|
||||
}
|
||||
</script>
|
||||
|
13
tsconfig.app.json
Normal file
13
tsconfig.app.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"extends": "@vue/tsconfig/tsconfig.dom.json",
|
||||
"include": ["env.d.ts", "src/**/*", "src/**/*.vue", "src/**/*.ts"],
|
||||
"exclude": ["src/**/__tests__/*"],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"noEmit": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
}
|
||||
}
|
@ -1,41 +1,14 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"strict": true,
|
||||
"jsx": "preserve",
|
||||
"moduleResolution": "node",
|
||||
"experimentalDecorators": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"useDefineForClassFields": true,
|
||||
"sourceMap": true,
|
||||
"baseUrl": ".",
|
||||
"types": [
|
||||
"webpack-env"
|
||||
],
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"src/*"
|
||||
]
|
||||
"files": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.node.json"
|
||||
},
|
||||
"lib": [
|
||||
"esnext",
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"scripthost"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx",
|
||||
"src/**/*.vue",
|
||||
"tests/**/*.ts",
|
||||
"tests/**/*.tsx"
|
||||
{
|
||||
"path": "./tsconfig.app.json"
|
||||
}
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
"compilerOptions": {
|
||||
"types": ["element-plus/global"]
|
||||
}
|
||||
}
|
||||
|
17
tsconfig.node.json
Normal file
17
tsconfig.node.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"extends": "@tsconfig/node18/tsconfig.json",
|
||||
"include": [
|
||||
"vite.config.*",
|
||||
"vitest.config.*",
|
||||
"cypress.config.*",
|
||||
"nightwatch.conf.*",
|
||||
"playwright.config.*"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"noEmit": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"types": ["node"]
|
||||
}
|
||||
}
|
15
vite.config.ts
Normal file
15
vite.config.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { fileURLToPath, URL } from 'node:url';
|
||||
|
||||
import { defineConfig } from 'vite';
|
||||
import vue from '@vitejs/plugin-vue';
|
||||
import vueJsx from '@vitejs/plugin-vue-jsx';
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [vue(), vueJsx()],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
||||
},
|
||||
},
|
||||
});
|
@ -1,4 +0,0 @@
|
||||
const { defineConfig } = require('@vue/cli-service');
|
||||
module.exports = defineConfig({
|
||||
transpileDependencies: true
|
||||
});
|
Loading…
Reference in New Issue
Block a user