118 lines
7.6 KiB
Vue
118 lines
7.6 KiB
Vue
<script setup lang="ts">
|
|
/**
|
|
* F006 供应商评级管理 - W16
|
|
*
|
|
* 沿用老 vue user/enterprise/enterprise-rating.vue 字段
|
|
*
|
|
* 端点: 4 URL 探测 (enterpriserating/page, /list, /search, /queryPage); 404 → FALLBACK seed
|
|
*/
|
|
import { ref, reactive, onMounted, computed } from 'vue'
|
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
import { httpGet } from '@/utils/request'
|
|
import PageHeader from '@/components/PageHeader/PageHeader.vue'
|
|
import SearchBar from '@/components/SearchBar/SearchBar.vue'
|
|
import DataTable from '@/components/DataTable/DataTable.vue'
|
|
|
|
interface RatingRow { id:string; enterpriseId:string; enterpriseName:string; supplierLevel:string; clientScore:number; performanceScore:number; awardScore:number; finalScore:number; deadline:string }
|
|
const ENDPOINTS = [
|
|
'/user/performancescorerecord/page',
|
|
'/user/performancescorerecord/list'
|
|
]
|
|
const FALLBACK: RatingRow[] = [
|
|
{ id:'R001', enterpriseId:'s20015', enterpriseName:'坚美建筑材料有限公司', supplierLevel:'A 战略级', clientScore:96, performanceScore:92, awardScore:5, finalScore:193, deadline:'2026-06-30 23:59:59' },
|
|
{ id:'R002', enterpriseId:'s20016', enterpriseName:'华建门窗厂', supplierLevel:'B 核心级', clientScore:88, performanceScore:85, awardScore:3, finalScore:176, deadline:'2026-06-30 23:59:59' },
|
|
{ id:'R003', enterpriseId:'s20017', enterpriseName:'融鑫管业', supplierLevel:'B 核心级', clientScore:84, performanceScore:88, awardScore:0, finalScore:172, deadline:'2026-06-30 23:59:59' },
|
|
{ id:'R004', enterpriseId:'s20018', enterpriseName:'东方五金机械', supplierLevel:'A 战略级', clientScore:92, performanceScore:90, awardScore:8, finalScore:190, deadline:'2026-06-30 23:59:59' },
|
|
{ id:'R005', enterpriseId:'s20019', enterpriseName:'美洁涂料', supplierLevel:'C 成长级', clientScore:78, performanceScore:76, awardScore:-2, finalScore:152, deadline:'2026-06-30 23:59:59' },
|
|
{ id:'R006', enterpriseId:'s20020', enterpriseName:'万家灯具', supplierLevel:'B 核心级', clientScore:86, performanceScore:82, awardScore:2, finalScore:170, deadline:'2026-06-30 23:59:59' },
|
|
{ id:'R007', enterpriseId:'s20021', enterpriseName:'顺达物流', supplierLevel:'D 入门级', clientScore:65, performanceScore:68, awardScore:0, finalScore:133, deadline:'2026-06-30 23:59:59' },
|
|
{ id:'R008', enterpriseId:'s20022', enterpriseName:'众成厨具', supplierLevel:'C 成长级', clientScore:75, performanceScore:72, awardScore:-3, finalScore:144, deadline:'2026-06-30 23:59:59' },
|
|
{ id:'R009', enterpriseId:'s20023', enterpriseName:'坚美建筑材料', supplierLevel:'A 战略级', clientScore:94, performanceScore:91, awardScore:6, finalScore:191, deadline:'2026-06-30 23:59:59' },
|
|
{ id:'R010', enterpriseId:'s20024', enterpriseName:'华建门窗厂', supplierLevel:'A 战略级', clientScore:90, performanceScore:89, awardScore:4, finalScore:183, deadline:'2026-06-30 23:59:59' },
|
|
{ id:'R011', enterpriseId:'s20025', enterpriseName:'融鑫管业', supplierLevel:'C 成长级', clientScore:74, performanceScore:78, awardScore:0, finalScore:152, deadline:'2026-06-30 23:59:59' },
|
|
{ id:'R012', enterpriseId:'s20026', enterpriseName:'美洁涂料', supplierLevel:'D 入门级', clientScore:62, performanceScore:65, awardScore:-1, finalScore:126, deadline:'2026-06-30 23:59:59' }
|
|
]
|
|
type Col = { type?: 'status'|'text'|'selection'; prop?: string; label: string; width?: number; slot?: string; fixed?: 'right'|true }
|
|
const columns: Col[] = [
|
|
{ type:'selection', label:'', width:50 },
|
|
{ prop:'id', label:'评级ID', width:90, fixed:true },
|
|
{ prop:'enterpriseName', label:'供应商', width:200 },
|
|
{ prop:'supplierLevel', label:'等级', width:120, slot:'level' },
|
|
{ prop:'clientScore', label:'客户评价', width:100 },
|
|
{ prop:'performanceScore', label:'履约评价', width:100 },
|
|
{ prop:'awardScore', label:'奖惩分', width:90, slot:'award' },
|
|
{ prop:'finalScore', label:'最终得分', width:100, slot:'final' },
|
|
{ prop:'deadline', label:'评级时间', width:170, slot:'deadline' },
|
|
{ prop:'actions', label:'操作', width:180, slot:'actions', fixed:'right' }
|
|
]
|
|
const STATUS_DEFAULTS = [
|
|
{ value:'', label:'全部' }
|
|
]
|
|
const list = ref<RatingRow[]>([]); const total = ref(0); const loading = ref(false); const page = ref(1); const limit = ref(10); const filterKw = ref(''); const statusTab = ref('')
|
|
const dataSource = ref<'api'|'mock'|'loading'>('loading'); const apiError = ref<string>('')
|
|
async function load() {
|
|
loading.value = true; dataSource.value = 'loading'
|
|
for (const ep of ENDPOINTS) {
|
|
try {
|
|
const data: any = await httpGet<any>(ep, { page: page.value, limit: limit.value })
|
|
const rows = (data?.list || []).map((it: any) => ({ ...it, raw: it }))
|
|
list.value = rows; total.value = Number(data?.total || rows.length); dataSource.value = 'api'; apiError.value = ''; loading.value = false; return
|
|
} catch {}
|
|
}
|
|
list.value = [...FALLBACK]; total.value = FALLBACK.length; dataSource.value = 'mock'; apiError.value = '后端 API 暂未实装, 已加载前端示例数据'; loading.value = false
|
|
}
|
|
const filtered = computed(() => {
|
|
const kw = filterKw.value.trim().toLowerCase(); let rs = list.value
|
|
if (kw) rs = rs.filter(r => String(r.enterpriseName).includes(kw))
|
|
return rs
|
|
})
|
|
const searchFields: Array<{ key: string; label: string; type: 'text' }> = [
|
|
{ key:'enterpriseName', label:'供应商', type:'text' }
|
|
]
|
|
const filters = reactive<Record<string, any>>({})
|
|
function onSearch() { filterKw.value = String(filters.enterpriseName || ''); page.value = 1 }
|
|
function onStatusTab(v: string) { statusTab.value = v; page.value = 1 }
|
|
function onView(row: RatingRow) { ElMessage.info(`详情 ${row.id}`) }
|
|
async function onEdit(row: RatingRow) {
|
|
ElMessage.success('已发起调整 (前端示例)')
|
|
}
|
|
function onExport() { ElMessage.success('已发起导出 (前端示例, 共 ' + filtered.value.length + ' 条)') }
|
|
onMounted(load)
|
|
</script>
|
|
|
|
<template>
|
|
<div class="rr-page">
|
|
<PageHeader title="供应商评级管理" sub="供应商基于多维度打分的最终评级" :data-source="dataSource" :api-error="apiError">
|
|
<el-button type="primary" @click="onExport">导出明细</el-button>
|
|
</PageHeader>
|
|
<el-tabs v-model="statusTab" @tab-change="onStatusTab" class="rr-tabs">
|
|
<el-tab-pane v-for="t in STATUS_DEFAULTS" :key="t.value" :name="t.value" :label="t.label" />
|
|
</el-tabs>
|
|
<SearchBar v-model:filters="filters" :fields="searchFields" @search="onSearch" />
|
|
<DataTable :columns="columns" :data="filtered"
|
|
:borderless="true" :loading="loading" :page="page" :limit="limit" :total="total" row-key="id"
|
|
@page-change="(p: number) => { page = p; load() }" @limit-change="(l: number) => { limit = l; page = 1; load() }">
|
|
<template #level="{ row }">
|
|
<el-tag :type="row.supplierLevel.startsWith('A') ? 'success' : row.supplierLevel.startsWith('B') ? 'primary' : row.supplierLevel.startsWith('C') ? 'warning' : 'info'" size="small">{{ row.supplierLevel }}</el-tag>
|
|
</template>
|
|
<template #award="{ row }">
|
|
<span :style="{ color: row.awardScore > 0 ? '#67c23a' : row.awardScore < 0 ? '#f56c6c' : '#999' }">
|
|
{{ row.awardScore > 0 ? '+' : '' }}{{ row.awardScore }}
|
|
</span>
|
|
</template>
|
|
<template #final="{ row }">
|
|
<span style="font-weight: 600;">{{ row.finalScore }}</span>
|
|
</template>
|
|
<template #deadline="{ row }">{{ row.deadline }}</template>
|
|
<template #actions="{ row }">
|
|
<el-button type="primary" link @click="onView(row)">详情</el-button>
|
|
<el-button type="success" link @click="onEdit(row)">调整分</el-button>
|
|
</template>
|
|
</DataTable>
|
|
</div>
|
|
</template>
|
|
<style scoped>
|
|
.rr-page { padding: 16px; }
|
|
.rr-tabs { margin-bottom: 12px; }
|
|
</style>
|