优化请求体

This commit is contained in:
闵宪瑞 2025-01-09 23:09:36 +08:00
parent 5765208844
commit cae25ad4ce
14 changed files with 279 additions and 204 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

View File

@ -1,7 +1,7 @@
import { adminRequest } from '~/composables/adminRequest'
/**
*
*
* @param data
*/
export function captchaAdmin(uid: Number) {
@ -38,8 +38,6 @@ export function logoutAdmin() {
*
* @param userId
*/
export function userInfoAdmin(userId: any) {
return adminRequest.get("/userInfo", {
params: { userId: userId }
})
export function userInfoAdmin() {
return adminRequest.get("/sys/user/info")
}

View File

@ -6,6 +6,9 @@ export function loginFront(data:any) {
frontRequest.post("/api/user/login", data).then(response =>{
const user = userStore()
user.frontToken = response.data.token
frontRequest.get("/api/user/userInfo").then(response =>{
user.frontUserInfo = response.data
})
})
}
/**

View File

@ -2,11 +2,11 @@
<div class="head">
<div class="head_l">
<!-- <img src="/icoimg.png" alt="收缩" />-->
</div>
<el-dropdown>
<div class="head_r">
<img :src="userStore().adminUserInfo.avatar" alt="头像" class="profile" />
<!-- <img :src="userStore().adminUserInfo.avatar" alt="头像" class="profile" />-->
<div class="head_user">
<div class="head_user_name">{{ userStore().adminUserInfo.username }}</div>
<div class="head_user_desc">管理员</div>

View File

@ -1,7 +1,9 @@
<!--前端样式1-->
<template>
<el-row justify="space-between">
<el-col :span="6"><div class="grid-content ep-bg-purple" /></el-col>
<el-row justify="space-between">
<el-col :span="6">
<div class="grid-content ep-bg-purple" />
</el-col>
<el-col :span="12">
<el-menu
:default-active="activeIndex"
@ -11,36 +13,37 @@
>
<el-menu-item
v-for="r of getFrontList()" :key="r.path"
:index="r.path">{{ r.name }}</el-menu-item>
:index="r.path">{{ r.name }}
</el-menu-item>
</el-menu>
</el-col>
<el-col :span="3">
<el-dropdown>
<el-row :gutter="20">
<el-col :span="8">
<el-avatar
:src="userStore().adminUserInfo.avatar"
/>
</el-col>
<el-col :span="16">
<h6>{{ userStore().adminUserInfo.username }}</h6>
</el-col>
</el-row>
<template #dropdown>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item >个人中心</el-dropdown-item>
<el-dropdown-item @click="logout">退出登录</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-dropdown>
<el-row :gutter="20">
<el-col :span="8">
<!-- <el-avatar :src="userStore().frontUserInfo.avatar" />-->
</el-col>
<el-col :span="16">
<h6>{{ userStore().frontUserInfo.username }}</h6>
</el-col>
</el-row>
<template #dropdown>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>个人中心</el-dropdown-item>
<el-dropdown-item @click="logout">退出登录</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</el-col>
</el-row>
</template>
<script setup lang="ts">
import { getFrontList } from '~/utils/utils'
import { useRouter } from "vue-router";
const router = useRouter();
import { useRouter } from 'vue-router'
import { logoutFront } from '~/api/user/frontUserApi'
const router = useRouter()
const activeIndex = ref('1')
const handleSelect = (key: string, keyPath: string[]) => {
console.log(key, keyPath)
@ -49,9 +52,9 @@ const handleSelect = (key: string, keyPath: string[]) => {
* 退出登录
*/
const logout = () => {
logoutAdmin().then(()=>{
toast.success("退出成功~")
router.push('/login');
logoutFront().then(() => {
toast.success('退出成功~')
router.push('/login')
})
}
</script>
@ -60,7 +63,8 @@ const logout = () => {
:deep(.el-menu--horizontal) {
border-bottom: none;
}
h6{
h6 {
font-weight: 700;
font-size: 16px;
padding-top: 15px;

View File

@ -32,12 +32,13 @@ adminRequest.interceptors.response.use(
return Promise.reject(response.data.msg)
case 401:
toast.error(response.data.msg)
window.open(`/login`, '_self')
return Promise.reject(response.data.msg)
default:
return response
return response.data
}
}
return response
return response.data
},
function (error) {
let { msg, message } = error.response?.data ?? {}

View File

@ -28,14 +28,13 @@ frontRequest.interceptors.response.use(
switch (code) {
case 500:
toast.error(response.data.msg)
break
return Promise.reject(response.data.msg)
case 401:
window.open(`/login`, '_self')
toast.error("请重新登录~")
break
default:
toast.error(response.data.msg)
break
return response
}
}
if (response.data) {

View File

@ -1,10 +1,100 @@
<template>
dsdsd
</template>
<script setup lang="ts">
<!-- 客户类型销售情况饼图 -->
<el-row>
<el-col :span="24">
<v-chart class="chart" :option="customerTypeSalesOption" autoresize />
</el-col>
</el-row>
<!-- 客户购买频次周期金额漏斗图 -->
<el-row>
<el-col :span="24">
<v-chart class="chart" :option="customerPurchaseFunnelOption" autoresize />
</el-col>
</el-row>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import VChart from 'vue-echarts'
import { PieChart, FunnelChart } from 'echarts/charts'
import { TitleComponent, TooltipComponent, LegendComponent, GridComponent } from 'echarts/components'
import { CanvasRenderer } from 'echarts/renderers'
import { use } from 'echarts/core'
// 使
use([CanvasRenderer, PieChart, FunnelChart, TitleComponent, TooltipComponent, LegendComponent, GridComponent])
//
const customerData = ref([
{ customerType: '家庭用户', salesAmount: 5000 },
{ customerType: '商用用户', salesAmount: 8000 },
{ customerType: '工业用户', salesAmount: 12000 },
// ...
])
const purchaseData = ref([
{ stage: '潜在客户', value: 300 },
{ stage: '关注产品', value: 200 },
{ stage: '询价客户', value: 150 },
{ stage: '下单客户', value: 100 },
{ stage: '支付客户', value: 70 },
// ...
])
//
const customerTypeSalesOption = ref({
title: { text: '客户类型销售情况', left: 'center' },
tooltip: { trigger: 'item' },
legend: {
orient: 'vertical',
left: 'left',
data: customerData.value.map(item => item.customerType)
},
series: [
{
name: '销售额',
type: 'pie',
radius: '55%',
data: customerData.value.map(item => ({
value: item.salesAmount,
name: item.customerType
})),
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
})
//
const customerPurchaseFunnelOption = ref({
title: { text: '客户购买漏斗分析', left: 'center' },
tooltip: { trigger: 'item' },
legend: {
data: ['客户转化情况']
},
series: [
{
name: '客户转化情况',
type: 'funnel',
left: '10%',
width: '80%',
data: purchaseData.value.map(item => ({
value: item.value,
name: item.stage
}))
}
]
})
</script>
<style scoped>
.chart {
height: 400px;
}
</style>

17
src/pages/front/index.vue Normal file
View File

@ -0,0 +1,17 @@
<template>
我是前台
</template>
<script setup lang="ts">
</script>
<style scoped>
</style>
<route lang="json">
{
"meta": {
"layout": "front"
}
}
</route>

View File

@ -1,100 +0,0 @@
<template>
<!-- 客户类型销售情况饼图 -->
<el-row>
<el-col :span="24">
<v-chart class="chart" :option="customerTypeSalesOption" autoresize />
</el-col>
</el-row>
<!-- 客户购买频次周期金额漏斗图 -->
<el-row>
<el-col :span="24">
<v-chart class="chart" :option="customerPurchaseFunnelOption" autoresize />
</el-col>
</el-row>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import VChart from 'vue-echarts'
import { PieChart, FunnelChart } from 'echarts/charts'
import { TitleComponent, TooltipComponent, LegendComponent, GridComponent } from 'echarts/components'
import { CanvasRenderer } from 'echarts/renderers'
import { use } from 'echarts/core'
// 使
use([CanvasRenderer, PieChart, FunnelChart, TitleComponent, TooltipComponent, LegendComponent, GridComponent])
//
const customerData = ref([
{ customerType: '家庭用户', salesAmount: 5000 },
{ customerType: '商用用户', salesAmount: 8000 },
{ customerType: '工业用户', salesAmount: 12000 },
// ...
])
const purchaseData = ref([
{ stage: '潜在客户', value: 300 },
{ stage: '关注产品', value: 200 },
{ stage: '询价客户', value: 150 },
{ stage: '下单客户', value: 100 },
{ stage: '支付客户', value: 70 },
// ...
])
//
const customerTypeSalesOption = ref({
title: { text: '客户类型销售情况', left: 'center' },
tooltip: { trigger: 'item' },
legend: {
orient: 'vertical',
left: 'left',
data: customerData.value.map(item => item.customerType)
},
series: [
{
name: '销售额',
type: 'pie',
radius: '55%',
data: customerData.value.map(item => ({
value: item.salesAmount,
name: item.customerType
})),
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
})
//
const customerPurchaseFunnelOption = ref({
title: { text: '客户购买漏斗分析', left: 'center' },
tooltip: { trigger: 'item' },
legend: {
data: ['客户转化情况']
},
series: [
{
name: '客户转化情况',
type: 'funnel',
left: '10%',
width: '80%',
data: purchaseData.value.map(item => ({
value: item.value,
name: item.stage
}))
}
]
})
</script>
<style scoped>
.chart {
height: 400px;
}
</style>

View File

@ -12,17 +12,26 @@
</div>
<div class="module_m">
<div class="module_text">密码</div>
<input class="module_input" type="password" placeholder="输入密码" v-model="login.password" />
<input class="module_input" type="password" placeholder="输入密码" v-model="login.password" />
</div>
<!-- <div class="module_m">-->
<!-- <div class="module_text">验证码</div>-->
<!-- <div class="module_code">-->
<!-- <input class="module_code_input" type="text" placeholder="输入验证码" v-model="login.captcha" />-->
<!-- <img class="module_code_img" :src="state.captchaUrl" @click="getCaptcha">-->
<!-- </div>-->
<!-- </div>-->
<div class="module_m">
<div class="module_text">验证码</div>
<div class="module_code">
<input class="module_code_input" type="text" placeholder="输入验证码" v-model="login.captcha" />
<img class="module_code_img" :src="state.captchaUrl" @click="getCaptcha">
<el-radio-group v-model="state.role" class="ml-4">
<el-radio :label=false size="large">普通用户</el-radio>
<el-radio :label=true size="large">管理员</el-radio>
</el-radio-group>
</div>
</div>
<!-- <div class="module_radio"><input type="radio"/>记住密码 </div>-->
<!-- <div class="forgetpwd" @click="router.push('/register')">没有密码吗</div>-->
<div class="forgetpwd" @click="router.push('/register')">没有密码吗</div>
<button class="module_button" :disabled="state.loading" @click="onLogin">登录</button>
</div>
</div>
@ -30,63 +39,76 @@
</div>
</template>
<script setup lang="ts">
import { useRouter } from "vue-router";
import { useRouter } from 'vue-router'
import { loginAdmin } from '~/api/user/adminUserApi'
import { getUuid } from '~/utils/utils'
const router = useRouter();
import { loginFront } from '~/api/user/frontUserApi'
const router = useRouter()
const state = reactive({
role: false,
captchaUrl: '',
loginFrom: {},
loading: false,
loading: false
})
const login = reactive({ username: 'admin', password: 'admin', captcha: '', uuid: '' })
const onLogin = () => {
state.loading = true;
loginAdmin(login).then(response =>{
state.loading = false;
ElMessage.success("登录成功");
userStore().isLogin = true
userStore().adminToken = response.data.token
router.push("/")
}).catch(()=>{
state.loading = false;
onRefreshCode()
})
state.loading = true
if (state.role) {
console.log("管理员")
loginAdmin(login).then(response => {
state.loading = false
ElMessage.success('登录成功')
userStore().isLogin = true
userStore().adminToken = response.data.token
router.push('/admin')
}).catch(() => {
state.loading = false
onRefreshCode()
})
} else {
loginFront(login)
toast.success("登录成功~")
router.push('/front')
}
}
/**
* 获取验证码
*/
const getCaptchaUrl = () => {
login.uuid = getUuid()
login.captcha = ""
login.captcha = ''
state.captchaUrl = import.meta.env.VITE_ADMIN_API_BASE_URL + `/captcha?uuid=${login.uuid}`
};
}
const onRefreshCode = () => {
getCaptchaUrl();
};
onMounted(()=>{
getCaptchaUrl();
getCaptchaUrl()
}
onMounted(() => {
getCaptchaUrl()
})
</script>
<style scoped>
.login-container{
.login-container {
width: 100%;
height: 100vh;
background: #FFFFFF;
}
.module{
.module {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
height: 100vh;
}
.module_img{
.module_img {
width: 60%;
height: auto;
}
.module_r{
.module_r {
width: 40%;
background: #e5efee;
height: 100vh;
@ -94,7 +116,7 @@ onMounted(()=>{
align-items: center;
justify-content: center;
.module_mian{
.module_mian {
width: 65%;
background: #FFFFFF;
height: auto;
@ -102,51 +124,59 @@ onMounted(()=>{
overflow: hidden;
padding-top: 40px;
padding-bottom: 40px;
.module_title{
.module_title {
font-size: 18px;
font-weight: 500;
text-align: center;
color:#333333;
color: #333333;
}
.module_desc{
.module_desc {
font-size: 12px;
text-align: center;
color:#a7a7a7;
color: #a7a7a7;
margin-bottom: 20px;
}
.module_m{
.module_m {
margin: 0 auto;
width: 80%;
height: auto;
margin-top: 10px;
.module_text{
.module_text {
font-size: 14px;
color: #333333;
margin-bottom: 5px;
}
.module_input{
.module_input {
width: 96%;
height: 40px;
padding-left: 2%;
padding-right: 2%;
border:1px solid #eee;
border: 1px solid #eee;
border-radius: 5px;
font-size: 12px;
}
.module_code{
.module_code {
width: 96%;
display: flex;
align-items: center;
.module_code_input{
.module_code_input {
width: 60%;
height: 40px;
border-radius: 5px;
border:1px solid #eee;
border: 1px solid #eee;
font-size: 12px;
padding-left: 2%;
padding-right: 2%;
}
.module_code_img{
.module_code_img {
width: 130px;
height: 40px;
border-radius: 5px;
@ -156,10 +186,11 @@ onMounted(()=>{
}
}
.module_radio input{
.module_radio input {
margin-right: 5px;
}
.forgetpwd{
.forgetpwd {
margin: 0 auto;
width: 80%;
font-size: 14px;
@ -167,18 +198,21 @@ onMounted(()=>{
margin-top: 10px;
cursor: pointer;
}
.module_button{
.module_button {
margin: 0 auto;
display: block;
width: 80%;
background:#328d86;
background: #328d86;
color: #FFFFFF;
height: 40px;
margin-top: 20px;
border-radius: 5px;
font-weight: 500;
cursor: pointer;
}
.module_button:active{
.module_button:active {
opacity: 0.4;
}
}

View File

@ -5,16 +5,16 @@
<div class="module_desc">输入用户名 & 密码</div>
<div class="module_m">
<div class="module_text">用户名</div>
<input class="module_input" type="text" placeholder="输入用户名" v-model="Email" />
<input class="module_input" type="text" placeholder="输入用户名" v-model="register.username" />
</div>
<div class="module_m">
<div class="module_text">密码</div>
<input class="module_input" type="password" placeholder="输入密码" v-model="Password" />
<input class="module_input" type="password" placeholder="输入密码" v-model="register.password" />
</div>
<div class="module_m">
<div class="module_text">确认密码</div>
<div class="module_code">
<input class="module_input" type="password" placeholder="再次输入密码" v-model="Password" />
<input class="module_input" type="password" placeholder="再次输入密码" v-model="register.confirmPassword" />
</div>
</div>
<!-- <div class="module_m">-->
@ -26,15 +26,31 @@
<!-- </div>-->
<!-- <div class="module_radio"><input type="radio"/>记住密码 </div>-->
<div class="forgetpwd" @click="router.push('/login')">有账号</div>
<button class="module_button">注册</button>
<button class="module_button" @click="onRegister">注册</button>
</div>
</div>
</template>
<script setup lang="ts">
import { useRouter } from "vue-router";
import { registerFront } from '~/api/user/frontUserApi'
const router = useRouter();
const register = reactive({
username: 'admin',
password: 'admin',
confirmPassword: 'admin',
captcha: '',
uuid: ''
})
/**
* 注册成功
*/
const onRegister = () => {
registerFront(register).then(() =>{
toast.success("注册成功~")
router.push("/login")
})
}
</script>
<style scoped>
.module{

View File

@ -1,6 +1,7 @@
import { setupLayouts } from 'virtual:meta-layouts'
import { createRouter, createWebHistory } from 'vue-router'
import { routes as fileRoutes } from 'vue-router/auto-routes'
import { userInfoAdmin } from '~/api/user/adminUserApi'
declare module 'vue-router' {}
// 重定向 BASE_URL
@ -13,6 +14,25 @@ export const router = createRouter({
})
// 路由拦截
router.beforeEach((to, from, next) => {
if (to.fullPath.includes("/front")){
console.log(to.fullPath)
console.log(to)
}
// 管理员全部限制
else if (to.fullPath.includes("/admin")){
console.log("管理员认证~")
// 判断有没有登录
if (userStore().adminToken === null || userStore().adminToken === undefined ||userStore().adminToken == ""){
next('/login');
}else {
// 获取用户信息
userInfoAdmin().then(response => {
userStore().adminUserInfo = response.data
})
}
}
// 在这里编写拦截逻辑
// 例如,检查用户是否认证
const isLogin = userStore().isLogin

View File

@ -6,15 +6,8 @@ export default defineStore('userStore', {
isLogin: true,
adminToken: "",
frontToken: "",
adminUserInfo:{
id: 0,
username: "18796357645",
email: "",
avatar: "https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png",
role: "",
createTime: 0,
updateTime: 0,
}
adminUserInfo:{},
frontUserInfo:{}
}
},
actions: {