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 || '匿名' }}
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
4562const loading = ref (false )
4663const items = ref ([])
4764const error = ref (' ' )
65+ const keyword = ref (' ' )
66+ const statusFilter = ref (' ALL' )
4867
4968function 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+
56124async 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
0 commit comments