Files
moc/src/views/my/effect/index.vue
T

240 lines
11 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 { ref, onMounted, computed } from 'vue';
import { Icon } from '@iconify/vue'
import { useThemeStore } from '@/store/modules/theme';
import { useRouterPush } from '@/hooks/common/router';
import { tagTextColor, tagBgColor, tagBorderColor } from '@/utils/common';
import { localStg } from '@/utils/storage';
import { systemConfigApi } from '@/service/api/system';
import { effectListApi, exportEffectApi } from '@/service/api/pendingChange';
const themeStore = useThemeStore();
const { routerPushByKey } = useRouterPush();
const permission = computed(() => {
return localStg.get('userInfo')?.role_id || 1;
})
const loading = ref<boolean>(false);
const list = ref<any[]>([]);
// 总投入
const totalCost = ref<number>(0);
// 完全达到率
const fullRate = ref<number>(0);
// 标准列表
const standardList = ref<any[]>([]);
// 分页
const pagination = ref<{page: number, pageSize: number, itemCount: number}>({
page: 1,
pageSize: 20,
itemCount: 0,
})
// 变更类型列表
const typeList = ref<any[]>([]);
// 变更等级列表
const levelList = ref<any[]>([]);
// 导出文件
const exportFile = async () => {
const {data,error} = await exportEffectApi();
if(!error){
if (!data) {
return;
}
// 确保 res 为 Blob 类型(若 API 返回 ArrayBuffer 或原始数据,需适配)
let blob;
if (data instanceof Blob) {
blob = data;
} else {
// 假设返回的是二进制数据(如 ArrayBuffer),指定 MIME 类型
blob = new Blob([data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
}
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = '变更投入和效果.xlsx'; // 推荐使用 download 属性
document.body.appendChild(link);
link.click();
// 清理:移除链接并释放 URL 对象
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
}
}
// 查看
const openRow = (r: any) => {
routerPushByKey("details_index",{ query: { id: r?.change_id?.toString(),key:'my_effect' } });
};
// 分页改变
const pageChange = (page: number) => {
pagination.value.page = page;
getList();
}
// 匹配对象
const matchObj= (list:any[],val:any) => {
const item = list.find((item:any) => item.value == val);
return item || null;
}
// 获取列表数据
const getList = async () => {
loading.value = true;
const {data,error} = await effectListApi({
page_num:pagination.value.page,
page_size:pagination.value.pageSize,
});
if(!error){
list.value = data?.list || [];
pagination.value.itemCount = data?.total || 0;
totalCost.value = data?.summary?.total_cost || 0;
fullRate.value = data?.summary?.full_rate || 0;
standardList.value = data?.summary?.items || [];
}
loading.value = false;
}
// 获取配置
const getConfig = async () => {
loading.value = true;
const {data,error} = await systemConfigApi();
if(!error){
// 解析 变更类型设置
const changeTypeGroup = data.find((group:{group_key:string}) => group.group_key === 'change_type');
if (changeTypeGroup) {
typeList.value = typeList.value.concat(changeTypeGroup.items);
}
// 解析 变更等级设置
const changeLevelGroup = data.find((group:{group_key:string}) => group.group_key === 'change_level');
if (changeLevelGroup) {
levelList.value = levelList.value.concat(changeLevelGroup.items);
}
getList();
}
loading.value = false;
}
onMounted(() => {
getConfig();
})
</script>
<template>
<NSpace vertical :size="16">
<NSpin :show="loading" size="small">
<NCard :bordered="false" size="small" :style="{ borderRadius: themeStore.themeRadius + 'px' }">
<div
class="h-100% flex flex-wrap items-center gap-x-5 gap-y-2 border border-slate-200 px-3 py-2 mb-3"
:style="{
borderRadius: themeStore.themeRadius + 'px',
'--success-bg': themeStore.otherColor.success,
'--warning-bg': themeStore.otherColor.warning,
'--error-bg': themeStore.otherColor.error,
}"
>
<span class="text-sm font-bold text-slate-800">{{permission===7?'本车间':permission===8?'全厂':''}}已验收变更</span>
<span class="text-xs text-slate-500">共 <b class="text-[#1D4E9C]">{{ pagination.itemCount }}</b> 起</span>
<span class="text-xs text-slate-500">投入合计 <b class="text-[#1D4E9C]">{{totalCost}}</b> 万元</span>
<span class="text-xs text-slate-500">完全达到率 <b class="text-[var(--success-bg)]">{{fullRate}}%</b></span>
<span class="hidden h-4 w-px bg-slate-200 sm:block"></span>
<template v-for="item in standardList" :key="item.key">
<span class="text-xs text-slate-500">
<span v-if="item.key==='3'" class="mr-1 inline-block h-2 w-2 rounded-full align-middle bg-[var(--success-bg)]"></span>
<span v-if="item.key==='2'" class="mr-1 inline-block h-2 w-2 rounded-full align-middle bg-[var(--warning-bg)]"></span>
<span v-if="item.key==='1'" class="mr-1 inline-block h-2 w-2 rounded-full align-middle bg-[var(--error-bg)]"></span>
{{item.label}}<b class="text-slate-800">{{item.count}}</b> 起 · <b class="text-slate-800">{{item.fee}}</b> 万元
</span>
</template>
<NButton size="small" ghost class="ml-auto text-xs" @click="exportFile">
<Icon icon="material-symbols:download" class="size-16px" />导出
</NButton>
</div>
<div class="rounded-lg bg-white shadow-[0_2px_8px_rgba(0,0,0,0.06)]">
<div class="flex items-center gap-3 border-b bg-slate-50 px-4 py-2 text-[11px] font-semibold text-slate-500">
<span class="min-w-0 flex-1">变更名称 / 编号</span>
<span class="w-[92px] shrink-0">申请日期</span>
<span class="w-[92px] shrink-0">投用日期</span>
<span class="w-[92px] shrink-0">验收日期</span>
<span class="w-[80px] shrink-0 text-right">费用(万元)</span>
<span class="w-[100px] shrink-0 text-center">是否达到预期效果</span>
<span class="w-[64px] shrink-0 text-right">操作</span>
</div>
<div class="scroll">
<div class="divide-y h-100%">
<div v-for="r in list" :key="r.id" class="flex flex-wrap items-center gap-x-3 gap-y-1.5 px-4 py-3 transition hover:bg-[#EAF0F9]/40">
<span class="min-w-0 flex-1">
<span class="flex flex-wrap items-center gap-1.5">
<span class="truncate text-sm font-medium text-slate-800">{{ r.title }}</span>
<!-- 等级 -->
<NTag
size="small"
:color="{
color: tagBgColor(matchObj(levelList,r.change_level)?.color),
textColor: tagTextColor(matchObj(levelList,r.change_level)?.color),
borderColor: tagBorderColor(matchObj(levelList,r.change_level)?.color),
}"
>
{{ matchObj(levelList,r.change_level)?.label }}
</NTag>
<!-- 类型 -->
<NTag
size="small"
:color="{
color: tagBgColor(matchObj(typeList,r.change_type)?.color),
textColor: tagTextColor(matchObj(typeList,r.change_type)?.color),
borderColor: tagBorderColor(matchObj(typeList,r.change_type)?.color),
}"
>
{{ matchObj(typeList,r.change_type)?.label }}
</NTag>
<NTag v-if="r.duration_type===2" size="small" type="warning">临时</NTag>
<NTag v-if="r.duration_type===1" size="small" type="info">永久</NTag>
</span>
<span class="mt-0.5 block font-mono text-[11px] text-slate-400">{{ r.change_no }} · {{ r.applicant_name }} · 费用取验收时填报的「变更材料和实施费」</span>
</span>
<span class="w-[92px] shrink-0 text-xs text-slate-600">{{ r.apply_time?.slice(0,10) }}</span>
<span class="w-[92px] shrink-0 text-xs text-slate-600">{{ r.use_time?.slice(0,10) }}</span>
<span class="w-[92px] shrink-0 text-xs text-slate-600">{{ r.accept_time?.slice(0,10) }}</span>
<span class="w-[80px] shrink-0 text-right text-sm font-bold text-slate-800">{{ r.cost }}</span>
<span class="w-[100px] shrink-0 text-center">
<!-- 完全达到 -->
<NTag v-if="r.result_status===3" size="small" type="success">{{r.result_label}}</NTag>
<!-- 基本达到 -->
<NTag v-if="r.result_status===2" size="small" type="warning">{{r.result_label}}</NTag>
<!-- 未达到 -->
<NTag v-if="r.result_status===1" size="small" type="error">{{r.result_label}}</NTag>
</span>
<span class="w-[64px] shrink-0 text-right">
<NButton size="small" ghost class="text-xs" @click="openRow(r)">查看</NButton>
</span>
</div>
<div v-if="list.length === 0" class="h-100% flex items-center justify-center">
<NEmpty description="暂无数据"></NEmpty>
</div>
</div>
</div>
</div>
<div class="flex justify-end mt-3">
<NPagination
v-model:page="pagination.page"
:page-size="pagination.pageSize"
:item-count="pagination.itemCount"
:on-update:page="pageChange"
>
<template #prefix="{ itemCount }">
共 {{ itemCount }} 条
</template>
</NPagination>
</div>
</NCard>
</NSpin>
</NSpace>
</template>
<style scoped lang="scss">
.scroll {
height: calc(100vh - 279px);
overflow-y: auto;
}
</style>