2026-03-05 16:52:12 +08:00
|
|
|
|
<template>
|
2026-03-14 10:22:48 +08:00
|
|
|
|
<!-- 移动端布局 -->
|
|
|
|
|
|
<el-drawer v-if="isMobile" v-model="dialogVisible" :title="dialogTitle" direction="rtl" size="100%" :close-on-press-escape="true" :destroy-on-close="true" :append-to-body="true" class="mobile-form-drawer">
|
2026-03-05 16:52:12 +08:00
|
|
|
|
<div class="mobile-form-wrapper" v-loading="formLoading">
|
|
|
|
|
|
<el-form ref="formRef" :model="formData" :rules="formRules" label-position="top">
|
|
|
|
|
|
<div class="mobile-form-section">
|
|
|
|
|
|
<div class="mobile-form-section__title">基本信息</div>
|
2026-03-14 10:22:48 +08:00
|
|
|
|
<el-form-item label="供应商" prop="supplierName"><el-input v-model="formData.supplierName" disabled /></el-form-item>
|
|
|
|
|
|
<el-form-item label="订单单号" prop="orderNo"><el-input v-model="formData.orderNo" disabled /></el-form-item>
|
2026-03-05 16:52:12 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
<div class="mobile-form-section">
|
|
|
|
|
|
<div class="mobile-form-section__title">评分标准(满分10分)</div>
|
2026-03-14 10:22:48 +08:00
|
|
|
|
<el-form-item label="质量评分" prop="qualityScore"><div class="rating-container"><el-rate v-model="formData.qualityScore" :max="10" show-score score-template="{value}分" allow-half /><div class="rating-desc">产品质量、规格符合度、缺陷率等</div></div></el-form-item>
|
|
|
|
|
|
<el-form-item label="服务评分" prop="serviceScore"><div class="rating-container"><el-rate v-model="formData.serviceScore" :max="10" show-score score-template="{value}分" allow-half /><div class="rating-desc">售前售后服务、响应速度、专业程度等</div></div></el-form-item>
|
|
|
|
|
|
<el-form-item label="价格评分" prop="priceScore"><div class="rating-container"><el-rate v-model="formData.priceScore" :max="10" show-score score-template="{value}分" allow-half /><div class="rating-desc">价格合理性、性价比、优惠政策等</div></div></el-form-item>
|
|
|
|
|
|
<el-form-item label="交付评分" prop="deliveryScore"><div class="rating-container"><el-rate v-model="formData.deliveryScore" :max="10" show-score score-template="{value}分" allow-half /><div class="rating-desc">交付及时性、包装质量、物流配送等</div></div></el-form-item>
|
2026-03-05 16:52:12 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
<div class="mobile-form-section">
|
|
|
|
|
|
<div class="mobile-form-section__title">综合评分</div>
|
2026-03-14 10:22:48 +08:00
|
|
|
|
<div class="total-score"><span class="score-value">{{ totalScore.toFixed(1) }}分</span><span class="score-level">{{ getScoreLevel(totalScore) }}</span></div>
|
2026-03-05 16:52:12 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
<div class="mobile-form-section">
|
|
|
|
|
|
<div class="mobile-form-section__title">评价备注</div>
|
2026-03-14 10:22:48 +08:00
|
|
|
|
<el-form-item prop="remark"><el-input v-model="formData.remark" type="textarea" :rows="3" placeholder="请输入评价备注(可选)" maxlength="500" show-word-limit /></el-form-item>
|
2026-03-05 16:52:12 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</el-form>
|
2026-03-14 10:22:48 +08:00
|
|
|
|
<div class="mobile-form__footer">
|
|
|
|
|
|
<el-button @click="dialogVisible = false">取 消</el-button>
|
|
|
|
|
|
<el-button type="primary" @click="submitForm" :loading="formLoading">确 定</el-button>
|
|
|
|
|
|
</div>
|
2026-03-05 16:52:12 +08:00
|
|
|
|
</div>
|
2026-03-14 10:22:48 +08:00
|
|
|
|
</el-drawer>
|
2026-03-05 16:52:12 +08:00
|
|
|
|
|
2026-03-14 10:22:48 +08:00
|
|
|
|
<!-- PC端布局 -->
|
|
|
|
|
|
<Dialog v-else v-model="dialogVisible" :title="dialogTitle" width="600px">
|
|
|
|
|
|
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="100px" v-loading="formLoading">
|
|
|
|
|
|
<el-form-item label="供应商" prop="supplierName"><el-input v-model="formData.supplierName" disabled /></el-form-item>
|
|
|
|
|
|
<el-form-item label="订单单号" prop="orderNo"><el-input v-model="formData.orderNo" disabled /></el-form-item>
|
|
|
|
|
|
<el-divider content-position="left">评分标准(满分10分)</el-divider>
|
|
|
|
|
|
<el-form-item label="质量评分" prop="qualityScore"><div class="rating-container"><el-rate v-model="formData.qualityScore" :max="10" show-score score-template="{value}分" allow-half /><div class="rating-desc">产品质量、规格符合度、缺陷率等</div></div></el-form-item>
|
|
|
|
|
|
<el-form-item label="服务评分" prop="serviceScore"><div class="rating-container"><el-rate v-model="formData.serviceScore" :max="10" show-score score-template="{value}分" allow-half /><div class="rating-desc">售前售后服务、响应速度、专业程度等</div></div></el-form-item>
|
|
|
|
|
|
<el-form-item label="价格评分" prop="priceScore"><div class="rating-container"><el-rate v-model="formData.priceScore" :max="10" show-score score-template="{value}分" allow-half /><div class="rating-desc">价格合理性、性价比、优惠政策等</div></div></el-form-item>
|
|
|
|
|
|
<el-form-item label="交付评分" prop="deliveryScore"><div class="rating-container"><el-rate v-model="formData.deliveryScore" :max="10" show-score score-template="{value}分" allow-half /><div class="rating-desc">交付及时性、包装质量、物流配送等</div></div></el-form-item>
|
|
|
|
|
|
<el-form-item label="综合评分" prop="totalScore"><div class="total-score"><span class="score-value">{{ totalScore.toFixed(1) }}分</span><span class="score-level">{{ getScoreLevel(totalScore) }}</span></div></el-form-item>
|
|
|
|
|
|
<el-form-item label="评价备注" prop="remark"><el-input v-model="formData.remark" type="textarea" :rows="3" placeholder="请输入评价备注(可选)" maxlength="500" show-word-limit /></el-form-item>
|
|
|
|
|
|
</el-form>
|
2026-03-05 16:52:12 +08:00
|
|
|
|
<template #footer>
|
2026-03-14 10:22:48 +08:00
|
|
|
|
<el-button @click="dialogVisible = false">取消</el-button>
|
|
|
|
|
|
<el-button type="primary" @click="submitForm" :loading="formLoading">确定</el-button>
|
2026-03-05 16:52:12 +08:00
|
|
|
|
</template>
|
|
|
|
|
|
</Dialog>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
2026-03-14 10:22:48 +08:00
|
|
|
|
import { computed } from 'vue'
|
2026-03-05 16:52:12 +08:00
|
|
|
|
import { SupplierEvaluationApi, SupplierEvaluationVO } from '@/api/erp/purchase/supplierEvaluation'
|
2026-03-14 10:22:48 +08:00
|
|
|
|
import { useWindowSize } from '@vueuse/core'
|
|
|
|
|
|
|
|
|
|
|
|
const { width } = useWindowSize()
|
|
|
|
|
|
const isMobile = computed(() => width.value < 768)
|
2026-03-05 16:52:12 +08:00
|
|
|
|
|
|
|
|
|
|
interface SupplierEvaluationForm {
|
|
|
|
|
|
id?: number
|
|
|
|
|
|
supplierId: number
|
|
|
|
|
|
supplierName: string
|
|
|
|
|
|
purchaseOrderId: number
|
|
|
|
|
|
orderNo: string
|
|
|
|
|
|
qualityScore: number
|
|
|
|
|
|
serviceScore: number
|
|
|
|
|
|
priceScore: number
|
|
|
|
|
|
deliveryScore: number
|
|
|
|
|
|
remark: string
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const { t } = useI18n()
|
|
|
|
|
|
const message = useMessage()
|
|
|
|
|
|
|
|
|
|
|
|
const dialogVisible = ref(false)
|
|
|
|
|
|
const dialogTitle = ref('')
|
|
|
|
|
|
const formLoading = ref(false)
|
|
|
|
|
|
const formType = ref('')
|
|
|
|
|
|
const formRef = ref()
|
|
|
|
|
|
const formData = ref<SupplierEvaluationForm>({
|
|
|
|
|
|
supplierId: 0,
|
|
|
|
|
|
supplierName: '',
|
|
|
|
|
|
purchaseOrderId: 0,
|
|
|
|
|
|
orderNo: '',
|
|
|
|
|
|
qualityScore: 5,
|
|
|
|
|
|
serviceScore: 5,
|
|
|
|
|
|
priceScore: 5,
|
|
|
|
|
|
deliveryScore: 5,
|
|
|
|
|
|
remark: ''
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const formRules = reactive({
|
|
|
|
|
|
qualityScore: [{ required: true, message: '请评价质量分数', trigger: 'change' }],
|
|
|
|
|
|
serviceScore: [{ required: true, message: '请评价服务分数', trigger: 'change' }],
|
|
|
|
|
|
priceScore: [{ required: true, message: '请评价价格分数', trigger: 'change' }],
|
|
|
|
|
|
deliveryScore: [{ required: true, message: '请评价交付分数', trigger: 'change' }]
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 计算综合评分
|
|
|
|
|
|
const totalScore = computed(() => {
|
|
|
|
|
|
return (formData.value.qualityScore + formData.value.serviceScore +
|
|
|
|
|
|
formData.value.priceScore + formData.value.deliveryScore) / 4
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 获取评分等级
|
|
|
|
|
|
const getScoreLevel = (score: number) => {
|
|
|
|
|
|
if (score >= 9) return '优秀'
|
|
|
|
|
|
if (score >= 8) return '良好'
|
|
|
|
|
|
if (score >= 7) return '一般'
|
|
|
|
|
|
if (score >= 6) return '及格'
|
|
|
|
|
|
return '不及格'
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 打开弹窗
|
|
|
|
|
|
const open = async (type: string, id?: number) => {
|
|
|
|
|
|
dialogVisible.value = true
|
|
|
|
|
|
dialogTitle.value = type === 'create' ? '新增供应商评价' : '修改供应商评价'
|
|
|
|
|
|
formType.value = type
|
|
|
|
|
|
resetForm()
|
|
|
|
|
|
|
|
|
|
|
|
// 修改时,设置数据
|
|
|
|
|
|
if (id) {
|
|
|
|
|
|
formLoading.value = true
|
|
|
|
|
|
try {
|
|
|
|
|
|
const data = await SupplierEvaluationApi.getSupplierEvaluation(id)
|
|
|
|
|
|
formData.value = {
|
|
|
|
|
|
...data,
|
|
|
|
|
|
supplierName: data.supplierName || '',
|
|
|
|
|
|
orderNo: data.orderNo || ''
|
|
|
|
|
|
}
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
formLoading.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 重置表单
|
|
|
|
|
|
const resetForm = () => {
|
|
|
|
|
|
formData.value = {
|
|
|
|
|
|
supplierId: 0,
|
|
|
|
|
|
supplierName: '',
|
|
|
|
|
|
purchaseOrderId: 0,
|
|
|
|
|
|
orderNo: '',
|
|
|
|
|
|
qualityScore: 5,
|
|
|
|
|
|
serviceScore: 5,
|
|
|
|
|
|
priceScore: 5,
|
|
|
|
|
|
deliveryScore: 5,
|
|
|
|
|
|
remark: ''
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 提交表单
|
|
|
|
|
|
const submitForm = async () => {
|
|
|
|
|
|
if (!formRef.value) return
|
|
|
|
|
|
const valid = await formRef.value.validate()
|
|
|
|
|
|
if (!valid) return
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
formLoading.value = true
|
|
|
|
|
|
const data = {
|
|
|
|
|
|
...formData.value,
|
|
|
|
|
|
totalScore: totalScore.value
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (formType.value === 'create') {
|
|
|
|
|
|
await SupplierEvaluationApi.createSupplierEvaluation(data)
|
|
|
|
|
|
message.success('新增成功')
|
|
|
|
|
|
} else {
|
|
|
|
|
|
await SupplierEvaluationApi.updateSupplierEvaluation(data)
|
|
|
|
|
|
message.success('修改成功')
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
dialogVisible.value = false
|
|
|
|
|
|
emit('success')
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('操作失败', error)
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
formLoading.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 定义事件
|
|
|
|
|
|
const emit = defineEmits<{
|
|
|
|
|
|
success: []
|
|
|
|
|
|
}>()
|
|
|
|
|
|
|
|
|
|
|
|
// 暴露方法
|
|
|
|
|
|
defineExpose({ open })
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
|
.mobile-form-wrapper {
|
|
|
|
|
|
padding: 0 4px;
|
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
|
-webkit-overflow-scrolling: touch;
|
|
|
|
|
|
}
|
|
|
|
|
|
.mobile-form-section {
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
border-radius: 10px;
|
|
|
|
|
|
padding: 14px;
|
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
|
box-shadow: 0 1px 4px rgba(0,0,0,0.06);
|
|
|
|
|
|
&__title {
|
|
|
|
|
|
font-size: 15px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: #303133;
|
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
|
padding-bottom: 8px;
|
|
|
|
|
|
border-bottom: 1px solid #f0f0f0;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-03-14 10:22:48 +08:00
|
|
|
|
.mobile-form__footer {
|
|
|
|
|
|
position: sticky;
|
|
|
|
|
|
bottom: 0;
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
padding: 12px 16px;
|
|
|
|
|
|
padding-bottom: calc(12px + env(safe-area-inset-bottom));
|
|
|
|
|
|
border-top: 1px solid #eee;
|
2026-03-05 16:52:12 +08:00
|
|
|
|
display: flex;
|
2026-03-14 10:22:48 +08:00
|
|
|
|
justify-content: flex-end;
|
2026-03-05 16:52:12 +08:00
|
|
|
|
gap: 12px;
|
2026-03-14 10:22:48 +08:00
|
|
|
|
z-index: 10;
|
|
|
|
|
|
margin: 0 -4px -12px;
|
|
|
|
|
|
.el-button { flex: 1; height: 40px; font-size: 15px; }
|
2026-03-05 16:52:12 +08:00
|
|
|
|
}
|
|
|
|
|
|
.rating-container {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 8px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.rating-desc {
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
color: #909399;
|
|
|
|
|
|
}
|
|
|
|
|
|
.total-score {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 12px;
|
|
|
|
|
|
padding: 10px 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
.score-value {
|
|
|
|
|
|
font-size: 24px;
|
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
|
color: #409eff;
|
|
|
|
|
|
}
|
|
|
|
|
|
.score-level {
|
|
|
|
|
|
padding: 4px 12px;
|
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
background-color: #f0f9ff;
|
|
|
|
|
|
color: #409eff;
|
|
|
|
|
|
border: 1px solid #b3d8ff;
|
|
|
|
|
|
}
|
|
|
|
|
|
:deep(.el-rate) {
|
|
|
|
|
|
height: auto;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
}
|
|
|
|
|
|
:deep(.el-rate__text) {
|
|
|
|
|
|
color: #409eff;
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
}
|
|
|
|
|
|
:deep(.mobile-eval-form-dialog .el-dialog__body) {
|
|
|
|
|
|
padding: 12px;
|
|
|
|
|
|
background: #f5f5f5;
|
|
|
|
|
|
}
|
|
|
|
|
|
:deep(.el-form-item) {
|
|
|
|
|
|
margin-bottom: 14px;
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|