Browse Source

add new

master
panda 10 months ago
commit
752a75a398
  1. 30
      .gitignore
  2. 3
      .vscode/extensions.json
  3. 33
      README.md
  4. 1
      env.d.ts
  5. 13
      index.html
  6. 1828
      package-lock.json
  7. 33
      package.json
  8. BIN
      public/favicon.ico
  9. 14
      src/App.vue
  10. BIN
      src/assets/images/login.jpg
  11. BIN
      src/assets/images/nav1.png
  12. BIN
      src/assets/images/nav10.png
  13. BIN
      src/assets/images/nav13.png
  14. 155
      src/components/admin/SignIn.vue
  15. 11
      src/components/blogs/HomePage.vue
  16. 50
      src/hooks/intex.ts
  17. 12
      src/main.ts
  18. 0
      src/router/admin.ts
  19. 0
      src/router/blog.ts
  20. 19
      src/router/index.ts
  21. 12
      src/stores/counter.ts
  22. 85
      src/tools/request.ts
  23. 14
      tsconfig.app.json
  24. 11
      tsconfig.json
  25. 19
      tsconfig.node.json
  26. 16
      vite.config.ts

30
.gitignore

@ -0,0 +1,30 @@
# 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
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
*.tsbuildinfo

3
.vscode/extensions.json

@ -0,0 +1,3 @@
{
"recommendations": ["Vue.volar"]
}

33
README.md

@ -0,0 +1,33 @@
# blog_front
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).
## 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 [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) to make the TypeScript language service aware of `.vue` types.
## Customize configuration
See [Vite Configuration Reference](https://vitejs.dev/config/).
## Project Setup
```sh
npm install
```
### Compile and Hot-Reload for Development
```sh
npm run dev
```
### Type-Check, Compile and Minify for Production
```sh
npm run build
```

1
env.d.ts

@ -0,0 +1 @@
/// <reference types="vite/client" />

13
index.html

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

1828
package-lock.json
File diff suppressed because it is too large
View File

33
package.json

@ -0,0 +1,33 @@
{
"name": "blog_front",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "run-p type-check \"build-only {@}\" --",
"preview": "vite preview",
"build-only": "vite build",
"type-check": "vue-tsc --build --force"
},
"dependencies": {
"ant-design-vue": "^4.2.1",
"axios": "^1.7.2",
"echarts": "^5.5.0",
"normalize.css": "^8.0.1",
"pinia": "^2.1.7",
"simplebar-vue": "^2.3.4",
"vue": "^3.4.21",
"vue-router": "^4.3.0"
},
"devDependencies": {
"@tsconfig/node20": "^20.1.4",
"@types/node": "^20.12.5",
"@vitejs/plugin-vue": "^5.0.4",
"@vue/tsconfig": "^0.5.1",
"npm-run-all2": "^6.1.2",
"typescript": "~5.4.0",
"vite": "^5.2.8",
"vue-tsc": "^2.0.11"
}
}

BIN
public/favicon.ico

14
src/App.vue

@ -0,0 +1,14 @@
<template>
<Simplebar style="height: 100vh;overflow: auto;">
<RouterView/>
</Simplebar>
</template>
<script setup lang="ts">
import Simplebar from 'simplebar-vue';
import 'simplebar-vue/dist/simplebar.min.css';
</script>
<style>
</style>

BIN
src/assets/images/login.jpg

After

Width: 1920  |  Height: 1080  |  Size: 48 KiB

BIN
src/assets/images/nav1.png

After

Width: 2560  |  Height: 1440  |  Size: 449 KiB

BIN
src/assets/images/nav10.png

After

Width: 2560  |  Height: 1440  |  Size: 1.3 MiB

BIN
src/assets/images/nav13.png

After

Width: 2560  |  Height: 1440  |  Size: 735 KiB

155
src/components/admin/SignIn.vue

@ -0,0 +1,155 @@
<template>
<context-holder />
<div id="container">
<a-spin v-if="loading" tip="Loading..."></a-spin>
<a-form v-else :model="formState" name="normal_login" class="login-form" @submit.prevent="login">
<h2>博客管理系统</h2>
<a-form-item label="用户名" name="username" :rules="[{ required: true, validator: verifyName }]">
<a-input v-model:value="formState.username" placeholder="请输入用户名">
<template #prefix>
<UserOutlined class="site-form-item-icon" />
</template>
</a-input>
</a-form-item>
<a-form-item label="密&nbsp;&nbsp;&nbsp;&nbsp;码" name="password"
:rules="[{ required: true, validator: verifyPassword }]">
<a-input-password v-model:value="formState.password" placeholder="请输入密码">
<template #prefix>
<LockOutlined class="site-form-item-icon" />
</template>
</a-input-password>
</a-form-item>
<a-form-item>
<a-form-item name="remember" no-style>
<a-checkbox v-model:checked="formState.remember">记住我</a-checkbox>
</a-form-item>
<a class="login-form-forgot" href="">忘记密码</a>
</a-form-item>
<a-form-item>
<a-button type="primary" html-type="submit" class="login-form-button" :disabled="disabled">
</a-button>
Or
<a href="">注册</a>
</a-form-item>
</a-form>
</div>
</template>
<script setup lang="ts">
import { reactive, computed, ref, onMounted } from 'vue';
import { UserOutlined, LockOutlined } from '@ant-design/icons-vue';
import { useRouter } from 'vue-router';
import { post } from '@/tools/request'
import { message } from 'ant-design-vue';
import { validator } from '@/hooks/intex'
const [messageApi, contextHolder] = message.useMessage();
interface FormState {
username: string;
password: string;
remember: boolean;
}
const verifyName = validator(4, 10, {
info: '请输入用户名',
tooshort: '用户名不能少于4个字符',
toolong: '用户名不能超过10个字符',
});
const verifyPassword = validator(6, 10, {
info: "请输入密码",
tooshort: "密码不能少于6个字符",
toolong: "密码不能超过10个字符"
});
const formState = reactive<FormState>({
username: '',
password: '',
remember: false,
});
const loading = ref(false)
const disabled = computed(() => {
return !(formState.username && formState.password);
});
const router = useRouter();
const login = async () => {
try {
loading.value = true;
const response = await post(
'users/token',
{
username: formState.username,
password: formState.password,
},
false,
{
'Accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded'
}
);
const token = response.data.access_token;
localStorage.setItem('token', token);
messageApi.success("登录成功")
if (formState.remember) {
localStorage.setItem('rememberedUsername', formState.username);
} else {
localStorage.removeItem('rememberedUsername');
}
setTimeout(() => {
router.push('/admin/dashboard');
}, 2000);
} catch (error) {
messageApi.error('用户或密码输入错误');
} finally {
loading.value = false;
}
};
onMounted(() => {
const rememberedUsername = localStorage.getItem('rememberedUsername');
if (rememberedUsername) {
formState.username = rememberedUsername;
formState.remember = true;
}
});
</script>
<style scoped>
#container {
display: flex;
width: 100vw;
height: 100vh;
justify-content: center;
align-items: center;
}
#container::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: url('@/assets/images/login.jpg');
opacity: 0.2;
/* 设置透明度 */
}
h2 {
text-align: center;
color: rgba(41, 39, 51, 0.88);
}
#components-form-demo-normal-login .login-form {
max-width: 300px;
}
#components-form-demo-normal-login .login-form-forgot {
float: right;
}
#components-form-demo-normal-login .login-form-button {
width: 100%;
}
</style>

11
src/components/blogs/HomePage.vue

@ -0,0 +1,11 @@
<template>
</template>
<script setup lang='ts'>
</script>
<style>
</style>

50
src/hooks/intex.ts

@ -0,0 +1,50 @@
import * as echarts from "echarts";
interface inputMessage {
info: string;
tooshort: string;
toolong: string;
}
interface selectMessage {
info: string;
}
/**
* antd vue的验证规则
*/
export const validator = (
minLength: number,
maxLength: number,
message: inputMessage
) => {
return (_: any, value: string) => {
if (!value) {
return Promise.reject(message.info);
} else if (value.length < minLength) {
return Promise.reject(message.tooshort);
} else if (value.length > maxLength) {
return Promise.reject(message.toolong);
} else {
return Promise.resolve();
}
};
};
export const verifySelect = (message: selectMessage) => {
return (_: any, value: string) => {
if (!value) {
return Promise.reject(message.info);
} else {
return Promise.resolve();
}
};
};
/**
* echarts配置
*/
export const createEcharts = (chartRef: any, option: any) => {
const myChart = echarts.init(chartRef.value);
myChart.setOption(option);
};

12
src/main.ts

@ -0,0 +1,12 @@
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import "normalize.css"
import App from './App.vue'
import router from './router'
import Antd from 'ant-design-vue';
const app = createApp(App)
app.use(createPinia())
app.use(router)
app.use(Antd)
app.mount('#app')

0
src/router/admin.ts

0
src/router/blog.ts

19
src/router/index.ts

@ -0,0 +1,19 @@
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: "/",
name: "page",
component: () => import("@/components/blogs/HomePage.vue"),
},
{
path: "/login",
name: "login",
component: () => import("@/components/admin/SignIn.vue"),
},
]
})
export default router

12
src/stores/counter.ts

@ -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 }
})

85
src/tools/request.ts

@ -0,0 +1,85 @@
import axios from 'axios';
import { type AxiosRequestConfig } from 'axios';
import router from '@/router';
const instance = axios.create({
// 添加url
baseURL: 'http://www.wuruilin.cn:8000/',
timeout: 5000,
});
// 请求拦截器
instance.interceptors.request.use(
config => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
error => {
return Promise.reject(error);
}
);
// 响应拦截器
instance.interceptors.response.use(
response => {
return response;
},
error => {
if (error.response && error.response.status === 401) {
router.push('/login');
}
return Promise.reject(error);
}
);
const request = (method: string, url: string, dataOrParams = {}, requiresAuth = false, headers: AxiosRequestConfig['headers'] = {}) => {
if (requiresAuth) {
const token = localStorage.getItem('token');
if (!token) {
return Promise.reject(new Error('请先登录'));
}
headers.Authorization = `Bearer ${token}`;
}
// return instance({
// method,
// url,
// data,
// headers,
// });
let requestOptions: AxiosRequestConfig = {
method,
url,
headers,
};
if (method.toLowerCase() === 'get' || method.toLowerCase() === 'delete') {
requestOptions.params = dataOrParams;
} else {
requestOptions.data = dataOrParams;
}
return instance(requestOptions);
};
// 封装 GET 请求
const get = (url: string, params = {}, requiresAuth = false, headers: AxiosRequestConfig['headers'] = {}) => {
return request('get', url, params, requiresAuth, headers);
};
// 封装 POST 请求
const post = (url: string, data = {}, requiresAuth = false, headers: AxiosRequestConfig['headers'] = {}) => {
return request('post', url, data, requiresAuth, headers);
};
// 封装 DELETE 请求
const remove = (url: string, data = {}, requiresAuth = false, headers: AxiosRequestConfig['headers'] = {}) => {
return request('delete', url, data, requiresAuth, headers);
};
// 封装 PUT 请求
const put = (url: string, data = {}, requiresAuth = false, headers: AxiosRequestConfig['headers'] = {}) => {
return request('put', url, data, requiresAuth, headers);
};
export { get, post, remove, put };

14
tsconfig.app.json

@ -0,0 +1,14 @@
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"include": ["env.d.ts", "src/**/*", "src/**/*.vue","src/**/*.ts"],
"exclude": ["src/**/__tests__/*"],
"compilerOptions": {
"composite": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}

11
tsconfig.json

@ -0,0 +1,11 @@
{
"files": [],
"references": [
{
"path": "./tsconfig.node.json"
},
{
"path": "./tsconfig.app.json"
}
]
}

19
tsconfig.node.json

@ -0,0 +1,19 @@
{
"extends": "@tsconfig/node20/tsconfig.json",
"include": [
"vite.config.*",
"vitest.config.*",
"cypress.config.*",
"nightwatch.conf.*",
"playwright.config.*"
],
"compilerOptions": {
"composite": true,
"noEmit": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"module": "ESNext",
"moduleResolution": "Bundler",
"types": ["node"]
}
}

16
vite.config.ts

@ -0,0 +1,16 @@
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
})
Loading…
Cancel
Save