宁波市生活垃圾分类志愿服务项目交流打分

This commit is contained in:
路 范
2025-10-17 13:26:24 +08:00
parent 0ad4e85834
commit 1eb4099af8
6 changed files with 372 additions and 4 deletions

View File

@@ -63,6 +63,6 @@
"GetHouseInfoCitysByCommunity": "http://sjk.test.ky.com/housesafety/statistics/GetHouseInfoCitysByCommunity?communityID={0}"
},
"UserScore": {
"ExpertNumber": 10
"ExpertNumber": 7
}
}

View File

@@ -0,0 +1,360 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0, minimum-scale=1.0,
maximum-scale=1.0, user-scalable=no"/>
<link rel="stylesheet" href="../../lib/element-ui/theme-chalk/index.css">
<script src="../../lib/vue2/vue.min.js"></script>
<script src="../../lib/element-ui/theme-chalk/index.js"></script>
<script src="../../lib/axios/axios.min.js"></script>
<script src="../../lib/qs/qs.min.js"></script>
</head>
<body>
<div id="app" v-loading="loading">
<h3 style="text-align:center;">宁波市生活垃圾分类志愿服务项目交流打分</h3>
<el-table v-loading="loading" :data="detailList" border>
<el-table-column label="路演顺序" align="center" width="100">
<template slot-scope="scope">
<span>第{{scope.row.no}}组</span>
</template>
</el-table-column>
<el-table-column label="项目名称" align="center">
<template slot-scope="scope">
<span>{{scope.row.postion}}</span>
</template>
</el-table-column>
<el-table-column label="区域" align="center" width="120">
<template slot-scope="scope">
<span>{{scope.row.name}}</span>
</template>
</el-table-column>
<el-table-column label="类型" align="center" width="120">
<template slot-scope="scope">
<span>{{scope.row.title}}</span>
</template>
</el-table-column>
<el-table-column width="100"
v-for="column in dynamicColumns"
:key="column.prop"
:prop="column.prop"
:label="column.label"
>
<template #default="scope">
<el-input
v-model="scope.row[column.prop]"
@blur="handleBlur(scope.row, column.prop)"
:style="getScoreStyle(scope.row, column.prop)"
class="score-input"
/>
</template>
</el-table-column>
<el-table-column label="最终得分" align="center" width="100">
<template slot-scope="scope">
<span>{{scope.row.finalscore || '0'}}</span>
</template>
</el-table-column>
<el-table-column label="排名" align="center" width="80" :cell-class-name="rankCellClass">
<template slot-scope="scope">
<span>{{ getRank(scope.row) }}</span>
</template>
</el-table-column>
<!-- <el-table-column label="操作" align="center" class-name="small-padding fixed-width">-->
<!-- <template slot-scope="scope">-->
<!-- <el-button-->
<!-- size="mini"-->
<!-- type="text"-->
<!-- icon="el-icon-edit"-->
<!-- @click="handleUpdate(scope.row)"-->
<!-- v-hasPermi="['detail:bill:edit']"-->
<!-- >修改-->
<!-- </el-button>-->
<!-- <el-button-->
<!-- size="mini"-->
<!-- type="text"-->
<!-- icon="el-icon-delete"-->
<!-- @click="handleDelete(scope.row)"-->
<!-- v-hasPermi="['detail:bill:remove']"-->
<!-- >删除-->
<!-- </el-button>-->
<!-- </template>-->
<!-- </el-table-column>-->
</el-table>
<!-- <h3 style="text-align:center;">-->
<!-- <el-input v-model="logincode" placeholder="请输入提交码"></el-input>-->
<!-- </h3>-->
<h3 style="text-align:center;">
<!-- <el-button @click="alltrue">重置</el-button>-->
<el-button type="primary" @click="submit">提交</el-button>
<el-button type="" @click="sortdefault">默认排序</el-button>
<el-button type="" @click="sortscore">按得分排序</el-button>
</h3>
</div>
</body>
<script>
new Vue({
el: '#app',
data: function () {
return {
detailList: [],
dynamicColumns: [],
confirm_title: '',
loading: false,
logincode: '',
order:''
}
},
created: function () {
this.loading = true;
this.load_projects();
},
methods: {
rankCellClass({ row }) {
const rank = this.getRank(row);
if (rank === 1) return 'rank-1';
if (rank === 2) return 'rank-2';
if (rank === 3) return 'rank-3';
return '';
},
getRank(row) {
// 创建分数副本并排序
const sorted = [...this.detailList]
.sort((a, b) => (b.finalscore || 0) - (a.finalscore || 0))
.map(item => item.finalscore || 0);
// 处理相同分数排名90,90,85 显示为1,1,3
const score = parseFloat(row.finalscore) || 0;
const firstIndex = sorted.findIndex(s => s === score);
return firstIndex >= 0 ? firstIndex + 1 : '-';
},
// 新增方法:获取最高分和最低分
getMinMaxScores(row) {
const scores = this.dynamicColumns
.map(expert => {
const val = row[expert.prop];
// 处理空字符串和无效值
return val === '' ? 0 : parseFloat(val) || 0;
})
.filter(score => score > 0);
if (scores.length < 1) return {min: 0, max: 0};
const sorted = [...scores].sort((a, b) => a - b);
return {
min: sorted[0],
max: sorted[sorted.length - 1]
};
},
// 新增方法:计算最终得分
calculateFinalScore(row) {
const scores = this.dynamicColumns
.map(expert => parseFloat(row[expert.prop]) || 0)
.filter(score => score > 0);
// if (scores.length <= 2) return '0';
if (scores.length < 3) return '0';
const sorted = [...scores].sort((a, b) => a - b);
sorted.pop(); // 去掉最高分
sorted.shift(); // 去掉最低分
const avg = sorted.reduce((a, b) => a + b, 0) / sorted.length;
return avg.toFixed(2);
},
// 新增方法:设置分数颜色
getScoreStyle(row, prop) {
const score = parseFloat(row[prop]) || 0;
const {min, max} = this.getMinMaxScores(row);
// 精确到小数点后两位比较
const current = Number(score.toFixed(2));
const minVal = Number(min.toFixed(2));
const maxVal = Number(max.toFixed(2));
if (current === maxVal && current > 0) {
return row[prop]?.toString() === maxVal?.toString() ?
{'--bg-color': '#ffd8d8', '--text-color': '#ff0000'} : {};
}
if (current === minVal && current > 0) {
return row[prop]?.toString() === minVal?.toString() ?
{'--bg-color': '#d8e3ff', '--text-color': '#0000ff'} : {};
}
return {};
},
handleBlur(row, prop) {
const score = parseFloat(row[prop]);
if (isNaN(score) || score < 0 || score > 100) {
this.$message.error('请输入0-100之间的数字');
row[prop] = ''; // 清除非法输入
} else {
// 实时更新最终得分
row.finalscore = this.calculateFinalScore(row);
}
// 强制更新视图
this.$set(row, prop, row[prop]);
},
alltrue() {
this.projects.map(a => {
a.data.map(b => {
b.vote = "true";
return b;
})
});
},
async submit() {
let _this = this;
this.$confirm('确定保存吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
_this.loading = true;
// 构建提交数据结构
const submitData = _this.detailList.map(user => ({
userId: user.id,
scores: _this.dynamicColumns.map(expert => ({
expertId: expert.prop,
score: parseFloat(user[expert.prop]) || ''
})),
finalscore: user.finalscore // 携带最终得分
}));
axios({
headers: {'Content-Type': 'application/json;charset=UTF-8'},
method: 'post',
url: '/gb/yjb/api/userscore20250801/submit',
data: submitData,
responseType: "json",
}).then(async response => {
if (response.data.data != true) {
alert(response.data.message);
} else {
alert("保存成功");
}
_this.loading = false;
}).catch(async error => {
console.log(error)
_this.$message({
type: 'error',
message: error.message
})
_this.loading = false;
})
}).catch(() => {
});
},
sortdefault(){
let _this = this;
_this.order="";
_this.load_projects();
},
sortscore(){
let _this = this;
_this.order="finalscore desc,No";
_this.load_projects();
},
load_projects() {
let _this = this;
Promise.all([
axios.post('/gb/yjb/api/userscore20250801/list',{order:_this.order}),
axios.post('/gb/yjb/api/userscore20250801/Experts')
]).then(([usersRes, expertsRes]) => {
_this.detailList = usersRes.data.data.map(user => {
// 解析历史分数
const scores = JSON.parse(user.scoresjson || '[]');
const scoreMap = Object.fromEntries(
scores.map(s => [s.expertId, s.score])
);
return {
...user,
// 初始化动态列字段(保留历史值)
...Object.fromEntries(expertsRes.data.data.map(expert => [
expert.prop,
scoreMap[expert.prop] || ''
]))
};
// ...user,
// // 初始化动态列字段
// ...Object.fromEntries(expertsRes.data.data.map(expert =>
// [expert.prop, user[expert.prop] || ''])
// )
});
_this.dynamicColumns = expertsRes.data.data;
}).finally(() => {
_this.loading = false;
});
// let _this = this;
// axios({
// headers: {'Content-Type': 'application/json;charset=UTF-8'},
// method: 'post',
// url: '/gb/yjb/api/userscore20250801/list',
// data: {},
// responseType: "json",
// }).then(function (response) {
// console.log(response);
// let _data = response.data.data
// _this.detailList = _data;
// _this.loading = false;
// }).catch(function (error) {
// console.log(error)
// _this.$message({
// type: 'error',
// message: error.message
// })
// _this.loading = false;
// })
// axios({
// headers: {'Content-Type': 'application/json;charset=UTF-8'},
// method: 'post',
// url: '/gb/yjb/api/userscore20250801/Experts',
// data: {},
// responseType: "json",
// }).then(function (response) {
// console.log(response);
// let _data = response.data.data
// _this.dynamicColumns = _data;
// _this.loading = false;
// }).catch(function (error) {
// console.log(error)
// _this.$message({
// type: 'error',
// message: error.message
// })
// _this.loading = false;
// })
},
loading_false() {
this.loading = false
},
}
})
</script>
<style scoped>
.score-input > .el-input__inner {
background-color: var(--bg-color, inherit) !important;
color: var(--text-color, inherit) !important;
/*text-align: center;*/
font-weight: bold;
}
/* 新增排名样式 */
.el-table /deep/ .rank-1 {
background-color: #e1f3d8 !important;
font-weight: bold;
text-align: center;
}
.el-table /deep/ .rank-2 {
background-color: #f0f9eb !important;
text-align: center;
}
.el-table /deep/ .rank-3 {
background-color: #fdf6ec !important;
text-align: center;
}
</style>
</html>

View File

@@ -42,10 +42,12 @@ namespace Vote.Services.ApiController
/// <returns></returns>
[HttpPost]
[Microsoft.AspNetCore.Authorization.AllowAnonymous]
public async Task<dynamic> List()
public async Task<dynamic> List(ListInput args)
{
args ??= new ListInput();
var data = await repuserscore.AsQueryable()
.OrderBy(a => a.No)
.OrderByIF(!string.IsNullOrWhiteSpace(args.order), args.order)
.OrderByIF(string.IsNullOrWhiteSpace(args.order), a => a.No)
.ToListAsync();
// var data = await repuserscore20250801.DetachedEntities
// .OrderBy(a => a.No)

View File

@@ -24,3 +24,8 @@ public class UserScoreInput
public string expertId { get; set; }
public decimal? score { get; set; }
}
public class ListInput
{
public string order { get; set; }
}

View File

@@ -15,6 +15,7 @@ public class userscores20250801 : DEntityBase
this.Title = "";
this.Postion = "";
this.Name = "";
this.finalscore = 0;
}
/// <summary>

View File

@@ -302,7 +302,7 @@
项目
</summary>
</member>
<member name="M:Vote.Services.ApiController.UserScore20250801Service.List">
<member name="M:Vote.Services.ApiController.UserScore20250801Service.List(Vote.Services.Dto.ListInput)">
<summary>
列表
</summary>