first commit
30
.gitignore
vendored
Normal file
@ -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
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"recommendations": ["Vue.volar"]
|
||||||
|
}
|
29
README.md
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# big-event
|
||||||
|
|
||||||
|
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).
|
||||||
|
|
||||||
|
## Customize configuration
|
||||||
|
|
||||||
|
See [Vite Configuration Reference](https://vite.dev/config/).
|
||||||
|
|
||||||
|
## Project Setup
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Compile and Hot-Reload for Development
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### Compile and Minify for Production
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm run build
|
||||||
|
```
|
13
index.html
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="">
|
||||||
|
<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.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
8
jsconfig.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./src/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"exclude": ["node_modules", "dist"]
|
||||||
|
}
|
4147
package-lock.json
generated
Normal file
26
package.json
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"name": "big-event",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@vueup/vue-quill": "^1.2.0",
|
||||||
|
"axios": "^1.7.9",
|
||||||
|
"element-plus": "^2.9.3",
|
||||||
|
"pinia": "^2.3.0",
|
||||||
|
"pinia-persistedstate-plugin": "^0.1.0",
|
||||||
|
"vue": "^3.5.13",
|
||||||
|
"vue-router": "^4.5.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vitejs/plugin-vue": "^5.2.1",
|
||||||
|
"sass": "^1.83.4",
|
||||||
|
"vite": "^6.0.5",
|
||||||
|
"vite-plugin-vue-devtools": "^7.6.8"
|
||||||
|
}
|
||||||
|
}
|
BIN
public/favicon.ico
Normal file
After Width: | Height: | Size: 4.2 KiB |
13
src/App.vue
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<script setup>
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<router-view></router-view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
</style>
|
31
src/api/article.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
export const articleCategoryListService = () => {
|
||||||
|
/*const tokenStore = useTokenStore()
|
||||||
|
return request.get('/category', {
|
||||||
|
headers: {
|
||||||
|
Authorization: tokenStore.token //pinia里面定义的响应式数据不需要.value
|
||||||
|
}
|
||||||
|
})*/
|
||||||
|
return request.get('/category')
|
||||||
|
}
|
||||||
|
|
||||||
|
export const addCategoryService = (categoryData) => {
|
||||||
|
return request.post('/category', categoryData)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const updateCategoryService = (categoryData) => {
|
||||||
|
return request.put(`/category`, categoryData)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const deleteCategoryService = (id) => {
|
||||||
|
return request.delete(`/category?id=`+id)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const articleListService = (params) => {
|
||||||
|
return request.get('/article', {params: params})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const articleAddService = (articleData) => {
|
||||||
|
return request.post('/article',articleData)
|
||||||
|
}
|
48
src/api/user.js
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
//注册接口
|
||||||
|
export const userRegisterService = (registerData)=>{
|
||||||
|
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
for(let key in registerData){
|
||||||
|
params.append(key,registerData[key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return request.post('/user/register',params);
|
||||||
|
}
|
||||||
|
|
||||||
|
//登录接口
|
||||||
|
export const userLoginService = (loginData)=>{
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
for(let key in loginData){
|
||||||
|
params.append(key,loginData[key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return request.post('/user/login',params);
|
||||||
|
}
|
||||||
|
|
||||||
|
//获取用户详细信息
|
||||||
|
export const userInfoService = ()=>{
|
||||||
|
return request.get('/user/userinfo')
|
||||||
|
}
|
||||||
|
|
||||||
|
//修改个人信息
|
||||||
|
export const userInfoUpdateService = (userInfoData)=>{
|
||||||
|
return request.put('/user/update',userInfoData)
|
||||||
|
}
|
||||||
|
|
||||||
|
//修改头像
|
||||||
|
export const userAvatarUpdateService = (avatarUrl)=>{
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
params.append('avatarUrl',avatarUrl)
|
||||||
|
return request.patch('/user/updateAvatar',params)
|
||||||
|
}
|
||||||
|
|
||||||
|
//修改密码
|
||||||
|
export const userPasswordUpdateService = (passwordData) => {
|
||||||
|
return request.patch('/user/updatePwd', passwordData, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
BIN
src/assets/avatar.jpg
Normal file
After Width: | Height: | Size: 9.7 KiB |
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;
|
||||||
|
}
|
BIN
src/assets/cover.jpg
Normal file
After Width: | Height: | Size: 9.5 KiB |
BIN
src/assets/default.png
Normal file
After Width: | Height: | Size: 4.0 KiB |
BIN
src/assets/login_bg.jpg
Normal file
After Width: | Height: | Size: 92 KiB |
BIN
src/assets/login_title.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
src/assets/logo.png
Normal file
After Width: | Height: | Size: 5.5 KiB |
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 |
BIN
src/assets/logo2.png
Normal file
After Width: | Height: | Size: 11 KiB |
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;
|
||||||
|
}
|
||||||
|
}
|
20
src/assets/main.scss
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fade-slide */
|
||||||
|
.fade-slide-leave-active,
|
||||||
|
.fade-slide-enter-active {
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade-slide-enter-from {
|
||||||
|
transform: translateX(-30px);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade-slide-leave-to {
|
||||||
|
transform: translateX(30px);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
21
src/main.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { createApp } from 'vue'
|
||||||
|
import App from './App.vue'
|
||||||
|
import router from './router'
|
||||||
|
import { createPinia } from 'pinia';
|
||||||
|
import {createPersistedState} from'pinia-persistedstate-plugin'
|
||||||
|
|
||||||
|
import ElementPlus from 'element-plus'
|
||||||
|
import 'element-plus/dist/index.css'
|
||||||
|
import zhCn from 'element-plus/es/locale/lang/zh-cn'
|
||||||
|
|
||||||
|
const pinia = createPinia()
|
||||||
|
const persist = createPersistedState()
|
||||||
|
|
||||||
|
const app = createApp(App)
|
||||||
|
|
||||||
|
pinia.use(persist)
|
||||||
|
app.use(pinia)
|
||||||
|
app.use(ElementPlus,{locale:zhCn})
|
||||||
|
app.use(router)
|
||||||
|
|
||||||
|
app.mount('#app')
|
36
src/router/index.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { createRouter, createWebHistory } from 'vue-router'
|
||||||
|
|
||||||
|
import LoginView from '@/views/LoginView.vue'
|
||||||
|
import LayoutView from '@/views/LayoutView.vue';
|
||||||
|
|
||||||
|
import ArticleCategoryVue from '@/views/article/ArticleCategory.vue'
|
||||||
|
import ArticleManageVue from '@/views/article/ArticleManage.vue'
|
||||||
|
import UserAvatarVue from '@/views/user/UserAvatar.vue'
|
||||||
|
import UserInfoVue from '@/views/user/UserInfo.vue'
|
||||||
|
import UserResetPasswordVue from '@/views/user/UserResetPassword.vue'
|
||||||
|
|
||||||
|
|
||||||
|
//define the routing table
|
||||||
|
const routes = [
|
||||||
|
{path: '/login', component: LoginView},
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
component: LayoutView,
|
||||||
|
redirect: '/article/category',
|
||||||
|
children:[
|
||||||
|
{ path: '/article/category', component: ArticleCategoryVue },
|
||||||
|
{ path: '/article/manage', component: ArticleManageVue },
|
||||||
|
{ path: '/user/info', component: UserInfoVue },
|
||||||
|
{ path: '/user/avatar', component: UserAvatarVue },
|
||||||
|
{ path: '/user/resetPassword', component: UserResetPasswordVue }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHistory(),
|
||||||
|
routes: routes,
|
||||||
|
})
|
||||||
|
|
||||||
|
//export router
|
||||||
|
export default router
|
31
src/stores/token.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
//定义store
|
||||||
|
import {defineStore} from 'pinia'
|
||||||
|
import {ref} from 'vue'
|
||||||
|
/*
|
||||||
|
第一个参数:名字,唯一性
|
||||||
|
第二个参数:函数,函数的内部可以定义状态的所有内容
|
||||||
|
|
||||||
|
返回值: 函数
|
||||||
|
*/
|
||||||
|
export const useTokenStore = defineStore('token',()=>{
|
||||||
|
//定义状态的内容
|
||||||
|
|
||||||
|
//1.响应式变量
|
||||||
|
const token = ref('')
|
||||||
|
|
||||||
|
//2.定义一个函数,修改token的值
|
||||||
|
const setToken = (newToken)=>{
|
||||||
|
token.value = newToken
|
||||||
|
}
|
||||||
|
|
||||||
|
//3.函数,移除token的值
|
||||||
|
const removeToken = ()=>{
|
||||||
|
token.value=''
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
token,setToken,removeToken
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
persist:true//持久化存储
|
||||||
|
});
|
21
src/stores/userInfo.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import {defineStore} from 'pinia'
|
||||||
|
import {ref} from 'vue'
|
||||||
|
const useUserInfoStore = defineStore('userInfo',()=>{
|
||||||
|
//定义状态相关的内容
|
||||||
|
|
||||||
|
const info = ref({})
|
||||||
|
|
||||||
|
const setInfo = (newInfo)=>{
|
||||||
|
info.value = newInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const removeInfo = ()=>{
|
||||||
|
info.value = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {info,setInfo,removeInfo}
|
||||||
|
|
||||||
|
},{persist:true})
|
||||||
|
|
||||||
|
export default useUserInfoStore;
|
53
src/utils/request.js
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
//定制请求的实例
|
||||||
|
|
||||||
|
//导入axios npm install axios
|
||||||
|
import axios from 'axios';
|
||||||
|
import { useTokenStore } from '@/stores/token';
|
||||||
|
import { ElMessage } from 'element-plus';
|
||||||
|
//定义一个变量,记录公共的前缀 , baseURL
|
||||||
|
const baseURL = '/api';
|
||||||
|
const instance = axios.create({baseURL})
|
||||||
|
|
||||||
|
//添加请求拦截器
|
||||||
|
instance.interceptors.request.use(
|
||||||
|
(config)=>{
|
||||||
|
//获取token
|
||||||
|
const tokenStore = useTokenStore();
|
||||||
|
//给请求头添加token
|
||||||
|
if(tokenStore.token){
|
||||||
|
config.headers.Authorization = tokenStore.token;
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
},
|
||||||
|
err=>{
|
||||||
|
//请求错误使用把异步的状态转化成失败的状态
|
||||||
|
return Promise.reject(err);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
import router from '@/router';
|
||||||
|
|
||||||
|
//添加响应拦截器
|
||||||
|
instance.interceptors.response.use(
|
||||||
|
result=>{
|
||||||
|
if (result.data.code === 0){
|
||||||
|
return result.data;
|
||||||
|
} else{
|
||||||
|
ElMessage.error(result.data.message?result.data.message:'服务异常');
|
||||||
|
return Promise.reject(result.data);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
err=>{
|
||||||
|
//未登录,提示,并且跳转登录页面
|
||||||
|
if (err.response.status === 401){
|
||||||
|
ElMessage.info('请先登录');
|
||||||
|
router.push('/login');
|
||||||
|
}else{
|
||||||
|
ElMessage.error('服务异常');
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.reject(err);//异步的状态转化成失败的状态
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export default instance;
|
290
src/views/LayoutView.vue
Normal file
@ -0,0 +1,290 @@
|
|||||||
|
<script setup>
|
||||||
|
import { ref, onMounted, onUnmounted } from 'vue';
|
||||||
|
import {
|
||||||
|
Management,
|
||||||
|
Promotion,
|
||||||
|
UserFilled,
|
||||||
|
User,
|
||||||
|
Crop,
|
||||||
|
EditPen,
|
||||||
|
SwitchButton,
|
||||||
|
CaretBottom
|
||||||
|
} from '@element-plus/icons-vue';
|
||||||
|
import avatar from '@/assets/default.png';
|
||||||
|
import { userInfoService } from '@/api/user.js';
|
||||||
|
import useUserInfoStore from '@/stores/userInfo.js';
|
||||||
|
import { useTokenStore } from '@/stores/token.js';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||||
|
|
||||||
|
const tokenStore = useTokenStore();
|
||||||
|
const userInfoStore = useUserInfoStore();
|
||||||
|
const router = useRouter();
|
||||||
|
const isPortrait = ref(false);
|
||||||
|
|
||||||
|
const getUserInfo = async () => {
|
||||||
|
let result = await userInfoService();
|
||||||
|
userInfoStore.setInfo(result.data);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCommand = (command) => {
|
||||||
|
if (command === 'logout') {
|
||||||
|
ElMessageBox.confirm(
|
||||||
|
'您确认要退出吗?',
|
||||||
|
'温馨提示',
|
||||||
|
{
|
||||||
|
confirmButtonText: '确认',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then(async () => {
|
||||||
|
tokenStore.removeToken();
|
||||||
|
userInfoStore.removeInfo();
|
||||||
|
router.push('/login');
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '退出登录成功',
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
ElMessage({
|
||||||
|
type: 'info',
|
||||||
|
message: '用户取消了退出登录',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
router.push('/user/' + command);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkIsPortrait = () => {
|
||||||
|
isPortrait.value = window.innerWidth / window.innerHeight < 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
checkIsPortrait();
|
||||||
|
window.addEventListener('resize', checkIsPortrait);
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('resize', checkIsPortrait);
|
||||||
|
});
|
||||||
|
|
||||||
|
getUserInfo();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<el-container class="layout-container">
|
||||||
|
<!-- 左侧菜单(宽高比大于1时显示) -->
|
||||||
|
<el-aside v-if="!isPortrait" width="200px">
|
||||||
|
<div class="el-aside__logo"></div>
|
||||||
|
<el-menu active-text-color="#ffd04b" background-color="#232323" text-color="#fff" router>
|
||||||
|
<el-menu-item index="/article/category">
|
||||||
|
<el-icon>
|
||||||
|
<Management />
|
||||||
|
</el-icon>
|
||||||
|
<span>文章分类</span>
|
||||||
|
</el-menu-item>
|
||||||
|
<el-menu-item index="/article/manage">
|
||||||
|
<el-icon>
|
||||||
|
<Promotion />
|
||||||
|
</el-icon>
|
||||||
|
<span>文章管理</span>
|
||||||
|
</el-menu-item>
|
||||||
|
<el-sub-menu>
|
||||||
|
<template #title>
|
||||||
|
<el-icon>
|
||||||
|
<UserFilled />
|
||||||
|
</el-icon>
|
||||||
|
<span>个人中心</span>
|
||||||
|
</template>
|
||||||
|
<el-menu-item index="/user/info">
|
||||||
|
<el-icon>
|
||||||
|
<User />
|
||||||
|
</el-icon>
|
||||||
|
<span>基本资料</span>
|
||||||
|
</el-menu-item>
|
||||||
|
<el-menu-item index="/user/avatar">
|
||||||
|
<el-icon>
|
||||||
|
<Crop />
|
||||||
|
</el-icon>
|
||||||
|
<span>更换头像</span>
|
||||||
|
</el-menu-item>
|
||||||
|
<el-menu-item index="/user/resetPassword">
|
||||||
|
<el-icon>
|
||||||
|
<EditPen />
|
||||||
|
</el-icon>
|
||||||
|
<span>重置密码</span>
|
||||||
|
</el-menu-item>
|
||||||
|
</el-sub-menu>
|
||||||
|
</el-menu>
|
||||||
|
</el-aside>
|
||||||
|
<!-- 顶部菜单(宽高比小于1时显示) -->
|
||||||
|
<el-header v-if="isPortrait" class="mobile-header">
|
||||||
|
<el-menu mode="horizontal" active-text-color="#ffd04b" background-color="#232323" text-color="#fff" router>
|
||||||
|
<el-menu-item index="/article/category">
|
||||||
|
<el-icon>
|
||||||
|
<Management />
|
||||||
|
</el-icon>
|
||||||
|
<span>文章分类</span>
|
||||||
|
</el-menu-item>
|
||||||
|
<el-menu-item index="/article/manage">
|
||||||
|
<el-icon>
|
||||||
|
<Promotion />
|
||||||
|
</el-icon>
|
||||||
|
<span>文章管理</span>
|
||||||
|
</el-menu-item>
|
||||||
|
<el-sub-menu>
|
||||||
|
<template #title>
|
||||||
|
<el-icon>
|
||||||
|
<UserFilled />
|
||||||
|
</el-icon>
|
||||||
|
<span>个人中心</span>
|
||||||
|
</template>
|
||||||
|
<el-menu-item index="/user/info">
|
||||||
|
<el-icon>
|
||||||
|
<User />
|
||||||
|
</el-icon>
|
||||||
|
<span>基本资料</span>
|
||||||
|
</el-menu-item>
|
||||||
|
<el-menu-item index="/user/avatar">
|
||||||
|
<el-icon>
|
||||||
|
<Crop />
|
||||||
|
</el-icon>
|
||||||
|
<span>更换头像</span>
|
||||||
|
</el-menu-item>
|
||||||
|
<el-menu-item index="/user/resetPassword">
|
||||||
|
<el-icon>
|
||||||
|
<EditPen />
|
||||||
|
</el-icon>
|
||||||
|
<span>重置密码</span>
|
||||||
|
</el-menu-item>
|
||||||
|
</el-sub-menu>
|
||||||
|
</el-menu>
|
||||||
|
<el-dropdown placement="bottom-end" @command="handleCommand">
|
||||||
|
<span class="el-dropdown__box">
|
||||||
|
<el-avatar :src="userInfoStore.info.userPic ? userInfoStore.info.userPic : avatar" />
|
||||||
|
<el-icon>
|
||||||
|
<CaretBottom />
|
||||||
|
</el-icon>
|
||||||
|
</span>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item command="info" :icon="User">基本资料</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="avatar" :icon="Crop">更换头像</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="resetPassword" :icon="EditPen">重置密码</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="logout" :icon="SwitchButton">退出登录</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
</el-header>
|
||||||
|
<!-- 右侧主区域 -->
|
||||||
|
<el-container>
|
||||||
|
<!-- 头部区域 -->
|
||||||
|
<el-header v-if="!isPortrait">
|
||||||
|
<div>程序员:<strong>{{ userInfoStore.info.nickname }}</strong></div>
|
||||||
|
<el-dropdown placement="bottom-end" @command="handleCommand">
|
||||||
|
<span class="el-dropdown__box">
|
||||||
|
<el-avatar :src="userInfoStore.info.userPic ? userInfoStore.info.userPic : avatar" />
|
||||||
|
<el-icon>
|
||||||
|
<CaretBottom />
|
||||||
|
</el-icon>
|
||||||
|
</span>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item command="info" :icon="User">基本资料</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="avatar" :icon="Crop">更换头像</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="resetPassword" :icon="EditPen">重置密码</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="logout" :icon="SwitchButton">退出登录</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
</el-header>
|
||||||
|
<!-- 中间区域 -->
|
||||||
|
<el-main>
|
||||||
|
<router-view></router-view>
|
||||||
|
</el-main>
|
||||||
|
<!-- 底部区域 -->
|
||||||
|
<el-footer>大事件 ©2025 Created by xclele</el-footer>
|
||||||
|
</el-container>
|
||||||
|
</el-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.layout-container {
|
||||||
|
height: 100vh;
|
||||||
|
|
||||||
|
.el-aside {
|
||||||
|
background-color: #232323;
|
||||||
|
|
||||||
|
&__logo {
|
||||||
|
height: 120px;
|
||||||
|
background: url('@/assets/logo.png') no-repeat center / 120px auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-menu {
|
||||||
|
border-right: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-header {
|
||||||
|
background-color: #fff;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.el-dropdown__box {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.el-icon {
|
||||||
|
color: #999;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active,
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-footer {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (orientation: portrait) {
|
||||||
|
.el-aside {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-header {
|
||||||
|
display: flex;
|
||||||
|
background-color: #232323;
|
||||||
|
.el-menu {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.el-dropdown__box {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.el-icon {
|
||||||
|
color: #999;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active,
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
223
src/views/LoginView.vue
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
<script setup>
|
||||||
|
import { User, Lock } from '@element-plus/icons-vue'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
//控制注册与登录表单的显示, 默认显示注册
|
||||||
|
const isRegister = ref(false)
|
||||||
|
//定义数据模型
|
||||||
|
const registerData = ref({
|
||||||
|
username: '',
|
||||||
|
password: '',
|
||||||
|
rePassword: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const loginData = ref({
|
||||||
|
username: '',
|
||||||
|
password: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
//repassword校验规则
|
||||||
|
const checkRePassword = (rule, value, callback) => {
|
||||||
|
if (value === '') {
|
||||||
|
callback(new Error('请再次确认密码'))
|
||||||
|
} else if (value !== registerData.value.password) {
|
||||||
|
callback(new Error('两次输入密码不一致!'))
|
||||||
|
} else {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//定义表单校验规则
|
||||||
|
const rules = {
|
||||||
|
username:[
|
||||||
|
{ required: true, message: '请输入用户名', trigger: 'blur' },
|
||||||
|
{ min: 5, max: 16, message: '长度在 5 到 16 个字符', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
password:[
|
||||||
|
{ required: true, message: '请输入密码', trigger: 'blur' },
|
||||||
|
{ min: 5, max: 16, message: '长度在 5 到 16 个字符', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
rePassword:[
|
||||||
|
{validator:checkRePassword, trigger: 'blur'}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
//调用后台接口注册
|
||||||
|
import { userRegisterService,userLoginService } from '@/api/user.js'
|
||||||
|
const register = async() => {
|
||||||
|
//registerdata是响应式对象,所以要用registerData.value
|
||||||
|
|
||||||
|
let result = await userRegisterService(registerData.value)
|
||||||
|
if (result.code === 0){
|
||||||
|
//注册成功
|
||||||
|
ElMessage.success(result.msg? result.msg:'注册成功')
|
||||||
|
}else{
|
||||||
|
//注册失败
|
||||||
|
ElMessage.error('注册失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//调用后台接口登录
|
||||||
|
import {useRouter} from 'vue-router';
|
||||||
|
import { useTokenStore } from '@/stores/token.js';
|
||||||
|
const router = useRouter();
|
||||||
|
const tokenStore = useTokenStore();
|
||||||
|
|
||||||
|
const login = async() => {
|
||||||
|
//loginData是响应式对象,所以要用loginData.value
|
||||||
|
let result = await userLoginService(loginData.value)
|
||||||
|
if (result.code === 0){
|
||||||
|
//登录成功
|
||||||
|
ElMessage.success(result.msg? result.msg:'登录成功')
|
||||||
|
//存储token
|
||||||
|
tokenStore.setToken(result.data)
|
||||||
|
router.push('/')
|
||||||
|
}else{
|
||||||
|
//登录失败
|
||||||
|
ElMessage.error('登录失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//根据宽高比判断是否为移动端
|
||||||
|
import { onMounted, onUnmounted } from 'vue';
|
||||||
|
|
||||||
|
const isPortrait = ref(false);
|
||||||
|
|
||||||
|
const checkIsPortrait = () => {
|
||||||
|
isPortrait.value = window.innerWidth / window.innerHeight < 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
checkIsPortrait();
|
||||||
|
window.addEventListener('resize', checkIsPortrait);
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('resize', checkIsPortrait);
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<el-row class="login-page">
|
||||||
|
<el-col :span="12" class="bg" v-if="!isPortrait"></el-col>
|
||||||
|
<el-col :span="isPortrait ? 24 :12" :offset="0" class="form">
|
||||||
|
<!-- 注册表单 -->
|
||||||
|
<el-form ref="form" size="medium" autocomplete="off" v-if="isRegister" :model="registerData" :rules="rules">
|
||||||
|
<el-form-item>
|
||||||
|
<h1>注册</h1>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item prop="username">
|
||||||
|
<el-input :prefix-icon="User" placeholder="请输入用户名" v-model="registerData.username"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item prop="password">
|
||||||
|
<el-input :prefix-icon="Lock" type="password" placeholder="请输入密码" v-model="registerData.password"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item prop="rePassword">
|
||||||
|
<el-input :prefix-icon="Lock" type="password" placeholder="请输入再次密码" v-model="registerData.rePassword"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<!-- 注册按钮 -->
|
||||||
|
<el-form-item>
|
||||||
|
<el-button class="button" type="primary" auto-insert-space @click="register">
|
||||||
|
注册
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item class="flex">
|
||||||
|
<el-link type="info" :underline="false" @click="isRegister = false">
|
||||||
|
← 返回
|
||||||
|
</el-link>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<!-- 登录表单 -->
|
||||||
|
<el-form ref="form" size="large" autocomplete="off" v-else :model="loginData" :rules="rules" @keyup.enter="login"> <!--复用登陆表单-->
|
||||||
|
<el-form-item>
|
||||||
|
<h1>登录</h1>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item prop="username">
|
||||||
|
<el-input :prefix-icon="User" placeholder="请输入用户名" v-model="loginData.username"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item prop="password">
|
||||||
|
<el-input name="password" :prefix-icon="Lock" type="password" placeholder="请输入密码" v-model="loginData.password"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item class="flex">
|
||||||
|
<div class="flex">
|
||||||
|
<el-checkbox>记住我</el-checkbox>
|
||||||
|
<el-link type="primary" :underline="false">忘记密码?</el-link>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<!-- 登录按钮 -->
|
||||||
|
<el-form-item>
|
||||||
|
<el-button class="button" type="primary" auto-insert-space @click="login">登录</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item class="flex">
|
||||||
|
<el-link type="info" :underline="false" @click="isRegister = true">
|
||||||
|
注册 →
|
||||||
|
</el-link>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
/* 样式 */
|
||||||
|
@media (orientation: Landscape){
|
||||||
|
.login-page {
|
||||||
|
height: 100vh;
|
||||||
|
background-color: #fff;
|
||||||
|
|
||||||
|
.bg {
|
||||||
|
background: url('@/assets/logo2.png') no-repeat 60% center / 240px auto,
|
||||||
|
url('@/assets/login_bg.jpg') no-repeat center / cover;
|
||||||
|
border-radius: 0 20px 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form {
|
||||||
|
width: 60%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
user-select: none;
|
||||||
|
padding: calc(10%) ;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (orientation: portrait) {
|
||||||
|
.form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
189
src/views/article/ArticleCategory.vue
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
<script setup>
|
||||||
|
import {
|
||||||
|
Edit,
|
||||||
|
Delete
|
||||||
|
} from '@element-plus/icons-vue'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
const categories = ref([
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"categoryName": "美食",
|
||||||
|
"categoryAlias": "my",
|
||||||
|
"createTime": "2023-09-02 12:06:59",
|
||||||
|
"updateTime": "2023-09-02 12:06:59"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"categoryName": "娱乐",
|
||||||
|
"categoryAlias": "yl",
|
||||||
|
"createTime": "2023-09-02 12:08:16",
|
||||||
|
"updateTime": "2023-09-02 12:08:16"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 5,
|
||||||
|
"categoryName": "军事",
|
||||||
|
"categoryAlias": "js",
|
||||||
|
"createTime": "2023-09-02 12:08:33",
|
||||||
|
"updateTime": "2023-09-02 12:08:33"
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
//控制添加分类弹窗
|
||||||
|
const dialogVisible = ref(false)
|
||||||
|
//控制删除确认弹窗
|
||||||
|
const deleteDialogVisible = ref(false)
|
||||||
|
//弹窗title
|
||||||
|
const title=ref('')
|
||||||
|
|
||||||
|
//显示编辑弹窗
|
||||||
|
const showEditDialog = (row) => {
|
||||||
|
dialogVisible.value = true;
|
||||||
|
title.value = '编辑分类';
|
||||||
|
//数据拷贝
|
||||||
|
categoryModel.value.categoryName = row.categoryName;
|
||||||
|
categoryModel.value.categoryAlias = row.categoryAlias;
|
||||||
|
//扩展id属性
|
||||||
|
categoryModel.value.id = row.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
//显示删除确认弹窗
|
||||||
|
const showDeleteDialog = (row) => {
|
||||||
|
deleteDialogVisible.value = true;
|
||||||
|
//数据拷贝
|
||||||
|
categoryModel.value.categoryName = row.categoryName;
|
||||||
|
categoryModel.value.categoryAlias = row.categoryAlias;
|
||||||
|
//读取id属性
|
||||||
|
categoryModel.value.id = row.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
//添加分类数据模型
|
||||||
|
const categoryModel = ref({
|
||||||
|
categoryName: '',
|
||||||
|
categoryAlias: ''
|
||||||
|
})
|
||||||
|
//添加分类表单校验
|
||||||
|
const rules = {
|
||||||
|
categoryName: [
|
||||||
|
{ required: true, message: '请输入分类名称', trigger: 'blur' },
|
||||||
|
],
|
||||||
|
categoryAlias: [
|
||||||
|
{ required: true, message: '请输入分类别名', trigger: 'blur' },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
//按照分类获取文章列表
|
||||||
|
import { articleCategoryListService,addCategoryService,updateCategoryService,deleteCategoryService } from '@/api/article.js'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
|
const articleCategoryList = async() => {
|
||||||
|
let result = await articleCategoryListService();
|
||||||
|
categories.value = result.data;
|
||||||
|
}
|
||||||
|
articleCategoryList(); //调用函数
|
||||||
|
|
||||||
|
//添加分类
|
||||||
|
const addCategory = async() => {
|
||||||
|
//调用接口添加分类
|
||||||
|
let result = await addCategoryService(categoryModel.value);
|
||||||
|
ElMessage.success(result.message ? result.message : '添加成功');
|
||||||
|
|
||||||
|
//刷新列表并关闭弹窗
|
||||||
|
articleCategoryList();
|
||||||
|
dialogVisible.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//编辑分类
|
||||||
|
const updateCategory = async() => {
|
||||||
|
//调用接口编辑分类
|
||||||
|
let result = await updateCategoryService(categoryModel.value);
|
||||||
|
ElMessage.success(result.message ? result.message : '修改成功');
|
||||||
|
|
||||||
|
//刷新列表并关闭弹窗
|
||||||
|
articleCategoryList();
|
||||||
|
dialogVisible.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//删除分类
|
||||||
|
const deleteCategory = async() => {
|
||||||
|
//调用接口删除分类
|
||||||
|
console.log(categoryModel.value.id)
|
||||||
|
let result = await deleteCategoryService(categoryModel.value.id);
|
||||||
|
ElMessage.success(result.message ? result.message : '删除成功');
|
||||||
|
|
||||||
|
//刷新列表
|
||||||
|
articleCategoryList();
|
||||||
|
deleteDialogVisible.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<el-card class="page-container">
|
||||||
|
<template #header>
|
||||||
|
<div class="header">
|
||||||
|
<span>文章分类</span>
|
||||||
|
<div class="extra">
|
||||||
|
<el-button type="primary" @click="title='添加分类';dialogVisible = true">添加分类</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-table :data="categories" style="width: 100%">
|
||||||
|
<el-table-column label="序号" width="100" type="index"> </el-table-column>
|
||||||
|
<el-table-column label="分类名称" prop="categoryName"></el-table-column>
|
||||||
|
<el-table-column label="分类别名" prop="categoryAlias"></el-table-column>
|
||||||
|
<el-table-column label="操作" width="100">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button :icon="Edit" circle plain type="primary" @click="showEditDialog(row)"></el-button>
|
||||||
|
<el-button :icon="Delete" circle plain type="danger" @click="showDeleteDialog(row)"></el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<template #empty>
|
||||||
|
<el-empty description="没有数据" />
|
||||||
|
</template>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 添加,编辑分类弹窗 -->
|
||||||
|
<el-dialog v-model="dialogVisible" :title="title" width="400">
|
||||||
|
<el-form :model="categoryModel" :rules="rules" label-width="100px" style="padding-right: 30px">
|
||||||
|
<el-form-item label="分类名称" prop="categoryName">
|
||||||
|
<el-input v-model="categoryModel.categoryName" minlength="1" maxlength="10"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="分类别名" prop="categoryAlias">
|
||||||
|
<el-input v-model="categoryModel.categoryAlias" minlength="1" maxlength="15"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="dialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="title==='添加分类' ? addCategory() : updateCategory()"> 确认 </el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<!--删除确认弹窗-->
|
||||||
|
<el-dialog title="提示" v-model="deleteDialogVisible" width="400">
|
||||||
|
<span>确认删除该分类吗?</span>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="deleteDialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="deleteCategory()"> 确认 </el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
</el-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.page-container {
|
||||||
|
min-height: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
294
src/views/article/ArticleManage.vue
Normal file
@ -0,0 +1,294 @@
|
|||||||
|
<script setup>
|
||||||
|
import {
|
||||||
|
Edit,
|
||||||
|
Delete
|
||||||
|
} from '@element-plus/icons-vue'
|
||||||
|
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
//文章分类数据模型
|
||||||
|
const categories = ref([
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"categoryName": "dawdwa",
|
||||||
|
"categoryAlias": "adw",
|
||||||
|
"createTime": "2023-09-02 12:06:59",
|
||||||
|
"updateTime": "2023-09-02 12:06:59"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"categoryName": "美食",
|
||||||
|
"categoryAlias": "my",
|
||||||
|
"createTime": "2023-09-02 12:06:59",
|
||||||
|
"updateTime": "2023-09-02 12:06:59"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"categoryName": "娱乐",
|
||||||
|
"categoryAlias": "yl",
|
||||||
|
"createTime": "2023-09-02 12:08:16",
|
||||||
|
"updateTime": "2023-09-02 12:08:16"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 5,
|
||||||
|
"categoryName": "军事",
|
||||||
|
"categoryAlias": "js",
|
||||||
|
"createTime": "2023-09-02 12:08:33",
|
||||||
|
"updateTime": "2023-09-02 12:08:33"
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
//用户搜索时选中的分类id
|
||||||
|
const categoryId=ref('')
|
||||||
|
|
||||||
|
//用户搜索时选中的发布状态
|
||||||
|
const state=ref('')
|
||||||
|
|
||||||
|
//文章列表数据模型
|
||||||
|
const articles = ref([
|
||||||
|
{
|
||||||
|
"id": 5,
|
||||||
|
"title": "陕西旅游攻略",
|
||||||
|
"content": "兵马俑,华清池,法门寺,华山...爱去哪去哪...",
|
||||||
|
"coverImg": "https://big-event-gwd.oss-cn-beijing.aliyuncs.com/9bf1cf5b-1420-4c1b-91ad-e0f4631cbed4.png",
|
||||||
|
"state": "草稿",
|
||||||
|
"categoryId": 2,
|
||||||
|
"createTime": "2023-09-03 11:55:30",
|
||||||
|
"updateTime": "2023-09-03 11:55:30"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 5,
|
||||||
|
"title": "陕西旅游攻略",
|
||||||
|
"content": "兵马俑,华清池,法门寺,华山...爱去哪去哪...",
|
||||||
|
"coverImg": "https://big-event-gwd.oss-cn-beijing.aliyuncs.com/9bf1cf5b-1420-4c1b-91ad-e0f4631cbed4.png",
|
||||||
|
"state": "草稿",
|
||||||
|
"categoryId": 2,
|
||||||
|
"createTime": "2023-09-03 11:55:30",
|
||||||
|
"updateTime": "2023-09-03 11:55:30"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 5,
|
||||||
|
"title": "陕西旅游攻略",
|
||||||
|
"content": "兵马俑,华清池,法门寺,华山...爱去哪去哪...",
|
||||||
|
"coverImg": "https://big-event-gwd.oss-cn-beijing.aliyuncs.com/9bf1cf5b-1420-4c1b-91ad-e0f4631cbed4.png",
|
||||||
|
"state": "草稿",
|
||||||
|
"categoryId": 2,
|
||||||
|
"createTime": "2023-09-03 11:55:30",
|
||||||
|
"updateTime": "2023-09-03 11:55:30"
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
//分页条数据模型
|
||||||
|
const pageNum = ref(1)//当前页
|
||||||
|
const total = ref(20)//总条数
|
||||||
|
const pageSize = ref(3)//每页条数
|
||||||
|
|
||||||
|
//当每页条数发生了变化,调用此函数
|
||||||
|
const onSizeChange = (size) => {
|
||||||
|
pageSize.value = size
|
||||||
|
getArticleList()
|
||||||
|
}
|
||||||
|
//当前页码发生变化,调用此函数
|
||||||
|
const onCurrentChange = (num) => {
|
||||||
|
pageNum.value = num
|
||||||
|
getArticleList()
|
||||||
|
}
|
||||||
|
|
||||||
|
//按照分类获取文章列表
|
||||||
|
import { articleCategoryListService } from '@/api/article.js'
|
||||||
|
|
||||||
|
const articleCategoryList = async() => {
|
||||||
|
let result = await articleCategoryListService();
|
||||||
|
categories.value = result.data;
|
||||||
|
}
|
||||||
|
articleCategoryList(); //调用函数
|
||||||
|
|
||||||
|
//从后端获取文章信息
|
||||||
|
import { articleListService } from '@/api/article'
|
||||||
|
const getArticleList = async() => {
|
||||||
|
const params = {
|
||||||
|
pageNum: pageNum.value,
|
||||||
|
pageSize: pageSize.value,
|
||||||
|
categoryId: categoryId.value ?categoryId.value :null,
|
||||||
|
state: state.value ? state.value : null
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await articleListService(params)
|
||||||
|
|
||||||
|
total.value = result.data.total
|
||||||
|
articles.value = result.data.items
|
||||||
|
}
|
||||||
|
getArticleList() //打开页面的时候获取文章列表
|
||||||
|
|
||||||
|
import { QuillEditor } from '@vueup/vue-quill'
|
||||||
|
import '@vueup/vue-quill/dist/vue-quill.snow.css'
|
||||||
|
import {Plus} from '@element-plus/icons-vue'
|
||||||
|
//控制抽屉是否显示
|
||||||
|
const visibleDrawer = ref(false)
|
||||||
|
//添加表单数据模型
|
||||||
|
const articleModel = ref({
|
||||||
|
title: '',
|
||||||
|
categoryId: '',
|
||||||
|
coverImg: '',
|
||||||
|
content:'',
|
||||||
|
state:''
|
||||||
|
})
|
||||||
|
|
||||||
|
//添加文章
|
||||||
|
import { articleAddService } from "@/api/article"
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
const addArticle = async(clickState) => {
|
||||||
|
articleModel.value.state = clickState
|
||||||
|
let result = await articleAddService(articleModel.value)
|
||||||
|
|
||||||
|
ElMessage.success(result.message? result.message: '添加成功')
|
||||||
|
visibleDrawer.value = false
|
||||||
|
getArticleList()
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<el-card class="page-container">
|
||||||
|
<template #header>
|
||||||
|
<div class="header">
|
||||||
|
<span>文章管理</span>
|
||||||
|
<div class="extra">
|
||||||
|
<el-button type="primary" @click="visibleDrawer=true">添加文章</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<!-- 搜索表单 -->
|
||||||
|
<el-form inline>
|
||||||
|
<el-form-item label="文章分类:">
|
||||||
|
<el-select placeholder="请选择" v-model="categoryId">
|
||||||
|
<el-option
|
||||||
|
v-for="c in categories"
|
||||||
|
:key="c.id"
|
||||||
|
:label="c.categoryName"
|
||||||
|
:value="c.id">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="发布状态:">
|
||||||
|
<el-select placeholder="请选择" v-model="state">
|
||||||
|
<el-option label="已发布" value="已发布"></el-option>
|
||||||
|
<el-option label="草稿" value="草稿"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="getArticleList">搜索</el-button>
|
||||||
|
<el-button @click="categoryId='';state=''">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<!-- 文章列表 -->
|
||||||
|
<el-table :data="articles" style="width: 100%">
|
||||||
|
<el-table-column label="文章标题" width="400" prop="title"></el-table-column>
|
||||||
|
<el-table-column label="分类" prop="categoryId"></el-table-column>
|
||||||
|
<el-table-column label="发表时间" prop="createTime"> </el-table-column>
|
||||||
|
<el-table-column label="状态" prop="state"></el-table-column>
|
||||||
|
<el-table-column label="操作" width="100">
|
||||||
|
<template #default>
|
||||||
|
<el-button :icon="Edit" circle plain type="primary"></el-button>
|
||||||
|
<el-button :icon="Delete" circle plain type="danger"></el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<template #empty>
|
||||||
|
<el-empty description="没有数据" />
|
||||||
|
</template>
|
||||||
|
</el-table>
|
||||||
|
<!-- 分页条 -->
|
||||||
|
<el-pagination v-model:current-page="pageNum" v-model:page-size="pageSize" :page-sizes="[3, 5 ,10, 15]"
|
||||||
|
layout="jumper, total, sizes, prev, pager, next" background :total="total" @size-change="onSizeChange"
|
||||||
|
@current-change="onCurrentChange" style="margin-top: 20px; justify-content: flex-end" />
|
||||||
|
|
||||||
|
<!-- 抽屉 -->
|
||||||
|
<el-drawer v-model="visibleDrawer" title="添加文章" direction="rtl" size="50%">
|
||||||
|
<!-- 添加文章表单 -->
|
||||||
|
<el-form :model="articleModel" label-width="100px" >
|
||||||
|
<el-form-item label="文章标题" >
|
||||||
|
<el-input v-model="articleModel.title" placeholder="请输入标题"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="文章分类">
|
||||||
|
<el-select placeholder="请选择" v-model="articleModel.categoryId">
|
||||||
|
<el-option v-for="c in categories" :key="c.id" :label="c.categoryName" :value="c.id">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="文章封面">
|
||||||
|
|
||||||
|
<el-upload class="avatar-uploader" :auto-upload="false" :show-file-list="false">
|
||||||
|
<img v-if="articleModel.coverImg" :src="articleModel.coverImg" class="avatar" />
|
||||||
|
<el-icon v-else class="avatar-uploader-icon">
|
||||||
|
<Plus />
|
||||||
|
</el-icon>
|
||||||
|
</el-upload>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="文章内容">
|
||||||
|
<div class="editor">
|
||||||
|
<quill-editor
|
||||||
|
theme="snow"
|
||||||
|
v-model:content="articleModel.content"
|
||||||
|
contentType="html"
|
||||||
|
>
|
||||||
|
</quill-editor>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="addArticle('已发布')">发布</el-button>
|
||||||
|
<el-button type="info" @click="addArticle('草稿')">草稿</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-drawer>
|
||||||
|
</el-card>
|
||||||
|
</template>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.page-container {
|
||||||
|
min-height: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 抽屉样式 */
|
||||||
|
.avatar-uploader {
|
||||||
|
:deep() {
|
||||||
|
.avatar {
|
||||||
|
width: 178px;
|
||||||
|
height: 178px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-upload {
|
||||||
|
border: 1px dashed var(--el-border-color);
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: var(--el-transition-duration-fast);
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-upload:hover {
|
||||||
|
border-color: var(--el-color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-icon.avatar-uploader-icon {
|
||||||
|
font-size: 28px;
|
||||||
|
color: #8c939d;
|
||||||
|
width: 178px;
|
||||||
|
height: 178px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.editor {
|
||||||
|
width: 100%;
|
||||||
|
:deep(.ql-editor) {
|
||||||
|
min-height: 200px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
3
src/views/user/UserAvatar.vue
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<template>
|
||||||
|
更换头像
|
||||||
|
</template>
|
64
src/views/user/UserInfo.vue
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import useUserInfoStore from '@/stores/userInfo.js';
|
||||||
|
|
||||||
|
const userInfoStore = useUserInfoStore()
|
||||||
|
const userInfo = ref({...userInfoStore.info}) //解构info数组
|
||||||
|
|
||||||
|
//更新个人信息
|
||||||
|
import { userInfoUpdateService } from '@/api/user'
|
||||||
|
import { ElMessage } from 'element-plus';
|
||||||
|
const userInfoUpdate = async() =>{
|
||||||
|
let result = await userInfoUpdateService(userInfo.value)
|
||||||
|
if (result.code === 0){
|
||||||
|
//注册成功
|
||||||
|
ElMessage.success(result.message? result.message: '修改成功')
|
||||||
|
userInfoStore.setInfo(userInfo.value)
|
||||||
|
}else{
|
||||||
|
//注册失败
|
||||||
|
ElMessage.error('修改失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const rules = {
|
||||||
|
nickname: [
|
||||||
|
{ required: true, message: '请输入用户昵称', trigger: 'blur' },
|
||||||
|
{
|
||||||
|
pattern: /^\S{2,10}$/,
|
||||||
|
message: '昵称必须是2-10位的非空字符串',
|
||||||
|
trigger: 'blur'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
email: [
|
||||||
|
{ required: true, message: '请输入用户邮箱', trigger: 'blur' },
|
||||||
|
{ type: 'email', message: '邮箱格式不正确', trigger: 'blur' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<el-card class="page-container">
|
||||||
|
<template #header>
|
||||||
|
<div class="header">
|
||||||
|
<span>基本资料</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form :model="userInfo" :rules="rules" label-width="100px" size="large">
|
||||||
|
<el-form-item label="登录名称">
|
||||||
|
<el-input v-model="userInfo.username" disabled></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="用户昵称" prop="nickname">
|
||||||
|
<el-input v-model="userInfo.nickname"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="用户邮箱" prop="email">
|
||||||
|
<el-input v-model="userInfo.email"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="userInfoUpdate">提交修改</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-card>
|
||||||
|
</template>
|
82
src/views/user/UserResetPassword.vue
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
const resetPwdData = ref({
|
||||||
|
old_pwd: '',
|
||||||
|
new_pwd: '',
|
||||||
|
re_pwd: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
//修改完重新登录
|
||||||
|
import useUserInfoStore from '@/stores/userInfo.js';
|
||||||
|
import { useTokenStore } from '@/stores/token.js';
|
||||||
|
import router from '@/router';
|
||||||
|
|
||||||
|
const tokenStore = useTokenStore();
|
||||||
|
const userInfoStore = useUserInfoStore();
|
||||||
|
const reLogin = () => {
|
||||||
|
tokenStore.removeToken();
|
||||||
|
userInfoStore.removeInfo();
|
||||||
|
router.push('/login');
|
||||||
|
}
|
||||||
|
|
||||||
|
//更新个人密码
|
||||||
|
import { userPasswordUpdateService } from '@/api/user'
|
||||||
|
import { ElMessage } from 'element-plus';
|
||||||
|
const userPasswordUpdate = async() =>{
|
||||||
|
console.log(resetPwdData.value)
|
||||||
|
let result = await userPasswordUpdateService(resetPwdData.value)
|
||||||
|
if (result.code === 0){
|
||||||
|
//修改成功
|
||||||
|
ElMessage.success(result.message? result.message:'修改成功')
|
||||||
|
userInfoStore.setInfo(userInfo.value)
|
||||||
|
reLogin()
|
||||||
|
ElMessage({type: 'success',message: '密码已修改,请用新密码重新登陆',});
|
||||||
|
}else{
|
||||||
|
//修改失败
|
||||||
|
ElMessage.error('修改失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const rules = {
|
||||||
|
old_pwd: [
|
||||||
|
{ required: true, message: '请输入当前密码', trigger: 'blur' },
|
||||||
|
{ min: 5, max: 16, message: '密码长度在 5 到 16 个字符', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
new_pwd: [
|
||||||
|
{ required: true, message: '请输入新密码', trigger: 'blur' },
|
||||||
|
{ min: 5, max: 16, message: '密码长度在 5 到 16 个字符', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
re_pwd: [
|
||||||
|
{ required: true, message: '请确认新密码', trigger: 'blur' },
|
||||||
|
{ min: 5, max: 16, message: '密码长度在 5 到 16 个字符', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<el-card class="page-container">
|
||||||
|
<template #header>
|
||||||
|
<div class="header">
|
||||||
|
<span>修改密码</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form :model="resetPwdData" :rules="rules" label-width="100px" size="large">
|
||||||
|
<el-form-item label="原密码" prop="old_pwd">
|
||||||
|
<el-input v-model="resetPwdData.old_pwd" show-password="false"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="新密码" prop="new_pwd">
|
||||||
|
<el-input v-model="resetPwdData.new_pwd" show-password="false"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="重复新密码" prop="re_pwd">
|
||||||
|
<el-input v-model="resetPwdData.re_pwd" show-password="false"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="userPasswordUpdate">提交修改</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-card>
|
||||||
|
</template>
|
27
vite.config.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { fileURLToPath, URL } from 'node:url'
|
||||||
|
|
||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
import vueDevTools from 'vite-plugin-vue-devtools'
|
||||||
|
|
||||||
|
// https://vite.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [
|
||||||
|
vue(),
|
||||||
|
vueDevTools(),
|
||||||
|
],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@': fileURLToPath(new URL('./src', import.meta.url))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
proxy: {
|
||||||
|
'/api': {
|
||||||
|
target: 'http://localhost:8080', //后台服务所在的源
|
||||||
|
changeOrigin: true, //更改源
|
||||||
|
rewrite: (path) => path.replace(/^\/api/, '') ///api换成空
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|