Browse Source

fix: login token fields, commonRefs init, admin detail 403

- accept accessToken/token/jToken; reject empty credentials
- initialize commonRefs with constructor fields for Vite HMR
- skip admin alarms/commands on DeviceDetail (backend user-only)
- proxy via agent when http_proxy is set; target LAN backend
main
xiaosi 1 month ago
parent
commit
bf8ebc5e84
  1. 46
      package-lock.json
  2. 1
      package.json
  3. 8
      src/stores/modules/userStore.js
  4. 34
      src/utils/commonRefs.js
  5. 9
      src/views/DeviceDetailView/DeviceDetailView.vue
  6. 12
      src/views/LoginView/LoginView.vue
  7. 11
      vite.config.js

46
package-lock.json

@ -18,6 +18,7 @@
}, },
"devDependencies": { "devDependencies": {
"@vitejs/plugin-vue": "^5.0.4", "@vitejs/plugin-vue": "^5.0.4",
"http-proxy-agent": "^9.1.0",
"unplugin-auto-import": "^21.1.0", "unplugin-auto-import": "^21.1.0",
"unplugin-vue-components": "^32.1.0", "unplugin-vue-components": "^32.1.0",
"vite": "^5.2.11" "vite": "^5.2.11"
@ -1230,6 +1231,7 @@
"integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
"dev": true, "dev": true,
"hasInstallScript": true, "hasInstallScript": true,
"peer": true,
"bin": { "bin": {
"esbuild": "bin/esbuild" "esbuild": "bin/esbuild"
}, },
@ -1456,6 +1458,31 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/http-proxy-agent": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-9.1.0.tgz",
"integrity": "sha512-2NxoveTT58mjYT4n3RPTEfCZGLMbidoO8XEieXfpSYxu+PQJ1qpx4ypwH6N+uF9twBPIvRRgvkvW5HUTYWENig==",
"dev": true,
"license": "MIT",
"dependencies": {
"agent-base": "9.0.0",
"debug": "^4.3.4",
"proxy-agent-negotiate": "1.1.0"
},
"engines": {
"node": ">= 20"
}
},
"node_modules/http-proxy-agent/node_modules/agent-base": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-9.0.0.tgz",
"integrity": "sha512-TQf59BsZnytt8GdJKLPfUZ54g/iaUL2OWDSFCCvMOhsHduDQxO8xC4PNeyIkVcA5KwL2phPSv0douC0fgWzmnA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 20"
}
},
"node_modules/https-proxy-agent": { "node_modules/https-proxy-agent": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
@ -1891,6 +1918,24 @@
"node": ">= 4.4.x" "node": ">= 4.4.x"
} }
}, },
"node_modules/proxy-agent-negotiate": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-agent-negotiate/-/proxy-agent-negotiate-1.1.0.tgz",
"integrity": "sha512-N8IBcM3UgCVzz2L2Lqv8DVntDnnC8/hiV4nEDUPkqq72TPUgYWjQc+bdZlBPZK9LzPAvOY//gAt0S0DApoOXWQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 20"
},
"peerDependencies": {
"kerberos": "^2.0.0"
},
"peerDependenciesMeta": {
"kerberos": {
"optional": true
}
}
},
"node_modules/proxy-from-env": { "node_modules/proxy-from-env": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz",
@ -1957,6 +2002,7 @@
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.4.tgz", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.4.tgz",
"integrity": "sha512-RXOqwaPsBGjMNMa4sQjDjHieHEZDFoj/Rdr46l2MU5DfEs16wHJPC2RPTPHWhNl+M3aI472LLqFkFKut4SblOg==", "integrity": "sha512-RXOqwaPsBGjMNMa4sQjDjHieHEZDFoj/Rdr46l2MU5DfEs16wHJPC2RPTPHWhNl+M3aI472LLqFkFKut4SblOg==",
"dev": true, "dev": true,
"peer": true,
"dependencies": { "dependencies": {
"@types/estree": "1.0.9" "@types/estree": "1.0.9"
}, },

1
package.json

@ -19,6 +19,7 @@
}, },
"devDependencies": { "devDependencies": {
"@vitejs/plugin-vue": "^5.0.4", "@vitejs/plugin-vue": "^5.0.4",
"http-proxy-agent": "^9.1.0",
"unplugin-auto-import": "^21.1.0", "unplugin-auto-import": "^21.1.0",
"unplugin-vue-components": "^32.1.0", "unplugin-vue-components": "^32.1.0",
"vite": "^5.2.11" "vite": "^5.2.11"

8
src/stores/modules/userStore.js

@ -6,9 +6,13 @@ export const useUserStore = defineStore('user', () => {
const user = ref(JSON.parse(localStorage.getItem('user') || 'null')) const user = ref(JSON.parse(localStorage.getItem('user') || 'null'))
function setLogin(tokenVal, userVal) { function setLogin(tokenVal, userVal) {
token.value = tokenVal
const nextToken = tokenVal == null ? '' : String(tokenVal)
if (!nextToken) {
throw new Error('登录凭证为空')
}
token.value = nextToken
user.value = userVal user.value = userVal
localStorage.setItem('token', tokenVal)
localStorage.setItem('token', nextToken)
localStorage.setItem('user', JSON.stringify(userVal)) localStorage.setItem('user', JSON.stringify(userVal))
} }

34
src/utils/commonRefs.js

@ -3,28 +3,26 @@
* 异步获取引用对象 * 异步获取引用对象
*/ */
const refs = Symbol('refs')
const pendingList = Symbol('pendingList')
class CommonRefs { class CommonRefs {
[refs] = {}
[pendingList] = {}
constructor() {
this._refs = {}
this._pendingList = {}
}
clearPendingList(key = '') { clearPendingList(key = '') {
if (!key) { if (!key) {
this[pendingList] = {}
this._pendingList = {}
} else { } else {
this[pendingList][key] = []
this._pendingList[key] = []
} }
} }
setRef(key, ref) { setRef(key, ref) {
// 允许显式 set null 唤醒 pending(无 token 等失败路径) // 允许显式 set null 唤醒 pending(无 token 等失败路径)
if (key in this[refs] && this[refs][key] != null) return
if (key in this._refs && this._refs[key] != null) return
this[refs][key] = ref
const { [key]: resolves } = this[pendingList]
this._refs[key] = ref
const resolves = this._pendingList[key]
;(resolves || []).forEach((resolve, index) => { ;(resolves || []).forEach((resolve, index) => {
if (!resolve) return if (!resolve) return
resolve(ref) resolve(ref)
@ -34,21 +32,21 @@ class CommonRefs {
getRef(key) { getRef(key) {
return new Promise((resolve) => { return new Promise((resolve) => {
if (key in this[refs]) {
resolve(this[refs][key])
if (key in this._refs) {
resolve(this._refs[key])
return return
} }
if (key in this[pendingList]) {
this[pendingList][key].push(resolve)
if (key in this._pendingList) {
this._pendingList[key].push(resolve)
} else { } else {
this[pendingList][key] = [resolve]
this._pendingList[key] = [resolve]
} }
}) })
} }
removeRef(key) { removeRef(key) {
if (key in this[refs]) {
delete this[refs][key]
if (key in this._refs) {
delete this._refs[key]
} }
} }
} }

9
src/views/DeviceDetailView/DeviceDetailView.vue

@ -461,12 +461,15 @@ function chargeText(value) {
onMounted(async () => { onMounted(async () => {
try { try {
await devices.load() await devices.load()
if (!isAdmin.value) {
await Promise.all([loadLogs(), loadAlarms()]) await Promise.all([loadLogs(), loadAlarms()])
}
} catch (e) { } catch (e) {
ui.toast(e.message || '加载设备失败') ui.toast(e.message || '加载设备失败')
} }
}) })
const COMMAND_NAMES = { const COMMAND_NAMES = {
'dock.open': '打开舱门', 'dock.open': '打开舱门',
'dock.close': '关闭舱门', 'dock.close': '关闭舱门',
@ -519,7 +522,7 @@ function fmtTime(t) {
async function loadLogs() { async function loadLogs() {
const current = record.value const current = record.value
if (!current) return
if (!current || isAdmin.value) return
try { try {
const page = await request.get(urls.COMMANDS, { params: { pageNum: 1, pageSize: 100, dockId: current.dockId } }) const page = await request.get(urls.COMMANDS, { params: { pageNum: 1, pageSize: 100, dockId: current.dockId } })
logs.value = (page?.records || []).filter((log) => current.kind !== 'drone' || log.droneSn === current.droneSn).map((log) => { logs.value = (page?.records || []).filter((log) => current.kind !== 'drone' || log.droneSn === current.droneSn).map((log) => {
@ -537,13 +540,14 @@ async function loadLogs() {
} }
}) })
} catch (e) { } catch (e) {
if (e?.response?.status === 403) return
ui.toast(e.message || '加载设备操作记录失败') ui.toast(e.message || '加载设备操作记录失败')
} }
} }
async function loadAlarms() { async function loadAlarms() {
const current = record.value const current = record.value
if (!current) return
if (!current || isAdmin.value) return
try { try {
const page = await request.get(urls.ALARMS, { const page = await request.get(urls.ALARMS, {
params: { params: {
@ -570,6 +574,7 @@ async function loadAlarms() {
} }
}) })
} catch (e) { } catch (e) {
if (e?.response?.status === 403) return
ui.toast(e.message || '加载告警记录失败') ui.toast(e.message || '加载告警记录失败')
} }
} }

12
src/views/LoginView/LoginView.vue

@ -69,7 +69,12 @@ async function submitLogin() {
phone: account.value, phone: account.value,
password: password.value, password: password.value,
}) })
userStore.setLogin(data.accessToken, data.user)
const token = data?.accessToken || data?.token || data?.jToken
if (!token) {
loginError.value = '登录成功但未返回 token,请检查后端响应字段。'
return
}
userStore.setLogin(token, data.user || data.userInfo || null)
ui.toast('登录成功') ui.toast('登录成功')
router.push('/monitor') router.push('/monitor')
} catch (e) { } catch (e) {
@ -101,8 +106,9 @@ async function submitRegister() {
email: reg.email, email: reg.email,
password: reg.password, password: reg.password,
}) })
if (data && data.accessToken) {
userStore.setLogin(data.accessToken, data.user)
const token = data?.accessToken || data?.token || data?.jToken
if (token) {
userStore.setLogin(token, data.user || data.userInfo || null)
ui.toast('注册成功') ui.toast('注册成功')
router.push('/monitor') router.push('/monitor')
} else { } else {

11
vite.config.js

@ -4,6 +4,11 @@ import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite' import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite' import Components from 'unplugin-vue-components/vite'
import { TDesignResolver } from 'unplugin-vue-components/resolvers' import { TDesignResolver } from 'unplugin-vue-components/resolvers'
import { HttpProxyAgent } from 'http-proxy-agent'
// 本机若配置了 http_proxy(如 7890),Vite 代理出站需显式走 agent,否则直连局域网可能 EHOSTUNREACH
const outboundProxy = process.env.http_proxy || process.env.HTTP_PROXY || ''
const proxyAgent = outboundProxy ? new HttpProxyAgent(outboundProxy) : undefined
export default defineConfig({ export default defineConfig({
plugins: [ plugins: [
@ -25,10 +30,12 @@ export default defineConfig({
host: '0.0.0.0', host: '0.0.0.0',
port: 5173, port: 5173,
proxy: { proxy: {
// 浏览器请求 /api/* → 后端 /*(去掉 /api 前缀)
'/api': { '/api': {
target: 'http://127.0.0.1:8080',
target: 'http://192.168.10.29:8080',
changeOrigin: true, changeOrigin: true,
rewrite: (p) => p.replace(/^\/api/, ''),
agent: proxyAgent,
rewrite: (path) => path.replace(/^\/api/, ''),
}, },
}, },
}, },

Loading…
Cancel
Save