2025年04月29日18:31:00
This commit is contained in:
parent
2b4a5a1002
commit
744295e916
@ -1,8 +1,8 @@
|
||||
# VITE_APP_URL = 'http://192.168.3.116:8001/'
|
||||
# VITE_APP_URL_WEBSOCKET = 'http://192.168.3.116:8000/api/websocket'
|
||||
VITE_APP_URL = 'http://192.168.3.116:8001/'
|
||||
VITE_APP_URL_WEBSOCKET = 'http://192.168.3.116:8000/api/websocket'
|
||||
|
||||
VITE_APP_URL = 'http://47.112.185.26:8001/'
|
||||
VITE_APP_URL_WEBSOCKET = 'ws://47.112.185.26:8000/api/websocket'
|
||||
# VITE_APP_URL = 'http://47.112.185.26:8001/'
|
||||
# VITE_APP_URL_WEBSOCKET = 'ws://47.112.185.26:8000/api/websocket'
|
||||
|
||||
# VITE_APP_URL = 'http://api.handcuff.zhuhaiguangdun.cn'
|
||||
# VITE_APP_URL_WEBSOCKET = 'ws://device.handcuff.zhuhaiguangdun.cn:8000/api/websocket'
|
||||
|
1
components.d.ts
vendored
1
components.d.ts
vendored
@ -44,7 +44,6 @@ declare module '@vue/runtime-core' {
|
||||
ElUpload: typeof import('element-plus/es')['ElUpload']
|
||||
Header: typeof import('./src/components/header.vue')['default']
|
||||
InfoWindow: typeof import('./src/components/InfoWindow.vue')['default']
|
||||
MyComm: typeof import('./src/components/MyComm.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
Sidebar: typeof import('./src/components/sidebar.vue')['default']
|
||||
|
@ -12,7 +12,7 @@
|
||||
securityJsCode: '83572bd6398cb4594c611f93f89b506a'
|
||||
}</script>
|
||||
<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>
|
||||
src="https://webapi.amap.com/maps?v=1.4.15&key=e1e6dde852b57c61bacdcf1af21a3d9a&plugin=AMap.MouseTool&plugin=AMap.MarkerClusterer=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>
|
||||
|
||||
|
2
src/api/index.d.ts
vendored
2
src/api/index.d.ts
vendored
@ -130,9 +130,11 @@ export namespace TLogin {
|
||||
|
||||
export namespace TAccount {
|
||||
export interface IAdd {
|
||||
id?: number | string;
|
||||
username: string;
|
||||
phone: string;
|
||||
name: string;
|
||||
password?: string;
|
||||
orgId?: number | string;
|
||||
roleId: number | string;
|
||||
status: number;
|
||||
|
BIN
src/assets/img/dir-via-marker.png
Normal file
BIN
src/assets/img/dir-via-marker.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.4 KiB |
Binary file not shown.
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 4.9 KiB |
Binary file not shown.
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 4.2 KiB |
@ -50,20 +50,20 @@ const onMessage = (res) => {
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
// const isReload = localStorage.getItem("isReload");
|
||||
// if (isReload !== "true") {
|
||||
// ElMessageBox.alert("由于浏览器安全策略,用户必须点击屏幕才能播放告警声音", "提示", {
|
||||
// confirmButtonText: "OK",
|
||||
// callback: () => {
|
||||
// localStorage.setItem("isReload", "true");
|
||||
// },
|
||||
// });
|
||||
// }
|
||||
const isReload = localStorage.getItem("isReload");
|
||||
if (isReload !== "true") {
|
||||
ElMessageBox.alert("由于浏览器安全策略,用户必须点击屏幕才能播放告警声音", "提示", {
|
||||
confirmButtonText: "OK",
|
||||
callback: () => {
|
||||
localStorage.setItem("isReload", "true");
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// if (ws.socket == null) {
|
||||
// ws.connect();
|
||||
// }
|
||||
// ws.onMessage(onMessage);
|
||||
if (ws.socket == null) {
|
||||
ws.connect();
|
||||
}
|
||||
ws.onMessage(onMessage);
|
||||
|
||||
window.addEventListener("beforeunload", handleBeforeUnload);
|
||||
});
|
||||
|
@ -118,7 +118,6 @@ export class MapCustom {
|
||||
// 创建marker
|
||||
marker(option) {
|
||||
return new AMap.Marker({
|
||||
map: this.map,
|
||||
offset: new AMap.Pixel(-13, -26),
|
||||
...option
|
||||
})
|
||||
|
@ -107,7 +107,7 @@ export default class WebSocketService {
|
||||
onMessage = (callback) => {
|
||||
this.socket.onmessage = (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
console.log(data, 'onMessage');
|
||||
// console.log(data, 'onMessage');
|
||||
|
||||
if (data.cmd == "webLogin" && !data.code == 200) return this.webScoketLogin()
|
||||
callback && callback(data);
|
||||
|
@ -64,9 +64,9 @@
|
||||
<el-form-item label="处理记录:" prop="content">
|
||||
<el-input v-model="ruleForm.content" clearable :autosize="{ minRows: 2, maxRows: 4 }" type="textarea" :disabled="disabled" />
|
||||
</el-form-item>
|
||||
<el-form-item label="处理图片:" prop="images">
|
||||
<!-- <el-form-item label="处理图片:" prop="images">
|
||||
<Upload v-model="ruleForm.images" :disabled="disabled" />
|
||||
</el-form-item>
|
||||
</el-form-item> -->
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
@ -127,7 +127,7 @@ const options = {
|
||||
trigger: "axis",
|
||||
formatter: function (params) {
|
||||
let unit = { 0: "次/分", 1: "%", 2: "℃" };
|
||||
var res = format(options.times[params[0].dataIndex]) + "<br/>";
|
||||
var res = format(Number(params[0].name)) + "<br/>";
|
||||
res += params
|
||||
.map(function (param, index) {
|
||||
return param.marker + param.seriesName + ":" + param.value + unit[index] + "<br/>";
|
||||
@ -149,8 +149,12 @@ const options = {
|
||||
type: "category",
|
||||
boundaryGap: false,
|
||||
data: [],
|
||||
axisLabel: {
|
||||
formatter: function (item) {
|
||||
return format(Number(item), "HH:mm:ss");
|
||||
},
|
||||
},
|
||||
},
|
||||
times: [],
|
||||
yAxis: {
|
||||
type: "value",
|
||||
},
|
||||
@ -270,8 +274,7 @@ const getData = async () => {
|
||||
|
||||
if (res.healthData && res.healthData.length) {
|
||||
res.healthData.forEach((item) => {
|
||||
options.times.push(item.time);
|
||||
options.xAxis.data.push(format(item.time, "HH:mm:ss"));
|
||||
options.xAxis.data.push(item.time);
|
||||
options.series[0].data.push(item.hr);
|
||||
options.series[1].data.push(item.bo);
|
||||
options.series[2].data.push(item.temp);
|
||||
|
@ -25,7 +25,7 @@ const options = {
|
||||
trigger: "axis",
|
||||
formatter: function (params) {
|
||||
let unit = { 心率: "次/分", 血氧: "%", 体表温度: "℃" };
|
||||
var res = format(options.times[params[0].dataIndex]) + "<br/>";
|
||||
var res = format(Number(params[0].name)) + "<br/>";
|
||||
res += params
|
||||
.map(function (param, index) {
|
||||
return param.marker + param.seriesName + ":" + param.value + unit[param.seriesName] + "<br/>";
|
||||
@ -34,10 +34,14 @@ const options = {
|
||||
return res;
|
||||
},
|
||||
},
|
||||
times: [],
|
||||
xAxis: {
|
||||
type: "category",
|
||||
data: [],
|
||||
axisLabel: {
|
||||
formatter: function (item) {
|
||||
return format(Number(item), "HH:mm:ss");
|
||||
},
|
||||
},
|
||||
},
|
||||
yAxis: {
|
||||
type: "value",
|
||||
@ -84,10 +88,10 @@ const options = {
|
||||
const getOptionsData = (list: { time: string; value: number }[], name: string, color: string) => {
|
||||
options.xAxis.data = [];
|
||||
options.series.data = [];
|
||||
|
||||
if (list && list.length) {
|
||||
list.forEach((item) => {
|
||||
options.times.push(item.time);
|
||||
options.xAxis.data.push(format(item.time, "HH:mm:ss"));
|
||||
options.xAxis.data.push(item.time);
|
||||
options.series.data.push(item.value);
|
||||
});
|
||||
options.series.name = name;
|
||||
|
@ -54,6 +54,10 @@
|
||||
<span class="lable">模式:</span>
|
||||
<el-tag :type="modeColor[item.mode]">{{ modeEnum[item.mode] }}</el-tag>
|
||||
</div>
|
||||
<div>
|
||||
<span class="lable">佩戴者:</span>
|
||||
{{ item.userNumber || "--" }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-popover>
|
||||
@ -210,6 +214,10 @@ watch(
|
||||
color: #787878;
|
||||
font-size: 16px;
|
||||
text-align: right;
|
||||
width: 80px;
|
||||
white-space: nowrap; // 强制一行显示
|
||||
overflow: hidden; // 超出隐藏
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -58,26 +58,52 @@ const getLocateRecord = () => {
|
||||
image: ViaMarker,
|
||||
size: [20, 29],
|
||||
});
|
||||
|
||||
let markers = [];
|
||||
list.forEach((item, index) => {
|
||||
let marker: any = "";
|
||||
if (index == 0) {
|
||||
marker = newMap.marker({ icon: endIcon, position: [item.lng, item.lat], zIndex: 13 });
|
||||
} else if (index == list.length - 1) {
|
||||
marker = newMap.marker({ icon: startIcon, position: [item.lng, item.lat], zIndex: 13 });
|
||||
if (list.length < 50) {
|
||||
let marker: any = "";
|
||||
if (index == 0) {
|
||||
marker = newMap.marker({ icon: endIcon, position: [item.lng, item.lat], zIndex: 13 });
|
||||
} else if (index == list.length - 1) {
|
||||
marker = newMap.marker({ icon: startIcon, position: [item.lng, item.lat], zIndex: 13 });
|
||||
} else {
|
||||
marker = newMap.marker({ icon: ViaIcon, position: [item.lng, item.lat], zIndex: 12 });
|
||||
}
|
||||
marker.on("click", () => {
|
||||
locationInfo.value = item;
|
||||
InfoWin = newMap.infoWindow();
|
||||
InfoWin.open(newMap.map, marker.getPosition());
|
||||
});
|
||||
markers.push(marker);
|
||||
} else {
|
||||
marker = newMap.marker({ icon: ViaIcon, position: [item.lng, item.lat], zIndex: 12 });
|
||||
if (index % 5 == 0) {
|
||||
let marker: any = "";
|
||||
if (index == 0) {
|
||||
marker = newMap.marker({ icon: endIcon, position: [item.lng, item.lat], zIndex: 13 });
|
||||
} else if (index == list.length - 1) {
|
||||
marker = newMap.marker({ icon: startIcon, position: [item.lng, item.lat], zIndex: 13 });
|
||||
} else {
|
||||
marker = newMap.marker({ icon: ViaIcon, position: [item.lng, item.lat], zIndex: 12 });
|
||||
}
|
||||
marker.on("click", () => {
|
||||
locationInfo.value = item;
|
||||
InfoWin = newMap.infoWindow();
|
||||
InfoWin.open(newMap.map, marker.getPosition());
|
||||
});
|
||||
|
||||
markers.push(marker);
|
||||
}
|
||||
}
|
||||
marker.setMap(newMap.map);
|
||||
marker.on("click", () => {
|
||||
locationInfo.value = item;
|
||||
InfoWin = newMap.infoWindow();
|
||||
InfoWin.open(newMap.map, marker.getPosition());
|
||||
});
|
||||
});
|
||||
newMap.map.add(markers);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const fn = () => {
|
||||
// this.cluster.on("click", this.clusterClickEvent);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
newMap = new MapCustom({ dom: "mapcontainer" });
|
||||
getLocateRecord();
|
||||
|
@ -52,59 +52,6 @@ let funcList = ref([
|
||||
{ title: "当前体表温度", en: "DANGQIANTIBIAOWENDU", icon: temperature, unit: "℃", num: 0, color: "#FF6905" },
|
||||
]);
|
||||
|
||||
const options = {
|
||||
tooltip: {
|
||||
trigger: "axis",
|
||||
formatter: function (params) {
|
||||
let unit = { 心率: "次/分", 血氧: "%", 体表温度: "℃" };
|
||||
var res = format(options.times[params[0].dataIndex]) + "<br/>";
|
||||
res += params
|
||||
.map(function (param, index) {
|
||||
return param.marker + param.seriesName + ":" + param.value + unit[param.seriesName] + "<br/>";
|
||||
})
|
||||
.join("");
|
||||
return res;
|
||||
},
|
||||
},
|
||||
times: [],
|
||||
xAxis: {
|
||||
type: "category",
|
||||
data: [],
|
||||
},
|
||||
yAxis: {
|
||||
type: "value",
|
||||
},
|
||||
grid: {
|
||||
left: "5%",
|
||||
right: "4%",
|
||||
bottom: "20%",
|
||||
},
|
||||
dataZoom: [
|
||||
{
|
||||
show: true, // 是否显示
|
||||
start: 0, // 开始位置,百分比
|
||||
end: 10, // 结束位置,百分比
|
||||
bottom: "10px",
|
||||
height: 25,
|
||||
},
|
||||
{
|
||||
type: "inside",
|
||||
start: 0,
|
||||
end: 100,
|
||||
},
|
||||
],
|
||||
series: {
|
||||
name: "",
|
||||
data: [],
|
||||
type: "line",
|
||||
showSymbol: false,
|
||||
itemStyle: {
|
||||
color: "#ff4567", // 设置线条颜色
|
||||
},
|
||||
smooth: true,
|
||||
},
|
||||
};
|
||||
|
||||
const devicePaging = reactive({
|
||||
page: 1,
|
||||
size: 10,
|
||||
@ -135,7 +82,6 @@ const getHealthLatestData = () => {
|
||||
funcList.value[0].num = res.hr;
|
||||
funcList.value[1].num = res.bo;
|
||||
funcList.value[2].num = res.temp;
|
||||
|
||||
handelRadio(devHisRef.value.radio);
|
||||
});
|
||||
};
|
||||
|
@ -79,6 +79,11 @@ const option = ref({
|
||||
type: "category",
|
||||
boundaryGap: false,
|
||||
data: [],
|
||||
axisLabel: {
|
||||
formatter: function (item) {
|
||||
return format(item, "MM-DD");
|
||||
},
|
||||
},
|
||||
},
|
||||
yAxis: {
|
||||
type: "value",
|
||||
@ -169,34 +174,36 @@ const getStatisticsUseCount = () => {
|
||||
};
|
||||
const getStatisticsContent = (req) => {
|
||||
statisticsContent(req).then((res) => {
|
||||
option.value.xAxis.data = res?.times;
|
||||
option.value.series = [
|
||||
{
|
||||
stack: "Total",
|
||||
name: "SOS预警数组",
|
||||
data: res?.sosArr,
|
||||
type: "line",
|
||||
},
|
||||
{
|
||||
stack: "Total",
|
||||
name: "围栏预警数组",
|
||||
data: res?.railArr,
|
||||
type: "line",
|
||||
},
|
||||
{
|
||||
stack: "Total",
|
||||
name: "破坏预警数组",
|
||||
data: res?.destroyArr,
|
||||
type: "line",
|
||||
},
|
||||
{
|
||||
stack: "Total",
|
||||
name: "生理预警数组",
|
||||
data: res?.healthArr,
|
||||
type: "line",
|
||||
},
|
||||
];
|
||||
myChart.setOption(option.value);
|
||||
if (res.times && res.times.length) {
|
||||
option.value.xAxis.data = res.times;
|
||||
option.value.series = [
|
||||
{
|
||||
stack: "Total",
|
||||
name: "SOS预警数组",
|
||||
data: res?.sosArr,
|
||||
type: "line",
|
||||
},
|
||||
{
|
||||
stack: "Total",
|
||||
name: "围栏预警数组",
|
||||
data: res?.railArr,
|
||||
type: "line",
|
||||
},
|
||||
{
|
||||
stack: "Total",
|
||||
name: "破坏预警数组",
|
||||
data: res?.destroyArr,
|
||||
type: "line",
|
||||
},
|
||||
{
|
||||
stack: "Total",
|
||||
name: "生理预警数组",
|
||||
data: res?.healthArr,
|
||||
type: "line",
|
||||
},
|
||||
];
|
||||
myChart.setOption(option.value);
|
||||
}
|
||||
});
|
||||
};
|
||||
const getStatisticsCount = () => {
|
||||
|
@ -7,18 +7,18 @@
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<div class="item"><span>手铐序号:</span>{{ query.id }}</div>
|
||||
<div class="item"><span>绑定管理员:</span>{{ query.name }}</div>
|
||||
<div class="item"><span>绑定管理员:</span>{{ query.adminName }}</div>
|
||||
<div class="item"><span>设备状态:</span>{{ statusEnum[query.status as string] }}</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="item"><span>IMEI号:</span>{{ query.deviceId }}</div>
|
||||
<div class="item"><span>绑定管理者账号:</span>{{ query.username }}</div>
|
||||
<div class="item"><span>绑定管理者账号:</span>{{ query.adminUsername }}</div>
|
||||
<div class="item"><span>当前电量:</span>{{ query.battery }}%</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="item"><span>首次绑定时间:</span>{{ query.createTime }}</div>
|
||||
<div class="item"><span>隶属组织:</span>{{ query.orgName }}</div>
|
||||
<div class="item"><span>联系电话:</span>{{ query.phone }}</div>
|
||||
<div class="item"><span>联系电话:</span>{{ query.adminPhone }}</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="item"><span>固件版本:</span>{{ query.deviceVersion }}</div>
|
||||
@ -33,6 +33,9 @@
|
||||
<template #status="{ rows }">
|
||||
{{ recordStatusEnum[rows.status] }}
|
||||
</template>
|
||||
<template #operator="{ rows }">
|
||||
<el-button link type="primary" size="small"> 历史记录 </el-button>
|
||||
</template>
|
||||
</TableCustom>
|
||||
</el-card>
|
||||
<el-card class="baseInfo">
|
||||
@ -95,6 +98,7 @@ let record = ref([
|
||||
{ prop: "userNumber", label: "佩戴者" },
|
||||
{ prop: "createTime", label: "开始使用时间" },
|
||||
{ prop: "updateTime", label: "结束使用时间" },
|
||||
{ prop: "operator", label: "操作" },
|
||||
]);
|
||||
|
||||
// 表格相关
|
||||
|
@ -93,7 +93,7 @@ const editOp = [
|
||||
// 查询相关
|
||||
const query = reactive({});
|
||||
const searchOpt = ref<FormOptionList[]>([
|
||||
{ type: "input", label: "手铐SN:", prop: "deviceId" },
|
||||
{ type: "input", label: "IMEI:", prop: "deviceId" },
|
||||
{ type: "input", label: "警察名称:", prop: "name" },
|
||||
{
|
||||
type: "select",
|
||||
|
@ -16,7 +16,7 @@
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item style="width: 100%" prop="userNumber" label="佩戴者编号:">
|
||||
<el-input v-model="ruleForm.userNumber" placeholder="请输入佩戴者编号" clearable />
|
||||
<el-input v-model="ruleForm.userNumber" :maxlength="10" placeholder="请输入佩戴者编号" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
@ -9,6 +9,9 @@
|
||||
<el-form-item label="警员号" prop="username">
|
||||
<el-input :maxlength="20" v-model="ruleForm.username" placeholder="请输入警员号" />
|
||||
</el-form-item>
|
||||
<el-form-item label="密码" prop="password" v-if="!ruleForm.id">
|
||||
<el-input :maxlength="20" v-model="ruleForm.password" type="password" show-password placeholder="请输入警员号" />
|
||||
</el-form-item>
|
||||
<el-form-item label="类型" prop="roleId">
|
||||
<el-select v-model="ruleForm.roleId" placeholder="请选择人员">
|
||||
<el-option v-for="item in comm.roleList" :label="item.name" :value="item.id" />
|
||||
@ -45,8 +48,8 @@ const { orgAllData, formData, api } = defineProps({
|
||||
dfault: () => {},
|
||||
},
|
||||
formData: {
|
||||
type: Object,
|
||||
required: true,
|
||||
type: Object as PropType<TAccount.IListRes>,
|
||||
default: () => {},
|
||||
},
|
||||
api: {
|
||||
type: Function,
|
||||
@ -54,20 +57,20 @@ const { orgAllData, formData, api } = defineProps({
|
||||
});
|
||||
const emit = defineEmits(["close"]);
|
||||
|
||||
const ruleForm = reactive(
|
||||
const ruleForm = reactive<TAccount.IAdd>(
|
||||
formData
|
||||
? { ...formData }
|
||||
: {
|
||||
name: "",
|
||||
phone: "",
|
||||
username: "",
|
||||
password: "111111",
|
||||
roleId: "",
|
||||
orgId: "",
|
||||
status: 1,
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {});
|
||||
const rules = reactive<FormRules<typeof ruleForm>>({
|
||||
name: [{ required: true, message: "请输入用户名称", trigger: "blur" }],
|
||||
phone: [
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<el-form ref="ruleFormRef" :model="ruleForm" :rules="rules" label-width="100px">
|
||||
<el-form-item label="新密码" prop="password">
|
||||
<el-input v-model="ruleForm.password" type="password" placeholder="请输入新密码" />
|
||||
<el-input v-model="ruleForm.password" type="password" autocomplete="new-password" show-password placeholder="请输入新密码" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item class="footr">
|
||||
|
Loading…
x
Reference in New Issue
Block a user