feat: 完善平台总后台模块

This commit is contained in:
2026-07-27 00:18:42 +08:00
parent 073eb89762
commit 642980960e
197 changed files with 22356 additions and 1060 deletions

View File

@@ -1,6 +1,6 @@
<script setup lang="ts">
import { computed, onMounted, reactive, ref } from 'vue';
import { platformAPI, type DashboardOverview, type IdnAccount, type OrgDeliveryPoint, type OrgGasStation, type OrgServicePerson, type Profile, type SafEvent } from './api/platform';
import { platformAPI, type DashboardOverview, type OrgDeliveryPoint, type OrgGasStation, type OrgServicePerson, type PlatfromAccount, type Profile, type SafEvent } from './api/platform';
type Tab = 'dashboard' | 'station' | 'delivery' | 'person' | 'user' | 'safety' | 'trade' | 'finance' | 'content' | 'track' | 'operation' | 'audit';
type CatalogItem = { title: string; description: string; operations: string[] };
@@ -13,12 +13,11 @@ const overview = ref<DashboardOverview>({});
const stations = ref<OrgGasStation[]>([]);
const deliveryPoints = ref<OrgDeliveryPoint[]>([]);
const servicePeople = ref<OrgServicePerson[]>([]);
const users = ref<IdnAccount[]>([]);
const users = ref<PlatfromAccount[]>([]);
const safetyEvents = ref<SafEvent[]>([]);
const profile = ref<Profile>();
const loginForm = reactive({ username: 'root', password: '' });
const stationForm = reactive({ stationCode: '', name: '', principal: '', serviceArea: '' });
const passwordForm = reactive({ currentPassword: '', newPassword: '', confirmPassword: '' });
const tabs: Array<{ key: Tab; label: string }> = [
{ key: 'dashboard', label: '运营总览' }, { key: 'station', label: '气站管理' }, { key: 'delivery', label: '配送点管理' },
@@ -49,7 +48,7 @@ async function loadData() {
try {
[profile.value, overview.value, stations.value, deliveryPoints.value, servicePeople.value, users.value, safetyEvents.value] = await Promise.all([
platformAPI.getProfile(), platformAPI.getDashboard(), platformAPI.listOrgGasStation(), platformAPI.listOrgDeliveryPoint(),
platformAPI.listOrgServicePerson(), platformAPI.listIdnAccount(), platformAPI.listSafEvent(),
platformAPI.listOrgServicePerson(), platformAPI.listPlatfromAccount(), platformAPI.listSafEvent(),
]);
} catch (error) {
errorMessage.value = error instanceof Error ? error.message : '加载数据失败';
@@ -75,14 +74,6 @@ async function createStation() {
finally { loading.value = false; }
}
async function changePassword() {
if (passwordForm.newPassword !== passwordForm.confirmPassword) { errorMessage.value = '两次输入的新密码不一致'; return; }
loading.value = true;
try { await platformAPI.changePassword(passwordForm.currentPassword, passwordForm.newPassword); Object.assign(passwordForm, { currentPassword: '', newPassword: '', confirmPassword: '' }); await loadData(); }
catch (error) { errorMessage.value = error instanceof Error ? error.message : '修改密码失败'; }
finally { loading.value = false; }
}
onMounted(loadData);
</script>
@@ -90,7 +81,7 @@ onMounted(loadData);
<main v-if="!loggedIn" class="login-page">
<form class="login-card" @submit.prevent="login">
<span class="brand-mark"></span><p class="eyebrow">HEQI PLATFORM</p><h1>可燃气体平台总后台</h1>
<p>使用平台管理员账号登录首次登录后必须修改初始密码</p>
<p>使用平台管理员账号登录</p>
<input v-model.trim="loginForm.username" autocomplete="username" placeholder="账号" required />
<input v-model="loginForm.password" type="password" autocomplete="current-password" placeholder="密码" required />
<p v-if="errorMessage" class="error">{{ errorMessage }}</p><button :disabled="loading" type="submit">{{ loading ? '登录中' : '登录' }}</button>
@@ -99,7 +90,7 @@ onMounted(loadData);
<main v-else class="shell">
<aside class="sidebar">
<div class="brand"><span class="brand-mark"></span><div><strong>可燃气体平台</strong><small>平台总后台</small></div></div>
<div class="brand"><img v-if="profile?.avatar" class="avatar" :src="profile.avatar" :alt="profile.displayName" /><span v-else 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="account"><strong>{{ profile?.displayName || '平台管理员' }}</strong><small>{{ profile?.roleCode || '加载中' }}</small><button class="link-button" @click="logout">退出登录</button></div>
</aside>
@@ -107,13 +98,11 @@ onMounted(loadData);
<section class="content">
<header><div><p class="eyebrow">PLATFORM ADMIN</p><h1>{{ activeLabel }}</h1></div><button class="secondary" :disabled="loading" @click="loadData">{{ loading ? '同步中' : '刷新数据' }}</button></header>
<p v-if="errorMessage" class="error">{{ errorMessage }}</p>
<article v-if="profile?.mustChangePassword" class="panel warning"><h2>首次登录安全设置</h2><p>root 初始密码仅可用于首次进入,请立即设置不少于 12 位的新密码。</p><form class="password-form" @submit.prevent="changePassword"><input v-model="passwordForm.currentPassword" type="password" placeholder="当前密码" required /><input v-model="passwordForm.newPassword" type="password" placeholder="新密码(至少 12 位)" required /><input v-model="passwordForm.confirmPassword" type="password" placeholder="确认新密码" required /><button :disabled="loading">修改密码</button></form></article>
<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>统一管理所有气站配送点服务人员和用户并对可燃气体业务的安全订单配送轨迹资金结算邀请二维码和高风险操作保留审计闭环</p></article></template>
<template v-if="currentTab === 'station'"><article class="panel"><h2>创建气站</h2><form @submit.prevent="createStation"><input v-model.trim="stationForm.stationCode" placeholder="气站编码,例如 GS-1002" /><input v-model.trim="stationForm.name" placeholder="气站名称" /><input v-model.trim="stationForm.principal" placeholder="负责人" /><input v-model.trim="stationForm.serviceArea" placeholder="服务区域" /><button :disabled="loading">提交审核</button></form><p>气站可继续管理其配送点、服务人员、用户及专属邀请注册二维码。</p></article><DataTable :headers="['编码', '名称', '负责人', '服务区域', '状态']" :rows="stations.map((item) => [item.stationCode, item.name, item.principal, item.serviceArea, item.status])" /></template>
<template v-if="currentTab === 'delivery'"><article class="panel"><h2>配送点管理边界</h2><p>配送点归属气站,负责管理服务人员、用户、配送班次、订单交付与配送轨迹;平台可跨组织查看、审核、冻结和迁移。</p></article><DataTable :headers="['编码', '配送点', '归属气站', '服务区域', '状态']" :rows="deliveryPoints.map((item) => [item.deliveryCode, item.name, item.gasStationName, item.serviceArea, item.status])" /></template>
<template v-if="currentTab === 'person'"><article class="panel"><h2>服务人员生命周期</h2><p>覆盖安装维修、安检、配送角色、资质、登录设备、接单状态、任务绩效及停用留档。</p></article><DataTable :headers="['姓名', '手机号', '角色', '工作状态', '资质状态']" :rows="servicePeople.map((item) => [item.name, item.phoneMasked, item.roles, item.workStatus, item.credentialStatus])" /></template>
<template v-if="currentTab === 'user'"><article class="panel"><h2>用户 360° 管理</h2><p>统一查看户资料、地址、智能瓶阀、订单、权益、投诉与风险处置;敏感信息按最小化原则展示。</p></article><DataTable :headers="['手机号', '账户类型', '状态', '服务区域']" :rows="users.map((item) => [item.phoneMasked, item.accountType, item.status, item.serviceArea])" /></template>
<template v-if="currentTab === 'user'"><article class="panel"><h2>平台账户管理</h2><p>统一查看平台账户资料、角色、头像、手机号、状态与操作记录;敏感信息按最小化原则展示。</p></article><DataTable :headers="['账号', '名称', '头像', '手机号', '角色', '状态']" :rows="users.map((item) => [item.username, item.displayName, item.avatar || '未设置', item.phoneMasked, item.roleCode, item.status])" /></template>
<template v-if="currentTab === 'safety'"><article class="panel"><h2>安全运营中心</h2><p>对智能瓶阀告警、安检异常、可燃气体风险、资质临期与订单异常进行分级、派发、升级和复盘。</p></article><DataTable :headers="['事件编码', '等级', '事件说明', '状态', '创建时间']" :rows="safetyEvents.map((item) => [item.eventCode, `${item.level} 级`, item.title, item.status, new Date(item.createdAt).toLocaleString()])" /></template>
<template v-if="catalog[currentTab]"><div class="catalog"><article v-for="item in catalog[currentTab]" :key="item.title" class="panel"><h2>{{ item.title }}</h2><p>{{ item.description }}</p><div class="tags"><span v-for="operation in item.operations" :key="operation">{{ operation }}</span></div></article></div></template>
</section>