Skip to content

Commit 3c2f0b4

Browse files
author
yqz
committed
添加后台管理功能
1 parent b3b1754 commit 3c2f0b4

3 files changed

Lines changed: 163 additions & 11 deletions

File tree

frontend/src/views/ConsoleFeedbackView.vue

Lines changed: 156 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,37 @@
44
<div class="console-page-title">
55
<h1>问题反馈</h1>
66
<div style="color: var(--console-muted, var(--muted)); font-size: 13px; margin-top: 6px">
7-
展示所有待处理反馈(同一 IP 每天仅可提交一次)。
7+
展示全部反馈问题(当前为前端 Mock,后续接入后端接口)。
88
</div>
99
</div>
1010
<div>
11-
<button class="btn" style="padding: 8px 12px" @click="load">刷新</button>
11+
<div class="fb-toolbar">
12+
<input v-model.trim="keyword" class="fb-search" type="search" placeholder="搜索反馈内容/提交者" />
13+
<button class="btn" style="padding: 8px 12px" @click="load">刷新</button>
14+
</div>
1215
</div>
1316
</header>
1417

1518
<div class="console-card console-inner-card">
1619
<div v-if="loading" style="color: var(--console-muted, var(--muted))">加载中...</div>
17-
<div v-else-if="items.length === 0" style="color: var(--console-muted, var(--muted))">暂无待处理反馈</div>
20+
<div v-else-if="filteredItems.length === 0" style="color: var(--console-muted, var(--muted))">暂无反馈</div>
21+
22+
<div v-else>
23+
<div class="fb-filters">
24+
<button type="button" class="fb-tab" :class="{ active: statusFilter === 'ALL' }" @click="statusFilter = 'ALL'">
25+
全部
26+
</button>
27+
<button type="button" class="fb-tab" :class="{ active: statusFilter === 'PENDING' }" @click="statusFilter = 'PENDING'">
28+
待处理
29+
</button>
30+
<button type="button" class="fb-tab" :class="{ active: statusFilter === 'DONE' }" @click="statusFilter = 'DONE'">
31+
已处理
32+
</button>
33+
<div class="fb-count" aria-label="数量">{{ filteredItems.length }}</div>
34+
</div>
1835

19-
<div v-else class="fb-list">
20-
<div v-for="f in items" :key="f.id" class="fb-item">
36+
<div class="fb-list">
37+
<div v-for="f in filteredItems" :key="f.id" class="fb-item">
2138
<div style="display: flex; align-items: baseline; justify-content: space-between; gap: 12px">
2239
<div style="font-weight: 950; overflow: hidden; text-overflow: ellipsis; white-space: nowrap">
2340
{{ f.submitterName || '匿名' }}
@@ -28,23 +45,25 @@
2845
</div>
2946
<div style="margin-top: 10px; white-space: pre-wrap; line-height: 1.7">{{ f.content }}</div>
3047
<div style="margin-top: 10px; color: var(--console-muted, var(--muted)); font-size: 12px">
31-
提交日:{{ f.submitDay || '' }} · 状态:{{ f.status || '' }} · ID:{{ f.id }}
48+
提交日:{{ f.submitDay || '' }} · 状态:{{ statusLabel(f.status) }} · ID:{{ f.id }}
3249
</div>
3350
</div>
3451
</div>
52+
</div>
3553

3654
<div v-if="error" class="error" style="margin-top: 10px">{{ error }}</div>
3755
</div>
3856
</div>
3957
</template>
4058

4159
<script setup>
42-
import { onMounted, ref } from 'vue'
43-
import { fetchPendingFeedback } from '../api/feedback-admin'
60+
import { computed, onMounted, ref } from 'vue'
4461
4562
const loading = ref(false)
4663
const items = ref([])
4764
const error = ref('')
65+
const keyword = ref('')
66+
const statusFilter = ref('ALL')
4867
4968
function formatDateTime(v) {
5069
if (!v) return ''
@@ -53,12 +72,60 @@ function formatDateTime(v) {
5372
return new Date(t).toISOString().replace('T', ' ').slice(0, 19)
5473
}
5574
75+
function statusLabel(s) {
76+
if (s === 'PENDING') return '待处理'
77+
if (s === 'DONE') return '已处理'
78+
return s || ''
79+
}
80+
81+
const filteredItems = computed(() => {
82+
const k = keyword.value.trim().toLowerCase()
83+
const s = statusFilter.value
84+
return (items.value || []).filter((it) => {
85+
if (s !== 'ALL' && it.status !== s) return false
86+
if (!k) return true
87+
const a = (it.submitterName || '').toLowerCase()
88+
const b = (it.content || '').toLowerCase()
89+
return a.includes(k) || b.includes(k)
90+
})
91+
})
92+
93+
function buildMock() {
94+
const now = Date.now()
95+
const day = (d) => new Date(d).toISOString().slice(0, 10)
96+
return [
97+
{
98+
id: 10001,
99+
submitterName: '匿名',
100+
content: '后台附件上传如果没选文件,提示可以更明显一点。',
101+
createdAt: new Date(now - 1000 * 60 * 18).toISOString(),
102+
submitDay: day(now - 1000 * 60 * 18),
103+
status: 'PENDING'
104+
},
105+
{
106+
id: 10002,
107+
submitterName: '访客',
108+
content: '文章详情页目录在小屏下希望有折叠入口。',
109+
createdAt: new Date(now - 1000 * 60 * 60 * 9).toISOString(),
110+
submitDay: day(now - 1000 * 60 * 60 * 9),
111+
status: 'DONE'
112+
},
113+
{
114+
id: 10003,
115+
submitterName: '小明',
116+
content: '控制台登录页能否支持回车提交。',
117+
createdAt: new Date(now - 1000 * 60 * 60 * 26).toISOString(),
118+
submitDay: day(now - 1000 * 60 * 60 * 26),
119+
status: 'PENDING'
120+
}
121+
]
122+
}
123+
56124
async function load() {
57125
loading.value = true
58126
error.value = ''
59127
try {
60-
const resp = await fetchPendingFeedback(0, 100)
61-
items.value = resp?.items || []
128+
items.value = buildMock()
62129
} catch (e) {
63130
const traceId = e?.traceId ? `traceId ${e.traceId}` : ''
64131
const prefix = e?.code ? `错误码 ${e.code}` : e?.httpStatus ? `HTTP ${e.httpStatus}` : ''
@@ -73,6 +140,75 @@ onMounted(() => load())
73140
</script>
74141
75142
<style scoped>
143+
.fb-toolbar {
144+
display: inline-flex;
145+
align-items: center;
146+
gap: 10px;
147+
}
148+
149+
.fb-search {
150+
width: min(360px, 46vw);
151+
box-sizing: border-box;
152+
padding: 9px 12px;
153+
border-radius: 10px;
154+
border: 1px solid var(--console-border, var(--border));
155+
background: var(--console-bg, var(--surface));
156+
color: var(--console-text, var(--text));
157+
font-size: 13px;
158+
outline: none;
159+
}
160+
161+
.fb-search:focus {
162+
border-color: rgba(0, 0, 0, 0.18);
163+
box-shadow: 0 0 0 4px rgba(0, 0, 0, 0.06);
164+
}
165+
166+
:global([data-theme='dark']) .fb-search:focus {
167+
border-color: rgba(255, 255, 255, 0.18);
168+
box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.06);
169+
}
170+
171+
.fb-filters {
172+
display: flex;
173+
align-items: center;
174+
gap: 10px;
175+
margin-bottom: 12px;
176+
}
177+
178+
.fb-tab {
179+
border: 1px solid var(--console-border, var(--border));
180+
background: var(--console-bg, var(--surface));
181+
color: var(--console-muted, var(--muted));
182+
border-radius: 10px;
183+
padding: 8px 12px;
184+
cursor: pointer;
185+
font: inherit;
186+
font-size: 13px;
187+
font-weight: 650;
188+
}
189+
190+
.fb-tab.active {
191+
color: var(--console-text, var(--text));
192+
border-color: rgba(0, 0, 0, 0.12);
193+
background: rgba(0, 0, 0, 0.04);
194+
}
195+
196+
:global([data-theme='dark']) .fb-tab.active {
197+
border-color: rgba(255, 255, 255, 0.12);
198+
background: rgba(255, 255, 255, 0.06);
199+
}
200+
201+
.fb-count {
202+
margin-left: auto;
203+
font-size: 12px;
204+
font-weight: 750;
205+
color: var(--console-muted, var(--muted));
206+
padding: 6px 10px;
207+
border-radius: 999px;
208+
border: 1px solid var(--console-border, var(--border));
209+
background: transparent;
210+
}
211+
76212
.fb-list {
77213
display: flex;
78214
flex-direction: column;
@@ -85,5 +221,15 @@ onMounted(() => load())
85221
border-radius: 12px;
86222
background: var(--surface-soft);
87223
}
224+
225+
@media (max-width: 560px) {
226+
.fb-toolbar {
227+
width: 100%;
228+
justify-content: flex-end;
229+
}
230+
.fb-search {
231+
width: 52vw;
232+
}
233+
}
88234
</style>
89235

src/main/java/com/yqz/openblog/config/AuthSecurityProperties.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public static class Slider {
3131
/**
3232
* 是否要求登录/注册前先完成「滑到尽头」验证(Redis 记录一次性凭证)。
3333
*/
34-
private boolean enabled = true;
34+
private boolean enabled = false;
3535
/**
3636
* challenge / 通过标记有效时间(秒)。
3737
*/

src/main/resources/application-local.example.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ openblog:
1313
cache:
1414
# 已发布文章正文 Redis TTL(分钟),可按需覆盖
1515
article-published-ttl-minutes: 30
16+
auth-security:
17+
slider:
18+
enabled: false
19+
ttl-seconds: 300
20+
login-lockout:
21+
enabled: false
1622
jwt:
1723
secret: "REPLACE_WITH_LONG_RANDOM_AT_LEAST_32_CHARS"
1824
storage:

0 commit comments

Comments
 (0)