数据结构优化

This commit is contained in:
李琦
2026-01-20 15:23:37 +08:00
parent 946855bff2
commit cdbd3dc038
14 changed files with 835 additions and 198 deletions

View File

@@ -22,6 +22,7 @@
<tr>
<th>ID</th>
<th>操作人</th>
<th>操作描述</th>
<th>IP地址</th>
<th>归属地</th>
<th>路径</th>
@@ -35,6 +36,7 @@
<tr v-for="log in logs.list" :key="log.id">
<td>{{ log.id }}</td>
<td>{{ log.username }}</td>
<td class="action-description">{{ log.action || `${log.method} ${log.path}` }}</td>
<td class="font-mono text-xs">{{ log.ip }}</td>
<td>{{ log.region || 'Unknown' }}</td>
<td class="log-path">{{ log.path }}</td>
@@ -238,6 +240,15 @@ onMounted(() => {
white-space: nowrap;
}
.action-description {
color: #d4b383;
font-weight: 500;
max-width: 180px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.method-badge {
display: inline-block;
padding: 0.125rem 0.375rem;

View File

@@ -17,6 +17,15 @@
</button>
</div>
<div class="flex gap-3">
<button
v-if="isEditing"
type="button"
class="admin-btn-secondary"
@click="openAccessLogModal"
title="查看访问记录"
>
📊 访问记录
</button>
<button type="button" class="admin-btn-secondary" @click="handleCancel">
取消
</button>
@@ -159,6 +168,14 @@
</div>
</div>
</form>
<!-- Access Log Modal -->
<AccessLogModal
v-if="isEditing"
v-model:isOpen="isAccessLogModalOpen"
:postId="route.params.id ? parseInt(route.params.id as string) : null"
:postTitle="form.title || '文章'"
/>
</div>
</template>
@@ -172,6 +189,7 @@ import 'md-editor-v3/lib/style.css'
import { useToast } from '../../composables/useToast'
import { createPost, updatePost, fetchPost, fetchCategories, fetchColumns, fetchTags, Tag } from '../../services/api'
import CustomSelect from '../../components/CustomSelect.vue'
import AccessLogModal from '../../components/admin/AccessLogModal.vue'
const router = useRouter()
const route = useRoute()
@@ -188,6 +206,12 @@ const isSubmitting = ref(false)
const isEditing = computed(() => !!route.params.id)
const errors = reactive<Record<string, string>>({})
// Access log modal state
const isAccessLogModalOpen = ref(false)
const openAccessLogModal = () => {
isAccessLogModalOpen.value = true
}
// Data sources
const categories = ref<{value: number, label: string}[]>([])
const columns = ref<{value: number, label: string}[]>([])

View File

@@ -307,33 +307,50 @@ const allProvinces = [
'云南', '西藏', '陕西', '甘肃', '青海', '宁夏', '新疆', '台湾', '香港', '澳门'
]
// 所有省份完整名称列表(用于匹配地图数据)
const allProvincesFullNames = [
'北京市', '天津市', '河北省', '山西省', '内蒙古自治区', '辽宁省', '吉林省', '黑龙江省',
'上海市', '江苏省', '浙江省', '安徽省', '福建省', '江西省', '山东省', '河南省',
'湖北省', '湖南省', '广东省', '广西壮族自治区', '海南省', '重庆市', '四川省', '贵州省',
'云南省', '西藏自治区', '陕西省', '甘肃省', '青海省', '宁夏回族自治区', '新疆维吾尔自治区', '台湾省', '香港特别行政区', '澳门特别行政区'
]
// 将数据转换为地图需要的格式没有值的省份默认为0
const getMapData = () => {
// 创建省份数据映射
const dataMap = new Map<string, number>()
// 如果有数据,先填充实际数据
// 后端现在直接返回完整的省份名称(如"浙江省"、"北京市"等)
if (stats.value.userRegions && stats.value.userRegions.length > 0) {
stats.value.userRegions.forEach((item: any) => {
let provinceName = item.Region
// 标准化省份名称(移除"省"、"市"、"自治区"等后缀)
provinceName = provinceName.replace(/省|市|自治区|特别行政区|壮族自治区|维吾尔自治区|回族自治区/g, '')
// 后端返回的字段名是 Region 和 Count首字母大写
const provinceName = item.Region || item.region
const count = Number(item.Count || item.count || 0)
// 特殊处理
if (provinceName === '内蒙古') provinceName = '内蒙古'
if (provinceName === '广西') provinceName = '广西'
if (provinceName === '西藏') provinceName = '西藏'
if (provinceName === '宁夏') provinceName = '宁夏'
if (provinceName === '新疆') provinceName = '新疆'
if (!provinceName) {
console.warn('Missing province name in item:', item)
return
}
dataMap.set(provinceName, item.Count || 0)
// 直接使用后端返回的完整省份名称,不需要任何处理
dataMap.set(provinceName, count)
console.log(`Set province data: ${provinceName} = ${count}`)
})
}
// 为所有省份生成数据没有值的默认为0
return allProvinces.map(province => {
return [province, dataMap.get(province) || 0]
// ECharts map 类型需要对象数组格式: [{name: '浙江省', value: 1}]
const result = allProvincesFullNames.map(province => {
const count = dataMap.get(province) ?? 0
return {
name: province,
value: count
}
})
console.log('Map data result:', result)
return result
}
const regionOption = computed(() => {
@@ -352,7 +369,15 @@ const regionOption = computed(() => {
}
const mapData = getMapData()
const maxValue = mapData.length > 0 ? Math.max(...mapData.map((d: any) => d[1] || 0)) : 1
// 计算最大值,支持对象格式 {name, value} 和数组格式 [name, value]
const maxValue = mapData.length > 0 ? Math.max(...mapData.map((d: any) => {
if (typeof d === 'object' && 'value' in d) {
return d.value || 0
} else if (Array.isArray(d)) {
return d[1] || 0
}
return 0
})) : 1
return {
tooltip: {