feat(m1): add customer detail aggregation view

This commit is contained in:
2026-05-25 01:28:36 +08:00
parent bfb8f23399
commit b5317d8f58
6 changed files with 109 additions and 7 deletions
@@ -38,6 +38,10 @@ export function deleteProject(id) {
return axios.delete(`/api/v1/projects/${id}`);
}
export function getCustomerSummary(id) {
return axios.get(`/api/v1/customers/${id}/summary`);
}
export function getProjectPhaseDictionary() {
return axios.get("/api/v1/dictionaries/PROJECT_PHASE");
}
@@ -14,6 +14,12 @@ const routes = [
component: () => import("../views/HomeView.vue"),
meta: { roles: ["SYS_ADMIN", "DEVELOPER", "OPS"] },
},
{
path: "customers/:id",
name: "customer-detail",
component: () => import("../views/CustomerDetailView.vue"),
meta: { roles: ["SYS_ADMIN", "DEVELOPER"], title: "客户详情" },
},
{
path: "customers",
name: "customers",
@@ -0,0 +1,60 @@
<script setup>
import { ref, onMounted } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useAuthStore } from '../stores/auth'
import { ElMessage } from 'element-plus'
import { apiErrorMessage } from '../utils/apiErrorMessage'
import { getCustomerSummary } from '../api/platform'
const auth = useAuthStore()
const route = useRoute()
const router = useRouter()
const loading = ref(false)
const summary = ref(null)
const customerId = route.params.id
onMounted(async () => {
auth.restoreAxiosAuth()
await loadSummary()
})
async function loadSummary() {
loading.value = true
try {
const { data } = await getCustomerSummary(customerId)
summary.value = data
} catch (e) {
ElMessage.error(apiErrorMessage(e, '加载客户详情失败'))
} finally {
loading.value = false
}
}
</script>
<template>
<div>
<el-button link type="primary" @click="router.push('/customers')"> 客户列表</el-button>
<h2>客户详情 #{{ customerId }}</h2>
<el-row :gutter="16" v-loading="loading" style="margin-top:16px">
<el-col :span="8">
<el-card shadow="never">
<template #header>关联项目</template>
<div style="font-size:2em;font-weight:bold;text-align:center">{{ summary?.projectCount ?? '—' }}</div>
</el-card>
</el-col>
<el-col :span="8">
<el-card shadow="never">
<template #header>在履约合同</template>
<div style="font-size:2em;font-weight:bold;text-align:center">{{ summary?.contractCount ?? '—' }}</div>
</el-card>
</el-col>
<el-col :span="8">
<el-card shadow="never">
<template #header>在途 SN</template>
<div style="font-size:2em;font-weight:bold;text-align:center">{{ summary?.snCount ?? '—' }}</div>
</el-card>
</el-col>
</el-row>
</div>
</template>
@@ -20,12 +20,13 @@
<el-table v-loading="loading" :data="rows" stripe style="width: 100%">
<el-table-column prop="name" label="客户名称" min-width="160" show-overflow-tooltip />
<el-table-column prop="creditCode" label="统一社会信用代码" min-width="200" show-overflow-tooltip />
<el-table-column label="操作" width="160" fixed="right">
<template #default="{ row }">
<el-button type="primary" link @click="openEdit(row)">编辑</el-button>
<el-button type="danger" link @click="onDelete(row)">删除</el-button>
</template>
</el-table-column>
<el-table-column label="操作" width="200" fixed="right">
<template #default="{ row }">
<el-button type="primary" link @click="goDetail(row.id)">详情</el-button>
<el-button type="primary" link @click="openEdit(row)">编辑</el-button>
<el-button type="danger" link @click="onDelete(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="pager">
@@ -73,11 +74,13 @@
<script setup>
import { ref, reactive, computed, onMounted } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import { useRouter } from "vue-router";
import { useAuthStore } from "../stores/auth";
import { listCustomers, createCustomer, updateCustomer, deleteCustomer } from "../api/platform";
import { apiErrorMessage } from "../utils/apiErrorMessage";
const auth = useAuthStore();
const router = useRouter();
const loading = ref(false);
const saving = ref(false);
@@ -115,6 +118,10 @@ function onSizeChange() {
load();
}
function goDetail(id) {
router.push(`/customers/${id}`);
}
async function load() {
loading.value = true;
try {