yxq 1 개월 전
부모
커밋
cd3545e054
12개의 변경된 파일253개의 추가작업 그리고 104개의 파일을 삭제
  1. +1
    -0
      SafeCampus.WEB/package.json
  2. +2
    -1
      SafeCampus.WEB/src/api/modules/index.ts
  3. +8
    -2
      SafeCampus.WEB/src/api/modules/sysconfig/ability.ts
  4. +16
    -0
      SafeCampus.WEB/src/api/modules/violation/index.ts
  5. +45
    -0
      SafeCampus.WEB/src/api/modules/violation/portraitSummary.ts
  6. +7
    -2
      SafeCampus.WEB/src/api/request/instance.ts
  7. +1
    -1
      SafeCampus.WEB/src/views/attendance/passenger/components/form/form_basic.vue
  8. +1
    -1
      SafeCampus.WEB/src/views/attendance/passenger/index.vue
  9. +1
    -1
      SafeCampus.WEB/src/views/userManage/dormitory/index.vue
  10. +93
    -25
      SafeCampus.WEB/src/views/violation/analysis/index.vue
  11. +18
    -14
      SafeCampus.WEB/src/views/violation/portrait/detail.vue
  12. +60
    -57
      SafeCampus.WEB/src/views/violation/portraitSummary/index.vue

+ 1
- 0
SafeCampus.WEB/package.json 파일 보기

@@ -2,6 +2,7 @@
"name": "simple-admin",
"private": true,
"version": "1.0.0",
"node-version": "18.12.0 - 20.9.0",
"type": "module",
"description": "SimpleAdmin是ElementUI最好看的开源通用业务型后台管理系统",
"author": {


+ 2
- 1
SafeCampus.WEB/src/api/modules/index.ts 파일 보기

@@ -20,4 +20,5 @@ export * from "./monitor";
export * from "./sysconfig";
export * from "./statistion";
export * from "./usermanage";
export * from "./attendance";
export * from "./attendance";
export * from "./violation";

+ 8
- 2
SafeCampus.WEB/src/api/modules/sysconfig/ability.ts 파일 보기

@@ -13,11 +13,11 @@
* @see https://gitee.com/dotnetmoyu/SimpleAdmin
*/
import { moduleRequest } from "@/api/request";
import { ReqId, ResPage, ZJRQ,setWarn } from "@/api/interface";
import { ReqId, ResPage, ZJRQ, setWarn } from "@/api/interface";
const http = moduleRequest("/business/warn/");
const http1 = moduleRequest("/violation/analysis/");

const abilityApi = {
/** 获取告警分组 */
warnGroup(params: ReqId) {
return http.get("getWarnGroup", params);
@@ -26,6 +26,12 @@ const abilityApi = {
setWarnGroup(params: setWarn) {
return http.post("setWarnGroupDevice", params);
},
/* 预警分析报告导出 */
reportExport(params: any) {
return http1.download("reportExport", params, {
showHeader: true
});
}
};

export { abilityApi };

+ 16
- 0
SafeCampus.WEB/src/api/modules/violation/index.ts 파일 보기

@@ -0,0 +1,16 @@
/**
* @description
* @license Apache License Version 2.0
* @Copyright (c) 2022-Now 少林寺驻北固山办事处大神父王喇嘛
* @remarks
* SimpleAdmin 基于 Apache License Version 2.0 协议发布,可用于商业项目,但必须遵守以下补充条款:
* 1.请不要删除和修改根目录下的LICENSE文件。
* 2.请不要删除和修改SimpleAdmin源码头部的版权声明。
* 3.分发源码时候,请注明软件出处 https://gitee.com/dotnetmoyu/SimpleAdmin
* 4.基于本软件的作品,只能使用 SimpleAdmin 作为后台服务,除外情况不可商用且不允许二次分发或开源。
* 5.请不得将本软件应用于危害国家安全、荣誉和利益的行为,不能以任何形式用于非法为目的的行为不要删除和修改作者声明。
* 6.任何基于本软件而产生的一切法律纠纷和责任,均于我司无关
* @see https://gitee.com/dotnetmoyu/SimpleAdmin
*/

export * from "./portraitSummary";

+ 45
- 0
SafeCampus.WEB/src/api/modules/violation/portraitSummary.ts 파일 보기

@@ -0,0 +1,45 @@
/**
* @description 学校画像接口
* @license Apache License Version 2.0
* @Copyright (c) 2022-Now 少林寺驻北固山办事处大神父王喇嘛
* @remarks
* SimpleAdmin 基于 Apache License Version 2.0 协议发布,可用于商业项目,但必须遵守以下补充条款:
* 1.请不要删除和修改根目录下的LICENSE文件。
* 2.请不要删除和修改SimpleAdmin源码头部的版权声明。
* 3.分发源码时候,请注明软件出处 https://gitee.com/dotnetmoyu/SimpleAdmin
* 4.基于本软件的作品,只能使用 SimpleAdmin 作为后台服务,除外情况不可商用且不允许二次分发或开源。
* 5.请不得将本软件应用于危害国家安全、荣誉和利益的行为,不能以任何形式用于非法为目的的行为不要删除和修改作者声明。
* 6.任何基于本软件而产生的一切法律纠纷和责任,均于我司无关
* @see https://gitee.com/dotnetmoyu/SimpleAdmin
*/
import { moduleRequest } from "@/api/request";
const http = moduleRequest("/violation/portraitSummary/");
/**
* @Description: 学校画像
* @Author: SYY
* @Date: 2024-7-30 15:34:54
*/
const portraitSummaryApi = {
/** 获取学生性别 */
getStudentSex() {
return http.get("getStudentSex");
},
/** 获取学生年龄 */
getStudentAge() {
return http.get("getStudentAge");
},
/** 获取地理位置 */
getAddress() {
return http.get("getAddress");
},
/** 获取属性标签 */
getStudentAttr() {
return http.get("getStudentAttr");
},
/** 获取学生属性标签 */
getStudentDetail(params: any) {
return http.get("getStudentDetail", params);
}
};

export { portraitSummaryApi };

+ 7
- 2
SafeCampus.WEB/src/api/request/instance.ts 파일 보기

@@ -22,6 +22,7 @@ import { checkStatus } from "../helper/checkStatus";
import { useUserStore } from "@/stores/modules";
import { AxiosCanceler } from "../helper/axiosCancel";
import router from "@/routers";
import { i } from "vite/dist/node/types.d-jgA8ss1A";

// 自定义 AxiosRequestConfig 接口,增加 noLoading 属性
export interface CustomAxiosRequestConfig extends InternalAxiosRequestConfig {
@@ -111,7 +112,7 @@ export default class RequestHttp {
(response: AxiosResponse) => {
// 检查并存储授权信息
this.checkAndStoreAuthentication(response);
const { data, config } = response;
const { data, config }: any = response;
const userStore = useUserStore();
axiosCanceler.removePending(config);
tryHideFullScreenLoading();
@@ -140,7 +141,11 @@ export default class RequestHttp {
});
}
// 成功请求(在页面上除非特殊情况,否则不用处理失败逻辑)
return data;
if (config.showHeader) {
return response; //平时用在打印、导出、下载的时候使用
} else {
return data;
}
},
async (error: AxiosError) => {
const { response } = error;


+ 1
- 1
SafeCampus.WEB/src/views/attendance/passenger/components/form/form_basic.vue 파일 보기

@@ -67,7 +67,7 @@ const splitOptions = ref([
]);
// 摄像头
let cameraldsOptions = ref([]);
monitorLIVEApi.list({ pageSize: 1000, pageNum: 1 }).then(res => {
monitorLIVEApi.list({ pageSize: 1000, pageNum: 1 }).then((res: any) => {
if (res.code == 200) {
cameraldsOptions.value = res.data.list.map(e => {
return {


+ 1
- 1
SafeCampus.WEB/src/views/attendance/passenger/index.vue 파일 보기

@@ -88,7 +88,7 @@ function onOpen(opt: FormOptEnum, record: {} | AttendancePassenger.PassengerInfo
const RefreshTable = () => {
proTable.value?.refresh();
};
const haseExtJson = row => {
const haseExtJson = (row: any) => {
let extJson = JSON.parse(row.extJson);
return extJson.length == 0;
};


+ 1
- 1
SafeCampus.WEB/src/views/userManage/dormitory/index.vue 파일 보기

@@ -160,7 +160,7 @@ const columns: ColumnProps<SysDormitory.ChamberInfo>[] = [
];
const size = ref<'default' | 'large' | 'small'>('default')
const timeVisible = ref(false); //是否显示时间设置
const timeForm = reactive({
const timeForm = reactive<any>({
dateArr: [],
timeArr: [],


+ 93
- 25
SafeCampus.WEB/src/views/violation/analysis/index.vue 파일 보기

@@ -6,57 +6,125 @@
<template>
<div class="main-box">
<div class="table-box">
<ProTable ref="proTable" title="预警分析" :columns="columns" rowKey="id" :request-api="userManagePersonnelApi.page">
<ProTable ref="proTable" title="预警分析" :columns="columns" rowKey="id" :pagination="false" :request-api="abilityApi.warnGroup">
<!-- 表格 header 按钮 -->
<template #tableHeader="scope">
<s-button :opt="FormOptEnums.EXPORT" plain @click="onView" :disabled="!scope.isSelected" />
<template #operation="scope">
<s-button icon="Download" link :opt="FormOptEnums.EXPORT" @click="exportFn(scope.row)" />
</template>
</ProTable>
</div>
<el-dialog v-model="timeForm.visible" title="选择导出时间范围" :before-close="closeTime" width="600px">
<el-form :model="timeForm" :rules="timeRules" ref="timeFormRef" label-width="100px">
<el-row>
<el-col :span="21">
<el-form-item label="时间:" prop="dateArr">
<el-date-picker
v-model="timeForm.dateArr"
type="daterange"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>

<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="onTimeSubmit">提交</el-button>
<el-button @click="closeTime">取消</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup lang="tsx" name="SysUserPersonnel">
import { userManagePersonnelApi} from "@/api";
import { abilityApi} from "@/api";
import { ColumnProps, ProTableInstance } from "@/components/ProTable/interface";
import { useRouter } from "vue-router";
const router = useRouter();
// 获取 ProTable 元素,调用其获取刷新数据方法(还能获取到当前查询参数,方便导出携带参数)
const proTable = ref<ProTableInstance>();
const timeForm:any= reactive({
dateArr:[],
rowItem:{},
visible:false
});
const timeFormRef = ref();
const timeRules = ref({
dateArr: [
{ required: true, message: "请选择导出时间范围", trigger: "change" }
]
});
/** 表单操作类型枚举 */
enum FormOptEnums {
/** 导出 */
EXPORT = "导出",
}
// 表格配置项
const columns: ColumnProps[] = [
{ type: "selection", fixed: "left", width: 50 },
{ type: "index", label: "序号", width: 60, fixed: "left" },
const columns: ColumnProps[] = [{ type: "index", label: "序号", width: 60, fixed: "left" },
{
prop: "name",
label: "姓名"
},
{
prop: "personId",
label: "人员ID"
prop: "cameraName",
label: "摄像头",
render: (scope: any) => {
return scope.row.cameraName.join(", ");
}
},
{
prop: "phone",
label: "手机号"
prop: "name",
label: "场景"
},
{
prop: "personSetName",
label: "所属班级",
prop: "state",
label: "状态",
render: (scope: any) => {
return scope.row.state ? <el-tag type="success">启用</el-tag> : <el-tag type="danger">禁用</el-tag>;
}
},
{ prop: "operation", label: "操作", width: 250, fixed: "right" }
];

const onView = (row: any) => {
router.push( {
path:'/violation/portrait/detail',
query: {
personId: row.personId
/* 导出 */
const onTimeSubmit = ()=>{
timeFormRef.value.validate((valid: any) => {
if (valid) {
abilityApi.reportExport({
groupCode:timeForm.rowItem.code,
startTime: timeForm.dateArr[0],
endTime: timeForm.dateArr[1],
}).then((res:any) => {
console.log(res,"....UTF-8''");
const filename:any = ref()
filename.value = window.decodeURI(res.headers["content-disposition"].split("=")[2]);
filename.value = filename.value.slice(7,-1)
let blobUrl = window.URL.createObjectURL(res.data);
const a = document.createElement('a');
a.style.display = 'none';
a.setAttribute("target", "_blank");
a.download = filename.value
a.href = blobUrl;
a.click();
a.remove();
ElMessage({
message: res.msg,
type: 'success'
});
closeTime();
});
} else {
return false;
}
});
};
const closeTime = () =>{
timeForm.visible = false
}
const exportFn = (row: any) => {
timeForm.rowItem = JSON.parse(JSON.stringify(row));
timeForm.visible = true
};

</script>
<style scoped lang="scss">


+ 18
- 14
SafeCampus.WEB/src/views/violation/portrait/detail.vue 파일 보기

@@ -40,7 +40,7 @@
</div>
</template>
<script setup lang="ts" name="ViolationPortraitDetail">
import { userManagePersonnelApi } from "@/api";
import { userManagePersonnelApi, portraitSummaryApi } from "@/api";
const route = useRoute();
const formData = ref<any>({}); //班级人员列表
import * as echarts from "echarts";
@@ -54,11 +54,12 @@ const getDetail = () => {
userManagePersonnelApi.detail({ id: route.query.personId }).then((res: any) => {
formData.value = res.data;
});
portraitSummaryApi.getStudentDetail({ id: route.query.personId }).then((res: any) => {
getCharts(res.data.label, res.data.value);
});
};
const getCharts = (data: any) => {
const getCharts = (nameArr: any, valueArr: any) => {
const chart = echarts.init(chartEl.value);
const nameArr = ["爱好运动", "爱好学习", "比较调皮(爱打闹)", "上课不认真听讲"];
const valueArr = [150, 60, 80, 180];
const option = {
title: {
text: "人员属性标签",
@@ -147,15 +148,6 @@ const getCharts = (data: any) => {
}
},
series: [
{
yAxisIndex: 0,
color: "rgba(12, 245, 229, 1)",
lineStyle: {
color: "rgba(12, 245, 229, 1)"
},
type: "line",
data: valueArr
},
{
type: "bar",
name: "",
@@ -163,6 +155,19 @@ const getCharts = (data: any) => {
emphasis: {
itemStyle: {
color: "#7fb7e9"
},
label: {
textStyle: {
color: "#7fb7e9"
}
}
},
label: {
show: true,
position: "top",
textStyle: {
color: "rgba(12, 245, 229, 1)",
padding: [0, 10]
}
},
itemStyle: {
@@ -208,7 +213,6 @@ const getCharts = (data: any) => {
};
onMounted(() => {
getDetail();
getCharts({});
});
</script>
<style lang="scss" scoped>


+ 60
- 57
SafeCampus.WEB/src/views/violation/portraitSummary/index.vue 파일 보기

@@ -51,6 +51,7 @@

<script setup lang="ts" name="home">
import * as echarts from "echarts";
import { portraitSummaryApi } from "@/api";
const chart1 = ref(null);
const chart2 = ref(null);
const chart4 = ref(null);
@@ -58,15 +59,35 @@ const chart5 = ref(null);
onMounted(() => {
getDataChart();
});
function getDataChart() {
getCharts1();
getCharts2();
getCharts4();
getCharts5();
}
const getDataChart = async () => {
/* 获取学生性别 */
let sex: any = await portraitSummaryApi.getStudentSex();
getCharts1(sex.data.label, sex.data.value);
/* 获取学生年龄 */
let age: any = await portraitSummaryApi.getStudentAge();
getCharts2(age.data.label, age.data.value);
/* 获取属性标签 */
let attr: any = await portraitSummaryApi.getStudentAttr();
getCharts4(attr.data.label, attr.data.value);
/* 获取地理位置 */
let address: any = await portraitSummaryApi.getAddress();
getCharts5(address.data.label, address.data.value);
};

const handleDate = (label: any, value: any, key: any = "") => {
let data: any = [];
label.forEach((item: any, index: number) => {
data.push({
value: value[index],
name: item + key
});
});
return data;
};
/* 性別 */
function getCharts1() {
function getCharts1(label: any, value: any) {
const chart = echarts.init(chart1.value);
const data = handleDate(label, value);
const option = {
tooltip: {
confine: true
@@ -76,7 +97,7 @@ function getCharts1() {
right: "0%",
top: "center",
orient: "vertical",
data: ["男", "女"]
data: label
},
series: [
{
@@ -104,16 +125,7 @@ function getCharts1() {
show: true
}
},
data: [
{
value: 62,
name: "男"
},
{
value: 100,
name: "女"
}
]
data: data
}
]
};
@@ -124,15 +136,18 @@ function getCharts1() {
});
}
/* 年齡 */
function getCharts2() {
function getCharts2(label: any, value: any) {
const chartstation = echarts.init(chart2.value);
const data = handleDate(label, value, "岁");
const labelArr: any = [];
label.map((item: any) => labelArr.push(item + "岁"));
const option = {
legend: {
show: true,
right: "0%",
top: "center",
orient: "vertical",
data: ["<20", ">60岁", "50-60岁", "40-50岁", "30-40岁", "20-30岁"]
data: labelArr
},
tooltip: {
trigger: "item",
@@ -146,32 +161,7 @@ function getCharts2() {
radius: [50, 140],

roseType: "area",
data: [
{
value: 10,
name: "<20"
},
{
value: 15,
name: ">60岁"
},
{
value: 25,
name: "50-60岁"
},
{
value: 20,
name: "40-50岁"
},
{
value: 35,
name: "30-40岁"
},
{
value: 30,
name: "20-30岁"
}
],
data: data,
itemStyle: {
normal: {
label: {
@@ -194,17 +184,19 @@ function getCharts2() {
});
}
/* 兴趣爱好 */
function getCharts4() {
function getCharts4(categoryData: any, dataArr: any) {
const chartstation = echarts.init(chart4.value);
var dataArr = [100, 200, 300, 400, 500];
var spaceLength = 5,
fixedData = [],
end = 0,
max = 300;
// var categoryData = ys.超市品牌;
var categoryData = ["调皮/爱打闹", "上课不认真听讲", "爱好运动", "爱好画画", "爱好学习"];
var xhao = [1, 2, 3, 4, 5];
var data1 = dataArr.map(item => {
let xhao: any = [];
for (var i = 0; i < dataArr.length; i++) {
xhao.push(i + 1);
}

var data1 = dataArr.map((item: any) => {
fixedData.push(spaceLength);
return item - spaceLength;
});
@@ -399,11 +391,9 @@ function getCharts4() {
});
}

/* 标签 */
function getCharts5() {
/* 标签、地理位置 */
function getCharts5(nameArr: any, valueArr: any) {
const chartstation5 = echarts.init(chart5.value);
const nameArr = ["杭州", "宁波", "金华", "台州", "嘉兴", "丽水", "温州", "潮州", "绍兴", "衢州"];
const valueArr = [150, 60, 80, 180, 120, 160, 80, 40, 20, 10];
const option = {
grid: {
top: "10%",
@@ -449,7 +439,7 @@ function getCharts5() {
},
axisLabel: {
textStyle: {
color: "#B3CFFF", // x轴文本颜色
color: "#333", // x轴文本颜色
fontSize: 12
}
// rotate:30,
@@ -471,7 +461,7 @@ function getCharts5() {
axisLabel: {
formatter: "{value}",
textStyle: {
color: "#B3CFFF", // x轴文本颜色
color: "#333", // x轴文本颜色
fontSize: 12
}
},
@@ -489,6 +479,19 @@ function getCharts5() {
emphasis: {
itemStyle: {
color: "#7fb7e9"
},
label: {
textStyle: {
color: "#7fb7e9"
}
}
},
label: {
show: true,
position: "top",
textStyle: {
color: "rgba(147, 157, 223, 1)",
padding: [0, 10]
}
},
itemStyle: {


불러오는 중...
취소
저장