前端安全修复:登录接口改用统一auth接口、Dashboard过滤条件修正、RBAC token处理

This commit is contained in:
notyclaw 2026-03-28 18:36:19 +08:00
parent 6f5d2b8d13
commit 6807cd94a1
17 changed files with 172 additions and 78 deletions

View File

@ -29,10 +29,15 @@ const router = createRouter({
routes
})
// 路由守卫
// 路由守卫:检查 token
router.beforeEach((to, from, next) => {
// 暂时禁用登录验证
next()
if (to.meta.requiresAuth && !localStorage.getItem('token')) {
next('/login')
} else if (to.path === '/login' && localStorage.getItem('token')) {
next('/')
} else {
next()
}
})
export default router

37
src/utils/api.js Normal file
View File

@ -0,0 +1,37 @@
/**
* 封装 axios自动附加 Authorization header
* 所有需要鉴权的 API 请求统一走这里
*/
import axios from 'axios'
const api = axios.create({
baseURL: '/api',
timeout: 15000
})
// 请求拦截器:自动附加 token
api.interceptors.request.use(
(config) => {
const token = localStorage.getItem('token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
},
(error) => Promise.reject(error)
)
// 响应拦截器token 过期则跳转登录
api.interceptors.response.use(
(response) => response,
(error) => {
if (error.response?.status === 401) {
localStorage.removeItem('token')
localStorage.removeItem('riderInfo')
window.location.href = '/login'
}
return Promise.reject(error)
}
)
export default api

View File

@ -80,7 +80,7 @@
<script setup>
import { ref, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import axios from 'axios'
import api from '../utils/api'
const searchForm = ref({ type: '', status: '' })
const tableData = ref([])
@ -90,7 +90,7 @@ const viewData = ref({})
const rejectReason = ref('')
const fetchData = async () => {
const res = await axios.get('http://localhost:3000/api/applications')
const res = await api.get('/applications')
if (res.data.success) tableData.value = res.data.data
}
const handleSearch = () => {
@ -102,7 +102,7 @@ const handleSearch = () => {
const handleReset = () => { searchForm.value = { type: '', status: '' }; fetchData() }
const handleView = (row) => { viewData.value = row; dialogVisible.value = true }
const handleApprove = async () => {
await axios.put(`http://localhost:3000/api/applications/${viewData.value._id}`, { status: '已通过' })
await api.put(`/applications/${viewData.value._id}`, { status: '已通过' })
ElMessage.success('已通过')
dialogVisible.value = false
fetchData()
@ -110,7 +110,7 @@ const handleApprove = async () => {
const handleReject = () => { rejectDialogVisible.value = true }
const confirmReject = async () => {
if (!rejectReason.value.trim()) { ElMessage.warning('请填写拒绝理由'); return }
await axios.put(`http://localhost:3000/api/applications/${viewData.value._id}`, { status: '已拒绝', rejectReason: rejectReason.value })
await api.put(`/applications/${viewData.value._id}`, { status: '已拒绝', rejectReason: rejectReason.value })
ElMessage.info('已拒绝')
rejectDialogVisible.value = false
rejectReason.value = ''

View File

@ -94,7 +94,7 @@
<script setup>
import { ref, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import axios from 'axios'
import api from '../utils/api'
const searchForm = ref({ type: '', status: '' })
const tableData = ref([])
@ -105,7 +105,7 @@ const form = ref({ _id: '', type: '', title: '', content: '', applicant: '', app
const rules = { type: [{ required: true, message: '请选择类型', trigger: 'change' }], title: [{ required: true, message: '请输入标题', trigger: 'blur' }] }
const fetchData = async () => {
const res = await axios.get('http://localhost:3000/api/approvals')
const res = await api.get('/approvals')
if (res.data.success) tableData.value = res.data.data
}
const handleSearch = () => {
@ -118,20 +118,20 @@ const handleReset = () => { searchForm.value = { type: '', status: '' }; fetchDa
const handleAdd = () => { dialogTitle.value = '创建审批'; form.value = { _id: '', type: '', title: '', content: '', applicant: '', approver: '', remark: '', status: '待审批' }; dialogVisible.value = true }
const handleEdit = (row) => { dialogTitle.value = '查看审批'; form.value = { ...row }; dialogVisible.value = true }
const handleApprove = async (row) => {
await axios.put(`http://localhost:3000/api/approvals/${row._id}`, { status: '已通过', approver: '管理员' })
await api.put(`/approvals/${row._id}`, { status: '已通过', approver: '管理员' })
ElMessage.success('审批通过'); fetchData()
}
const handleReject = async (row) => {
await axios.put(`http://localhost:3000/api/approvals/${row._id}`, { status: '已拒绝', approver: '管理员' })
await api.put(`/approvals/${row._id}`, { status: '已拒绝', approver: '管理员' })
ElMessage.info('已拒绝'); fetchData()
}
const handleSubmit = async () => {
await formRef.value.validate()
if (form.value._id) {
await axios.put(`http://localhost:3000/api/approvals/${form.value._id}`, form.value)
await api.put(`/approvals/${form.value._id}`, form.value)
ElMessage.success('修改成功')
} else {
await axios.post('http://localhost:3000/api/approvals', form.value)
await api.post('/approvals', form.value)
ElMessage.success('创建成功')
}
dialogVisible.value = false; fetchData()

View File

@ -94,7 +94,7 @@
<script setup>
import { ref, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import axios from 'axios'
import api from '../utils/api'
const searchForm = ref({ type: '', status: '' })
const tableData = ref([])
@ -105,7 +105,7 @@ const form = ref({ _id: '', type: '', content: '', handler: '', response: '', st
const rules = { type: [{ required: true, message: '请选择类型', trigger: 'change' }], content: [{ required: true, message: '请输入内容', trigger: 'blur' }] }
const fetchData = async () => {
const res = await axios.get('http://localhost:3000/api/complaints')
const res = await api.get('/complaints')
if (res.data.success) tableData.value = res.data.data
}
const handleSearch = () => {
@ -120,17 +120,17 @@ const handleEdit = (row) => { dialogTitle.value = '处理投诉'; form.value = {
const handleSubmit = async () => {
await formRef.value.validate()
if (form.value._id) {
await axios.put(`http://localhost:3000/api/complaints/${form.value._id}`, form.value)
await api.put(`/complaints/${form.value._id}`, form.value)
ElMessage.success('处理成功')
} else {
await axios.post('http://localhost:3000/api/complaints', form.value)
await api.post('/complaints', form.value)
ElMessage.success('添加成功')
}
dialogVisible.value = false; fetchData()
}
const handleDelete = async (row) => {
await ElMessageBox.confirm('确定删除?', '提示', { type: 'warning' })
await axios.delete(`http://localhost:3000/api/complaints/${row._id}`)
await api.delete(`/complaints/${row._id}`)
ElMessage.success('删除成功'); fetchData()
}
const getStatusType = (status) => ({ '待处理': 'danger', '处理中': 'warning', '已解决': 'success', '已关闭': 'info' }[status] || 'info')

View File

@ -103,7 +103,7 @@
<script setup>
import { ref, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import axios from 'axios'
import api from '../utils/api'
const searchForm = ref({ type: '', status: '' })
const tableData = ref([])
@ -114,7 +114,7 @@ const form = ref({ _id: '', type: '', title: '', partyA: '', partyB: '', content
const rules = { type: [{ required: true, message: '请选择类型', trigger: 'change' }], title: [{ required: true, message: '请输入标题', trigger: 'blur' }] }
const fetchData = async () => {
const res = await axios.get('http://localhost:3000/api/conflicts')
const res = await api.get('/conflicts')
if (res.data.success) tableData.value = res.data.data
}
const handleSearch = () => {
@ -129,17 +129,17 @@ const handleEdit = (row) => { dialogTitle.value = '处理矛盾'; form.value = {
const handleSubmit = async () => {
await formRef.value.validate()
if (form.value._id) {
await axios.put(`http://localhost:3000/api/conflicts/${form.value._id}`, form.value)
await api.put(`/conflicts/${form.value._id}`, form.value)
ElMessage.success('修改成功')
} else {
await axios.post('http://localhost:3000/api/conflicts', form.value)
await api.post('/conflicts', form.value)
ElMessage.success('登记成功')
}
dialogVisible.value = false; fetchData()
}
const handleDelete = async (row) => {
await ElMessageBox.confirm('确定删除?', '提示', { type: 'warning' })
await axios.delete(`http://localhost:3000/api/conflicts/${row._id}`)
await api.delete(`/conflicts/${row._id}`)
ElMessage.success('删除成功'); fetchData()
}
const getStatusType = (status) => ({ '待处理': 'danger', '处理中': 'warning', '已解决': 'success', '已关闭': 'info' }[status] || 'info')

View File

@ -83,7 +83,7 @@
<script setup>
import { ref, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import axios from 'axios'
import api from '../utils/api'
const searchForm = ref({ name: '', phone: '' })
const tableData = ref([])
@ -110,7 +110,7 @@ const rules = {
const fetchCustomers = async () => {
try {
const res = await axios.get('http://localhost:3000/api/customers')
const res = await api.get('/customers')
if (res.data.success) {
tableData.value = res.data.data
}
@ -154,10 +154,10 @@ const handleSubmit = async () => {
try {
await formRef.value.validate()
if (form.value._id) {
const res = await axios.put(`http://localhost:3000/api/customers/${form.value._id}`, form.value)
const res = await api.put(`/customers/${form.value._id}`, form.value)
if (res.data.success) { ElMessage.success('修改成功') }
} else {
const res = await axios.post('http://localhost:3000/api/customers', form.value)
const res = await api.post('/customers', form.value)
if (res.data.success) { ElMessage.success('添加成功') }
}
dialogVisible.value = false
@ -168,7 +168,7 @@ const handleSubmit = async () => {
const handleDelete = async (row) => {
try {
await ElMessageBox.confirm('确定要删除这个客户吗?', '提示', { type: 'warning' })
const res = await axios.delete(`http://localhost:3000/api/customers/${row._id}`)
const res = await api.delete(`/customers/${row._id}`)
if (res.data.success) { ElMessage.success('删除成功'); fetchCustomers() }
} catch {}
}

View File

@ -104,16 +104,16 @@
<el-table :data="recentPayments" stripe size="small">
<el-table-column label="类型" width="80">
<template #default="{ row }">
<el-tag :type="row.type === 'income' ? 'success' : 'danger'" size="small">
{{ row.type === 'income' ? '收入' : '支出' }}
<el-tag :type="row.type === '收入' ? 'success' : 'danger'" size="small">
{{ row.type === '收入' ? '收入' : '支出' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="party" label="对方" width="120" />
<el-table-column prop="amount" label="金额" width="100">
<template #default="{ row }">
<span :style="{ color: row.type === 'income' ? '#52c41a' : '#f5222d', fontWeight: 'bold' }">
{{ row.type === 'income' ? '+' : '-' }}¥{{ row.amount }}
<span :style="{ color: row.type === '收入' ? '#52c41a' : '#f5222d', fontWeight: 'bold' }">
{{ row.type === '收入' ? '+' : '-' }}¥{{ row.amount }}
</span>
</template>
</el-table-column>
@ -173,19 +173,21 @@ const barChartRef = ref(null)
const loadData = async () => {
try {
const token = localStorage.getItem('token')
const headers = token ? { Authorization: `Bearer ${token}` } : {}
//
const vehicleRes = await fetch('http://localhost:3000/api/vehicles')
const vehicleRes = await fetch('/api/vehicles', { headers })
const vehicleData = await vehicleRes.json()
if (vehicleData.success) {
const vehicles = vehicleData.data
stats.value.totalVehicles = vehicles.length
stats.value.availableVehicles = vehicles.filter(v => v.status === '空闲').length
stats.value.rentedVehicles = vehicles.filter(v => v.status === '出租中' || v.status === '在租').length
stats.value.rentedVehicles = vehicles.filter(v => v.status === '在租').length
stats.value.maintenanceVehicles = vehicles.filter(v => v.status === '维修中').length
}
//
const financeRes = await fetch('http://localhost:3000/api/finance')
const financeRes = await fetch('/api/finance', { headers })
const financeData = await financeRes.json()
if (financeData.success) {
const list = financeData.data.list || []
@ -195,8 +197,8 @@ const loadData = async () => {
financeStats.value.totalExpense = summary.totalExpense || 0
financeStats.value.balance = summary.balance || 0
financeStats.value.totalCount = list.length
financeStats.value.incomeCount = list.filter(p => p.type === 'income').length
financeStats.value.expenseCount = list.filter(p => p.type === 'expense').length
financeStats.value.incomeCount = list.filter(p => p.type === '收入').length
financeStats.value.expenseCount = list.filter(p => p.type === '支出').length
// 5
recentPayments.value = list.slice(0, 5)

View File

@ -74,7 +74,7 @@
<script setup>
import { ref, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import axios from 'axios'
import api from '../utils/api'
const searchForm = ref({ type: '', status: '' })
const tableData = ref([])
@ -83,7 +83,7 @@ const viewData = ref({})
const result = ref('')
const fetchData = async () => {
const res = await axios.get('http://localhost:3000/api/disputes')
const res = await api.get('/disputes')
if (res.data.success) tableData.value = res.data.data
}
const handleSearch = () => {
@ -95,7 +95,7 @@ const handleSearch = () => {
const handleReset = () => { searchForm.value = { type: '', status: '' }; fetchData() }
const handleView = (row) => { viewData.value = row; result.value = row.result || ''; dialogVisible.value = true }
const handleResolve = async () => {
await axios.put(`http://localhost:3000/api/disputes/${viewData.value._id}`, { status: '已解决', result: result.value })
await api.put(`/disputes/${viewData.value._id}`, { status: '已解决', result: result.value })
ElMessage.success('已标记为解决')
dialogVisible.value = false
fetchData()

View File

@ -94,7 +94,9 @@ const barChartRef = ref(null)
const refresh = async () => {
try {
const res = await fetch('http://localhost:3000/api/finance')
const token = localStorage.getItem('token')
const headers = token ? { Authorization: `Bearer ${token}` } : {}
const res = await fetch('/api/finance', { headers })
const data = await res.json()
if (data.success) {
payments.value = data.data.list

View File

@ -55,7 +55,7 @@
</el-badge>
<el-dropdown @command="handleCommand">
<span class="user-info">
👤 管理员
👤 {{ userName }}
<el-icon><arrow-down /></el-icon>
</span>
<template #dropdown>
@ -85,6 +85,10 @@ const route = useRoute()
const activeMenu = computed(() => route.path)
const notificationCount = ref(2)
//
const riderInfo = JSON.parse(localStorage.getItem('riderInfo') || '{}')
const userName = ref(riderInfo.name || '管理员')
const pageTitle = computed(() => {
const titles = {
'/': '数据看板',
@ -104,7 +108,8 @@ const showNotifications = () => {
const handleCommand = (command) => {
if (command === 'logout') {
localStorage.removeItem('isLoggedIn')
localStorage.removeItem('token')
localStorage.removeItem('riderInfo')
window.location.href = '/login'
}
}

View File

@ -14,10 +14,10 @@
<el-input v-model="loginForm.password" type="password" placeholder="请输入密码" show-password />
</el-form-item>
<el-form-item>
<el-button type="primary" style="width: 100%;" @click="handleLogin">登录</el-button>
<el-button type="primary" style="width: 100%;" :loading="loading" @click="handleLogin">登录</el-button>
</el-form-item>
</el-form>
<div class="tips">演示账号: admin / admin</div>
<div class="tips">演示账号: admin / admin123</div>
</el-card>
</div>
</template>
@ -29,6 +29,7 @@ import { ElMessage } from 'element-plus'
const router = useRouter()
const formRef = ref(null)
const loading = ref(false)
const loginForm = ref({ username: '', password: '' })
const rules = {
@ -36,18 +37,41 @@ const rules = {
password: [{ required: true, message: '请输入密码', trigger: 'blur' }]
}
const handleLogin = () => {
formRef.value.validate((valid) => {
if (valid) {
if (loginForm.value.username === 'admin' && loginForm.value.password === 'admin') {
ElMessage.success('登录成功!')
localStorage.setItem('isLoggedIn', 'true')
router.push('/')
} else {
ElMessage.error('用户名或密码错误')
}
const handleLogin = async () => {
const valid = await formRef.value.validate().catch(() => false)
if (!valid) return
loading.value = true
try {
const res = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(loginForm.value)
})
const data = await res.json()
if (data.success) {
localStorage.setItem('token', data.data.token)
localStorage.setItem('userInfo', JSON.stringify({
id: data.data.id,
username: data.data.username,
name: data.data.name,
type: data.data.type,
role: data.data.role,
roleLabel: data.data.roleLabel,
permissions: data.data.permissions
}))
localStorage.setItem('permissions', JSON.stringify(data.data.permissions))
ElMessage.success('登录成功!')
router.push('/')
} else {
ElMessage.error(data.message || '登录失败')
}
})
} catch (e) {
ElMessage.error('网络错误,请检查后端服务是否启动')
} finally {
loading.value = false
}
}
</script>

View File

@ -126,7 +126,7 @@
<script setup>
import { ref, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import axios from 'axios'
import api from '../utils/api'
const searchForm = ref({ orderNumber: '', status: '' })
const tableData = ref([])
@ -150,15 +150,15 @@ const rules = {
const fetchOrders = async () => {
try {
const res = await axios.get('http://localhost:3000/api/orders')
const res = await api.get('/orders')
if (res.data.success) tableData.value = res.data.data
} catch { ElMessage.error('获取订单列表失败') }
}
const fetchOptions = async () => {
const [cRes, vRes] = await Promise.all([
axios.get('http://localhost:3000/api/customers'),
axios.get('http://localhost:3000/api/vehicles')
api.get('/customers'),
api.get('/vehicles')
])
if (cRes.data.success) customers.value = cRes.data.data
if (vRes.data.success) vehicles.value = vRes.data.data.filter(v => v.status === '空闲')
@ -195,10 +195,10 @@ const handleSubmit = async () => {
try {
await formRef.value.validate()
if (form.value._id) {
const res = await axios.put(`http://localhost:3000/api/orders/${form.value._id}`, form.value)
const res = await api.put(`/orders/${form.value._id}`, form.value)
if (res.data.success) ElMessage.success('修改成功')
} else {
const res = await axios.post('http://localhost:3000/api/orders', form.value)
const res = await api.post('/orders', form.value)
if (res.data.success) ElMessage.success('创建成功')
}
dialogVisible.value = false
@ -209,7 +209,7 @@ const handleSubmit = async () => {
const handleDelete = async (row) => {
try {
await ElMessageBox.confirm('确定要删除这个订单吗?', '提示', { type: 'warning' })
const res = await axios.delete(`http://localhost:3000/api/orders/${row._id}`)
const res = await api.delete(`/orders/${row._id}`)
if (res.data.success) { ElMessage.success('删除成功'); fetchOrders() }
} catch {}
}

View File

@ -98,7 +98,7 @@
<script setup>
import { ref, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import axios from 'axios'
import api from '../utils/api'
const searchForm = ref({ type: '', status: '' })
const tableData = ref([])
@ -109,7 +109,7 @@ const form = ref({ _id: '', type: '', amount: 0, method: '', account: '', operat
const rules = { type: [{ required: true, message: '请选择类型', trigger: 'change' }], amount: [{ required: true, message: '请输入金额', trigger: 'blur' }] }
const fetchData = async () => {
const res = await axios.get('http://localhost:3000/api/payments')
const res = await api.get('/payments')
if (res.data.success) tableData.value = res.data.data
}
const handleSearch = () => {
@ -122,16 +122,16 @@ const handleReset = () => { searchForm.value = { type: '', status: '' }; fetchDa
const handleAdd = () => { dialogTitle.value = '创建打款'; form.value = { _id: '', type: '', amount: 0, method: '', account: '', operator: '', remark: '', status: '待打款' }; dialogVisible.value = true }
const handleEdit = (row) => { dialogTitle.value = '编辑打款'; form.value = { ...row }; dialogVisible.value = true }
const handleProcess = async (row) => {
await axios.put(`http://localhost:3000/api/payments/${row._id}`, { status: '已打款' })
await api.put(`/payments/${row._id}`, { status: '已打款' })
ElMessage.success('打款成功'); fetchData()
}
const handleSubmit = async () => {
await formRef.value.validate()
if (form.value._id) {
await axios.put(`http://localhost:3000/api/payments/${form.value._id}`, form.value)
await api.put(`/payments/${form.value._id}`, form.value)
ElMessage.success('修改成功')
} else {
await axios.post('http://localhost:3000/api/payments', form.value)
await api.post('/payments', form.value)
ElMessage.success('创建成功')
}
dialogVisible.value = false; fetchData()

View File

@ -124,7 +124,7 @@
<script setup>
import { ref, onMounted, computed } from 'vue'
import { ElMessage } from 'element-plus'
import axios from 'axios'
import api from '../utils/api'
const searchForm = ref({ name: '', approvalStatus: '' })
const tableData = ref([])
@ -132,7 +132,7 @@ const dialogVisible = ref(false)
const viewData = ref({})
const fetchData = async () => {
const res = await axios.get('http://localhost:3000/api/stores')
const res = await api.get('/stores')
if (res.data.success) tableData.value = res.data.data
}
const handleSearch = () => {
@ -150,7 +150,7 @@ const rejectReason = ref('')
const showRejectDialog = ref(false)
const handleApprove = async () => {
await axios.put(`http://localhost:3000/api/stores/${viewData.value._id}`, { approvalStatus: '已通过' })
await api.put(`/stores/${viewData.value._id}`, { approvalStatus: '已通过' })
ElMessage.success('已通过审批')
dialogVisible.value = false
fetchData()
@ -160,7 +160,7 @@ const confirmReject = async () => {
ElMessage.warning('请填写拒绝理由')
return
}
await axios.put(`http://localhost:3000/api/stores/${viewData.value._id}`, { approvalStatus: '已拒绝', rejectReason: rejectReason.value })
await api.put(`/stores/${viewData.value._id}`, { approvalStatus: '已拒绝', rejectReason: rejectReason.value })
ElMessage.info('已拒绝')
showRejectDialog.value = false
rejectReason.value = ''

View File

@ -107,7 +107,7 @@
<script setup>
import { ref, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import axios from 'axios'
import api from '../utils/api'
import { exportExcel, formatDate } from '../utils'
const searchForm = ref({ vehicleId: '', status: '' })
@ -128,7 +128,7 @@ const rules = {
const fetchVehicles = async () => {
try {
const res = await axios.get('http://localhost:3000/api/vehicles')
const res = await api.get('/vehicles')
if (res.data.success) tableData.value = res.data.data
} catch { ElMessage.error('获取车辆列表失败') }
}
@ -158,10 +158,10 @@ const handleSubmit = async () => {
try {
await formRef.value.validate()
if (form.value._id) {
const res = await axios.put(`http://localhost:3000/api/vehicles/${form.value._id}`, form.value)
const res = await api.put(`/vehicles/${form.value._id}`, form.value)
if (res.data.success) ElMessage.success('修改成功')
} else {
const res = await axios.post('http://localhost:3000/api/vehicles', form.value)
const res = await api.post('/vehicles', form.value)
if (res.data.success) ElMessage.success('添加成功')
}
dialogVisible.value = false
@ -172,7 +172,7 @@ const handleSubmit = async () => {
const handleDelete = async (row) => {
try {
await ElMessageBox.confirm('确定要删除这辆车吗?', '提示', { type: 'warning' })
const res = await axios.delete(`http://localhost:3000/api/vehicles/${row._id}`)
const res = await api.delete(`/vehicles/${row._id}`)
if (res.data.success) { ElMessage.success('删除成功'); fetchVehicles() }
} catch {}
}

View File

@ -1,7 +1,26 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vite.dev/config/
export default defineConfig({
plugins: [vue()],
server: {
port: 5173,
host: '0.0.0.0',
allowedHosts: ['51bike.online', 'admin.51bike.online', '43.156.200.173'],
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
// 显式确保 Authorization header 被转发Vite 默认会转发,此处明确声明以防被代理中间件清除)
configure: (proxy) => {
proxy.on('proxyReq', (proxyReq) => {
// 确保 Authorization header 被传递到后端
if (proxyReq.getHeader('authorization')) {
proxyReq.setHeader('authorization', proxyReq.getHeader('authorization'))
}
})
}
}
}
}
})