Browse Source

fit: 单位问题画像、个人问题画像

main
wxc 1 year ago
parent
commit
87005ed7dc
  1. 11
      src/api/sensitivePerception/profileDepart.ts
  2. 28
      src/api/sensitivePerception/profilePolice.ts
  3. 10
      src/components/datav/chart-bar.vue
  4. 4
      src/components/description-pair.vue
  5. 192
      src/views/sensitivePerception/DepartNegative.vue
  6. 420
      src/views/sensitivePerception/PoliceNegative.vue

11
src/api/sensitivePerception/profileDepart.ts

@ -7,8 +7,15 @@ export function listDepartNegative(query) {
});
}
export function getDepartProfile(departId) {
export function getDepartProfile(departId, query) {
return request.get({
url: `/profile/depart/${departId}`
url: `/profile/depart/${departId}`,
query
});
}
export function listNegativeMonthly(departId) {
return request.get({
url: `/profile/depart/${departId}/month`
});
}

28
src/api/sensitivePerception/profilePolice.ts

@ -0,0 +1,28 @@
import request from "@/api/request";
export function listPoliceNegative(query) {
return request.get({
url: `/profile/police`,
query
});
}
export function getPoliceProfile(idCode, query) {
return request.get({
url: `/profile/police/${idCode}`,
query
});
}
export function listNegativeByPoliceIdCode(idCode, query) {
return request.get({
url: `/profile/police/${idCode}/negative`,
query
});
}
export function listNegativeMonthly(idCode) {
return request.get({
url: `/profile/police/${idCode}/month`
});
}

10
src/components/datav/chart-bar.vue

@ -15,7 +15,7 @@
class="bar-item_content-bar"
:style="{
width: `${(item.value / max) * 100}%`,
background: getColor(item.value / max),
background: getColor(item.value / max * 100),
}"
></div>
</div>
@ -86,8 +86,7 @@ function getColor(val) {
}
if (props.color instanceof Array) {
const colors = [...props.color]
colors.reduce((a, b) => b.percentage - a.percentage)
console.log(colors)
colors.sort((a, b) => b.percentage - a.percentage)
for (let i = 0; i < colors.length; i++) {
if (val > colors[0].percentage) {
return colors[0].color;
@ -120,6 +119,10 @@ function getColor(val) {
}
}
}
&[size="small"] {
height: 25px;
font-size: 12px;
}
.bar-item-name {
width: 19%;
text-align: right;
@ -130,7 +133,6 @@ function getColor(val) {
width: 0;
height: 9px;
background: linear-gradient(270deg, #63e700 0%, #19674c 100%);
box-shadow: 1px 0 0px 0px #020b5f;
transition: width 0.3s;
}
}

4
src/components/description-pair.vue

@ -17,7 +17,7 @@ defineProps({
default: "",
},
value1: {
type: String,
type: Number,
default: "",
},
label2: {
@ -25,7 +25,7 @@ defineProps({
default: "",
},
value2: {
type: String,
type: Number,
default: "",
},
size: {

192
src/views/sensitivePerception/DepartNegative.vue

@ -1,5 +1,7 @@
<template>
<div class="container">
<el-tabs>
<el-tab-pane label="所队">
<header>
<el-form :label-width="114">
<el-row>
@ -11,6 +13,11 @@
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="单位简称">
<el-input v-model="query.departName" placeholder="请输入" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="统计时间">
<date-time-range-picker-ext
@ -94,6 +101,8 @@
>
</el-pagination>
</div>
</el-tab-pane>
</el-tabs>
</div>
<el-dialog title="单位问题画像" v-model="show" width="85vw" top="2vh">
@ -101,14 +110,15 @@
<label>统计范围</label>
<div style="width: 320px">
<date-time-range-picker-ext
v-model="query.crtTime"
v-model="time"
@change="getProfileData"
style="width: 300px"
/>
</div>
<el-button type="primary">查询</el-button>
</header>
<main v-loading="loading">
<el-row>
<el-row class="mb-20">
<el-col :span="8">
<h5>单位情况</h5>
<div class="row">
@ -154,7 +164,10 @@
</el-col>
<el-col :span="8">
<h5>问题情况</h5>
<el-row>
<el-row
class="flex v-center"
style="height: calc(100% - 76px)"
>
<el-col :span="6" class="text-center">
<div class="text-primary" style="font-size: 34px">
{{ activeRow.verifySize }}
@ -167,16 +180,16 @@
<description-pair
label1="110接处警量"
label2="问题数"
value1="132"
value2="21"
:value1="negativeInfo.jcjBusinessSize"
:value2="negativeInfo.jcjSize"
/>
</el-col>
<el-col :span="12">
<description-pair
label1="执法办案"
label2="问题数"
value1="132"
value2="21"
:value1="negativeInfo.zfbaBusinessSize"
:value2="negativeInfo.zfbaSize"
/>
</el-col>
</el-row>
@ -216,12 +229,12 @@
</div>
</el-col>
</el-row>
<el-row>
<el-row class="mb-20">
<el-col :span="8">
<h5>问题来源占比</h5>
<v-charts
style="height: 320px"
:option="pieOptions"
:option="problemSourcesPieOptions"
autoresize
/>
</el-col>
@ -229,36 +242,41 @@
<h5>业务类型占比</h5>
<v-charts
style="height: 320px"
:option="pieOptions"
:option="businessTypePieOptions"
autoresize
/>
</el-col>
<el-col :span="8">
<h5>风险问题构成</h5>
<v-charts
style="height: 320px"
:option="radarOption"
autoresize
/>
</el-col>
</el-row>
<el-row>
<el-row class="mb-20">
<el-col :span="8">
<h5>个人问题排名</h5>
<datav-chart-bar :data="barList" :color="colors" />
<datav-chart-bar
size="small"
:data="policeBarList"
:color="colors"
/>
</el-col>
<el-col :span="8">
<h5>突出问题排名</h5>
<datav-chart-bar :data="barList" />
<datav-chart-bar size="small" :data="[]" />
</el-col>
<el-col :span="8">
<h5>单位问题标签</h5>
<datav-chart-bar :data="barList" />
<datav-chart-bar size="small" :data="[]" />
</el-col>
</el-row>
<div style="margin-top: 60px">
<h5>问题变化趋势</h5>
<v-charts style="height: 320px" :option="barOption" autoresize />
<v-charts
style="height: 320px"
:option="barOption"
autoresize
/>
<div></div>
</div>
<h5>问题清单</h5>
<el-table :data="negativeList">
<el-table-column label="发现时间" prop="discoveryTime" />
@ -268,9 +286,19 @@
<el-table-column
label="核查情况"
prop="checkStatusName"
width="180"
width="140"
align="center"
/>
<el-table-column label="操作" width="160">
<template #default="{ row }">
<el-button
type="primary"
link
@click="handleAction(row)"
>查看详情</el-button
>
</template>
</el-table-column>
</el-table>
<div class="flex end mt-8">
<el-pagination
@ -287,12 +315,19 @@
</div>
</main>
</el-dialog>
<negative-dialog
v-model="negativeShow"
:id="activeNegativeId"
@close="negativeShow = false"
/>
</template>
<script lang="ts" setup>
import vCharts from "vue-echarts";
import {
listDepartNegative,
getDepartProfile,
listNegativeMonthly
} from "@/api/sensitivePerception/profileDepart";
import { listNegative } from "@/api/work/negative";
import { InspectCase } from "@/enums/dictEnums";
@ -326,6 +361,7 @@ onMounted(() => {
const activeRow = ref({});
const show = ref(false);
const departInfo = ref({});
const negativeInfo = ref({});
const loading = ref(false);
const negativeQuery = ref({
@ -333,16 +369,77 @@ const negativeQuery = ref({
});
const negativeList = ref([]);
const negativeTotal = ref(0);
const problemSourcesPieOptions = ref({
tooltip: {
trigger: "item",
},
series: [
{
type: "pie",
radius: ["40%", "70%"],
data: [],
},
],
});
const businessTypePieOptions = ref({
tooltip: {
trigger: "item",
},
series: [
{
type: "pie",
radius: ["40%", "70%"],
data: [],
},
],
});
const policeBarList = ref([]);
const barOption = ref({
xAxis: {
type: "category",
data: [],
},
yAxis: {
type: "value",
},
series: [
{
data: [],
type: "bar",
color: "#5B8FF9 ",
},
],
});
async function handleShowProfile(row) {
activeRow.value = row;
loading.value = true;
show.value = true;
await getProfileData();
listNegativeMonthly(row.departId).then(data => {
barOption.value.xAxis.data = data.months
barOption.value.series[0].data = data.values
})
}
negativeQuery.value.involveDepartId = row.departId;
const time = ref([]);
async function getProfileData() {
negativeQuery.value.involveDepartId = activeRow.value.departId;
getNegativeList();
const data = await getDepartProfile(row.departId);
const data = await getDepartProfile(activeRow.value.departId, {
beginTime: time.value.length ? time.value[0] : "",
endTime: time.value.length ? time.value[1] : "",
});
departInfo.value = data.departInfo;
negativeInfo.value = data.negativeInfo;
problemSourcesPieOptions.value.series[0].data = data.problemSourcesList;
businessTypePieOptions.value.series[0].data = data.businessTypeList;
policeBarList.value = data.policeBarList;
loading.value = false;
}
@ -354,11 +451,9 @@ function getNegativeList() {
}
const colors = [
{ color: "#f56c6c", percentage: 20 },
{ color: "#e6a23c", percentage: 40 },
{ color: "#5cb87a", percentage: 60 },
{ color: "#1989fa", percentage: 80 },
{ color: "#6f7ad3", percentage: 100 },
{ color: "#5AD8A6", percentage: 40 },
{ color: "#F6BD16", percentage: 60 },
{ color: "#E8684A ", percentage: 80 },
];
const barList = [
@ -384,26 +479,6 @@ const barList = [
},
];
const pieOptions = computed(() => {
return {
series: [
{
type: "pie",
radius: ["40%", "70%"],
data: [
{ value: 311, name: "慢作为" },
{ value: 735, name: "意见建议" },
{ value: 580, name: "违法违纪违规" },
{ value: 484, name: "乱作为" },
{ value: 300, name: "其他" },
{ value: 300, name: "不作为" },
],
},
],
};
});
const radarOption = {
radar: {
// shape: 'circle',
@ -429,21 +504,12 @@ const radarOption = {
],
};
const barOption = {
xAxis: {
type: "category",
data: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
},
yAxis: {
type: "value",
},
series: [
{
data: [120, 200, 150, 80, 70, 110, 130],
type: "bar",
},
],
};
const negativeShow = ref(false);
const activeNegativeId = ref("");
function handleAction(row) {
negativeShow.value = true;
activeNegativeId.value = row.id;
}
</script>
<style lang="scss" scoped>
</style>

420
src/views/sensitivePerception/PoliceNegative.vue

@ -4,20 +4,39 @@
<el-form :label-width="114">
<el-row>
<el-col :span="6">
<el-form-item label="所在单位">
<depart-tree-select
v-model="query.departId"
:check-strictly="false"
<el-form-item label="统计时间">
<date-time-range-picker-ext
v-model="query.crtTime"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="统计时间">
<date-time-range-picker-ext
v-model="query.crtTime"
<el-form-item label="姓名">
<el-input
v-model="query.name"
placeholder="请输入"
clearable
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="警号">
<el-input
v-model="query.empNo"
placeholder="请输入"
clearable
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="所在单位">
<depart-tree-select
v-model="query.departId"
:check-strictly="false"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div class="mb-25 flex end">
@ -35,19 +54,37 @@
<div class="table-container">
<el-table :data="list">
<el-table-column label="姓名" prop="name" />
<el-table-column label="性别" />
<el-table-column label="职务" prop="" />
<el-table-column label="性别">
<template #default="{ row }">
<span>{{ getGenderFromIdCode(row.idCode) }}</span>
</template>
</el-table-column>
<el-table-column label="职务" prop="position" />
<el-table-column label="警号" prop="empNo" />
<el-table-column label="任职年限" prop="" />
<el-table-column label="单位名称" prop="policeSize" />
<el-table-column label="任职年限" prop="employmentDate" />
<el-table-column label="所属单位" prop="departName">
<template #default="{ row }">
<span
>{{
row.parentDepartName
? row.parentDepartName + "/"
: row.parentDepartName
}}{{ row.departName }}</span
>
</template>
</el-table-column>
<el-table-column
label="查实问题数"
prop="verifySize"
width="120"
align="center"
/>
<el-table-column label="操作" width="160">
<template #default="{ row }">
<el-button type="primary" link @click="show = true"
<el-button
type="primary"
link
@click="handleShowProfile(row)"
>问题画像</el-button
>
</template>
@ -70,59 +107,74 @@
</div>
</div>
<el-dialog title="单位问题画像" v-model="show" width="85vw" top="2vh">
<el-dialog title="个人问题画像" v-model="show" width="85vw" top="2vh">
<header class="flex center v-center gap">
<label>统计范围</label>
<div style="width: 320px">
<date-time-range-picker-ext v-model="query.crtTime" style="width: 300px" />
<date-time-range-picker-ext
v-model="time"
style="width: 300px"
@change="getProfileData"
/>
</div>
<el-button type="primary">查询</el-button>
<el-button type="primary" @click="getProfileData">查询</el-button>
</header>
<main>
<el-row>
<main v-loading="loading">
<el-row class="mb-20">
<el-col :span="8">
<h5>单位情况</h5>
<h5>民警基本情况</h5>
<el-row>
<el-col :span="6"> </el-col>
<el-col :span="18">
<div class="row">
<div class="col col-24">
<label>单位名称</label>
<span>{{}}</span>
<div class="col col-12">
<label>姓名</label>
<span>{{ policeInfo.name }}</span>
</div>
<div class="col col-24">
<label>所长</label>
<span>{{}}</span>
<div class="col col-12">
<label>性别</label>
<span>{{
getGenderFromIdCode(policeInfo.idCode)
}}</span>
</div>
<div class="col col-24">
<label>副所长</label>
<span>{{}}</span>
<label>所属单位</label>
<span>
<span
>{{
activeRow.parentDepartName
? activeRow.parentDepartName +
"/"
: activeRow.parentDepartName
}}{{ activeRow.departName }}</span
>
</span>
</div>
<div class="col col-12">
<label>警号</label>
<span>{{ policeInfo.empNo }}</span>
</div>
<div class="col col-12">
<label>任职年份</label>
<span>{{ policeInfo.employmentDate }}</span>
</div>
<div class="col col-12">
<label>手机号</label>
<span>{{ policeInfo.mobile }}</span>
</div>
</div>
<el-row>
<el-col :span="12">
<description-pair
label1="民警总人数"
label2="问题涉及民警数"
value1="132"
value2="21"
size="large"
/>
</el-col>
<el-col :span="12">
<description-pair
label1="协辅警人数"
label2="问题涉及协辅警数"
value1="132"
value2="21"
size="large"
/>
</el-col>
</el-row>
</el-col>
<el-col :span="8">
<h5>问题情况</h5>
<el-row>
<el-row
class="flex v-center"
style="height: calc(100% - 76px)"
>
<el-col :span="6" class="text-center">
<div class="text-primary" style="font-size: 34px">
51
{{ activeRow.verifySize }}
</div>
<div>问题总数</div>
</el-col>
@ -132,16 +184,16 @@
<description-pair
label1="110接处警量"
label2="问题数"
value1="132"
value2="21"
:value1="negativeInfo.jcjBusinessSize"
:value2="negativeInfo.jcjSize"
/>
</el-col>
<el-col :span="12">
<description-pair
label1="执法办案"
label2="问题数"
value1="132"
value2="21"
:value1="negativeInfo.zfbaBusinessSize"
:value2="negativeInfo.zfbaSize"
/>
</el-col>
</el-row>
@ -158,21 +210,35 @@
:width="250"
color="#DC6231"
>
<div>
<span style="">78</span>
<div style="line-height: 64px" class="mt-40">
<span
style="
font-weight: 600;
font-size: 60px;
color: #e87749;
"
>78</span
>
<span style="font-size: 18px; color: #999"
></span
>
</div>
<div style="font-size: 14px" class="mb-10">
分险指标值
</div>
<div style="color: #e87749; font-size: 28px">
中风险
</div>
<div>分险指标值</div>
<div>中风险</div>
</el-progress>
</div>
</el-col>
</el-row>
<el-row>
<el-row class="mb-20">
<el-col :span="8">
<h5>问题来源占比</h5>
<v-charts
style="height: 320px"
:option="pieOptions"
:option="problemSourcesPieOptions"
autoresize
/>
</el-col>
@ -180,62 +246,67 @@
<h5>业务类型占比</h5>
<v-charts
style="height: 320px"
:option="pieOptions"
:option="businessTypePieOptions"
autoresize
/>
</el-col>
<el-col :span="8">
<h5>风险问题构成</h5>
<v-charts
style="height: 320px"
:option="radarOption"
autoresize
/>
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<h5>个人问题排名</h5>
<datav-chart-bar :data="barList" />
</el-col>
<el-col :span="8">
<h5>突出问题排名</h5>
<datav-chart-bar :data="barList" />
</el-col>
<el-col :span="8">
<h5>单位问题标签</h5>
<datav-chart-bar :data="barList" />
</el-col>
</el-row>
<h5>问题变化趋势</h5>
<v-charts style="height: 320px" :option="barOption" autoresize />
<div></div>
<h5>问题清单</h5>
<el-table>
<el-table-column label="发现时间" prop="" />
<el-table-column label="问题来源" prop="" />
<el-table-column label="业务类别" prop="" />
<el-table-column label="涉及单位" prop="" />
<el-table-column label="是否属实" prop="" width="180" />
<el-table-column
label="完善情况"
prop="departName"
width="120"
/>
<el-table :data="negativeList">
<el-table-column label="发现时间" prop="discoveryTime" />
<el-table-column label="问题来源" prop="problemSources" />
<el-table-column label="业务类别" prop="businessTypeName" />
<el-table-column label="涉及单位" prop="involveDepartName" />
<el-table-column
label="最后编辑人"
prop="fxzs"
width="120"
label="核查情况"
prop="checkStatusName"
width="140"
align="center"
/>
<el-table-column label="操作" width="160">
<template #default="{ row }">
<el-button type="primary" link @click="handleAction(row)">查看详情</el-button>
</template>
</el-table-column>
</el-table>
<div class="flex end mt-8">
<el-pagination
@size-change="getNegativeList"
@current-change="getNegativeList"
:current-page="negativeQuery.current"
:page-sizes="[10, 20, 50]"
:page-size="negativeQuery.size"
v-model:current-page="negativeQuery.current"
layout="total, sizes, prev, pager, next"
:total="negativeTotal"
>
</el-pagination>
</div>
</main>
</el-dialog>
<negative-dialog
v-model="negativeShow"
:id="activeNegativeId"
@close="negativeShow = false"
/>
</template>
<script lang="ts" setup>
import vCharts from "vue-echarts";
import { listDepartNegative } from "@/api/sensitivePerception/profileDepart";
import { onMounted } from "vue";
import {
listPoliceNegative,
getPoliceProfile,
listNegativeByPoliceIdCode,
listNegativeMonthly
} from "@/api/sensitivePerception/profilePolice";
import { InspectCase } from "@/enums/dictEnums";
const query = ref({
current: 1,
@ -245,58 +316,120 @@ const list = ref<any[]>([]);
const total = ref(0);
function getList() {
listPoliceNegative(query.value).then((data) => {
list.value = data.records;
total.value = data.total;
});
}
function reset() {
query.value = {
current: 1,
size: 10,
};
getList();
}
onMounted(() => {
getList();
});
const activeRow = ref({});
const show = ref(false);
const policeInfo = ref({});
const negativeInfo = ref({});
const loading = ref(false);
const barList = [
{
label: "张三",
value: 67,
},
{
label: "李四",
value: 80,
},
{
label: "王五",
value: 40,
const negativeQuery = ref({
checkStatusList: [InspectCase.TRUE, InspectCase.TRUE],
});
const negativeList = ref([]);
const negativeTotal = ref(0);
const problemSourcesPieOptions = ref({
tooltip: {
trigger: "item",
},
series: [
{
label: "王五",
value: 40,
type: "pie",
radius: ["40%", "70%"],
data: [],
},
{
label: "王五",
value: 10,
],
});
const businessTypePieOptions = ref({
tooltip: {
trigger: "item",
},
];
const pieOptions = computed(() => {
return {
series: [
{
type: "pie",
radius: ["40%", "70%"],
data: [
{ value: 311, name: "慢作为" },
{ value: 735, name: "意见建议" },
{ value: 580, name: "违法违纪违规" },
{ value: 484, name: "乱作为" },
{ value: 300, name: "其他" },
{ value: 300, name: "不作为" },
data: [],
},
],
});
const barOption = ref({
xAxis: {
type: "category",
data: [],
},
yAxis: {
type: "value",
},
series: [
{
data: [],
type: "bar",
color: "#5B8FF9 ",
},
],
};
});
async function handleShowProfile(row) {
activeRow.value = row;
show.value = true;
time.value = []
await getProfileData();
listNegativeMonthly(row.idCode).then(data => {
barOption.value.xAxis.data = data.months
barOption.value.series[0].data = data.values
})
}
const time = ref([]);
async function getProfileData() {
loading.value = true;
negativeQuery.value.idCode = activeRow.value.idCode;
getNegativeList();
const data = await getPoliceProfile(activeRow.value.idCode, {
beginTime: time.value.length ? time.value[0] : "",
endTime: time.value.length ? time.value[1] : "",
});
policeInfo.value = data.policeInfo;
negativeInfo.value = data.negativeInfo;
problemSourcesPieOptions.value.series[0].data = data.problemSourcesList;
businessTypePieOptions.value.series[0].data = data.businessTypeList;
loading.value = false;
}
function getNegativeList() {
if (time.value.length === 2) {
negativeQuery.value.beginTime = time.value[0];
negativeQuery.value.endTime = time.value[1];
}
listNegativeByPoliceIdCode(activeRow.value.idCode, negativeQuery.value).then(
(data) => {
negativeList.value = data.records;
negativeTotal.value = data.total;
}
);
}
const radarOption = {
radar: {
// shape: 'circle',
@ -322,21 +455,34 @@ const radarOption = {
],
};
const barOption = {
xAxis: {
type: "category",
data: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
},
yAxis: {
type: "value",
},
series: [
{
data: [120, 200, 150, 80, 70, 110, 130],
type: "bar",
},
],
};
function getGenderFromIdCode(idCode) {
// 18
if (!idCode || idCode.length !== 18) {
return '';
}
// 17
const genderCode = parseInt(idCode.charAt(16), 10);
//
if (isNaN(genderCode)) {
return '';
}
// 17
return genderCode % 2 === 0 ? "女" : "男";
}
const negativeShow = ref(false)
const activeNegativeId = ref('')
function handleAction(row) {
negativeShow.value = true;
activeNegativeId.value = row.id;
}
</script>
<style lang="scss" scoped>
.col {
--label-width: 60px;
}
</style>
Loading…
Cancel
Save