Merge pull request 'feature/login-screen' (#5) from feature/login-screen into master

Reviewed-on: #5
This commit is contained in:
zyb 2025-07-03 00:43:45 +08:00
commit 0ceda90d0f
7 changed files with 113 additions and 6 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -1,7 +1,7 @@
import axios from 'axios'; import axios from 'axios';
import { logoutUser } from '../utils/jwt'; // logoutUser会处理清除存储和重定向 import { logoutUser } from '../utils/jwt'; // logoutUser会处理清除存储和重定向
const API_BASE_URL = 'https://zybdatasupport.online'; const API_BASE_URL = 'https://api.zybdatasupport.online';
const axiosInstance = axios.create({ const axiosInstance = axios.create({
baseURL: API_BASE_URL, baseURL: API_BASE_URL,

BIN
src/assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -209,6 +209,12 @@ const showCompetitionMenu = computed(() => showCompetition.value)
<router-view></router-view> <router-view></router-view>
</main> </main>
<footer class="footer"> <footer class="footer">
<div class="footer-top">
<div class="footer-brand">
<img src="../assets/logo.png" class="footer-logo">
<span class="footer-title">红色警戒3数据分析中心</span>
</div>
</div>
<div class="footer-bottom"> <div class="footer-bottom">
<p>Byz解忧杂货铺</p> <p>Byz解忧杂货铺</p>
<p class="beian"> <p class="beian">
@ -682,4 +688,39 @@ const showCompetitionMenu = computed(() => showCompetition.value)
margin: 5px 0; margin: 5px 0;
} }
} }
.footer-top {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 1rem;
}
.footer-brand {
display: flex;
align-items: center;
gap: 10px;
background: none;
padding: 0;
border-radius: 0;
box-shadow: none;
}
.footer-logo {
width: 40px;
height: 40px;
border-radius: 8px;
box-shadow: none;
object-fit: contain;
background: transparent;
}
.footer-title {
font-size: 1rem;
font-weight: 600;
color: #fff;
letter-spacing: 1px;
text-shadow: none;
}
</style> </style>

View File

@ -18,6 +18,7 @@
<th>请求内容</th> <th>请求内容</th>
<th>悬赏金额</th> <th>悬赏金额</th>
<th>需求创建时间</th> <th>需求创建时间</th>
<th>回复数量</th>
<th>回复</th> <th>回复</th>
</tr> </tr>
</thead> </thead>
@ -38,6 +39,11 @@
</span> </span>
</td> </td>
<td class="date">{{ formatDate(demand.date) }}</td> <td class="date">{{ formatDate(demand.date) }}</td>
<td class="reply-count">
<span :class="['tag', getReplyCount(demand) > 0 ? 'has-replies' : 'no-reward']">
{{ getReplyCount(demand) }}
</span>
</td>
<td> <td>
<button class="btn-common btn-gradient btn-reply" @click.stop="showReply(demand)">回复</button> <button class="btn-common btn-gradient btn-reply" @click.stop="showReply(demand)">回复</button>
</td> </td>
@ -321,6 +327,18 @@ const filteredDemands = computed(() => {
return demands.value.filter(demand => !demand.content?.startsWith('&DEL')) return demands.value.filter(demand => !demand.content?.startsWith('&DEL'))
}) })
const getReplyCount = (demand) => {
// replies
if (demand.replies && Array.isArray(demand.replies)) {
return demand.replies.length;
}
// sendcontent
if (demand.sendcontent && typeof demand.sendcontent === 'string' && demand.sendcontent.trim() !== '') {
return demand.sendcontent.split('|').filter(reply => reply.trim() !== '').length;
}
return 0;
}
// //
const formatDate = (dateString) => { const formatDate = (dateString) => {
if (!dateString || dateString === 'Test_date') return '日期未提供' if (!dateString || dateString === 'Test_date') return '日期未提供'
@ -718,7 +736,14 @@ const onRefresh = () => {
border: 1px solid #d9d9d9; border: 1px solid #d9d9d9;
} }
.has-replies {
background: #e3f0ff;
color: #1d4ed8;
border: 1px solid #b6d2ff;
}
.maps-table td.reward, .maps-table td.reward,
.maps-table td.reply-count,
.maps-table tr { .maps-table tr {
box-shadow: none !important; box-shadow: none !important;
filter: none !important; filter: none !important;
@ -810,7 +835,8 @@ const onRefresh = () => {
padding: 12px 8px; padding: 12px 8px;
} }
.maps-table td.reward { .maps-table td.reward,
.maps-table td.reply-count {
text-align: center; text-align: center;
vertical-align: middle; vertical-align: middle;
} }

View File

@ -23,7 +23,7 @@
</div> </div>
<div class="filters-row"> <div class="filters-row">
<div class="filters"> <div class="filters">
<div class="search-box"> <div class="search-box sticky-search-box">
<span class="search-label">搜索</span> <span class="search-label">搜索</span>
<input <input
type="text" type="text"
@ -332,6 +332,29 @@ const scrollToTop = () => {
window.scrollTo({ top: 0, behavior: 'smooth' }) window.scrollTo({ top: 0, behavior: 'smooth' })
} }
let searchBoxEl = null
const tabBarHeight = 56 // tab
const stickyStyles = {
position: 'fixed',
top: tabBarHeight + 'px',
left: '50%',
transform: 'translateX(-50%)',
zIndex: 2001,
width: '92vw',
maxWidth: '98vw',
boxShadow: '0 2px 12px rgba(37,99,235,0.13)'
}
function handleStickySearchBox() {
if (!searchBoxEl) return
if (window.innerWidth <= 700 && window.scrollY > tabBarHeight) {
Object.entries(stickyStyles).forEach(([k, v]) => (searchBoxEl.style[k] = v))
} else {
Object.keys(stickyStyles).forEach(k => (searchBoxEl.style[k] = ''))
}
}
onMounted(() => { onMounted(() => {
// //
const savedState = sessionStorage.getItem('maps_state') const savedState = sessionStorage.getItem('maps_state')
@ -360,9 +383,13 @@ onMounted(() => {
window.addEventListener('resize', () => { window.addEventListener('resize', () => {
isMobile.value = window.innerWidth <= 700 isMobile.value = window.innerWidth <= 700
}) })
searchBoxEl = document.querySelector('.sticky-search-box')
window.addEventListener('scroll', handleStickySearchBox)
window.addEventListener('resize', handleStickySearchBox)
}) })
onUnmounted(() => { onUnmounted(() => {
window.removeEventListener('scroll', handleScroll) window.removeEventListener('scroll', handleScroll)
window.removeEventListener('resize', handleStickySearchBox)
}) })
</script> </script>
@ -590,13 +617,26 @@ onUnmounted(() => {
width: 100%; width: 100%;
} }
.search-box { .search-box {
width: 100%; /* 移除 position: fixed 相关样式交由JS控制 */
margin-bottom: 0; /* position: fixed; */
/* top: 0; */
/* left: 50%; */
/* transform: translateX(-50%); */
/* z-index: 2001; */
/* width: 92vw; */
/* max-width: 98vw; */
/* box-shadow: 0 2px 12px rgba(37,99,235,0.13); */
} }
.search-input { .search-input {
width: 100%; width: 100%;
min-width: 0; min-width: 0;
font-size: 15px; font-size: 15px;
background: transparent;
border: none;
outline: none;
}
body {
padding-top: 70px;
} }
.filter-select { .filter-select {
width: 100%; width: 100%;

View File

@ -74,7 +74,7 @@
error: null, error: null,
currentPage: 1, currentPage: 1,
itemsPerPage: 100, itemsPerPage: 100,
apiBaseUrl: 'https://zybdatasupport.online', apiBaseUrl: 'https://api.zybdatasupport.online',
categoryList: [], categoryList: [],
selectedCategory: '全部', selectedCategory: '全部',
}; };