2025年04月29日18:31:00

This commit is contained in:
luojiayi 2025-04-29 18:31:02 +08:00
parent 2b4a5a1002
commit 744295e916
21 changed files with 137 additions and 136 deletions

View File

@ -1,8 +1,8 @@
# VITE_APP_URL = 'http://192.168.3.116:8001/' VITE_APP_URL = 'http://192.168.3.116:8001/'
# VITE_APP_URL_WEBSOCKET = 'http://192.168.3.116:8000/api/websocket' VITE_APP_URL_WEBSOCKET = 'http://192.168.3.116:8000/api/websocket'
VITE_APP_URL = 'http://47.112.185.26:8001/' # VITE_APP_URL = 'http://47.112.185.26:8001/'
VITE_APP_URL_WEBSOCKET = 'ws://47.112.185.26:8000/api/websocket' # VITE_APP_URL_WEBSOCKET = 'ws://47.112.185.26:8000/api/websocket'
# VITE_APP_URL = 'http://api.handcuff.zhuhaiguangdun.cn' # VITE_APP_URL = 'http://api.handcuff.zhuhaiguangdun.cn'
# VITE_APP_URL_WEBSOCKET = 'ws://device.handcuff.zhuhaiguangdun.cn:8000/api/websocket' # VITE_APP_URL_WEBSOCKET = 'ws://device.handcuff.zhuhaiguangdun.cn:8000/api/websocket'

1
components.d.ts vendored
View File

@ -44,7 +44,6 @@ declare module '@vue/runtime-core' {
ElUpload: typeof import('element-plus/es')['ElUpload'] ElUpload: typeof import('element-plus/es')['ElUpload']
Header: typeof import('./src/components/header.vue')['default'] Header: typeof import('./src/components/header.vue')['default']
InfoWindow: typeof import('./src/components/InfoWindow.vue')['default'] InfoWindow: typeof import('./src/components/InfoWindow.vue')['default']
MyComm: typeof import('./src/components/MyComm.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink'] RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView'] RouterView: typeof import('vue-router')['RouterView']
Sidebar: typeof import('./src/components/sidebar.vue')['default'] Sidebar: typeof import('./src/components/sidebar.vue')['default']

View File

@ -12,7 +12,7 @@
securityJsCode: '83572bd6398cb4594c611f93f89b506a' securityJsCode: '83572bd6398cb4594c611f93f89b506a'
}</script> }</script>
<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> <script src=https://a.amap.com/jsapi_demos/static/demo-center/js/demoutils.js></script>
</head> </head>

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

@ -130,9 +130,11 @@ export namespace TLogin {
export namespace TAccount { export namespace TAccount {
export interface IAdd { export interface IAdd {
id?: number | string;
username: string; username: string;
phone: string; phone: string;
name: string; name: string;
password?: string;
orgId?: number | string; orgId?: number | string;
roleId: number | string; roleId: number | string;
status: number; status: number;

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

View File

@ -50,20 +50,20 @@ const onMessage = (res) => {
}; };
onMounted(() => { onMounted(() => {
// const isReload = localStorage.getItem("isReload"); const isReload = localStorage.getItem("isReload");
// if (isReload !== "true") { if (isReload !== "true") {
// ElMessageBox.alert("", "", { ElMessageBox.alert("由于浏览器安全策略,用户必须点击屏幕才能播放告警声音", "提示", {
// confirmButtonText: "OK", confirmButtonText: "OK",
// callback: () => { callback: () => {
// localStorage.setItem("isReload", "true"); localStorage.setItem("isReload", "true");
// }, },
// }); });
// } }
// if (ws.socket == null) { if (ws.socket == null) {
// ws.connect(); ws.connect();
// } }
// ws.onMessage(onMessage); ws.onMessage(onMessage);
window.addEventListener("beforeunload", handleBeforeUnload); window.addEventListener("beforeunload", handleBeforeUnload);
}); });

View File

@ -118,7 +118,6 @@ export class MapCustom {
// 创建marker // 创建marker
marker(option) { marker(option) {
return new AMap.Marker({ return new AMap.Marker({
map: this.map,
offset: new AMap.Pixel(-13, -26), offset: new AMap.Pixel(-13, -26),
...option ...option
}) })

View File

@ -107,7 +107,7 @@ export default class WebSocketService {
onMessage = (callback) => { onMessage = (callback) => {
this.socket.onmessage = (event) => { this.socket.onmessage = (event) => {
const data = JSON.parse(event.data); const data = JSON.parse(event.data);
console.log(data, 'onMessage'); // console.log(data, 'onMessage');
if (data.cmd == "webLogin" && !data.code == 200) return this.webScoketLogin() if (data.cmd == "webLogin" && !data.code == 200) return this.webScoketLogin()
callback && callback(data); callback && callback(data);

View File

@ -64,9 +64,9 @@
<el-form-item label="处理记录:" prop="content"> <el-form-item label="处理记录:" prop="content">
<el-input v-model="ruleForm.content" clearable :autosize="{ minRows: 2, maxRows: 4 }" type="textarea" :disabled="disabled" /> <el-input v-model="ruleForm.content" clearable :autosize="{ minRows: 2, maxRows: 4 }" type="textarea" :disabled="disabled" />
</el-form-item> </el-form-item>
<el-form-item label="处理图片:" prop="images"> <!-- <el-form-item label="处理图片:" prop="images">
<Upload v-model="ruleForm.images" :disabled="disabled" /> <Upload v-model="ruleForm.images" :disabled="disabled" />
</el-form-item> </el-form-item> -->
</el-form> </el-form>
</div> </div>
</div> </div>
@ -127,7 +127,7 @@ const options = {
trigger: "axis", trigger: "axis",
formatter: function (params) { formatter: function (params) {
let unit = { 0: "次/分", 1: "%", 2: "℃" }; let unit = { 0: "次/分", 1: "%", 2: "℃" };
var res = format(options.times[params[0].dataIndex]) + "<br/>"; var res = format(Number(params[0].name)) + "<br/>";
res += params res += params
.map(function (param, index) { .map(function (param, index) {
return param.marker + param.seriesName + "" + param.value + unit[index] + "<br/>"; return param.marker + param.seriesName + "" + param.value + unit[index] + "<br/>";
@ -149,8 +149,12 @@ const options = {
type: "category", type: "category",
boundaryGap: false, boundaryGap: false,
data: [], data: [],
axisLabel: {
formatter: function (item) {
return format(Number(item), "HH:mm:ss");
},
},
}, },
times: [],
yAxis: { yAxis: {
type: "value", type: "value",
}, },
@ -270,8 +274,7 @@ const getData = async () => {
if (res.healthData && res.healthData.length) { if (res.healthData && res.healthData.length) {
res.healthData.forEach((item) => { res.healthData.forEach((item) => {
options.times.push(item.time); options.xAxis.data.push(item.time);
options.xAxis.data.push(format(item.time, "HH:mm:ss"));
options.series[0].data.push(item.hr); options.series[0].data.push(item.hr);
options.series[1].data.push(item.bo); options.series[1].data.push(item.bo);
options.series[2].data.push(item.temp); options.series[2].data.push(item.temp);

View File

@ -25,7 +25,7 @@ const options = {
trigger: "axis", trigger: "axis",
formatter: function (params) { formatter: function (params) {
let unit = { 心率: "次/分", 血氧: "%", 体表温度: "℃" }; let unit = { 心率: "次/分", 血氧: "%", 体表温度: "℃" };
var res = format(options.times[params[0].dataIndex]) + "<br/>"; var res = format(Number(params[0].name)) + "<br/>";
res += params res += params
.map(function (param, index) { .map(function (param, index) {
return param.marker + param.seriesName + "" + param.value + unit[param.seriesName] + "<br/>"; return param.marker + param.seriesName + "" + param.value + unit[param.seriesName] + "<br/>";
@ -34,10 +34,14 @@ const options = {
return res; return res;
}, },
}, },
times: [],
xAxis: { xAxis: {
type: "category", type: "category",
data: [], data: [],
axisLabel: {
formatter: function (item) {
return format(Number(item), "HH:mm:ss");
},
},
}, },
yAxis: { yAxis: {
type: "value", type: "value",
@ -84,10 +88,10 @@ const options = {
const getOptionsData = (list: { time: string; value: number }[], name: string, color: string) => { const getOptionsData = (list: { time: string; value: number }[], name: string, color: string) => {
options.xAxis.data = []; options.xAxis.data = [];
options.series.data = []; options.series.data = [];
if (list && list.length) { if (list && list.length) {
list.forEach((item) => { list.forEach((item) => {
options.times.push(item.time); options.xAxis.data.push(item.time);
options.xAxis.data.push(format(item.time, "HH:mm:ss"));
options.series.data.push(item.value); options.series.data.push(item.value);
}); });
options.series.name = name; options.series.name = name;

View File

@ -54,6 +54,10 @@
<span class="lable">模式</span> <span class="lable">模式</span>
<el-tag :type="modeColor[item.mode]">{{ modeEnum[item.mode] }}</el-tag> <el-tag :type="modeColor[item.mode]">{{ modeEnum[item.mode] }}</el-tag>
</div> </div>
<div>
<span class="lable">佩戴者</span>
{{ item.userNumber || "--" }}
</div>
</div> </div>
</template> </template>
</el-popover> </el-popover>
@ -210,6 +214,10 @@ watch(
color: #787878; color: #787878;
font-size: 16px; font-size: 16px;
text-align: right; text-align: right;
width: 80px;
white-space: nowrap; //
overflow: hidden; //
text-overflow: ellipsis;
} }
} }
} }

View File

@ -58,8 +58,9 @@ const getLocateRecord = () => {
image: ViaMarker, image: ViaMarker,
size: [20, 29], size: [20, 29],
}); });
let markers = [];
list.forEach((item, index) => { list.forEach((item, index) => {
if (list.length < 50) {
let marker: any = ""; let marker: any = "";
if (index == 0) { if (index == 0) {
marker = newMap.marker({ icon: endIcon, position: [item.lng, item.lat], zIndex: 13 }); marker = newMap.marker({ icon: endIcon, position: [item.lng, item.lat], zIndex: 13 });
@ -68,16 +69,41 @@ const getLocateRecord = () => {
} else { } else {
marker = newMap.marker({ icon: ViaIcon, position: [item.lng, item.lat], zIndex: 12 }); marker = newMap.marker({ icon: ViaIcon, position: [item.lng, item.lat], zIndex: 12 });
} }
marker.setMap(newMap.map);
marker.on("click", () => { marker.on("click", () => {
locationInfo.value = item; locationInfo.value = item;
InfoWin = newMap.infoWindow(); InfoWin = newMap.infoWindow();
InfoWin.open(newMap.map, marker.getPosition()); InfoWin.open(newMap.map, marker.getPosition());
}); });
markers.push(marker);
} else {
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);
}
}
});
newMap.map.add(markers);
} }
}); });
}; };
const fn = () => {
// this.cluster.on("click", this.clusterClickEvent);
};
onMounted(() => { onMounted(() => {
newMap = new MapCustom({ dom: "mapcontainer" }); newMap = new MapCustom({ dom: "mapcontainer" });
getLocateRecord(); getLocateRecord();

View File

@ -52,59 +52,6 @@ let funcList = ref([
{ title: "当前体表温度", en: "DANGQIANTIBIAOWENDU", icon: temperature, unit: "℃", num: 0, color: "#FF6905" }, { 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({ const devicePaging = reactive({
page: 1, page: 1,
size: 10, size: 10,
@ -135,7 +82,6 @@ const getHealthLatestData = () => {
funcList.value[0].num = res.hr; funcList.value[0].num = res.hr;
funcList.value[1].num = res.bo; funcList.value[1].num = res.bo;
funcList.value[2].num = res.temp; funcList.value[2].num = res.temp;
handelRadio(devHisRef.value.radio); handelRadio(devHisRef.value.radio);
}); });
}; };

View File

@ -79,6 +79,11 @@ const option = ref({
type: "category", type: "category",
boundaryGap: false, boundaryGap: false,
data: [], data: [],
axisLabel: {
formatter: function (item) {
return format(item, "MM-DD");
},
},
}, },
yAxis: { yAxis: {
type: "value", type: "value",
@ -169,7 +174,8 @@ const getStatisticsUseCount = () => {
}; };
const getStatisticsContent = (req) => { const getStatisticsContent = (req) => {
statisticsContent(req).then((res) => { statisticsContent(req).then((res) => {
option.value.xAxis.data = res?.times; if (res.times && res.times.length) {
option.value.xAxis.data = res.times;
option.value.series = [ option.value.series = [
{ {
stack: "Total", stack: "Total",
@ -197,6 +203,7 @@ const getStatisticsContent = (req) => {
}, },
]; ];
myChart.setOption(option.value); myChart.setOption(option.value);
}
}); });
}; };
const getStatisticsCount = () => { const getStatisticsCount = () => {

View File

@ -7,18 +7,18 @@
<el-row :gutter="20"> <el-row :gutter="20">
<el-col :span="6"> <el-col :span="6">
<div class="item"><span>手铐序号</span>{{ query.id }}</div> <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> <div class="item"><span>设备状态</span>{{ statusEnum[query.status as string] }}</div>
</el-col> </el-col>
<el-col :span="6"> <el-col :span="6">
<div class="item"><span>IMEI号</span>{{ query.deviceId }}</div> <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> <div class="item"><span>当前电量</span>{{ query.battery }}%</div>
</el-col> </el-col>
<el-col :span="6"> <el-col :span="6">
<div class="item"><span>首次绑定时间</span>{{ query.createTime }}</div> <div class="item"><span>首次绑定时间</span>{{ query.createTime }}</div>
<div class="item"><span>隶属组织</span>{{ query.orgName }}</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>
<el-col :span="6"> <el-col :span="6">
<div class="item"><span>固件版本</span>{{ query.deviceVersion }}</div> <div class="item"><span>固件版本</span>{{ query.deviceVersion }}</div>
@ -33,6 +33,9 @@
<template #status="{ rows }"> <template #status="{ rows }">
{{ recordStatusEnum[rows.status] }} {{ recordStatusEnum[rows.status] }}
</template> </template>
<template #operator="{ rows }">
<el-button link type="primary" size="small"> 历史记录 </el-button>
</template>
</TableCustom> </TableCustom>
</el-card> </el-card>
<el-card class="baseInfo"> <el-card class="baseInfo">
@ -95,6 +98,7 @@ let record = ref([
{ prop: "userNumber", label: "佩戴者" }, { prop: "userNumber", label: "佩戴者" },
{ prop: "createTime", label: "开始使用时间" }, { prop: "createTime", label: "开始使用时间" },
{ prop: "updateTime", label: "结束使用时间" }, { prop: "updateTime", label: "结束使用时间" },
{ prop: "operator", label: "操作" },
]); ]);
// //

View File

@ -93,7 +93,7 @@ const editOp = [
// //
const query = reactive({}); const query = reactive({});
const searchOpt = ref<FormOptionList[]>([ const searchOpt = ref<FormOptionList[]>([
{ type: "input", label: "手铐SN", prop: "deviceId" }, { type: "input", label: "IMEI", prop: "deviceId" },
{ type: "input", label: "警察名称:", prop: "name" }, { type: "input", label: "警察名称:", prop: "name" },
{ {
type: "select", type: "select",

View File

@ -16,7 +16,7 @@
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item style="width: 100%" prop="userNumber" label="佩戴者编号:"> <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-form-item>
</el-col> </el-col>
</el-row> </el-row>

View File

@ -9,6 +9,9 @@
<el-form-item label="警员号" prop="username"> <el-form-item label="警员号" prop="username">
<el-input :maxlength="20" v-model="ruleForm.username" placeholder="请输入警员号" /> <el-input :maxlength="20" v-model="ruleForm.username" placeholder="请输入警员号" />
</el-form-item> </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-form-item label="类型" prop="roleId">
<el-select v-model="ruleForm.roleId" placeholder="请选择人员"> <el-select v-model="ruleForm.roleId" placeholder="请选择人员">
<el-option v-for="item in comm.roleList" :label="item.name" :value="item.id" /> <el-option v-for="item in comm.roleList" :label="item.name" :value="item.id" />
@ -45,8 +48,8 @@ const { orgAllData, formData, api } = defineProps({
dfault: () => {}, dfault: () => {},
}, },
formData: { formData: {
type: Object, type: Object as PropType<TAccount.IListRes>,
required: true, default: () => {},
}, },
api: { api: {
type: Function, type: Function,
@ -54,20 +57,20 @@ const { orgAllData, formData, api } = defineProps({
}); });
const emit = defineEmits(["close"]); const emit = defineEmits(["close"]);
const ruleForm = reactive( const ruleForm = reactive<TAccount.IAdd>(
formData formData
? { ...formData } ? { ...formData }
: { : {
name: "", name: "",
phone: "", phone: "",
username: "", username: "",
password: "111111",
roleId: "", roleId: "",
orgId: "", orgId: "",
status: 1, status: 1,
} }
); );
onMounted(() => {});
const rules = reactive<FormRules<typeof ruleForm>>({ const rules = reactive<FormRules<typeof ruleForm>>({
name: [{ required: true, message: "请输入用户名称", trigger: "blur" }], name: [{ required: true, message: "请输入用户名称", trigger: "blur" }],
phone: [ phone: [

View File

@ -1,7 +1,7 @@
<template> <template>
<el-form ref="ruleFormRef" :model="ruleForm" :rules="rules" label-width="100px"> <el-form ref="ruleFormRef" :model="ruleForm" :rules="rules" label-width="100px">
<el-form-item label="新密码" prop="password"> <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>
<el-form-item class="footr"> <el-form-item class="footr">