feat: 新增电工APP端与定时任务管理功能

- 新增电工APP独立鉴权中间件与登录注册接口
- 新增定时任务管理模块,支持任务配置、执行日志与手动触发
- 新增运维费定价与电工派单成本价目管理
- 新增电工资质证照管理与到期提醒定时任务
- 完善数据库实体与DTO校验,优化权限控制
This commit is contained in:
andyesfly committed 2026-10-02 16:08:00 +08:00
1 parent 1803ad679c
commit f4454ad014
57 files changed
+9388 -200

No files matched your search

+114
View File
@@ -233,12 +233,58 @@
</div>
</div>
</div>
<!-- 电工资质到期提醒 -->
<div class="remind-grid">
<div class="card">
<div class="card-head">
<div>
<div class="card-title-row">
<h3 class="card-title">电工资质到期提醒</h3>
<span class="card-tag">CERT ALERT</span>
</div>
<p class="card-sub">临期(30 天内)与已过期的电工资质证照清单,已过期优先展示</p>
</div>
<a class="card-link" @click="goWorkerRegistry">
查看全部
<ArrowRightOutlined />
</a>
</div>
<div class="table-scroll">
<table class="dw-table">
<thead>
<tr>
<th>电工姓名</th>
<th>证书类型</th>
<th>有效期至</th>
<th class="ta-r">到期状态</th>
</tr>
</thead>
<tbody>
<tr v-for="row in certReminders" :key="row.key">
<td class="strong">{{ row.workerName }}</td>
<td>{{ row.certType }}</td>
<td class="mono muted">{{ row.expireDate }}</td>
<td class="ta-r">
<span class="tag" :class="row.expired ? 'tag-danger' : 'tag-warn'">
{{ row.expired ? `已过期 ${Math.abs(row.days)} 天` : `剩余 ${row.days} 天` }}
</span>
</td>
</tr>
</tbody>
</table>
<div class="table-empty" v-if="!certReminders.length">暂无临期或已过期的电工资质</div>
</div>
</div>
</div>
</a-spin>
</div>
</template>
<script setup lang="ts">
import { ref, computed, onMounted, onUnmounted, nextTick, markRaw } from 'vue';
import { useRouter } from 'vue-router';
import {
Button,
Spin,
@@ -258,8 +304,11 @@ import {
ArrowRightOutlined,
} from '@ant-design/icons-vue';
import * as echarts from 'echarts';
import dayjs from 'dayjs';
import { dashboardApi, type DashboardStats } from '@/api/dashboard';
import { workerRegistryApi } from '@/api/workerRegistry';
const router = useRouter();
const AButton = Button;
const ASpin = Spin;
@@ -272,6 +321,63 @@ let donutChart: echarts.ECharts | null = null;
let refreshTimer: ReturnType<typeof setInterval> | null = null;
const lastUpdateTime = ref('--');
// ===== 电工资质到期提醒 =====
interface CertReminderRow {
key: string;
workerId: number;
workerName: string;
certType: string;
expireDate: string;
days: number;
expired: boolean;
}
const certReminders = ref<CertReminderRow[]>([]);
// 取临期与已过期各前 5 条电工,展开证书后按到期天数排序,已过期的排前面
async function loadCertReminders() {
try {
const [expiredRes, expiringRes] = await Promise.all([
workerRegistryApi.getList({ page: 1, pageSize: 5, certStatus: 'expired' }),
workerRegistryApi.getList({ page: 1, pageSize: 5, certStatus: 'expiring' }),
]);
const today = dayjs().startOf('day');
const buildRows = (list: any[], expiredFlag: boolean): CertReminderRow[] => {
const rows: CertReminderRow[] = [];
for (const worker of list || []) {
for (const cert of worker.certs || []) {
if (!cert.expireDate) continue;
const expire = String(cert.expireDate).slice(0, 10);
const days = dayjs(expire).startOf('day').diff(today, 'day');
// 已过期组取 < 0,临期组取 0 ~ 30 天
const inRange = expiredFlag ? days < 0 : days >= 0 && days <= 30;
if (!inRange) continue;
rows.push({
key: `${worker.id}-${cert.id}`,
workerId: Number(worker.id),
workerName: worker.workerName,
certType: cert.certType || '资质证书',
expireDate: expire,
days,
expired: expiredFlag,
});
}
}
rows.sort((a, b) => a.days - b.days);
return rows.slice(0, 5);
};
const expiredRows = buildRows(expiredRes.code === 200 ? expiredRes.data.list : [], true);
const expiringRows = buildRows(expiringRes.code === 200 ? expiringRes.data.list : [], false);
certReminders.value = [...expiredRows, ...expiringRows];
} catch (error) {
// 提醒数据加载失败不影响主看板
certReminders.value = [];
}
}
function goWorkerRegistry() {
router.push('/dispatch/worker');
}
const totalStatus = computed(() =>
(stats.value?.customerStatus || []).reduce((s, it) => s + it.value, 0)
);
@@ -396,6 +502,7 @@ async function loadStats() {
lastUpdateTime.value = new Date().toLocaleTimeString('zh-CN');
updateCharts();
}
await loadCertReminders();
} catch (error) {
console.error('加载仪表盘数据失败:', error);
message.error('加载数据失败,请稍后重试');
@@ -721,6 +828,12 @@ function disposeCharts() {
.card { } // col-7 / col-5
}
// 电工资质到期提醒
.remind-grid {
margin-top: 16px;
.card { }
}
.live-alerts { font-size: 11px; font-weight: 700; color: var(--dw-error); background: rgba(220,38,38,.1); padding: 2px 8px; border-radius: 6px; animation: dw-pulse 1.8s infinite; }
// ===== 表格 =====
@@ -760,6 +873,7 @@ function disposeCharts() {
&-primary { color: var(--dw-primary); background: rgba(0,84,205,.12); }
&-warn { color: var(--dw-warn); background: rgba(224,133,43,.14); }
&-tertiary { color: var(--dw-tertiary); background: rgba(0,104,95,.12); }
&-danger { color: var(--dw-error); background: rgba(220,38,38,.12); }
}
// ===== 看板 =====