数据结构优化
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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}[]>([])
|
||||
|
||||
@@ -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: {
|
||||
|
||||
Reference in New Issue
Block a user