145 lines
6.5 KiB
Vue
145 lines
6.5 KiB
Vue
|
|
<script setup lang="ts">
|
|||
|
|
import { computed, onMounted, reactive, ref } from 'vue';
|
|||
|
|
import {
|
|||
|
|
platformAPI,
|
|||
|
|
type DashboardOverview,
|
|||
|
|
type IdnAccount,
|
|||
|
|
type OrgDeliveryPoint,
|
|||
|
|
type OrgGasStation,
|
|||
|
|
type OrgServicePerson,
|
|||
|
|
type SafEvent,
|
|||
|
|
} from './api/platform';
|
|||
|
|
|
|||
|
|
type Tab = 'dashboard' | 'station' | 'delivery' | 'person' | 'user' | 'safety';
|
|||
|
|
|
|||
|
|
const currentTab = ref<Tab>('dashboard');
|
|||
|
|
const loading = ref(false);
|
|||
|
|
const errorMessage = ref('');
|
|||
|
|
const overview = ref<DashboardOverview>({});
|
|||
|
|
const stations = ref<OrgGasStation[]>([]);
|
|||
|
|
const deliveryPoints = ref<OrgDeliveryPoint[]>([]);
|
|||
|
|
const servicePeople = ref<OrgServicePerson[]>([]);
|
|||
|
|
const users = ref<IdnAccount[]>([]);
|
|||
|
|
const safetyEvents = ref<SafEvent[]>([]);
|
|||
|
|
const form = reactive({ stationCode: '', name: '', principal: '', serviceArea: '' });
|
|||
|
|
|
|||
|
|
const tabs: Array<{ key: Tab; label: string }> = [
|
|||
|
|
{ key: 'dashboard', label: '运营概览' },
|
|||
|
|
{ key: 'station', label: '气站管理' },
|
|||
|
|
{ key: 'delivery', label: '配送点管理' },
|
|||
|
|
{ key: 'person', label: '服务人员' },
|
|||
|
|
{ key: 'user', label: '用户管理' },
|
|||
|
|
{ key: 'safety', label: '安全运营' },
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
const overviewCards = computed(() => [
|
|||
|
|
['启用气站', overview.value.gasStationCount ?? 0],
|
|||
|
|
['启用配送点', overview.value.deliveryPointCount ?? 0],
|
|||
|
|
['在岗服务人员', overview.value.servicePersonCount ?? 0],
|
|||
|
|
['服务用户', overview.value.userCount ?? 0],
|
|||
|
|
['待处理安全事件', overview.value.pendingSafetyCount ?? 0],
|
|||
|
|
]);
|
|||
|
|
|
|||
|
|
async function loadData() {
|
|||
|
|
loading.value = true;
|
|||
|
|
errorMessage.value = '';
|
|||
|
|
try {
|
|||
|
|
[overview.value, stations.value, deliveryPoints.value, servicePeople.value, users.value, safetyEvents.value] = await Promise.all([
|
|||
|
|
platformAPI.getDashboard(),
|
|||
|
|
platformAPI.listOrgGasStation(),
|
|||
|
|
platformAPI.listOrgDeliveryPoint(),
|
|||
|
|
platformAPI.listOrgServicePerson(),
|
|||
|
|
platformAPI.listIdnAccount(),
|
|||
|
|
platformAPI.listSafEvent(),
|
|||
|
|
]);
|
|||
|
|
} catch (error) {
|
|||
|
|
errorMessage.value = error instanceof Error ? error.message : '加载数据失败';
|
|||
|
|
} finally {
|
|||
|
|
loading.value = false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function createStation() {
|
|||
|
|
if (!form.stationCode || !form.name || !form.principal || !form.serviceArea) {
|
|||
|
|
errorMessage.value = '请完整填写气站资料';
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
loading.value = true;
|
|||
|
|
errorMessage.value = '';
|
|||
|
|
try {
|
|||
|
|
await platformAPI.createOrgGasStation({ ...form });
|
|||
|
|
Object.assign(form, { stationCode: '', name: '', principal: '', serviceArea: '' });
|
|||
|
|
await loadData();
|
|||
|
|
} catch (error) {
|
|||
|
|
errorMessage.value = error instanceof Error ? error.message : '创建气站失败';
|
|||
|
|
loading.value = false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
onMounted(loadData);
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<template>
|
|||
|
|
<main class="shell">
|
|||
|
|
<aside class="sidebar">
|
|||
|
|
<div class="brand">
|
|||
|
|
<span class="brand-mark">阀</span>
|
|||
|
|
<div><strong>可燃气体平台</strong><small>平台总后台</small></div>
|
|||
|
|
</div>
|
|||
|
|
<nav>
|
|||
|
|
<button v-for="tab in tabs" :key="tab.key" :class="{ active: currentTab === tab.key }" @click="currentTab = tab.key">
|
|||
|
|
{{ tab.label }}
|
|||
|
|
</button>
|
|||
|
|
</nav>
|
|||
|
|
<div class="mock-note">支付、地图、IoT 与消息服务均使用 Mock 边界。</div>
|
|||
|
|
</aside>
|
|||
|
|
|
|||
|
|
<section class="content">
|
|||
|
|
<header>
|
|||
|
|
<div><p class="eyebrow">M0 + M1</p><h1>{{ tabs.find((tab) => tab.key === currentTab)?.label }}</h1></div>
|
|||
|
|
<button class="secondary" :disabled="loading" @click="loadData">{{ loading ? '同步中…' : '刷新数据' }}</button>
|
|||
|
|
</header>
|
|||
|
|
<p v-if="errorMessage" class="error">{{ errorMessage }}</p>
|
|||
|
|
|
|||
|
|
<template v-if="currentTab === 'dashboard'">
|
|||
|
|
<div class="cards"><article v-for="([label, value]) in overviewCards" :key="label"><span>{{ label }}</span><strong>{{ value }}</strong></article></div>
|
|||
|
|
<article class="panel"><h2>安全处置提示</h2><p>高风险安全事件由安全运营中心统一升级、审计和派发。当前待处理事件:{{ overview.pendingSafetyCount ?? 0 }} 项。</p></article>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<template v-if="currentTab === 'station'">
|
|||
|
|
<article class="panel"><h2>创建气站</h2><form @submit.prevent="createStation"><input v-model.trim="form.stationCode" placeholder="站点编码,例如 GS-1002" /><input v-model.trim="form.name" placeholder="气站名称" /><input v-model.trim="form.principal" placeholder="负责人" /><input v-model.trim="form.serviceArea" placeholder="服务区域" /><button :disabled="loading" type="submit">提交审核</button></form></article>
|
|||
|
|
<DataTable :headers="['编码', '名称', '负责人', '服务区域', '状态']" :rows="stations.map((item) => [item.stationCode, item.name, item.principal, item.serviceArea, item.status])" />
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<template v-if="currentTab === 'delivery'">
|
|||
|
|
<DataTable :headers="['编码', '配送点', '归属气站', '服务区域', '状态']" :rows="deliveryPoints.map((item) => [item.deliveryCode, item.name, item.gasStationName, item.serviceArea, item.status])" />
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<template v-if="currentTab === 'person'">
|
|||
|
|
<DataTable :headers="['姓名', '手机号', '角色', '工作状态', '资质状态']" :rows="servicePeople.map((item) => [item.name, item.phoneMasked, item.roles, item.workStatus, item.credentialStatus])" />
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<template v-if="currentTab === 'user'">
|
|||
|
|
<DataTable :headers="['手机号', '账户类型', '状态', '服务区域']" :rows="users.map((item) => [item.phoneMasked, item.accountType, item.status, item.serviceArea])" />
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<template v-if="currentTab === 'safety'">
|
|||
|
|
<DataTable :headers="['事件编码', '等级', '事件说明', '状态', '创建时间']" :rows="safetyEvents.map((item) => [item.eventCode, `${item.level} 级`, item.title, item.status, new Date(item.createdAt).toLocaleString()])" />
|
|||
|
|
</template>
|
|||
|
|
</section>
|
|||
|
|
</main>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script lang="ts">
|
|||
|
|
import { defineComponent, type PropType } from 'vue';
|
|||
|
|
|
|||
|
|
export default defineComponent({
|
|||
|
|
components: {
|
|||
|
|
DataTable: defineComponent({
|
|||
|
|
props: { headers: { type: Array as PropType<string[]>, required: true }, rows: { type: Array as PropType<string[][]>, required: true } },
|
|||
|
|
template: `<article class="panel table-panel"><table><thead><tr><th v-for="header in headers" :key="header">{{ header }}</th></tr></thead><tbody><tr v-for="(row, index) in rows" :key="index"><td v-for="(cell, cellIndex) in row" :key="cellIndex">{{ cell }}</td></tr><tr v-if="rows.length === 0"><td :colspan="headers.length">暂无数据</td></tr></tbody></table></article>`,
|
|||
|
|
}),
|
|||
|
|
},
|
|||
|
|
});
|
|||
|
|
</script>
|