2025年04月07日18:35:52

This commit is contained in:
luojiayi 2025-04-07 18:35:54 +08:00
parent 26fa338589
commit dab5da7c1e
32 changed files with 442 additions and 122 deletions

5
components.d.ts vendored
View File

@ -7,10 +7,8 @@ export {}
declare module '@vue/runtime-core' {
export interface GlobalComponents {
2: typeof import('./src/components/alarm copy 2.vue')['default']
Alarm: typeof import('./src/components/alarm.vue')['default']
BatchImp: typeof import('./src/components/batch-imp.vue')['default']
copy: typeof import('./src/components/alarm copy.vue')['default']
Countup: typeof import('./src/components/countup.vue')['default']
DeviceInfo: typeof import('./src/components/deviceInfo.vue')['default']
ElAvatar: typeof import('element-plus/es')['ElAvatar']
@ -45,13 +43,12 @@ declare module '@vue/runtime-core' {
ElTable: typeof import('element-plus/es')['ElTable']
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
ElTag: typeof import('element-plus/es')['ElTag']
ElTooltip: typeof import('element-plus/es')['ElTooltip']
ElUpload: typeof import('element-plus/es')['ElUpload']
Header: typeof import('./src/components/header.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
Sidebar: typeof import('./src/components/sidebar.vue')['default']
StudentInfo: typeof import('./src/components/studentInfo.vue')['default']
SvgIcon: typeof import('./src/components/SvgIcon.vue')['default']
TableCustom: typeof import('./src/components/table-custom.vue')['default']
TableDetail: typeof import('./src/components/table-detail.vue')['default']
TableEdit: typeof import('./src/components/table-edit.vue')['default']

View File

@ -7,11 +7,12 @@
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>智能手铐管理系统</title>
<link rel="stylesheet" href="//at.alicdn.com/t/c/font_830376_92o68tc95je.css">
<link rel="icon" href="/public/favicon.ico">
<script>window._AMapSecurityConfig = {
securityJsCode: '83572bd6398cb4594c611f93f89b506a'
}</script>
<script
src="https://webapi.amap.com/maps?v=1.4.15&key=e1e6dde852b57c61bacdcf1af21a3d9a&plugin=AMap.MouseTool&plugin=AMap.PolygonEditor&plugin=AMap.CircleEditor&plugin=AMap.MoveAnimation&plugin=AMap.PlaceSearch&plugin=AMap.AutoComplete&plugin=AMap.MoveAnimation"></script>
src="https://webapi.amap.com/maps?v=1.4.15&key=e1e6dde852b57c61bacdcf1af21a3d9a&plugin=AMap.MouseTool&plugin=AMap.PolygonEditor&plugin=AMap.PolyEditor&plugin=AMap.CircleEditor&plugin=AMap.MoveAnimation&plugin=AMap.PlaceSearch&plugin=AMap.AutoComplete&plugin=AMap.MoveAnimation"></script>
<script src=https://a.amap.com/jsapi_demos/static/demo-center/js/demoutils.js></script>
</head>

View File

@ -1,5 +1,5 @@
{
"name": "vue-manage-system",
"name": "yak_handcuffs",
"version": "5.5.0",
"private": true,
"scripts": {

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

BIN
public/logo.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 425 KiB

59
src/api/index.d.ts vendored
View File

@ -9,6 +9,11 @@ export interface IpagingRes<T> {
pages: number
records: T[]
}
export interface TRoleList {
id: number
name: string
createTime: string
}
export namespace TLogin {
export interface Ireq {
@ -150,3 +155,57 @@ export namespace TDevice {
}
}
export namespace TOrg {
export interface IAdd {
name: string;
rails: string;
parentId: string;
}
export interface IListReq extends Ipaging {
name?: number;
}
export interface Idel {
id: number;
}
export interface IOrgRecordRes {
id: number
name: string
parentId: number
status: number
updateUser: string
updateTime: string
creatUser: string
createTime: string
}
}
export interface TStatisticsDevice {
addCount: number
deviceTotal: number
onlineCount: number
warnCount: number
}
export interface statisticsContentReq {
type: string
startDate: string
endDate: string
}
export interface statisticsContentRes {
sosCount: number
railCount: number
destroyCount: number
batteryCount: number
heartRateCount: number
bloodOxygenCount: number
tempCount: number
times: any[],
sosArr: any[],
railArr: any[],
destroyArr: any[],
healthArr: any[],
}

View File

@ -1,5 +1,5 @@
import request from '../utils/request';
import { TLogin, TAccount, IpagingRes, TDevice } from "./index.d";
import { TLogin, TAccount, IpagingRes, TDevice, TOrg, TRoleList, TStatisticsDevice, statisticsContentReq, statisticsContentRes } from "./index.d";
export const fetchLogin = (p: TLogin.Ireq): Promise<TLogin.IRes> => {
return request({
@ -9,6 +9,15 @@ export const fetchLogin = (p: TLogin.Ireq): Promise<TLogin.IRes> => {
});
};
// 批量导入账号样例
export const exportDemoAccount = (): Promise<any> => {
return request({
url: '/v1/web/upload/exportDemo/account',
method: 'get',
responseType: 'blob'
});
};
export const fetchData = () => {
return request({
@ -139,3 +148,62 @@ export const warningRecord = (p: TDevice.IRecordReq): Promise<IpagingRes<TDevice
params: p
});
};
// 新增机构
export const orgAdd = (p: TOrg.IAdd): Promise<null> => {
return request({
url: '/v1/web/org/add',
method: 'post',
data: p
});
};
// 修改机构
export const orgModify = (p: TOrg.IAdd): Promise<null> => {
return request({
url: '/v1/web/org/modify',
method: 'post',
data: p
});
};
// 机构列表
export const orgList = (p?: TOrg.IListReq): Promise<IpagingRes<TOrg.IOrgRecordRes>> => {
return request({
url: '/v1/web/org/list',
method: 'get',
params: p
});
};
// 删除机构
export const orgDelete = (p?: TOrg.Idel): Promise<null> => {
return request({
url: '/v1/web/org/delete',
method: 'post',
data: p
});
};
// 获取角色列表
export const roleList = (): Promise<TRoleList[]> => {
return request({
url: '/v1/web/account/role/list',
method: 'get',
});
};
// 设备在线统计
export const statisticsDevice = (): Promise<TStatisticsDevice> => {
return request({
url: '/v1/web/statistics/device',
method: 'get',
});
};
// 内容数据
export const statisticsContent = (p: statisticsContentReq): Promise<statisticsContentRes> => {
return request({
url: '/v1/web/statistics/content',
method: 'post',
data: p
});
};

BIN
src/assets/img/m1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 983 B

BIN
src/assets/img/m1_a.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 802 B

BIN
src/assets/img/m2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
src/assets/img/m2_a.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 983 B

BIN
src/assets/img/m3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 717 B

BIN
src/assets/img/m3_a.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 580 B

BIN
src/assets/img/m4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1011 B

BIN
src/assets/img/m4_a.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 729 B

View File

@ -1,18 +1,12 @@
<template>
<el-dialog title="批量创建" v-model="dialogVisible" width="500px" destroy-on-close :close-on-click-modal="false">
<el-upload
:on-success="handleSuccess"
:before-upload="beforeUpload"
class="upload-demo"
drag
action="https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15"
>
<el-upload :headers="HEADERS" :on-success="handleSuccess" :on-error="handleError" :before-upload="beforeUpload" class="upload-demo" drag :action="ACTION">
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
<div class="el-upload__text"><em>点击</em>或将文件拖拽到这里上传</div>
<template #tip>
<div class="tip-text">
<div class="el-upload__tip">只能上传excel文件且不超过500KB</div>
<div class="down el-upload__tip">下载导入模板</div>
<div class="down el-upload__tip" @click="dowdDemo">下载导入模板</div>
</div>
</template>
</el-upload>
@ -28,11 +22,35 @@
<script setup lang="ts">
import { ref } from "vue";
import { ElMessage } from "element-plus";
import { useCommonStore } from "@/store/common";
import { exportDemoAccount } from "@/api/index";
interface Tdata {
code: number;
data: {
errMsg: string;
errorCount: number;
};
msg: string;
}
const comm = useCommonStore();
const ACTION = import.meta.env.VITE_APP_URL + "/v1/web/upload/account";
const HEADERS = {
"Access-Token": comm.user.token,
};
const dialogVisible = ref(false);
defineExpose({ dialogVisible });
const handleSuccess = (rawFile: any) => {
console.log(rawFile);
const emit = defineEmits(["success"]);
const handleSuccess = ({ code, data, msg }: Tdata) => {
if (code != 200) return ElMessage.error(msg);
if (data.errorCount > 0) return ElMessage.error({ message: data.errMsg, dangerouslyUseHTMLString: true });
ElMessage.success("用户导入成功");
emit("success");
};
const handleError = ({ msg }: { msg: string }) => {
ElMessage.error(msg);
};
const beforeUpload = (rawFile: any) => {
if (rawFile.size / 1024 > 500) {
@ -43,10 +61,25 @@ const beforeUpload = (rawFile: any) => {
ElMessage.error("只能上传excel文件");
return false;
}
dialogVisible.value = false;
};
const dowdDemo = async () => {
try {
const response = await exportDemoAccount();
const blob = new Blob([response], { type: "application/vnd.ms-excel" });
const link = document.createElement("a");
link.href = window.URL.createObjectURL(blob);
link.download = "批量创建用户模板.xlsx";
link.click();
window.URL.revokeObjectURL(link.href);
ElMessage.success("模板下载成功");
} catch (error) {
ElMessage.error("模板下载失败");
}
};
</script>
<style scoped>
<style scoped lang="less">
.tip-text {
display: flex;
align-items: center;
@ -56,7 +89,9 @@ const beforeUpload = (rawFile: any) => {
.down {
cursor: pointer;
text-decoration: underline;
color: #409eff;
}
/deep/ .el-upload-list {
display: none;
}
</style>

View File

@ -20,9 +20,7 @@
<template v-if="item.children">
<el-sub-menu :index="item.name" :key="index">
<template #title>
<el-icon>
<component :is="item.meta.icon"></component>
</el-icon>
<div class="icon" :style="getBackgroundStyle(item.meta.icon, item.meta.activeIcon)"></div>
<span>{{ item.meta.tabs[0] }}</span>
</template>
<template v-for="(subItem, subIde) in item.children">
@ -41,9 +39,7 @@
</template>
<template v-else>
<el-menu-item :index="item.name" :key="index" @click="menuClick(item)">
<el-icon>
<component :is="item.meta.icon"></component>
</el-icon>
<div class="icon" :style="getBackgroundStyle(item.meta.icon, item.meta.activeIcon)"></div>
<template #title>{{ item.meta.tabs[0] }}</template>
</el-menu-item>
</template>
@ -65,9 +61,17 @@ const onRoutes = computed(() => {
if (String(route.name).indexOf("/") != -1) {
return String(route.name).split("/")[0];
}
return route.name;
});
const getBackgroundStyle = (icon: string, activeIcon: string) => {
return {
"--icon": `url(${icon})`,
"--activeIcon": `url(${activeIcon})`,
};
};
const menuClick = (item: RouteRecordRaw) => {
router.push(item.path);
};
@ -160,6 +164,11 @@ const collapseChage = () => {
color: #061451;
background: #061451;
color: #fff;
.icon {
background-image: var(--activeIcon) !important;
background-repeat: no-repeat;
background-size: 20px 20px;
}
}
.el-menu-item {
border-radius: 6px;
@ -167,9 +176,19 @@ const collapseChage = () => {
margin: 0 auto;
display: flex;
align-items: center;
.icon {
background-image: var(--icon);
background-repeat: no-repeat;
background-size: 20px 20px;
}
&:hover {
background: #061451;
color: #fff;
.icon {
background-image: var(--activeIcon) !important;
background-repeat: no-repeat;
background-size: 20px 20px;
}
}
}
.el-sub-menu {
@ -184,6 +203,11 @@ const collapseChage = () => {
border-radius: 6px;
background: #061451;
color: #fff;
.icon {
background-image: var(--activeIcon) !important;
background-repeat: no-repeat;
background-size: 20px 20px;
}
}
}
@ -191,6 +215,11 @@ const collapseChage = () => {
border-radius: 6px;
width: 228px;
margin: 0 auto;
.icon {
background-image: var(--icon);
background-repeat: no-repeat;
background-size: 20px 20px;
}
}
.el-menu {
.el-menu-item {
@ -219,6 +248,11 @@ const collapseChage = () => {
.sidebar-el-menu {
flex: 1;
.icon {
width: 20px;
height: 20px;
margin-right: 10px;
}
// border: none;
// min-height: 100%;
}

View File

@ -7,7 +7,6 @@ import { usePermissStore } from './store/permiss';
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
import 'element-plus/dist/index.css';
import './assets/css/icon.css';
import { ElNotification } from 'element-plus'
const app = createApp(App);
const pinia = createPinia();

View File

@ -5,6 +5,14 @@ import { useTabsStore } from "@/store/tabs";
import Layout from '@/layout/index.vue';
import NProgress from 'nprogress';
import 'nprogress/nprogress.css';
import m1 from "@/assets/img/m1.png";
import m2 from "@/assets/img/m2.png";
import m3 from "@/assets/img/m3.png";
import m4 from "@/assets/img/m4.png";
import m1_a from "@/assets/img/m1_a.png";
import m2_a from "@/assets/img/m2_a.png";
import m3_a from "@/assets/img/m3_a.png";
import m4_a from "@/assets/img/m4_a.png";
export const routes: RouteRecordRaw[] = [
{
@ -16,37 +24,37 @@ export const routes: RouteRecordRaw[] = [
path: '/statisticalCenter',
name: 'statisticalCenter',
component: () => import('@/views/statisticalCenter/index.vue'),
meta: { tabs: ['统计中心'], icon: 'el-icon-set-up' }
meta: { tabs: ['统计中心'], icon: m1, activeIcon: m1_a }
},
{
path: '/monitoringCenter',
name: 'monitoringCenter',
component: () => import('@/views/monitoringCenter/index.vue'),
meta: { tabs: ['监控中心'], icon: 'el-icon-set-up' }
meta: { tabs: ['监控中心'], icon: m2, activeIcon: m2_a }
},
{
path: '/alarmCenter',
name: 'alarmCenter',
component: () => import('@/views/alarmCenter/index.vue'),
meta: { tabs: ['告警中心'], icon: 'el-icon-set-up' },
meta: { tabs: ['告警中心'], icon: m3, activeIcon: m3_a },
},
{
path: '/incidentDispose',
name: 'alarmCenter/incidentDispose',
component: () => import('@/views/incidentDispose/index.vue'),
meta: { tabs: ['告警中心', '处理事件'], icon: 'el-icon-set-up', hideMenu: true, }
meta: { tabs: ['告警中心', '处理事件'], hideMenu: true, }
},
{
path: '/synthesizeManage',
name: 'synthesizeManage',
meta: { tabs: ['综合管理中心'], icon: 'el-icon-school' },
meta: { tabs: ['综合管理中心'], icon: m4, activeIcon: m4_a, },
children: [
{
path: '/synthesizeManage/deviceManage',
name: 'deviceManage',
component: () => import('@/views/synthesizeManage/deviceManage/index.vue'),
meta: { tabs: ['综合管理中心', '设备管理'], icon: 'el-icon-set-up' }
meta: { tabs: ['综合管理中心', '设备管理'], }
},
{
path: '/synthesizeManage/deviceInfo',
@ -70,13 +78,13 @@ export const routes: RouteRecordRaw[] = [
path: '/synthesizeManage/userManage',
name: 'userManage',
component: () => import('@/views/synthesizeManage/userManage/index.vue'),
meta: { tabs: ['综合管理中心', '人员管理'], icon: 'el-icon-set-up' }
meta: { tabs: ['综合管理中心', '人员管理'] }
},
{
path: '/synthesizeManage/areaManage',
name: 'areaManage',
component: () => import('@/views/synthesizeManage/areaManage/index.vue'),
meta: { tabs: ['综合管理中心', '辖区管理'], icon: 'el-icon-set-up' }
meta: { tabs: ['综合管理中心', '辖区管理'] }
},
]
},

View File

@ -8,6 +8,36 @@ export class MapCustom {
...data,
});
}
// 多边形回显
polyEditor(list, fn) {
this.map.clearMap();
let pathList = [];
list.forEach((item, index) => {
let lngLat = new AMap.LngLat(item.lng, item.lat);
pathList.push(lngLat);
});
const circle = new AMap.Polygon({
map: this.map,
path: pathList, // 圆心位置
strokeWeight: 2,
strokeStyle: "solid",
fillColor: "#00aeff57",
strokeColor: "#00AEFF", //描边颜色
fillOpacity: 0.3, //填充透明度
});
let PolygonEditor = new AMap.PolyEditor(this.map, circle);
PolygonEditor.open();
PolygonEditor.on("adjust", (event) => {
const pathList = event.target.getPath();
let list = [];
pathList.forEach((e, i) => {
list.push({ lat: e.lat, lng: e.lng });
});
fn && fn(list);
// this.ruleForm.points = list;
});
}
draw() {
return new Promise((resolve) => {
this.drawResolve = resolve;

View File

@ -22,23 +22,22 @@ service.interceptors.request.use(
service.interceptors.response.use(
(response: AxiosResponse) => {
const { data } = response;
if (data.code !== 200) {
ElMessage.error(data.msg)
if (data.code === 1003) {
// const comm = useCommonStore();
// comm.clearStore()
// router.replace('/login');
const { data, headers } = response;
if (headers['content-type'] == 'application/json') {
if (data.code !== 200) {
ElMessage.error(data.msg)
if (data.code === 1003) {
const comm = useCommonStore();
comm.clearStore()
router.replace('/login');
}
return Promise.reject(data);
} else {
return data.data;
}
return Promise.reject(data);
}
if (response.status === 200) {
return data.data;
} else {
Promise.reject(data);
}
return data;
},
(error: AxiosError) => {
console.log(error);

View File

@ -3,21 +3,23 @@
<div class="card-head">
<div class="title">内容数据</div>
<div class="condition">
<el-button-group>
<el-button type="primary">心率</el-button>
<el-button>血氧</el-button>
<el-button>体表温度</el-button>
</el-button-group>
<el-radio-group v-model="radioType">
<el-radio-button label="时" value="hour" />
<el-radio-button label="天" value="day" />
<el-radio-button label="周" value="week" />
</el-radio-group>
<div class="condition-time">
<el-date-picker v-if="radioType == 'week'" v-model="value" type="week" value-format="YYYY-MM-DD" placeholder="请选择周" @change="handleWeekChange" />
<el-date-picker
v-else
style="width: 350px"
v-model="value1"
type="datetimerange"
start-placeholder="Start date"
end-placeholder="End date"
format="YYYY-MM-DD HH:mm:ss"
date-format="YYYY/MM/DD ddd"
time-format="A hh:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
start-placeholder="开始时间"
end-placeholder="结束时间"
@change="handleDateChange"
/>
</div>
</div>
@ -27,7 +29,23 @@
</template>
<script setup>
import { ref } from "vue";
const value = ref("");
const value1 = ref("");
const radioType = ref("hour");
const { api } = defineProps({
api: {
type: Function,
required: true,
},
});
const handleWeekChange = (val, res) => {
console.log(val, "handleWeekChange");
api({ type: radioType.value, startDate: `${val} 00:00:00`, endDate: `${val} 00:00:00` });
};
const handleDateChange = (val) => {
api({ type: radioType.value, startDate: val[0], endDate: val[1] });
};
</script>
<style scoped lang="less">
.card {

View File

@ -1,9 +1,9 @@
<template>
<div class="container scrollbar">
<MonitoringTop />
<MonitoringTop :list="funcList" />
<el-row :gutter="20">
<el-col class="elcol" :span="16" :xs="24" :sm="24" :md="24" :lg="16" :xl="16">
<DeviceHistory>
<DeviceHistory :api="getStatisticsContent">
<template #chart>
<div ref="chartRef" style="flex: 1"></div>
</template>
@ -38,10 +38,15 @@ import OnLineStatistics from "./onLineStatistics.vue";
import EmergencyList from "./emergencyList.vue";
import * as echarts from "echarts";
import { MapCustom } from "@/utils/mapCustom";
import { fetchData } from "@/api/index";
import { fetchData, statisticsDevice, statisticsContent } from "@/api/index";
import { TableItem } from "@/types/table";
import { onMounted, ref, reactive, onDeactivated } from "vue";
import { debounce } from "@/utils";
import { TStatisticsDevice } from "@/api/index.d";
import handcuffs from "@/assets/img/handcuffs.png";
import onLine from "@/assets/img/onLine.png";
import report from "@/assets/img/report.png";
import newly from "@/assets/img/newly.png";
const chartRef = ref(null);
const chartRef1 = ref(null);
@ -51,6 +56,13 @@ let myChart = null;
let myChart1 = null;
let myChart2 = null;
let funcList = ref([
{ title: "手铐总数", en: "SHOUKAOZONGSHU", icon: handcuffs, num: 0, color: "##71DDF9" },
{ title: "在线数量", en: "ZAIXIANSHULIANG", icon: onLine, num: 0, color: "##71DDF9" },
{ title: "告警数量", en: "GAOJINGSHULIANG", icon: report, num: 0, color: "#8B51FD" },
{ title: "较昨日新增", en: "JIAOZUORIXINZENG", icon: newly, num: 0, color: "#FF6905" },
]);
const option = {
xAxis: {
type: "category",
@ -100,7 +112,17 @@ const getData = async () => {
const res = await fetchData();
tableData.value = res.data.list;
};
getData();
const getStatisticsDevice = () => {
statisticsDevice().then((res) => {
funcList.value[0].num = res.deviceTotal;
funcList.value[1].num = res.onlineCount;
funcList.value[2].num = res.warnCount;
funcList.value[3].num = res.addCount;
});
};
const getStatisticsContent = (res) => {
statisticsContent(res).then((res) => {});
};
const changePage = (val: number) => {
page.index = val;
@ -114,6 +136,9 @@ const handleResize = debounce(() => {
}, 200);
onMounted(() => {
getData();
getStatisticsDevice();
new MapCustom({ dom: "mapcontainer" });
if (chartRef.value) {

View File

@ -1,6 +1,6 @@
<template>
<el-row :gutter="20" class="monitoring-top">
<el-col :span="24" :xs="24" :sm="12" :md="12" :lg="6" :xl="6" v-for="item in funcList">
<el-col :span="24" :xs="24" :sm="12" :md="12" :lg="6" :xl="6" v-for="item in list">
<div class="item">
<div class="item-left">
<div class="item-left-head">
@ -20,16 +20,13 @@
</el-row>
</template>
<script setup>
import handcuffs from "@/assets/img/handcuffs.png";
import onLine from "@/assets/img/onLine.png";
import report from "@/assets/img/report.png";
import newly from "@/assets/img/newly.png";
let funcList = [
{ title: "手铐总数", en: "SHOUKAOZONGSHU", icon: handcuffs, num: 1326, color: "##71DDF9" },
{ title: "在线数量", en: "SHOUKAOZONGSHU", icon: onLine, num: 326, color: "##71DDF9" },
{ title: "告警数量", en: "SHOUKAOZONGSHU", icon: report, num: 99, color: "#8B51FD" },
{ title: "较昨日新增", en: "SHOUKAOZONGSHU", icon: newly, num: 86, color: "#FF6905" },
];
const { list } = defineProps({
//
list: {
type: Array,
default: () => [],
},
});
</script>
<style scoped lang="less">
.monitoring-top {

View File

@ -21,8 +21,8 @@
version="1.1"
xmlns="http://www.w3.org/2000/svg"
p-id="5520"
width="18"
height="18"
width="25"
height="25"
>
<path
d="M229.12 475.52l687.296 183.872-1.408 1.472-1.024 12.864c-2.56 34.048-6.4 62.976-17.92 139.52l-6.528 43.648c-3.84 26.048-6.528 46.976-8.768 66.304-4.224 36.544-23.488 65.28-54.656 81.6-15.424 8.128-32 12.672-45.824 13.44l-4.032 0.192-90.432-24.32a18.88 18.88 0 0 1-12.16-26.688l35.392-71.552-20.352 26.24a515.328 515.328 0 0 1-47.36 50.56 19.008 19.008 0 0 1-18.112 4.672l-75.456-20.16a18.88 18.88 0 0 1-12.16-26.624l35.456-71.552-20.352 26.24a510.336 510.336 0 0 1-47.36 50.56 18.944 18.944 0 0 1-18.112 4.672l-88.448-23.68a18.944 18.944 0 0 1-12.096-26.56l35.392-71.552-20.352 26.24a515.84 515.84 0 0 1-47.36 50.56 18.944 18.944 0 0 1-18.112 4.672l-62.528-16.704a18.944 18.944 0 0 1-12.096-26.688l35.392-71.488-20.352 26.24c-8.32 10.688-20.48 24-36.48 39.936a44.736 44.736 0 0 1-43.136 11.52l-78.08-20.864-23.104-8.704c-29.632-25.536-44.608-61.44-39.36-95.552 5.76-37.12 32.32-67.648 82.112-88.704 34.368-14.656 67.584-74.176 89.664-157.44l3.84-14.464-1.024-1.792z m-42.88-76.16a60.224 60.224 0 0 1 5.056-6.4l123.776-135.424c9.92-10.752 24.832-19.392 41.856-23.936 16.64-4.416 33.984-4.672 48.64-0.768l123.584 33.024 4.096-2.368 55.36-205.824c8.704-32.448 43.968-51.456 78.72-42.24l83.2 22.272c34.752 9.28 55.808 43.392 47.104 75.84l-55.36 205.824 2.368 4.096 118.528 31.68c14.592 3.84 29.568 12.8 41.728 24.96 12.416 12.352 21.12 27.2 24.192 41.472l39.36 179.072a58.688 58.688 0 0 1 1.28 9.856c-0.704 6.592-5.312 10.304-13.696 11.2L194.944 418.048c-10.432-2.944-13.376-9.216-8.768-18.752z"
@ -39,7 +39,7 @@
</template>
<template #reference>
<div @click="draw">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 34.295 30">
<svg xmlns="http://www.w3.org/2000/svg" width="25" height="25" viewBox="0 0 34.295 30">
<g id="组_608" data-name=" 608" transform="translate(-1304 -128.546)">
<path
id="路径_489"
@ -76,7 +76,7 @@
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="handleBtn()">取消</el-button>
<el-button @click="closeDialog">取消</el-button>
<el-button type="primary" @click="handleBtn(ruleFormRef)">确定</el-button>
</div>
</template>
@ -86,19 +86,37 @@
import { ref, reactive, onMounted, nextTick } from "vue";
import { MapCustom } from "@/utils/mapCustom";
import type { FormRules, FormInstance } from "element-plus";
import { useCommonStore } from "@/store/common";
const comm = useCommonStore();
const { update, rowData } = defineProps({
update: {
type: Function,
required: true,
},
rowData: {
type: Object,
dfault: () => ({}),
},
});
const visible = ref(true);
const ruleFormRef = ref<FormInstance>();
const ruleForm = reactive({
name: "",
location: "",
});
const ruleForm = reactive(
rowData.id
? { ...rowData }
: {
name: "",
rails: "",
parentId: comm.user.orgId,
}
);
let mapInfo: any = null;
const emit = defineEmits(["closeDialog"]);
const emit = defineEmits(["closeDialog", "handleBtn"]);
const validatePass = (_: any, _1: any, callback: any) => {
if (ruleForm.location == "") {
if (ruleForm.rails == "") {
callback(new Error("请绘制围栏"));
} else {
callback();
@ -107,7 +125,7 @@ const validatePass = (_: any, _1: any, callback: any) => {
const rules = reactive<FormRules<typeof ruleForm>>({
name: [{ required: true, message: "请输入辖区名称" }],
location: [
rails: [
{ required: true, message: "请绘制围栏" },
{ validator: validatePass, trigger: "blur" },
],
@ -116,17 +134,21 @@ const rules = reactive<FormRules<typeof ruleForm>>({
onMounted(() => {
nextTick(() => {
mapInfo = new MapCustom({ dom: "mapcontainer" });
console.log(ruleForm, "ruleForm");
mapInfo.polyEditor(ruleForm.rails, (res) => {
ruleForm.rails = res;
});
});
});
const clearMap = () => {
mapInfo.clearMap();
ruleForm.location = "";
ruleForm.rails = "";
};
const draw = () => {
mapInfo.draw().then((res) => {
console.log(res, 1111);
ruleForm.location = JSON.stringify(res);
ruleForm.rails = res;
});
};
const closeDialog = () => {
@ -136,12 +158,10 @@ const handleBtn = (formEl?: FormInstance | undefined) => {
if (formEl) {
formEl.validate((valid) => {
if (valid) {
console.log("submit!");
update(ruleForm);
}
});
} else {
}
emit("closeDialog");
};
</script>

View File

@ -2,28 +2,24 @@
<div class="container">
<TableSearch :query="query" :options="searchOpt" :search="handleSearch" />
<div class="table-container">
<TableCustom :columns="columns" :tableData="tableData" :total="page.total" :refresh="getData" :currentPage="page.index" :changePage="changePage">
<TableCustom :columns="columns" :tableData="tableData" :total="paging.total" :refresh="getData" :currentPage="paging.page" :changePage="changePage">
<template #toolbarBtn>
<el-button type="primary" @click="handleAdd">新增</el-button>
<el-button>导出</el-button>
<el-button type="danger">删除</el-button>
<!-- <el-button>导出</el-button> -->
<!-- <el-button type="danger">删除</el-button> -->
</template>
<template #money="{ rows }"> {{ rows.money }} </template>
<template #thumb="{ rows }">
<el-image class="table-td-thumb" :src="rows.thumb" :z-index="10" :preview-src-list="[rows.thumb]" preview-teleported> </el-image>
</template>
<template #state="{ rows }">
<el-tag :type="rows.state ? 'success' : 'danger'">
{{ rows.state ? "正常" : "异常" }}
<template #status="{ rows }">
<el-tag :type="rows.status ? 'success' : 'danger'">
{{ StatusEnum[rows.status] }}
</el-tag>
</template>
<template #operator="{ rows }">
<el-button link type="primary" size="small" @click="handleEdit(rows)"> 修改 </el-button>
<el-button link type="danger" size="small" @click="handelDel(rows)"> 删除 </el-button>
<el-button link type="danger" size="small" @click="handelDel(rows.id)"> 删除 </el-button>
</template>
</TableCustom>
</div>
<AddFence ref="addRef" v-if="visible" @closeDialog="visible = false" />
<AddFence ref="addRef" :rowData="rowData" v-if="visible" @closeDialog="visible = false" :update="updateData" />
</div>
</template>
@ -31,14 +27,19 @@
import { ref, reactive } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import { CirclePlusFilled } from "@element-plus/icons-vue";
import { fetchData } from "@/api/index";
import { fetchData, orgAdd, orgModify, orgList, orgDelete } from "@/api/index";
import TableCustom from "@/components/table-custom.vue";
import TableSearch from "@/components/table-search.vue";
import AddFence from "./addFence.vue";
import { TableItem } from "@/types/table";
import { FormOptionList } from "@/types/form-option";
import Alarm from "@/components/alarm.vue";
import { TOrg } from "@/api/index.d";
// 0- 1-
enum StatusEnum {
"禁用" = 0,
"启用",
}
//
const query = reactive({
name: "",
@ -59,31 +60,33 @@ const searchOpt = ref<FormOptionList[]>([
//
let columns = ref([
{ type: "selection" },
// { type: "selection" },
{ type: "index", label: "序号", width: 55, align: "center" },
{ prop: "name", label: "辖区编号" },
{ prop: "money", label: "辖区名称" },
{ prop: "thumb", label: "创建角色" },
{ prop: "time", label: "创建时间" },
{ prop: "state", label: "账户状态" },
{ prop: "id", label: "辖区编号" },
{ prop: "name", label: "辖区名称" },
{ prop: "creatUser", label: "创建角色" },
{ prop: "createTime", label: "创建时间" },
// { prop: "status", label: "" },
{ prop: "operator", label: "操作", width: 200 },
]);
const page = reactive({
index: 1,
const paging = reactive({
page: 1,
size: 10,
total: 200,
total: 0,
});
const tableData = ref<TableItem[]>([]);
const tableData = ref<TOrg.IOrgRecordRes[]>([]);
const getData = async () => {
const res = await fetchData();
tableData.value = res.data.list;
const res = await orgList(paging);
tableData.value = res.records;
paging.total = res.total;
};
getData();
const handleSearch = () => {
changePage(1);
};
const changePage = (val: number) => {
page.index = val;
paging.page = val;
getData();
};
@ -97,12 +100,15 @@ const handleEdit = (row: TableItem) => {
visible.value = true;
};
//
const handelDel = (row: TableItem) => {
const handelDel = (id: number) => {
ElMessageBox.confirm("确定要删除吗?", "提示", {
type: "warning",
})
.then(async () => {
// ElMessage.success("");
orgDelete({ id }).then(() => {
ElMessage.success("删除成功");
getData();
});
})
.catch(() => {});
};
@ -110,6 +116,18 @@ const handelDel = (row: TableItem) => {
const handleAdd = () => {
visible.value = true;
};
const updateData = (res) => {
let api, msg;
api = res.id ? orgModify : orgAdd;
msg = res.id ? "修改成功" : "新增成功";
api(res).then((res) => {
ElMessage.success(msg);
visible.value = false;
getData();
});
};
</script>
<style scoped></style>

View File

@ -57,6 +57,7 @@ import DeviceControl from "./deviceControl.vue";
import { TableItem } from "@/types/table";
import { FormOption, FormOptionList } from "@/types/form-option";
import { useRouter } from "vue-router";
enum stateEnum {
"离线" = 0,
"在线",

View File

@ -43,14 +43,14 @@
<el-dialog title="编辑类型" v-model="typeVisible" width="800px" destroy-on-close :close-on-click-modal="false" @close="closeDialog">
<UserType />
</el-dialog>
<BatchImp ref="batchImpRef" />
<BatchImp ref="batchImpRef" @success="handleSearch" />
</div>
</template>
<script setup lang="ts" name="basetable">
import { ref, reactive } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import { accountAdd, accountModify, accountList, accountDeletet, passwordReset } from "@/api/index";
import { accountAdd, accountModify, accountList, accountDeletet, passwordReset, roleList } from "@/api/index";
import TableCustom from "@/components/table-custom.vue";
import TableSearch from "@/components/table-search.vue";
import TableEdit from "@/components/table-edit.vue";
@ -59,7 +59,7 @@ import { useCommonStore } from "@/store/common";
import UserType from "./userType.vue";
import { TableItem } from "@/types/table";
import { FormOption, FormOptionList } from "@/types/form-option";
import { TAccount } from "@/api/index.d";
import { TAccount, TRoleList } from "@/api/index.d";
// import { Hide, View } from "@element-plus/icons-vue";
enum roleEnum {
@ -75,6 +75,7 @@ const typeVisible = ref(false);
const isEdit = ref(false);
const dialogTitle = ref("");
const rowData = ref<TAccount.IListRes | undefined>();
//
const query = reactive({
name: "",
@ -86,6 +87,7 @@ const paging = reactive({
total: 0,
});
const tableData = ref<TAccount.IListRes[]>([]);
const roleListData = ref<TRoleList[]>([]);
const searchOpt = ref<FormOptionList[]>([
{ type: "input", label: "用户名:", prop: "name" },
@ -178,7 +180,14 @@ const changePage = (val: number) => {
paging.page = val;
getData();
};
const roleListFn = () => {
roleList().then((res) => {
let opts = res.map((item) => {
return { label: item.name, value: item.id };
});
addOpt.list[3].opts = opts;
});
};
const getData = async () => {
const res = await accountList(paging);
tableData.value = res.records.map((item) => {
@ -187,6 +196,7 @@ const getData = async () => {
});
paging.total = res.total;
};
roleListFn();
getData();
//

View File

@ -53,6 +53,7 @@
</template>
</el-table-column>
</el-table-column>
<el-table-column prop="date" label="操作" width="200" align="center">
<template #default="scope">
<el-button link type="primary" size="small">锁定</el-button>