Browse Source

feat: 加上问题详情

master
buaixuexideshitongxue 4 weeks ago
parent
commit
5e4ce21690
  1. 17
      src/components/datav/chart-bar-fixbug.vue
  2. 27
      src/components/negativeInfo/depart-dialog.vue
  3. 25
      src/views/data/ComplaintCollectionSource.vue
  4. 25
      src/views/data/ComplaintCollection_jzxx.vue
  5. 47
      src/views/datav/Lmgz.vue
  6. 452
      src/views/sensitivePerception/PoliceNegative.vue

17
src/components/datav/chart-bar-fixbug.vue

@ -12,7 +12,12 @@
:style="{ '--label-width': `${labelWidth}px` }"
:position="labelPosition"
>
<span class="bar-item-label mr-8" style="margin-right: 20px">
<span
class="bar-item-label mr-8"
:class="{ 'bar-item-label-clickable': clickable && item.detailId }"
style="margin-right: 20px"
@click="clickable && item.detailId && emit('item-click', item)"
>
{{ item.label }}
</span>
<div class="bar-item_content " :long="!item.denominator">
@ -40,6 +45,7 @@
<script setup>
import {onMounted} from "vue";
const emit = defineEmits(['item-click']);
const props = defineProps({
title: {
type: String,
@ -73,6 +79,10 @@ const props = defineProps({
type: String,
default: "left",
},
clickable: {
type: Boolean,
default: false,
},
});
@ -150,6 +160,11 @@ function getColor(val) {
text-overflow: ellipsis;
}
.bar-item-label-clickable {
cursor: pointer;
text-decoration: underline;
}
&[position="left"] {
height: 32px;
line-height: 32px;

27
src/components/negativeInfo/depart-dialog.vue

@ -1,5 +1,12 @@
<template>
<el-dialog title="单位问题详情" width="85vw" top="1vh" style="margin: 1vh auto">
<el-dialog
title="单位问题详情"
:model-value="modelValue"
@update:model-value="emit('update:modelValue', $event)"
width="85vw"
top="1vh"
style="margin: 1vh auto"
>
<header class="flex center v-center gap">
<label>统计范围</label>
<div style="width: 320px">
@ -278,10 +285,13 @@ import { listNegative } from "@/api/work/negative";
import moment from "moment";
const props = defineProps({
modelValue: Boolean,
departId: {
type: String,
},
initialTime: { type: Array, default: () => [] },
});
const emit = defineEmits(["update:modelValue"]);
const time = ref([
moment().startOf("year").format("YYYY-MM-DD HH:mm:ss"),
@ -354,10 +364,17 @@ const barOption = ref({
});
watch(
() => props.departId,
() => {
() => [props.departId, props.modelValue],
([departId, isOpen]) => {
if (!isOpen || !departId) return;
time.value = props.initialTime.length === 2
? [...props.initialTime]
: [
moment().startOf("year").format("YYYY-MM-DD HH:mm:ss"),
moment().format("YYYY-MM-DD HH:mm:ss"),
];
getProfileData();
listNegativeMonthly(props.departId).then((data) => {
listNegativeMonthly(departId).then((data) => {
barOption.value.xAxis.data = data.months;
barOption.value.series[0].data = data.values;
});
@ -507,4 +524,4 @@ h5 {
.h-300 {
height: 300px;
}
</style>
</style>

25
src/views/data/ComplaintCollectionSource.vue

@ -322,9 +322,16 @@
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="操作" width="120" fixed="right">
<el-table-column label="操作" width="190" fixed="right">
<template #default="{ row }">
<el-button type="primary" link @click="handleActionDetail(row)">办理详情</el-button>
<el-button
v-if="row.sourceTable === '23' || row.problemSources === '局长信箱'"
type="primary"
link
:loading="refreshingId === row.id"
@click="handleRefresh(row)"
>再次更新</el-button>
</template>
</el-table-column>
</el-table>
@ -364,7 +371,8 @@ import { ref, computed, onMounted, watch } from 'vue';
import { useRoute } from 'vue-router';
import { timeFormat } from "@/utils/util";
import useCatchStore from "@/stores/modules/catch";
import { exportData, getComplaintCollectionPage } from "@/api/data/complaintCollection.ts";
import { exportData, getComplaintCollectionPage, refreshMayorMailbox } from "@/api/data/complaintCollection.ts";
import feedback from "@/utils/feedback";
const route = useRoute();
const catchStore = useCatchStore();
@ -410,6 +418,7 @@ const query = ref({
const list = ref([]);
const total = ref(0);
const loading = ref(false)
const refreshingId = ref('')
const getList = async () => {
loading.value = true;
@ -553,6 +562,18 @@ function handleActionDetail(row) {
}
}
const handleRefresh = async (row) => {
await feedback.confirm(`确定从局长信箱重新同步信件“${row.originId}”的最新办理结果吗?`)
refreshingId.value = row.id
try {
const message = await refreshMayorMailbox({id: row.id})
feedback.msgSuccess(message || '再次更新成功')
await getList()
} finally {
refreshingId.value = ''
}
}
const handleExport = async () => {
let finalSourceTableList = [...dynamicSourceTableList.value];
if (query.value.sourceTableSubOneList && query.value.sourceTableSubOneList.length > 0) {

25
src/views/data/ComplaintCollection_jzxx.vue

@ -325,15 +325,9 @@
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="操作" width="190" fixed="right">
<el-table-column label="操作" width="120" fixed="right">
<template #default="{ row }">
<el-button type="primary" link @click="handleActionDetail(row)">办理详情</el-button>
<el-button
type="primary"
link
:loading="refreshingId === row.id"
@click="handleRefresh(row)"
>再次更新</el-button>
</template>
</el-table-column>
</el-table>
@ -374,10 +368,8 @@ import {timeFormat} from "@/utils/util";
import useCatchStore from "@/stores/modules/catch";
import {
exportData,
getComplaintCollectionPage,
refreshMayorMailbox
getComplaintCollectionPage
} from "@/api/data/complaintCollection.ts";
import feedback from "@/utils/feedback";
const catchStore = useCatchStore();
const show = ref(false);
@ -399,7 +391,6 @@ const query = ref({
const list = ref([]);
const total = ref(0);
const loading = ref(false)
const refreshingId = ref('')
const getList = async () => {
loading.value = true;
@ -524,18 +515,6 @@ function handleActionDetail(row) {
}
}
const handleRefresh = async (row) => {
await feedback.confirm(`确定从局长信箱重新同步信件“${row.originId}”的最新办理结果吗?`)
refreshingId.value = row.id
try {
const message = await refreshMayorMailbox({id: row.id})
feedback.msgSuccess(message || '再次更新成功')
await getList()
} finally {
refreshingId.value = ''
}
}
const handleExport = async () => {
const body = {
...query.value,

47
src/views/datav/Lmgz.vue

@ -117,6 +117,8 @@
<el-scrollbar height="200px">
<datav-chart-bar-fixbug
:data="list2"
clickable
@item-click="openDetail($event, 'depart')"
size="small"
:color="colors"
/>
@ -126,6 +128,8 @@
<el-scrollbar height="200px">
<datav-chart-bar-fixbug
:data="list2"
clickable
@item-click="openDetail($event, 'depart')"
:max="11"
size="small"
:color="colors"
@ -136,6 +140,8 @@
<el-scrollbar height="200px">
<datav-chart-bar-fixbug
:data="list2"
clickable
@item-click="openDetail($event, 'depart')"
:max="11"
size="small"
:color="colors"
@ -166,6 +172,8 @@
<el-scrollbar height="200px">
<datav-chart-bar-fixbug
:data="list3"
clickable
@item-click="openDetail($event, 'depart')"
size="small"
:color="colors"
/>
@ -175,6 +183,8 @@
<el-scrollbar height="200px">
<datav-chart-bar-fixbug
:data="list3"
clickable
@item-click="openDetail($event, 'depart')"
:max="11"
size="small"
:color="colors"
@ -185,6 +195,8 @@
<el-scrollbar height="200px">
<datav-chart-bar-fixbug
:data="list3"
clickable
@item-click="openDetail($event, 'depart')"
:max="11"
size="small"
:color="colors"
@ -217,6 +229,8 @@
<el-scrollbar height="200px">
<datav-chart-bar-fixbug
:data="list4"
clickable
@item-click="openDetail($event, 'police')"
size="small"
:color="colors"
/>
@ -245,6 +259,8 @@
<el-scrollbar height="200px">
<datav-chart-bar-fixbug
:data="list5"
clickable
@item-click="openDetail($event, 'police')"
size="small"
:color="colors"
/>
@ -273,6 +289,8 @@
<el-scrollbar height="200px">
<datav-chart-bar-fixbug
:data="list6"
clickable
@item-click="openDetail($event, 'police')"
size="small"
:color="colors"
/>
@ -282,6 +300,16 @@
</datav-card>
</el-col>
</el-row>
<negativeInfo-depart-dialog
v-model="departDetailOpen"
:depart-id="detailId"
:initial-time="detailTime"
/>
<negativeInfo-police-dialog
v-model="policeDetailOpen"
:id-code="detailId"
:initial-time="detailTime"
/>
</div>
</el-scrollbar>
</template>
@ -327,6 +355,25 @@ const list3 = ref([])
const list4 = ref([])
const list5 = ref([])
const list6 = ref([])
const departDetailOpen = ref(false);
const policeDetailOpen = ref(false);
const detailId = ref("");
const detailTime = ref([]);
function openDetail(item, type) {
if (!item.detailId) return;
detailId.value = item.detailId;
detailTime.value = [
moment(time.value[0]).startOf("day").format("YYYY-MM-DD HH:mm:ss"),
moment(time.value[1]).endOf("day").format("YYYY-MM-DD HH:mm:ss"),
];
if (type === "depart") {
departDetailOpen.value = true;
} else {
policeDetailOpen.value = true;
}
}
function getData() {
totalStatistics(time.value).then(res => {
statisticsTotal.value.aTotal = res.atotal;

452
src/views/sensitivePerception/PoliceNegative.vue

@ -114,281 +114,33 @@
</div>
</div>
<el-dialog
title="个人问题详情"
v-model="show"
width="85vw"
top="1vh"
style="margin: 1vh auto"
>
<header class="flex center v-center gap">
<label>统计范围</label>
<div style="width: 320px">
<date-time-range-picker-ext
v-model="time"
style="width: 300px"
@change="getProfileData"
/>
</div>
<el-button type="primary" @click="getProfileData">查询</el-button>
</header>
<el-scrollbar
max-height="calc(98vh - 120px)"
v-loading="loading"
element-loading-text="个人问题详情加载中..."
>
<main>
<el-row class="mb-20" :gutter="20">
<el-col :span="8">
<h5>民警基本情况</h5>
<div class="card-item h-220">
<el-row>
<el-col :span="6">
<div class="flex v-center" style="height: 100%">
<img
v-if="policeInfo.avatarUrl"
:src="`${BASE_PATH}/file/stream/${policeInfo.avatarUrl}`"
class="police-avatar"
style="width: 94px"
/>
<icon name="local-icon-police" :size="120" v-else />
</div>
</el-col>
<el-col :span="18">
<div class="row">
<div class="col col-12">
<label>姓名</label>
<span>{{ policeInfo.name }}</span>
</div>
<div class="col col-12">
<label>性别</label>
<span>{{ getGenderFromIdCode(policeInfo.idCode) }}</span>
</div>
<div class="col col-24">
<label>所属单位</label>
<span>
<span
>{{
activeRow.parentDepartName
? activeRow.parentDepartName + "/"
: activeRow.parentDepartName
}}{{ activeRow.departName }}</span
>
</span>
</div>
<div class="col col-24">
<label>警号</label>
<span>{{ policeInfo.empNo }}</span>
</div>
<div class="col col-24">
<label>身份证号</label>
<span>{{ policeInfo.idCode }}</span>
</div>
<div class="col col-24">
<label>入职时间</label>
<span>{{ policeInfo.employmentDate || "/" }}</span>
</div>
<div class="col col-24">
<label>手机号</label>
<span>{{ policeInfo.mobile || "/" }}</span>
</div>
</div>
</el-col>
</el-row>
</div>
</el-col>
<el-col :span="8">
<h5>问题情况</h5>
<div class="card-item h-220">
<div class="flex v-center h100">
<div style="width: 25%" class="text-center">
<div class="text-primary" style="font-size: 34px">
{{ negativeInfo.size }}
</div>
<div class="mb-10">问题总数</div>
</div>
<div style="width: 75%" class="text-center">
<div
v-if="negativeInfo.jcj110BusinessSize || negativeInfo.jcj110Size"
class="mb-20"
>
<description-pair
label="110接处警"
label1="量"
label2="问题数"
:value1="negativeInfo.jcj110BusinessSize"
:value2="negativeInfo.jcj110Size"
/>
</div>
<div
v-if="negativeInfo.jcj122BusinessSize || negativeInfo.jcj122Size"
class="mb-20"
>
<description-pair
label="122接处警"
label1="量"
label2="问题数"
:value1="negativeInfo.jcj122BusinessSize"
:value2="negativeInfo.jcj122Size"
/>
</div>
<div>
<description-pair
label="执法办案"
label2="问题数"
:value1="negativeInfo.zfbaBusinessSize"
:value2="negativeInfo.zfbaSize"
/>
</div>
</div>
</div>
</div>
</el-col>
<el-col :span="8">
<h5>问题累计分值</h5>
<div class="flex center card-item wrap h-220">
<el-progress
type="dashboard"
:percentage="100"
:stroke-width="16"
:width="200"
color="#5B8FF9"
>
<div class="score-progress-body">
<div>
<div style="font-size: 16px">分数</div>
<div>
<span
class="score-progress_score score-theme text-bold"
style="color: #19257d"
>{{ score }}</span
>
</div>
</div>
</div>
</el-progress>
<div style="width: 100%" class="text-center">累计{{ days }}天分值</div>
</div>
</el-col>
</el-row>
<el-row class="mb-20" :gutter="20">
<el-col :span="8">
<h5>问题来源占比</h5>
<div class="card-item">
<v-charts
style="height: 320px"
:option="problemSourcesPieOptions"
autoresize
/>
</div>
</el-col>
<el-col :span="8">
<h5>问题涉及方面</h5>
<div class="card-item">
<v-charts
style="height: 320px"
:option="problemTypePieOptions"
autoresize
/>
</div>
</el-col>
<el-col :span="8">
<h5>突出问题排名</h5>
<div class="flex center card-item h-320">
<datav-chart-bar1
size="small"
:data="problemTypeBarList"
:color="colors"
labelPosition="top"
/>
<el-empty v-if="problemTypeBarList.length === 0" />
</div>
</el-col>
</el-row>
<h5>问题变化趋势</h5>
<div class="card-item">
<v-charts style="height: 320px" :option="barOption" autoresize />
</div>
<h5>问题清单 {{ negativeList.length }}</h5>
<el-table :data="negativeList" height="500">
<el-table-column label="发现时间" prop="discoveryTime" width="160" />
<el-table-column label="问题来源" prop="problemSources" width="150" />
<el-table-column label="业务类别" prop="businessTypeName" width="150" />
<el-table-column label="问题详情" prop="thingDesc" show-overflow-tooltip />
<el-table-column
label="核查情况"
prop="checkStatusDesc"
show-overflow-tooltip
/>
<el-table-column label="问题类型" prop="problemType" show-overflow-tooltip />
<el-table-column label="分值" width="100" align="center">
<template #default="{ row }">
<span class="text-danger">{{ row.score }}</span>
</template>
</el-table-column>
<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>
</main>
</el-scrollbar>
</el-dialog>
<negativeInfo-police-dialog v-model="show" :id-code="activeRow.idCode" :active-row="activeRow" />
<alarm-dialog
v-model="showNotification"
:departId="activeDepartId"
@close="showNotification = false"
/>
<negative-dialog
v-model="negativeShow"
:id="activeNegativeId"
@close="negativeShow = false"
/>
</template>
<script lang="ts" setup>
import { BASE_PATH } from "@/api/request";
import vCharts from "vue-echarts";
import {
listPoliceNegative,
getPoliceProfile,
listNegativeByPoliceIdCode,
listNegativeMonthly,
} from "@/api/sensitivePerception/profilePolice";
import { HostLevel, InspectCase } from "@/enums/dictEnums";
import { listPoliceNegative } from "@/api/sensitivePerception/profilePolice";
import { getDictLable, getGenderFromIdCode } from "@/utils/util";
import moment from "moment";
import useCatchStore from "@/stores/modules/catch";
import feedback from "~/utils/feedback";
const catchStore = useCatchStore();
const dict = catchStore.getDicts(["personType"]);
let showNotification = ref(false);
const showNotification = ref(false);
const activeDepartId = ref("");
const handleShowNotification = (row) => {
showNotification.value = true;
activeDepartId.value = row.departId;
};
const activeRow = ref<any>({});
const show = ref(false);
const query = ref({
current: 1,
size: 10,
crtTime: [
// moment().startOf("year").format("YYYY-MM-DD HH:mm:ss"),
// moment().format("YYYY-MM-DD HH:mm:ss"),
],
crtTime: [] as string[],
});
const list = ref<any[]>([]);
const total = ref(0);
const mainLoading = ref(false);
function getList() {
@ -412,195 +164,15 @@ function reset() {
getList();
}
onMounted(() => {
getList();
});
const activeRow = ref({});
const show = ref(false);
const policeInfo = ref({});
const negativeInfo = ref({});
const loading = ref(false);
const negativeList = ref([]);
const negativeTotal = ref(0);
const problemSourcesPieOptions = ref({
tooltip: {
trigger: "item",
},
series: [
{
type: "pie",
radius: ["40%", "70%"],
data: [],
},
],
});
const problemTypePieOptions = ref({
tooltip: {
trigger: "item",
},
series: [
{
type: "pie",
radius: ["40%", "70%"],
data: [],
},
],
});
const barOption = ref({
xAxis: {
type: "category",
data: [],
},
yAxis: {
type: "value",
},
series: [
{
data: [],
type: "bar",
color: "#5B8FF9 ",
},
],
});
function handleShowNotification(row: any) {
showNotification.value = true;
activeDepartId.value = row.departId;
}
async function handleShowProfile(row) {
function handleShowProfile(row: any) {
activeRow.value = row;
show.value = true;
}
watch(activeRow, async () => {
time.value = [
moment().startOf("year").format("YYYY-MM-DD HH:mm:ss"),
moment().format("YYYY-MM-DD HH:mm:ss"),
];
await getProfileData();
listNegativeMonthly(activeRow.value.idCode).then((data) => {
barOption.value.xAxis.data = data.months;
barOption.value.series[0].data = data.values;
});
});
const time = ref([]);
const problemTypeBarList = ref([]);
const score = ref(0);
const days = ref(0);
async function getProfileData() {
loading.value = true;
const data = await getPoliceProfile(activeRow.value.idCode, {
beginTime: time.value.length ? time.value[0] : "",
endTime: time.value.length ? time.value[1] : "",
});
score.value = Math.round(data.score);
days.value = data.days;
negativeList.value = data.negatives;
policeInfo.value = data.policeInfo;
negativeInfo.value = data.negativeInfo;
problemSourcesPieOptions.value.series[0].data = data.problemSourcesList;
problemTypePieOptions.value.series[0].data = data.problemTypeList;
problemTypeBarList.value = data.problemTypeBarList;
loading.value = false;
}
const negativeShow = ref(false);
const activeNegativeId = ref("");
function handleAction(row) {
negativeShow.value = true;
activeNegativeId.value = row.id;
}
const colors = [
{ color: "var(--success-color)", percentage: 60 },
{ color: "#DC6231", percentage: 80 },
{ color: "var(--danger-color)", percentage: 100 },
];
function getType(val) {
if (val < 60) {
return "success";
}
if (val < 80) {
return "warning";
}
return "danger";
}
function getColor(val) {
if (val < 60) {
return "var(--success-color)";
}
if (val < 80) {
return "#e87749";
}
return "var(--danger-color)";
}
function getScoreLabel() {
if (score.value === 0) {
return "无法预测";
}
if (score.value < 60) {
return "低风险";
}
if (score.value < 80) {
return "中风险";
}
return "高风险";
}
onMounted(getList);
</script>
<style lang="scss" scoped>
.col {
--label-width: 60px;
}
.score-progress-body {
text-align: center;
font-size: 18px;
.score-progress_score {
font-size: 50px;
line-height: 70px;
}
.score-progress_title {
font-size: 26px;
line-height: 22px;
}
}
.score-theme {
&[type="success"] {
color: var(--success-color);
}
&[type="warning"] {
color: #e87749;
}
&[type="danger"] {
color: var(--danger-color);
}
}
h5 {
margin-bottom: 12px;
}
.card-item {
background: #f9faff;
padding: 16px;
min-height: 200px;
}
.h-220 {
height: 220px;
}
.h-320 {
height: 320px;
}
</style>

Loading…
Cancel
Save