Files
moc/src/views/home/modules/template1.vue
T
2026-10-19 10:11:16 +08:00

349 lines
18 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import { onMounted, onUnmounted, ref } from 'vue';
import { Icon } from '@iconify/vue'
import { useThemeStore } from '@/store/modules/theme';
import { useRouterPush } from '@/hooks/common/router';
import { getColorWithOpacity } from '@/utils/common';
import { homeAdminApi } from "@/service/api/statistics";
const themeStore = useThemeStore();
const { routerPushByKey } = useRouterPush();
const loading = ref<boolean>(false);
// 时间显示
const clock = ref<string>('');
let timer: any = null;
// 首页数据
const homeData = ref<any>(null);
// 跳转变更申请页面、变更台账、我的任务、发起变更
const jumpTo = (key: string,qid:number = 0) => {
if(key==='details_approval' && qid){
routerPushByKey(key,{ query: { id: qid.toString(),key:'home' } });
}else if(key==='details_index' && qid){
routerPushByKey(key,{ query: { id: qid.toString(),key:'home' } });
}else{
routerPushByKey(key as any);
}
}
// 获取当前时间字符串
const getCurrentTimeStr = () => {
const now = new Date();
const year = now.getFullYear();
const month = now.getMonth() + 1;
const date = now.getDate();
const week = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'][now.getDay()];
const hours = String(now.getHours()).padStart(2, '0');
const minutes = String(now.getMinutes()).padStart(2, '0');
return `${year}年${month}月${date}日 ${week} ${hours}:${minutes}`;
}
// 更新时间显示
const tick = () => {
clock.value = getCurrentTimeStr();
// 计算距离下一分钟还有多少毫秒(+50ms 容错,防止定时器提前触发)
const now = new Date();
const delay = (60 - now.getSeconds()) * 1000 - now.getMilliseconds() + 50;
timer = setTimeout(tick, delay);
}
// 获取数据
const getData = async () => {
loading.value = true;
const {data,error} = await homeAdminApi();
if(!error){
homeData.value = data;
}
loading.value = false;
}
onMounted(() => {
tick();
getData();
})
onUnmounted(() => {
if(timer){
clearTimeout(timer);
timer = null;
}
})
</script>
<template>
<NSpin :show="loading" size="small">
<div class="space-y-3.5">
<NCard
:bordered="false"
size="small"
class="shadow-[0_0_15px_rgba(0,0,0,0.1)]"
:style="{ borderRadius: themeStore.themeRadius + 'px' }">
<NGrid :x-gap="16" :y-gap="16" responsive="screen" item-responsive>
<NGi span="24 s:24 m:16">
<div class="flex-y-center">
<p
class="py-1.5 px-1.5 flex items-center justify-center bg-[rgb(var(--primary-color))] text-white text-xs font-bold"
:style="{ borderRadius: themeStore.themeRadius + 'px' }"
>
MOC
</p>
<p class="pl-12px text-14px font-semibold">我的工作台</p>
</div>
</NGi>
<NGi span="24 s:24 m:8" class="flex items-center justify-end gap-12px">
<p class="text-#999 text-xs">{{clock}}</p>
<NButton ghost size="small" @click="jumpTo('systemmanage_setting')">
系统设置
</NButton>
</NGi>
</NGrid>
</NCard>
<!-- 卡片数据 -->
<NGrid cols="s:1 m:2 l:2" responsive="screen" :x-gap="16" :y-gap="16">
<NGi>
<div
class="flex-1 bg-[#fff2f0] px-16px py-12px text-[#e15555] shadow-[0_0_15px_rgba(0,0,0,0.1)]"
:style="{ borderRadius: themeStore.themeRadius + 'px' }"
>
<p class="text-12px font-600">待关闭确认</p>
<div class="flex justify-between">
<CountTo
:start-value="0"
:end-value="homeData?.close_pending?.total || 0"
class="text-26px font-600"
/>
</div>
<p class="text-12px">验收完成 · 资料齐备 · 专责确认</p>
</div>
</NGi>
<NGi>
<div
class="flex-1 bg-white px-16px py-12px text-[#94a3b8] cursor-pointer shadow-[0_0_15px_rgba(0,0,0,0.1)]"
:style="{ borderRadius: themeStore.themeRadius + 'px' }"
@click="jumpTo('changeledger')"
>
<p class="text-12px font-600">在途变更</p>
<div class="flex justify-between">
<CountTo
:start-value="0"
:end-value="homeData?.open_changes?.total || 0"
class="text-26px font-600 text-[#333]"
/>
</div>
<p class="text-12px">全流程跟踪</p>
</div>
</NGi>
</NGrid>
<NGrid :x-gap="16" :y-gap="16" responsive="screen" item-responsive>
<!-- 左侧 -->
<NGi span="24 s:24 m:24 l:16">
<!-- 待关闭确认 -->
<NCard
:bordered="false"
size="small"
class="shadow-[0_0_15px_rgba(0,0,0,0.1)]"
:style="{ borderRadius: themeStore.themeRadius + 'px' }">
<div class="flex items-center justify-between mb-3">
<div class="flex items-center gap-2">
<Icon icon="ix:success" class="size-16px" :style="{ color: themeStore.themeColor }" />
<p class="text-13px font-700 text-#64749a">待关闭确认</p>
<p class="text-11px text-#94a3b8 mt-2px">变更主管部门(归口部门)专责确认 · 系统记录关闭时间</p>
</div>
</div>
<div class="flex flex-col gap-3">
<template v-if="homeData?.close_pending?.total > 0">
<div v-for="row in homeData?.close_pending?.items || []" :key="row.change_id"
class="flex w-full items-center gap-3 border px-3.5 py-2.5 text-left transition"
:style="{borderRadius: themeStore.themeRadius + 'px'}"
>
<div class="min-w-0 flex-1">
<div class="flex flex-wrap items-center gap-2">
<!-- 状态标签 -->
<NTag size="small" type="error">待关闭</NTag>
<p class="flex-1 truncate text-sm font-medium text-slate-800 dark:text-white">{{ row.title }}</p>
<span v-if="row.status==='CLOSED'" class="text-[11px]" :style="{color: themeStore.otherColor.success}">关闭时间 2026/9/28 14:29:57</span>
<NButton size="small" type="success" class="text-xs" @click="jumpTo('details_approval', row.change_id)">确认关闭</NButton>
<NButton size="small" ghost class="text-xs" @click="jumpTo('details_index', row.change_id)">打开变更单</NButton>
</div>
<div class="mt-0.5 truncate text-[11px] text-slate-400 flex items-center gap-2">
<span>{{ row.change_no }}</span>
<span>{{row.org_name}}</span>
<span>{{row.applicant_name}}</span>
</div>
</div>
</div>
</template>
<template v-else>
<div class="h-100% flex items-center justify-center py-3">
<NEmpty description="暂无数据" class="!text-xs"></NEmpty>
</div>
</template>
</div>
</NCard>
<!-- 在途变更跟踪 -->
<NCard
:bordered="false"
size="small"
class="mt-16px"
:style="{ borderRadius: themeStore.themeRadius + 'px' }">
<div class="flex items-center justify-between mb-3">
<div class="flex items-center gap-2">
<Icon icon="akar-icons:file" class="size-16px" :style="{ color: themeStore.themeColor }" />
<p class="text-13px font-700 text-#64749a">在途变更跟踪</p>
<p class="text-11px text-#94a3b8 mt-2px">全流程在途 · 点击打开变更单</p>
</div>
<NButton v-if="homeData?.open_changes?.total > 0" ghost size="small" class="text-xs" @click="jumpTo('changeledger')">
变更台账
<Icon icon="formkit:right" class="size-12px" />
</NButton>
</div>
<div class="flex flex-col gap-3">
<div class="flex items-center gap-2">
<NTag v-for="item in homeData?.open_changes?.items || []" :key="item.status" size="small" type="primary">{{item.label}}{{item.count}}</NTag>
</div>
<template v-if="homeData?.open_changes?.total > 0">
<div v-for="row in homeData?.open_changes?.rows || []" :key="row.change_id" @click="jumpTo('details_index', row.change_id)"
class="cursor-pointer flex w-full items-center gap-3 border px-3.5 py-2.5 text-left transition hover:border-[var(--hover-border)] hover:bg-[var(--hover-bg)] hover:bg-[#EAF0F9]/40"
:style="{
borderRadius: themeStore.themeRadius + 'px',
'--hover-border': themeStore.themeColor,
'--hover-bg': getColorWithOpacity(themeStore.themeColor, 0.05)
}"
>
<div class="min-w-0 flex-1">
<div class="flex flex-wrap items-center gap-2">
<!-- 状态标签 -->
<NTag v-if="row.status==='APPROVING'" size="small" type="warning">审批中</NTag>
<NTag v-if="row.status==='APPROVED'" size="small" type="success">已通过</NTag>
<NTag v-if="row.status==='IMPLEMENTED'" size="small" type="success">待验收</NTag>
<NTag v-if="row.status==='ACCEPTED'" size="small" type="error">待关闭</NTag>
<NTag v-if="row.status==='CLOSED'" size="small" type="success">已关闭</NTag>
<NTag v-if="row.status==='WITHDRAWN'" size="small" type="error">已撤回</NTag>
<NTag v-if="row.status==='REJECTED'" size="small" type="error">已驳回</NTag>
<span class="min-w-0 flex-1 truncate text-sm font-medium text-slate-800 dark:text-white">{{ row.title }}</span>
<NTag v-if="row.overdue_days" size="small" type="error">超期{{row.overdue_days}}天</NTag>
<NButton text><Icon icon="formkit:right" class="size-14px text-slate-400" /></NButton>
</div>
<div class="mt-1 truncate flex items-center gap-2 text-[11px] text-slate-400">
<span>{{ row.change_no }}</span>
<span>{{row.org_name}}</span>
<span>{{row.applicant_name}}</span>
<span v-if="row.pending_handlers">当前待办:{{row.pending_handlers}}</span>
</div>
</div>
</div>
</template>
<template v-else>
<div class="h-100% flex items-center justify-center py-3">
<NEmpty description="暂无数据" class="!text-xs"></NEmpty>
</div>
</template>
</div>
</NCard>
</NGi>
<!-- 右侧 -->
<NGi span="24 s:24 m:24 l:8">
<!-- 系统设置 -->
<NCard
:bordered="false"
size="small"
class="shadow-[0_0_15px_rgba(0,0,0,0.1)]"
:style="{ borderRadius: themeStore.themeRadius + 'px' }">
<div class="flex items-center flex-wrap gap-2">
<Icon icon="keyline-icons:sliders-horizontal" class="size-16px" :style="{ color: themeStore.themeColor }" />
<p class="text-13px font-700 text-#64749a">系统设置</p>
<p class="text-11px text-#94a3b8">点击进入对应设置项</p>
</div>
<div class="space-y-2.5 mt-3">
<div class="rounded-md border px-3.5 py-2.5 flex items-center cursor-pointer" @click="jumpTo('systemmanage_template')">
<div class="flex-1">
<div class="flex items-center gap-2">
<span class="min-w-0 flex-1 truncate text-sm font-medium text-slate-800">审批流程设置</span>
</div>
<div class="mt-1 text-[11px] text-slate-400">按等级路由 · 会签 · 最终批准</div>
</div>
<NButton text><Icon icon="formkit:right" class="size-14px text-slate-400" /></NButton>
</div>
<div class="rounded-md border px-3.5 py-2.5 flex items-center cursor-pointer" @click="jumpTo('systemmanage_setting')">
<div class="flex-1">
<div class="flex items-center gap-2">
<span class="min-w-0 flex-1 truncate text-sm font-medium text-slate-800">变更类型管理</span>
</div>
<div class="mt-1 text-[11px] text-slate-400">工艺 / 设备 / 管理 · 支持自定义</div>
</div>
<NButton text><Icon icon="formkit:right" class="size-14px text-slate-400" /></NButton>
</div>
<div class="rounded-md border px-3.5 py-2.5 flex items-center cursor-pointer" @click="jumpTo('systemmanage_setting')">
<div class="flex-1">
<div class="flex items-center gap-2">
<span class="min-w-0 flex-1 truncate text-sm font-medium text-slate-800">变更等级规则</span>
</div>
<div class="mt-1 text-[11px] text-slate-400">等级评分表 · 重要 / 一般判定</div>
</div>
<NButton text><Icon icon="formkit:right" class="size-14px text-slate-400" /></NButton>
</div>
<div class="rounded-md border px-3.5 py-2.5 flex items-center cursor-pointer" @click="jumpTo('systemmanage_user')">
<div class="flex-1">
<div class="flex items-center gap-2">
<span class="min-w-0 flex-1 truncate text-sm font-medium text-slate-800">账号设定</span>
</div>
<div class="mt-1 text-[11px] text-slate-400">用户账号 · 组织 · 启停用</div>
</div>
<NButton text><Icon icon="formkit:right" class="size-14px text-slate-400" /></NButton>
</div>
<div class="rounded-md border px-3.5 py-2.5 flex items-center cursor-pointer" @click="jumpTo('systemmanage_role')">
<div class="flex-1">
<div class="flex items-center gap-2">
<span class="min-w-0 flex-1 truncate text-sm font-medium text-slate-800">角色和权限分配</span>
</div>
<div class="mt-1 text-[11px] text-slate-400">角色 · 菜单 · 数据权限</div>
</div>
<NButton text><Icon icon="formkit:right" class="size-14px text-slate-400" /></NButton>
</div>
<div class="rounded-md border px-3.5 py-2.5 flex items-center cursor-pointer" @click="jumpTo('systemmanage_setting')">
<div class="flex-1">
<div class="flex items-center gap-2">
<span class="min-w-0 flex-1 truncate text-sm font-medium text-slate-800">通知提醒设置</span>
</div>
<div class="mt-1 text-[11px] text-slate-400">超期 · 临期 · 关闭提醒</div>
</div>
<NButton text><Icon icon="formkit:right" class="size-14px text-slate-400" /></NButton>
</div>
<div class="rounded-md border px-3.5 py-2.5 flex items-center cursor-pointer" @click="jumpTo('systemmanage_setting')">
<div class="flex-1">
<div class="flex items-center gap-2">
<span class="min-w-0 flex-1 truncate text-sm font-medium text-slate-800">AI 开关与维护</span>
</div>
<div class="mt-1 text-[11px] text-slate-400">AI 辅助开关 · 模型与提示词</div>
</div>
<NButton text><Icon icon="formkit:right" class="size-14px text-slate-400" /></NButton>
</div>
</div>
<div
:style="{
'--warning-bg': getColorWithOpacity(themeStore.otherColor.warning, 0.1),
'--warning-color': themeStore.otherColor.warning,
'--success-color': themeStore.otherColor.success,
'--success-bg': getColorWithOpacity(themeStore.otherColor.success, 0.1),
}"
>
<div v-if="homeData?.ai_assist_enabled" class="mt-3 flex items-center gap-2 rounded-lg bg-[var(--success-bg)] px-3 py-2 text-[11px] text-[var(--success-color)]">
<Icon icon="lucide:sparkles" class="size-12px" />
AI 辅助当前已启用 · 可在「AI 开关与维护」调整
</div>
<div v-else class="mt-3 flex items-center gap-2 rounded-lg bg-[var(--warning-bg)] px-3 py-2 text-[11px] text-[var(--warning-color)]">
<Icon icon="lucide:sparkles" class="size-12px" />
AI 辅助当前未启用 · 可在「AI 开关与维护」调整
</div>
</div>
</NCard>
</NGi>
</NGrid>
</div>
</NSpin>
</template>
<style scoped></style>