内容更改

This commit is contained in:
dzl
2025-09-26 17:09:26 +08:00
parent 662832364b
commit 207438f2f8
9 changed files with 777 additions and 176 deletions

View File

@@ -4,7 +4,7 @@
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>后台管理系统</title>
<title>供应商后台管理系统</title>
<style>
* {
margin: 0;

View File

@@ -4,7 +4,7 @@
<el-aside :width="isCollapse ? '64px' : '200px'" class="sidebar">
<div class="logo">
<img v-if="!isCollapse" src="/logo.svg" alt="Logo" class="logo-img" />
<span v-if="!isCollapse" class="logo-text">后台管理</span>
<span v-if="!isCollapse" class="logo-text">供应商后台管理</span>
<el-icon v-else class="logo-icon"><Setting /></el-icon>
</div>
@@ -29,6 +29,10 @@
<el-icon><List /></el-icon>
<template #title>订单管理</template>
</el-menu-item>
<el-menu-item v-if="userStore.isAdmin" index="/income">
<el-icon><Money /></el-icon>
<template #title>提现管理</template>
</el-menu-item>
<el-menu-item index="/profile">
<el-icon><UserFilled /></el-icon>
<template #title>个人资料</template>
@@ -252,7 +256,7 @@ const breadcrumbs = computed(() => {
matched.forEach(item => {
if (item.path !== '/') {
breadcrumbList.push({
title: item.meta.title.replace(' - 后台管理系统', ''),
title: item.meta.title.replace(' - 供应商后台管理系统', ''),
path: item.path
})
}

View File

@@ -82,7 +82,16 @@ const routes = [
requiresAdmin: true
}
},
{
path: 'income',
name: 'Income',
component: () => import('@/views/Income.vue'),
meta: {
title: '提现管理',
icon: 'Money',
requiresAdmin: true
}
},
{
path: 'profile',
name: 'Profile',

View File

@@ -1,4 +1,166 @@
import {apiRequest, midRequest} from './request'
// 工厂函数(复用拦截器逻辑)
import axios from 'axios'
import { ElMessage, ElLoading } from 'element-plus'
import NProgress from 'nprogress'
let loadingInstance = null
let requestCount = 0
let isLoggingOut = false // 防止重复登出
export const createRequest = (baseURL) => {
const request = axios.create({
baseURL,
timeout: 10000,
headers: {
'Content-Type': 'application/json'
}
})
const showLoading = () => {
if (requestCount === 0) {
loadingInstance = ElLoading.service({
text: '加载中...',
background: 'rgba(0, 0, 0, 0.7)'
})
}
requestCount++
}
const hideLoading = () => {
requestCount--
if (requestCount <= 0) {
requestCount = 0
if (loadingInstance) {
loadingInstance.close()
loadingInstance = null
}
}
}
// 请求拦截器
request.interceptors.request.use(
(config) => {
// 开始进度条
NProgress.start()
// 显示加载动画(除了某些不需要的请求)
if (!config.hideLoading) {
showLoading()
}
// 添加认证token
const token = localStorage.getItem('admin_token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
},
(error) => {
hideLoading()
NProgress.done()
return Promise.reject(error)
}
)
// 响应拦截器
request.interceptors.response.use(
(response) => {
hideLoading()
NProgress.done()
return response
},
(error) => {
hideLoading()
NProgress.done()
const { response } = error
if (response) {
switch (response.status) {
case 401:
// 防止重复处理401错误
if (!isLoggingOut) {
isLoggingOut = true
// 只在非登录页面显示错误消息
if (window.location.pathname !== '/admin/login') {
ElMessage.error('登录已过期,请重新登录')
}
// 清除本地存储
localStorage.removeItem('admin_token')
localStorage.removeItem('admin_user')
// 立即跳转到登录页,减少延迟
setTimeout(() => {
if (typeof window !== 'undefined' && window.location.pathname !== '/admin/login') {
window.location.href = '/admin/login'
}
// 重置标志
setTimeout(() => {
isLoggingOut = false
}, 1000)
}, 500)
}
break
case 403:
// 检查是否是用户被拉黑
if (response.data.code === 'USER_BLACKLISTED') {
// 防止重复处理拉黑错误
if (!isLoggingOut) {
isLoggingOut = true
ElMessage.error(response.data.message || '账户已被拉黑,请联系管理员')
// 清除本地存储
localStorage.removeItem('admin_token')
localStorage.removeItem('admin_user')
// 跳转到登录页
setTimeout(() => {
if (typeof window !== 'undefined' && window.location.pathname !== '/admin/login') {
window.location.href = '/admin/login'
}
// 重置标志
setTimeout(() => {
isLoggingOut = false
}, 1000)
}, 500)
}
} else {
ElMessage.error('没有权限访问此资源')
}
break
case 404:
ElMessage.error('请求的资源不存在')
break
case 422:
ElMessage.error(response.data.message || '请求参数错误')
break
case 429:
ElMessage.error('请求过于频繁,请稍后再试')
break
case 500:
ElMessage.error('服务器内部错误')
break
case 400:
ElMessage.error(response.data.message || '请求参数错误')
break
default:
ElMessage.error(response.data.error.message || '请求失败')
}
} else {
ElMessage.error('网络错误,请检查网络连接')
}
return Promise.reject(error)
}
)
return request
}
// 生成不同的实例
export const apiRequest = createRequest(import.meta.env.VITE_API_BASE_URL || '/api')
export const midRequest = createRequest(import.meta.env.VITE_UPLOAD_BASE_URL || '/mid')
// API接口定义
@@ -60,7 +222,10 @@ const api = {
getAttributes: (productId) => apiRequest.get(`/products/${productId}/attributes`),
createAttribute: (productId, data) => apiRequest.post(`/products/${productId}/attributes`, data),
updateAttribute: (productId, attrId, data) => apiRequest.put(`/products/${productId}/attributes/${attrId}`, data),
deleteAttribute: (productId, attrId) => apiRequest.delete(`/products/${productId}/attributes/${attrId}`)
deleteAttribute: (productId, attrId) => apiRequest.delete(`/products/${productId}/attributes/${attrId}`),
// 订单相关
delivery: (data) => apiRequest.post('/orders/delivery', data),
},
// 新的规格管理系统(笛卡尔积)
@@ -80,6 +245,20 @@ const api = {
updateCombination: (id, data) => apiRequest.put(`/specifications/combinations/${id}`, data),
deleteCombination: (id) => apiRequest.delete(`/specifications/combinations/${id}`)
},
// 提现管理
income: {
getIncomeList: (params) => apiRequest.get('/income', {params}),
createIncome: (data) => apiRequest.post('/income', data),
},
// 为了向后兼容,添加直接的 get、post 等方法
get: (url, config) => apiRequest.get(url, config),
post: (url, data, config) => apiRequest.post(url, data, config),
put: (url, data, config) => apiRequest.put(url, data, config),
delete: (url, config) => apiRequest.delete(url, config)
}
export default api

View File

@@ -1,164 +0,0 @@
// 工厂函数(复用拦截器逻辑)
import axios from 'axios'
import { ElMessage, ElLoading } from 'element-plus'
import NProgress from 'nprogress'
let loadingInstance = null
let requestCount = 0
let isLoggingOut = false // 防止重复登出
export const createRequest = (baseURL) => {
const request = axios.create({
baseURL,
timeout: 10000,
headers: {
'Content-Type': 'application/json'
}
})
const showLoading = () => {
if (requestCount === 0) {
loadingInstance = ElLoading.service({
text: '加载中...',
background: 'rgba(0, 0, 0, 0.7)'
})
}
requestCount++
}
const hideLoading = () => {
requestCount--
if (requestCount <= 0) {
requestCount = 0
if (loadingInstance) {
loadingInstance.close()
loadingInstance = null
}
}
}
// 请求拦截器
request.interceptors.request.use(
(config) => {
// 开始进度条
NProgress.start()
// 显示加载动画(除了某些不需要的请求)
if (!config.hideLoading) {
showLoading()
}
// 添加认证token
const token = localStorage.getItem('admin_token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
},
(error) => {
hideLoading()
NProgress.done()
return Promise.reject(error)
}
)
// 响应拦截器
request.interceptors.response.use(
(response) => {
hideLoading()
NProgress.done()
return response
},
(error) => {
hideLoading()
NProgress.done()
const { response } = error
if (response) {
switch (response.status) {
case 401:
// 防止重复处理401错误
if (!isLoggingOut) {
isLoggingOut = true
// 只在非登录页面显示错误消息
if (window.location.pathname !== '/admin/login') {
ElMessage.error('登录已过期,请重新登录')
}
// 清除本地存储
localStorage.removeItem('admin_token')
localStorage.removeItem('admin_user')
// 立即跳转到登录页,减少延迟
setTimeout(() => {
if (typeof window !== 'undefined' && window.location.pathname !== '/admin/login') {
window.location.href = '/admin/login'
}
// 重置标志
setTimeout(() => {
isLoggingOut = false
}, 1000)
}, 500)
}
break
case 403:
// 检查是否是用户被拉黑
if (response.data.code === 'USER_BLACKLISTED') {
// 防止重复处理拉黑错误
if (!isLoggingOut) {
isLoggingOut = true
ElMessage.error(response.data.message || '账户已被拉黑,请联系管理员')
// 清除本地存储
localStorage.removeItem('admin_token')
localStorage.removeItem('admin_user')
// 跳转到登录页
setTimeout(() => {
if (typeof window !== 'undefined' && window.location.pathname !== '/admin/login') {
window.location.href = '/admin/login'
}
// 重置标志
setTimeout(() => {
isLoggingOut = false
}, 1000)
}, 500)
}
} else {
ElMessage.error('没有权限访问此资源')
}
break
case 404:
ElMessage.error('请求的资源不存在')
break
case 422:
ElMessage.error(response.data.message || '请求参数错误')
break
case 429:
ElMessage.error('请求过于频繁,请稍后再试')
break
case 500:
ElMessage.error('服务器内部错误')
break
case 400:
ElMessage.error(response.data.message || '请求参数错误')
break
default:
ElMessage.error(response.data.error.message || '请求失败')
}
} else {
ElMessage.error('网络错误,请检查网络连接')
}
return Promise.reject(error)
}
)
return request
}
// 生成不同的实例
export const apiRequest = createRequest(import.meta.env.VITE_API_BASE_URL || '/api')
export const midRequest = createRequest(import.meta.env.VITE_UPLOAD_BASE_URL || '/mid')
export const statsRequest = createRequest(import.meta.env.VITE_STATS_BASE_URL || '/stats')

494
src/views/Income.vue Normal file
View File

@@ -0,0 +1,494 @@
<template>
<div class="income-container">
<!-- 页面头部 -->
<div class="page-header">
<div class="header-content">
<h1 class="page-title">
提现管理
</h1>
<el-button
type="primary"
size="large"
class="withdraw-btn"
@click="getIncome"
>
<el-icon><Plus /></el-icon>
申请提现
</el-button>
</div>
</div>
<!-- 数据表格 -->
<div class="table-container">
<el-table
:data="incomeList"
v-loading="loading"
stripe
class="income-table"
header-row-class-name="table-header"
>
<el-table-column prop="id" label="ID" width="80" align="center" />
<el-table-column prop="user.name" label="用户" width="120" />
<el-table-column prop="amount" label="金额" width="120" align="right">
<template #default="{ row }">
<span class="amount-text">¥{{ row.amount }}</span>
</template>
</el-table-column>
<el-table-column prop="status" label="状态" width="100" align="center">
<template #default="{ row }">
<el-tag
:type="getStatusType(row.status)"
size="small"
class="status-tag"
>
{{ getStatusText(row.status) }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="createdAt" label="创建时间" min-width="160">
<template #default="{ row }">
<span class="time-text">{{ formatTime(row.createdAt) }}</span>
</template>
</el-table-column>
</el-table>
</div>
<!-- 分页器 -->
<div class="pagination-container">
<el-pagination
v-model:current-page="pagination.page"
v-model:page-size="pagination.limit"
:page-sizes="[10, 20, 50, 100]"
:total="pagination.total"
layout="total, sizes, prev, pager, next, jumper"
@size-change="loadIncomeList"
@current-change="loadIncomeList"
class="custom-pagination"
/>
</div>
<!-- 提现对话框 -->
<el-dialog
title="申请提现"
v-model="drawerVisible"
width="500px"
:close-on-click-modal="false"
>
<div class="dialog-content">
<el-form
:model="form"
:rules="rules"
ref="formRef"
label-width="80px"
>
<el-form-item label="提现金额" prop="amount">
<el-input
v-model="form.amount"
type="number"
placeholder="请输入提现金额"
>
<template #prefix>
<span>¥</span>
</template>
</el-input>
</el-form-item>
</el-form>
<div class="dialog-actions">
<el-button @click="drawerVisible = false" size="large">
取消
</el-button>
<el-button
type="primary"
@click="submitForm"
size="large"
>
<el-icon><Check /></el-icon>
确认提现
</el-button>
</div>
</div>
</el-dialog>
</div>
</template>
<script setup>
import { ref, onMounted, reactive } from 'vue'
import { useUserStore } from '@/stores/user'
import api from '@/utils/api'
import { ElForm, ElFormItem, ElInput, ElMessage } from 'element-plus'
import { Money, Plus, Check } from '@element-plus/icons-vue'
const userStore = useUserStore()
const incomeList = ref([])
const loading = ref(false)
const drawerVisible = ref(false)
const formRef = ref(null)
const form = reactive({
amount: 0
})
const rules = reactive({
amount: [
{ required: true, message: '请输入金额', trigger: 'blur' },
{ type: 'number', min: 0.01, message: '金额必须大于0', trigger: 'blur' }
]
})
const pagination = reactive({
page: 1,
limit: 20,
total: 0
})
// 状态类型映射
const getStatusType = (status) => {
const statusMap = {
'pending': 'warning',
'processing': 'primary',
'completed': 'success',
'failed': 'danger',
'cancelled': 'info'
}
return statusMap[status] || 'info'
}
// 状态文本映射
const getStatusText = (status) => {
const statusMap = {
'pending': '待处理',
'processing': '处理中',
'completed': '已完成',
'failed': '失败',
'cancelled': '已取消'
}
return statusMap[status] || status
}
// 时间格式化
const formatTime = (time) => {
if (!time) return '-'
const date = new Date(time)
return date.toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
})
}
const loadIncomeList = async () => {
loading.value = true
try {
const { data } = await api.income.getIncomeList({
page: pagination.page,
limit: pagination.limit
})
incomeList.value = data
pagination.total = data.total
} catch (error) {
console.error('获取提现列表失败:', error)
ElMessage.error('获取提现列表失败')
} finally {
loading.value = false
}
}
const getIncome = async () => {
drawerVisible.value = true
// 重置表单
form.amount = 0
if (formRef.value) {
formRef.value.resetFields()
}
}
const submitForm = async () => {
if (!formRef.value) return
try {
const valid = await formRef.value.validate()
if (!valid) return
const response = await api.income.createIncome({
amount: Number(form.amount),
userId: userStore.user.id
})
if (response.data) {
ElMessage.success('提现申请提交成功')
loadIncomeList()
drawerVisible.value = false
}
} catch (error) {
console.error('提现失败:', error)
ElMessage.error('提现申请失败,请重试')
}
}
onMounted(async () => {
loadIncomeList()
})
</script>
<style scoped>
/* 页面容器 */
.income-container {
padding: 24px;
min-height: 100vh;
}
/* 页面头部 */
.page-header {
margin-bottom: 24px;
}
.header-content {
display: flex;
justify-content: space-between;
align-items: center;
}
.page-title {
font-size: 1.5em;;
font-weight: 600;
margin: 0;
display: flex;
align-items: center;
gap: 12px;
}
/* 表格容器 */
.table-container {
background: white;
border-radius: 12px;
padding: 24px;
margin-bottom: 24px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
}
.income-table {
width: 100%;
}
:deep(.table-header) {
background-color: #f8fafc;
}
:deep(.table-header th) {
background-color: #f8fafc !important;
color: #475569;
font-weight: 600;
border-bottom: 2px solid #e2e8f0;
}
:deep(.el-table__row) {
transition: all 0.3s ease;
}
:deep(.el-table__row:hover) {
background-color: #f8fafc;
}
.status-tag {
font-weight: 500;
border-radius: 6px;
padding: 4px 12px;
}
.time-text {
color: #64748b;
font-size: 14px;
}
/* 分页器 */
.pagination-container {
display: flex;
justify-content: center;
background: white;
padding: 20px;
border-radius: 12px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
}
:deep(.custom-pagination) {
--el-pagination-button-color: #64748b;
--el-pagination-hover-color: #667eea;
}
/* 对话框样式 */
:deep(.withdraw-dialog) {
border-radius: 16px;
overflow: hidden;
}
:deep(.withdraw-dialog .el-dialog__header) {
color: white;
padding: 20px 24px;
margin: 0;
}
:deep(.withdraw-dialog .el-dialog__title) {
color: white;
font-weight: 600;
font-size: 18px;
}
:deep(.withdraw-dialog .el-dialog__headerbtn .el-dialog__close) {
color: white;
font-size: 20px;
}
:deep(.withdraw-dialog .el-dialog__body) {
padding: 0;
}
.dialog-content {
padding: 32px 24px 24px;
}
:deep(.withdraw-form .el-form-item__label) {
color: #374151;
font-weight: 500;
}
:deep(.amount-input .el-input__inner) {
padding-left: 40px;
font-size: 16px;
height: 48px;
border-radius: 8px;
border: 2px solid #e5e7eb;
transition: all 0.3s ease;
}
:deep(.amount-input .el-input__inner:focus) {
border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}
.currency-symbol {
color: #6b7280;
font-weight: 500;
font-size: 16px;
}
.dialog-actions {
display: flex;
justify-content: flex-end;
gap: 12px;
padding-top: 20px;
border-top: 1px solid #e5e7eb;
}
/* 响应式设计 */
@media (max-width: 768px) {
.income-container {
padding: 16px;
}
.page-header {
padding: 20px 16px;
}
.header-content {
flex-direction: column;
gap: 16px;
align-items: stretch;
}
.page-title {
font-size: 24px;
justify-content: center;
}
.withdraw-btn {
width: 100%;
justify-content: center;
}
.table-container {
padding: 16px;
overflow-x: auto;
}
:deep(.withdraw-dialog) {
width: 90% !important;
margin: 0 auto;
}
.dialog-content {
padding: 24px 16px 16px;
}
.dialog-actions {
flex-direction: column;
gap: 8px;
}
.dialog-actions .el-button {
width: 100%;
}
}
@media (max-width: 480px) {
.income-container {
padding: 12px;
}
.page-header {
padding: 16px 12px;
}
.page-title {
font-size: 20px;
}
.table-container {
padding: 12px;
}
:deep(.el-table .cell) {
padding: 8px 4px;
font-size: 14px;
}
.pagination-container {
padding: 16px 12px;
}
:deep(.custom-pagination .el-pagination__sizes),
:deep(.custom-pagination .el-pagination__jump) {
display: none;
}
}
/* 加载动画优化 */
:deep(.el-loading-mask) {
background-color: rgba(255, 255, 255, 0.8);
backdrop-filter: blur(4px);
}
/* 表格条纹优化 */
:deep(.el-table--striped .el-table__body tr.el-table__row--striped td) {
background-color: #fafbfc;
}
/* 滚动条样式 */
:deep(.el-table__body-wrapper)::-webkit-scrollbar {
height: 8px;
}
:deep(.el-table__body-wrapper)::-webkit-scrollbar-track {
background: #f1f5f9;
border-radius: 4px;
}
:deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb {
background: #cbd5e1;
border-radius: 4px;
}
:deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb:hover {
background: #94a3b8;
}
</style>

View File

@@ -78,7 +78,7 @@
{{ formatDate(row.created_at) }}
</template>
</el-table-column>
<el-table-column label="商品信息" min-width="200">
<el-table-column label="商品信息">
<template #default="{ row }">
<div class="order-items">
<div v-for="item in row.items" :key="item.id" class="order-item">
@@ -97,6 +97,18 @@
</div>
</template>
</el-table-column>
<el-table-column prop="producer" label="供应商">
<template #default="{ row }">
{{ row.producer }}
</template>
</el-table-column>
<el-table-column prop="logistics_info" label="物流信息">
<template #default="{ row }">
快递单号{{ row.delivery_code || '暂无' }}
<br>
物流公司{{ row.logistics_company || '暂无' }}
</template>
</el-table-column>
<el-table-column label="操作" width="200" fixed="right">
<template #default="{ row }">
<el-button type="primary" size="small" @click="viewOrder(row)">
@@ -206,7 +218,34 @@
</template>
</el-table-column>
</el-table>
<el-button type="primary" size="small" @click="deliveryDialogVisible = true; deliveryForm.id = selectedOrder.id;" class="delivery-btn">
发货
</el-button>
</div>
<!-- 发货对话框 -->
<el-dialog
v-model="deliveryDialogVisible"
title="发货"
width="400px"
:before-close="closeDialog"
>
<el-form :model="deliveryForm" :rules="deliveryRules" ref="deliveryFormRef">
<el-form-item label="物流公司" prop="logistics_company">
<el-input v-model="deliveryForm.logistics_company" placeholder="请输入物流公司名称" />
</el-form-item>
<el-form-item label="物流单号" prop="logistics_no">
<el-input v-model="deliveryForm.logistics_no" placeholder="请输入物流单号" />
</el-form-item>
</el-form>
<template #footer>
<el-button type="primary" @click="submitDelivery">
提交发货
</el-button>
</template>
</el-dialog>
</el-dialog>
</div>
</template>
@@ -222,6 +261,7 @@ import dayjs from 'dayjs';
const loading = ref(false);
const orders = ref([]);
const dialogVisible = ref(false);
const deliveryDialogVisible = ref(false);
const selectedOrder = ref(null);
const filters = reactive({
@@ -237,6 +277,12 @@ const pagination = reactive({
total: 0,
});
const deliveryForm = reactive({
logistics_company: '',
logistics_no: '',
id: '',
});
// 加载订单列表
const loadOrders = async () => {
loading.value = true;
@@ -259,6 +305,7 @@ const loadOrders = async () => {
pagination.total = data.data.total;
} catch (error) {
ElMessage.error('加载订单列表失败');
console.error('加载订单列表失败:', error);
} finally {
loading.value = false;
}
@@ -314,6 +361,12 @@ const updateOrderStatus = async (order, newStatus) => {
// 关闭对话框
const closeDialog = () => {
dialogVisible.value = false;
deliveryDialogVisible.value = false;
deliveryForm.value = {
logistics_company: '',
logistics_no: '',
id: '',
};
selectedOrder.value = null;
};
@@ -340,6 +393,23 @@ const getStatusText = (status) => {
return texts[status] || status;
};
const deliveryRules = reactive({
logistics_company: [{ required: true, message: '请输入物流公司名称', trigger: 'blur' }],
logistics_no: [{ required: true, message: '请输入物流单号', trigger: 'blur' }],
});
const submitDelivery = async () => {
try {
await api.products.delivery(deliveryForm);
ElMessage.success('发货成功');
closeDialog();
loadOrders();
} catch (error) {
ElMessage.error('发货失败');
console.error('发货失败:', error);
}
};
// 格式化日期
const formatDate = (dateString) => {
return dayjs(dateString).format('YYYY-MM-DD HH:mm:ss');
@@ -419,4 +489,11 @@ onMounted(() => {
display: flex;
justify-content: center;
}
.delivery-btn {
display: flex;
justify-content: center;
margin: 0 auto;
margin-top: 10px;
}
</style>

View File

@@ -496,7 +496,8 @@ const loadProduct = async () => {
console.log('表单数据:', form);
} catch (error) {
ElMessage.error('加载商品信息失败',error)
ElMessage.error('加载商品信息失败');
console.error('加载商品信息失败:', error);
// router.back()
}
}

View File

@@ -16,16 +16,17 @@ export default defineConfig({
host: '0.0.0.0',
proxy: {
'/api': {
target: 'http://192.168.0.11:3000',
changeOrigin: true
target: 'http://192.168.0.12:3008',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
},
'/mid': {
target: 'http://localhost:3005',
target: 'http://192.168.0.4:3005/',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/mid/, '')
},
'/uploads': {
target: 'http://localhost:3000',
target: 'http://192.168.0.12:3008',
changeOrigin: true
}
}