feat: 积分商城-完成

This commit is contained in:
2026-07-31 19:08:15 +08:00
parent 898e239463
commit 20d059443c
21 changed files with 2122 additions and 181 deletions
@@ -0,0 +1,260 @@
<template>
<main class="supplier-logs-page">
<van-nav-bar title="供应商日志" left-arrow @click-left="$router.back()" />
<section class="toolbar">
<span :class="{ active: listening }">{{ listening ? '监听中' : '已暂停' }}</span>
<button type="button" @click="loadLogs">刷新</button>
<button type="button" @click="clearLogs">清除</button>
</section>
<section class="console-panel">
<div v-if="consoleLogs.length === 0" class="console-empty">暂无日志,正在监听请求...</div>
<article v-for="item in consoleLogs" :key="item.id" class="console-line" :class="item.type">
<p><span>{{ formatTime(item.time) }}</span><b>{{ item.title }}:</b></p>
<pre>{{ formatRequestLine(item) }}</pre>
<pre>请求参数:{{ formatRequestParams(item.content) }}</pre>
</article>
</section>
</main>
</template>
<script>
import { Toast } from 'vant'
import { getFunction } from '@/api/supplied-service'
const API_BASE = process.env.VUE_APP_SUPPLIED_API_BASE || 'http://localhost:8888/api'
const LOG_GROUPS = [
{ type: 'backend-receive', title: '当前后端接收请求', emptyText: '暂无后端接收请求' },
{ type: 'zhgh-send', title: '向ZHGH发送请求', emptyText: '暂无向ZHGH发送请求' },
{ type: 'zhgh-receive', title: '接收ZHGH请求', emptyText: '暂无接收ZHGH请求' }
]
const POLL_INTERVAL = 2000
export default {
name: 'JoyfulSelectionSupplierLogs',
data: () => ({
logs: [],
timer: null,
listening: false
}),
computed: {
consoleLogs() {
return this.logs
.filter(item => LOG_GROUPS.some(group => group.type === item.type))
.slice()
.reverse()
}
},
created() {
this.loadLogs()
this.startListening()
},
beforeDestroy() {
this.stopListening()
},
methods: {
async loadLogs() {
try {
const data = await getFunction('supplier-logs')
this.logs = data?.logs || []
} catch (error) {
Toast(error.message || '查询日志失败')
}
},
async clearLogs() {
try {
const response = await fetch(`${API_BASE.replace(/\/$/, '')}/supplier-logs`, {
method: 'DELETE'
})
const payload = await response.json().catch(() => ({}))
if (!response.ok || payload.success === false) {
throw new Error(payload.error?.message || payload.msg || '清除日志失败')
}
this.logs = []
Toast('日志已清除')
} catch (error) {
Toast(error.message || '清除日志失败')
}
},
startListening() {
this.stopListening()
this.listening = true
this.timer = window.setInterval(() => {
this.loadLogs()
}, POLL_INTERVAL)
},
stopListening() {
if (this.timer) {
window.clearInterval(this.timer)
this.timer = null
}
this.listening = false
},
formatTime(time) {
if (!time) return '-'
const date = new Date(time)
if (Number.isNaN(date.getTime())) return time
const pad = value => String(value).padStart(2, '0')
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`
},
formatRequestLine(item) {
const content = item.content || {}
const method = content.method || 'POST'
const url = this.fullUrl(content.url || content.path || '-')
return `${method} ${url}`
},
fullUrl(url) {
if (!url || url === '-') return '-'
if (/^https?:\/\//i.test(url)) return url
const base = API_BASE.replace(/\/api\/?$/, '').replace(/\/$/, '')
const path = String(url).startsWith('/') ? url : `/${url}`
return `${base}${path}`
},
formatRequestParams(content = {}) {
const params = {
...(content.query || {}),
...(content.body || {})
}
const entries = Object.entries(params).filter(([, value]) => value !== undefined && value !== null && value !== '')
if (entries.length === 0) {
return '-'
}
return entries.map(([key, value]) => `${key}=${this.formatParamValue(value)}`).join('&')
},
formatParamValue(value) {
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
return value
}
return JSON.stringify(value)
}
}
}
</script>
<style scoped lang="less">
.supplier-logs-page {
height: 100vh;
padding: 56px 10px 18px;
overflow: hidden;
background: #f7f8fa;
}
.supplier-logs-page ::v-deep .van-nav-bar {
position: fixed;
z-index: 20;
top: 0;
right: 0;
left: 0;
}
.toolbar {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 10px;
margin-bottom: 10px;
}
.toolbar span {
position: relative;
padding-left: 14px;
color: #999;
font-size: 12px;
}
.toolbar span::before {
content: '';
position: absolute;
top: 50%;
left: 0;
width: 8px;
height: 8px;
border-radius: 50%;
background: #bbb;
transform: translateY(-50%);
}
.toolbar span.active {
color: #20b879;
}
.toolbar span.active::before {
background: #20b879;
}
.toolbar button {
height: 32px;
padding: 0 14px;
border: 1px solid #ff5b20;
border-radius: 6px;
color: #ff5b20;
font-size: 13px;
background: #fff7f2;
}
.console-panel {
height: calc(100vh - 116px);
overflow: auto;
padding: 12px;
border-radius: 8px;
background: #111;
}
.console-empty {
color: #7fda89;
font-size: 12px;
line-height: 20px;
}
.console-line + .console-line {
margin-top: 14px;
}
.console-line p {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin: 0;
color: #7fda89;
font-size: 12px;
line-height: 20px;
}
.console-line span {
color: #8aa0b4;
}
.console-line b {
color: #ffd166;
font-weight: 700;
}
.console-line.backend-receive b {
color: #7fda89;
}
.console-line.zhgh-send b {
color: #5cc8ff;
}
.console-line.zhgh-receive b {
color: #ff8a5c;
}
.console-line pre {
overflow-x: auto;
margin: 4px 0 0;
color: #e6edf3;
font-size: 12px;
line-height: 18px;
white-space: pre-wrap;
word-break: break-all;
}
</style>