宁波市生活垃圾分类志愿服务项目交流打分
This commit is contained in:
@@ -63,6 +63,6 @@
|
||||
"GetHouseInfoCitysByCommunity": "http://sjk.test.ky.com/housesafety/statistics/GetHouseInfoCitysByCommunity?communityID={0}"
|
||||
},
|
||||
"UserScore": {
|
||||
"ExpertNumber": 10
|
||||
"ExpertNumber": 7
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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)
|
||||
|
||||
@@ -23,4 +23,9 @@ public class UserScoreInput
|
||||
{
|
||||
public string expertId { get; set; }
|
||||
public decimal? score { get; set; }
|
||||
}
|
||||
|
||||
public class ListInput
|
||||
{
|
||||
public string order { get; set; }
|
||||
}
|
||||
@@ -15,6 +15,7 @@ public class userscores20250801 : DEntityBase
|
||||
this.Title = "";
|
||||
this.Postion = "";
|
||||
this.Name = "";
|
||||
this.finalscore = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user