anytrek-front-public-component
| Install | |
|---|---|
npm install anytrek-front-public-component |
|
| Latest Version: | 1.15.15 |
| Vue: | 3 |
| License: | ISC |
| Last Updated: | Oct 22, 2026 |
| Links: | npm |
front-public-component
A public component library
📦 Installation
npm install anytrek-front-public-component --save
🚀 Usage
import frontPublicComponent from 'anytrek-front-public-component';
import 'anytrek-front-public-component/style.css';
app.use(frontPublicComponent);
🧩 Component: <anytrekTimerangePick>
Select a time range
<anytrekTimerangePick
v-model:visible="visible"
v-model:value="value"
v-model:valueType="valueType"
v-model:startTime="startTime"
v-model:endTime="endTime"
v-model:timeZone="timeZone"
:showQuick="true"
:immediate="true"
@change="onRangeChange"
/>
🔧 Example Setup
const visible = ref(false);
const value = ref<'custom' | '' | number>('custom');
const valueType = ref<string>('week');
const startTime = ref<string>('');
const endTime = ref<string>('');
const timeZone = ref<string>('');
function onRangeChange(start: string, end: string, tz: string) {
console.log(start, end, tz);
}
📋 Props Reference
| 参数名称 | 描述 | 数据类型 | 默认值 |
|---|---|---|---|
value |
当选择快捷选项时,这个字段是数量。value为1,valueType为'month'时表示选择的时间段为从现在开始往前推一个月到现在,当自定义时间时这个字段固定为'custom'。 |
custom | '' | number |
'custom' |
valueType |
快捷选项的单位(当value值为custom时,这个字段无效) |
hour | day | week | month | quarter | year |
'' |
startTime |
开始时间 |
String |
'' |
endTime |
结束时间 |
String |
'' |
valueFormat |
startTime、endTime值的格式(如果自定义这个格式没有带上小时,则认为是只选择日期,此时showFormat也应该不带小时及以后部分) |
String |
YYYY-MM-DDTHH:mm:ssZ |
showFormat |
startTime、endTime显示格式 |
String |
YYYY-MM-DD HH:mm |
timeZone |
时区 |
String |
当前网页时区(当needNowTimeZone为false时,timeZone默认取PST) |
visible |
popover弹框的展示与否 |
Boolean |
false |
teleported |
将popover弹框插入至 body 元素 |
Boolean |
true |
disabled |
禁用选择器 |
Boolean |
false |
showQuick |
是否显示快捷选项 |
Boolean |
true |
hourList |
小时快捷选项 |
Number[] |
[] |
dayList |
天快捷选项 |
Number[] |
[] |
weekList |
周快捷选项 |
Number[] |
[1, 2] |
monthList |
月快捷选项 |
Number[] |
[1] |
quarterList |
季度快捷选项 |
Number[] |
[] |
yearList |
年快捷选项 |
Number[] |
[] |
disabledDate |
最多可以选择多少天 |
Number |
31 |
immediate |
是否需要开始时执行一次change事件 |
Boolean |
false |
needNowTimeZone |
是否需要在时区列表显示当前网页时区 |
Boolean |
true |
haveRangSwitch |
是否需要左右进行区间移动 |
Boolean |
false |
needMaxIndex |
是否需要弹框在最上层 |
Boolean |
false |
timeZoneList |
时区选项 |
Object[] |
[{value:'PST',label:'PST'},{value:'PDT',label:'PDT'},{value:'MST',label:'MST'},{value:'MDT',label:'MDT'},{value:'CST',label:'CST'},{value:'CDT',label:'CDT'},{value:'EST',label:'EST'},{value:'EDT',label:'EDT'},{value:'AST',label:'AST'},{value:'ADT',label:'ADT'},{value:'AKST',label:'AKST'},{value:'AKDT',label:'AKDT'}] |
unlinkPanels |
是否取消两个日期面板之间的联动 |
Boolean |
true |
isLast |
默认快捷选项都是最近一天,最近一年,要想设置成当天、当月就得把这个值改成false |
Boolean |
true |
showTimeZoneChoose |
是否允许选择时区 |
Boolean |
true |
disableDayFuntion |
一个用来判断该日期是否被禁用的函数,接受一个Date对象作为参数。 应该返回一个Boolean值。 |
(date:String,tz:String ) => Boolean |
|
disableHourFuntion |
禁止选择部分小时选项。数组内有效的值为0-23 |
(date:String,tz:String ) => Number[] |
|
disableMinuteFuntion |
禁止选择部分分钟选项。数组内有效的值为0-59 |
(date:String,hour:String,tz:String ) => Number[] |
|
showTime |
没选时间时默认指定哪个月份(最好为15号) |
YYYY-MM-DDTHH:mm:ssZ |
|
quarterMessage |
当前只能选择哪个季度(季度值取1、2、3、4) |
{ year: Number, quarter: Number } |
|
assetTimeList |
资产利用率数据数组 |
{ startTime: Number(时间戳或者YYYY-MM-DDTHH:mm:ssZ), endTime: Number(时间戳或者YYYY-MM-DDTHH:mm:ssZ) } |
🧩 Component: <anytrekTimePick>
Select a time
<anytrekTimePick
v-model:value="anytrekTimePickValue"
v-model:timeZone="anytrekTimePickTimeZone"
v-model:visible="anytrekTimePickVisible"
:immediate="true"
@change="timeChange"
/>
🔧 Example Setup
const anytrekTimePickValue = ref<string>('2018-01-31T14:32:19.213Z');
const anytrekTimePickTimeZone = ref<string>('');
const anytrekTimePickVisible = ref<boolean>(false);
function timeChange(time: string, tz: string) {
console.log(time, tz);
}
📋 Props Reference
| 参数名称 | 描述 | 数据类型 | 默认值 |
|---|---|---|---|
value |
选择的时间值 |
String |
'' |
valueFormat |
value 值的格式(如果自定义这个格式没有带上小时,则认为是只选择日期,此时showFormat也应该不带小时及以后部分) |
String |
YYYY-MM-DDTHH:mm:ssZ |
showFormat |
value 显示格式 |
String |
YYYY-MM-DD HH:mm |
timeZone |
时区 |
String |
当前网页时区(当needNowTimeZone为false时,timeZone默认取PST) |
visible |
popover弹框的展示与否 |
Boolean |
false |
teleported |
将popover弹框插入至 body 元素 |
Boolean |
true |
disabled |
禁用选择器 |
Boolean |
false |
immediate |
是否需要开始时执行一次 change事件 |
Boolean |
false |
disableFuntion |
一个用来判断该日期是否被禁用的函数,接受一个Date对象作为参数。 应该返回一个Boolean值。 |
(date:String,tz:String ) => Boolean |
|
needNowTimeZone |
是否需要在时区列表显示当前网页时区 |
Boolean |
true |
needMaxIndex |
是否需要弹框在最上层 |
Boolean |
false |
showTimeZoneChoose |
是否允许选择时区 |
Boolean |
true |
showTime |
没选时间时默认指定哪个月份(最好为15号) |
YYYY-MM-DDTHH:mm:ssZ |
|
quarterMessage |
当前只能选择哪个季度(季度值取1、2、3、4) |
{ year: Number, quarter: Number } |
🧩 Component: <vehicle-select>
Viewing and selecting vehicles
<div style="height: 500px">
<vehicleSelect
:data="testData"
:loading="loading"
:keyword="keyword"
:modleList="modleList"
v-model:sortKey="sortKey"
v-model:vehicleId="vehicleId"
v-model:deviceId="deviceId"
v-model:retract="retract"
/>
</div>
🔧 Example Setup
const testData = ref<any>({});
const loading = ref<boolean>(true);
const keyword = ref<string>('');
const modleList = ref<number[]>([62]);
const sortKey = ref<string>('asc')
const vehicleId = ref<number>(15884)
const deviceId = ref<number>(0)
const retract = ref<boolean>(false);
onMounted(() => {
testData.value = {
tagList: [],
deviceStatusList: [],
vehicleDeviceList: [],
landmarkDeviceList: [],
landmarkList: [],
vehicleType: [],
deviceList: [],
vehicleList: [],
landmarkGroupList: [],
}
loading.value = false;
});
📋 Props Reference
| 参数名称 | 描述 | 数据类型 | 默认值 |
|---|---|---|---|
keyword |
搜索的关键字,用此字段搜索车辆/设备(右上角搜索框) |
String |
'' |
loading |
加载中,做状态显示,只要为true,就会出现一层盖在上面的遮罩(第一次loading变成false时,会去合并data中的数据,所以必须在请求到data中的数据之后置为false) |
Boolean |
true |
data |
接口返回的原始数据(见下方备注) |
Object |
{} |
sortKey |
排序规则(默认A->Z排序。可选值(driving,idle,waring,offline,battery,last,create,asc,desc)) |
String |
asc |
modleList |
如果需要只筛选某些型号的设备及其关联的车辆,可以将型号Id加入这个数组 |
Number[] |
[](比如只想显示型号为2002和2310的设备及其关联的车辆,可以传[37, 54];如果只想显示型号为2308的设备,可以传[62]) |
limitList |
对于modleList的加强限制(见下方备注) |
Object[] |
[] |
vehicleId |
当前选择的车辆ID(如果匹配不上,会默认到第一辆车)(外部改变选择的车辆/设备靠这个参数和下面的deviceId) |
Number |
0 |
deviceId |
当前选择的设备ID(如果上述vehicleId为0且deviceId有值时才生效)(外部改变选择的车辆/设备靠这个参数和上面的vehicleId) |
Number |
0 |
immediate |
当vehicleId和deviceId都为0时是否需要选择第一辆车 |
Boolean |
false |
align |
String |
middle |
|
retract |
外面控制组件关闭的功能 false 打开 true关闭 |
Boolean |
false |
clikcOutRetract |
是否开启点击组件外部关闭组件的功能 false 关闭 true 打开 |
Boolean |
false |
showLoading |
整个组件上方显示加载样式(只做样式控制) |
Boolean |
false |
haveBorderRadius |
四个角是否有圆角 |
Boolean |
false |
haveLeftBorder |
是否有左边框 |
Boolean |
false |
isArray |
选择是否以数组的方式进行 |
Boolean |
false |
vehicleArray |
isArray为true时绑定的值,每一项为{ vehicleId:xxx, deviceId: 0, isChoose: Boolean }或者{ vehicleId:0, deviceId: xxx, isChoose: Boolean } |
Object[] |
[] |
vehicleMaxlength |
isArray为true时最多同时选择的车辆数量 |
Number |
3 |
showAllName |
收起来之后显示全称 |
Boolean |
false |
clickToMdvr |
点击跳到mdvr界面 |
Function |
|
useCompleteVehicleScope |
是否显示全部车辆(此时不显示设备) |
Boolean |
false |
inMaint |
是否在maint网页(maint网页需要特殊处理,所有状态设备都可以被选) |
Boolean |
false |
sensorTypeList |
是否只显示绑定了特定传感器的车辆,目前支持的传感器为AS2521、AS2528、AS2530、AS2540 |
String[] |
[] |
备注:现在data支持2种情况了: 1.包含数组:tagList,deviceStatusList,vehicleDeviceList,landmarkDeviceList,landmarkList,vehicleType,deviceList,vehicleList 2.包含数组:relationDeviceList,tagList,landmarkList,vehicleType
备注:limitList的使用
- 必须有modleList才能生效;
- 用于对于modelList中的设备详细限制,比如想限制2310固件版本(fwVer)在11309及以上,则可以传 const limitList = ref([ { modelId: 54, key: 'fwVer', regex: '^(1130[9]|113[1-9]\d|11[4-9]\d{2}|1[2-9]\d{3}|[2-9]\d{4}|[1-9]\d{5,})$' } ])
📋 Events Reference
| 事件名 | 描述 | 备注 |
|---|---|---|
clickItem |
通过监听此事件得知车辆/设备选择变化 |
参数为两个(第一个为{vehicleId: xxx,deviceId: xxx},第二个为选择的车辆/设备信息) |
chooseItem |
只有点击车辆后会触发 |
参数为两个(第一个为{vehicleId: xxx,deviceId: xxx},第二个为选择的车辆/设备信息) |
onlyClickChange |
只有点击车辆后会触发 |
参数为两个(第一个为{vehicleId: xxx,deviceId: xxx},第二个为选择的车辆/设备信息) |
clickLandmarkOrTag |
点击tag或者landmark(关闭/打开)触发 |
参数为三个(1.tag/landmark,2.是否关闭,3.选择的tag/landmark信息) |
eventTracking |
触发埋点 |
参数为一个(是具体触发的埋点名称) |
changeArray |
isArray为true时车辆选择变化时触发 |
参数为两个(第一个为{vehicleId: xxx,deviceId: xxx}数组,第二个为选择的车辆/设备信息数组) |
🧩 Component: <addOrEditVehicle>
add or edit vehicle
<add-or-edit-vehicle
v-model="drawerShow"
:loginInfo="loginInfo"
:customerHabit="customerHabit"
:vehicleData="drawerForm"
:allAssetTypes="allAssetTypes"
:allVehicles="allVehicles"
:allDevices="allDevices"
:modelTypes="modelTypes"
:allVehicleToDevice="allVehicleToDevice"
:allTags="allTags"
:assetTypeFn="assetTypeFn"
:bindDeviceHis="bindDeviceHis"
:deviceBindTimeLimit="deviceBindTimeLimit"
:saveFunctionVehicle="saveFunctionVehicle"
:allLandmarks="allLandmarks"
:landmarkGroup="landmarkGroup"
:saveFunctionTag="saveFunctionTag"
:unitAllState="unitAllState"
:GOOGLE_MAP_API_KEY="GOOGLE_MAP_API_KEY"
:MAP_STYLES="MAP_STYLES"
:searhTrimblemapAddress="searhTrimblemapAddress"
:saveFunctionLandmark="saveFunctionLandmark"
:conDetSensorList="conDetSensorList"
:needConDetSensor="needConDetSensor"
></add-or-edit-vehicle>
🔧 Example Setup
const assetTypeFn = async (data: any, flag: 'add' | 'delete') => {
if (flag === 'delete') {
const res = await Api.editCustomizeVehicleType([
{
id: data.id,
isDelete: 1,
},
]);
if (!res || (res.errorCode && res.errorCode !== 200)) return;
} else if (flag === 'add') {
const result = await Api.addCustomizeVehicleType([data]);
if (!result || result.errorCode) return;
}
await getCustomizeVehicleType();
};
const bindDeviceHis = async (params: any) => {
const res = await Api.loadBoundDevices(params);
if (!res || (res.errorCode && res.errorCode !== 200)) return;
return res;
};
const deviceBindTimeLimit = async (deviceId: any) => {
const res = await Api.getDeviceTimeLimitApi({
deviceId: deviceId,
});
if (!res || res.errorCode) return;
if (res.length) {
return [
res[0].limitStartTime,
dayjs(new Date()).utc().format('YYYY-MM-DDTHH:mm:ss[Z]'),
];
} else {
return [null, dayjs(new Date()).utc().format('YYYY-MM-DDTHH:mm:ss[Z]')];
}
};
const saveFunctionVehicle = async (
params: any,
flag = false,
successFn: Function,
failFn: Function,
) => {
let result;
if (params.id) {
result = await Api.editVehicleNEW([params]);
} else {
result = await Api.addVehicleNEW([params]);
}
// 设备状态为1的需要切换为2
if (flag) {
const res = await Api.editDeviceNew([
{
id: params.deviceId,
content: {
deviceStatus: 2,
modifyTime: dayjs(new Date()).utc().format(),
activateTime: dayjs(new Date()).utc().format(),
},
},
]);
if (!res || res.errorCode) return failFn();
if (Array.isArray(result) && result[0].errorCode) {
let errorCode = result[0].errorCode;
const errLang1 = errLang[errorCode as string];
const errMessage = errLang1 || `Error Code: ${errorCode}`;
return failFn(errMessage);
}
}
if (!result || result.errorCode) return failFn();
successFn();
refresh();
};
📋 Props Reference
| 参数名称 | 描述 | 数据类型 | 默认值 |
|---|---|---|---|
| drawerShow | 控制drawer的显示和隐藏 | Boolean |
false |
| readOnly | 是否只读 | Boolean |
false |
| loginInfo | 用户信息,包含用户的type和customerId | Object |
{} |
| customerHabit | 用户的偏好设置,包含localTimezone、timeZone | Object |
{} |
| vehicleData | 新增/编辑的车辆,编辑需要传id | Object |
{ } |
| allAssetTypes | 资产类型列表 | Array |
[] |
| allVehicles | 车辆列表 | Array |
[] |
| allDevices | 设备列表 | Array |
[] |
| allDeviceStatus | 设备状态列表 | Array |
[] |
| modelTypes | 设备类型列表 | Object |
{} |
| allVehicleToDevice | 车辆设备绑定关系列表 | Array |
[] |
| allTags | tag列表 | Array |
[] |
| assetTypeFn | 资产类型新增/删除方法 | Function |
|
| bindDeviceHis | 车辆绑定历史记录 | Function |
|
| deviceBindTimeLimit | 设备绑定时间限制 | Function |
|
| saveFunctionVehicle | 车辆保存 | Function |
|
| allLandmarks | landmark列表 | Array |
[] |
| landmarkGroup | landmarkGroup列表 | Array |
[] |
| saveFunctionTag | tag保存 | Function |
|
| unitAllState | 城市列表 | Array |
[] |
| GOOGLE_MAP_API_KEY | 地图apikey | String |
|
| MAP_STYLES | 地图样式 | Array |
[] |
| searhTrimblemapAddress | 查询地址api | Function |
|
| saveFunctionLandmark | landmark保存 | Function |
|
| conDetSensorList | 传感器列表 | Array |
[] |
| needConDetSensor | 是否显示集装箱检测传感器功能 | Boolean |
true |
🧩 Component: <addOrEditTag>
add or edit tag
<add-or-edit-tag
v-model="drawerShow"
:loginInfo="loginInfo"
:customerHabit="customerHabit"
:itemData="drawerForm"
:allVehicles="allVehicles"
:allAssetTypes="allAssetTypes"
:allDevices="allDevices"
:modelTypes="modelTypes"
:allVehicleToDevice="allVehicleToDevice"
:allTags="allTags"
:allLandmarks="allLandmarks"
:landmarkGroup="landmarkGroup"
:saveFunctionTag="saveFunctionTag"
:assetTypeFn="assetTypeFn"
:bindDeviceHis="bindDeviceHis"
:deviceBindTimeLimit="deviceBindTimeLimit"
:saveFunctionVehicle="saveFunctionVehicle"
:unitAllState="unitAllState"
:GOOGLE_MAP_API_KEY="GOOGLE_MAP_API_KEY"
:MAP_STYLES="MAP_STYLES"
:searhTrimblemapAddress="searhTrimblemapAddress"
:saveFunctionLandmark="saveFunctionLandmark"
:conDetSensorList="conDetSensorList"
:needConDetSensor="needConDetSensor"
></add-or-edit-tag>
🔧 Example Setup
const saveFunctionTag = async (
params: any,
successFn: Function,
failFn: Function,
) => {
let result;
if (params.id) {
result = await Api.updateTagsList([params]);
} else {
result = await Api.saveTagsList([params]);
}
if (!result || result.errorCode) return failFn();
successFn();
handleClose();
};
const handleAddBurialPoint = (type: string) => {
Api.addBurialPoint(type);
};
📋 Props Reference
| 参数名称 | 描述 | 数据类型 | 默认值 |
|---|---|---|---|
| drawerShow | 控制drawer的显示和隐藏 | Boolean |
false |
| readOnly | 是否只读 | Boolean |
false |
| loginInfo | 用户信息,包含用户的type和customerId | Object |
{} |
| customerHabit | 用户的偏好设置,包含localTimezone、timeZone | Object |
{} |
| itemData | 新增/编辑的tag,编辑需要传id | Object |
{ } |
| allVehicles | 车辆列表 | Array |
[] |
| allDeviceStatus | 设备状态列表 | Array |
[] |
| allDevices | 设备列表 | Array |
[] |
| modelTypes | 设备类型列表 | Object |
{} |
| allVehicleToDevice | 车辆设备绑定关系列表 | Array |
[] |
| allTags | tag列表 | Array |
[] |
| allLandmarks | landmark列表 | Array |
[] |
| landmarkGroup | landmarkGroup列表 | Array |
[] |
| saveFunctionTag | tag保存 | Function |
|
| assetTypeFn | 资产类型新增/删除方法 | Function |
|
| bindDeviceHis | 车辆绑定历史记录 | Function |
|
| deviceBindTimeLimit | 设备绑定时间限制 | Function |
|
| saveFunctionVehicle | 车辆保存 | Function |
|
| unitAllState | 城市列表 | Array |
[] |
| GOOGLE_MAP_API_KEY | 地图apikey | String |
|
| MAP_STYLES | 地图样式 | Array |
[] |
| searhTrimblemapAddress | 查询地址api | Function |
|
| saveFunctionLandmark | landmark保存 | Function |
|
| conDetSensorList | 传感器列表 | Array |
[] |
| needConDetSensor | 是否显示集装箱检测传感器功能 | Boolean |
true |
🧩 Component: <addOrEditLandmark>
add or edit landmark
<add-or-edit-landmark
v-model="drawerShow"
:itemData="drawerForm"
:unitAllState="unitAllState"
:GOOGLE_MAP_API_KEY="GOOGLE_MAP_API_KEY"
:MAP_STYLES="MAP_STYLES"
:allLandmarkGroup="landmarkGroup"
:searhTrimblemapAddress="searhTrimblemapAddress"
:saveFunctionLandmark="saveFunctionLandmark"
@addGroup="handleAddGroup"
></add-or-edit-landmark>
🔧 Example Setup
const searhTrimblemapAddress = async (params: any) => {
const res = await Api.searhTrimblemapAddress(params);
return res;
};
const saveFunctionLandmark = async (
params: any,
successFn: Function,
failFn: Function,
) => {
let result;
if (params.id) {
result = await Api.editLandmark([params]);
} else {
result = await Api.addLandmark([params]);
}
if (!result || result.errorCode) return failFn();
successFn();
initData();
};
const handleAddGroup = (params: any) => {
groupFormVisible.value = true;
groupForm = params;
};
📋 Props Reference
| 参数名称 | 描述 | 数据类型 | 默认值 |
|---|---|---|---|
| drawerShow | 控制drawer的显示和隐藏 | Boolean |
false |
| readOnly | 是否只读 | Boolean |
false |
| itemData | 新增/编辑的landmark | Object |
|
| allLandmarks | landmark列表 | Array |
[] |
| unitAllState | 城市列表 | Array |
[] |
| GOOGLE_MAP_API_KEY | 地图apikey | String |
|
| MAP_STYLES | 地图样式 | Array |
[] |
| allLandmarkGroup | landmarkGroup列表 | Array |
[] |
| searhTrimblemapAddress | 查询地址api | Function |
|
| saveFunctionLandmark | landmark保存 | Function |
|
| @addGroup | 新增group | Function |
🧩 Component: <AddOrEditShare>
add or edit Share
<AddOrEditShare
v-model="addVisible"
:vehicleLoading="vehicleLoading"
:readOnly=""
:data="testData"
:itemData="itemData"
:unitAllState="unitAllState"
:getShareHistoryUrl="getShareHistoryUrl"
:uploadShareFile="uploadShareFile"
:saveFunctionShare="saveFunctionShare"
:loginInfo="loginInfo"
:assetTypeFn="assetTypeFn"
:bindDeviceHis="bindDeviceHis"
:deviceBindTimeLimit="deviceBindTimeLimit"
:saveFunctionVehicle="saveFunctionVehicle"
:saveFunctionTag="saveFunctionTag"
:unitAllState="unitAllState"
:GOOGLE_MAP_API_KEY="GOOGLE_MAP_API_KEY"
:MAP_STYLES="MAP_STYLES"
:searhTrimblemapAddress="searhTrimblemapAddress"
:saveFunctionLandmark="saveFunctionLandmark"
></AddOrEditShare>
🔧 Example Setup
const addVisible = ref(false);
const vehicleLoading = ref(false);
const testData = ref<any>({});
onMounted(() => {
testData.value = {
tagList: [],
deviceStatusList: [],
vehicleDeviceList: [],
landmarkDeviceList: [],
landmarkList: [],
vehicleType: [],
deviceList: [],
vehicleList: [],
landmarkGroupList: [],
}
vehicleLoading.value = false;
});
const readOnly = ref(false);
const itemData = ref<any>({});
const getShareHistoryUrl = Api.getHistoryUrl;
const uploadShareFile = Api.uploadFile;
const saveFunctionShare = async (
params: any,
successFn: Function,
failFn: Function,
) => {
let result;
if (params.id) {
result = await Api.editShare([params]);
} else {
result = await Api.addShare([params]);
}
if (!result || result.errorCode) return failFn();
successFn();
initData();
};
const { loginInfo } = store;
const assetTypeFn = async (data: any, flag: 'add' | 'delete') => {
if (flag === 'delete') {
const res = await Api.editCustomizeVehicleType([
{
id: data.id,
isDelete: 1,
},
]);
if (!res || (res.errorCode && res.errorCode !== 200)) return;
} else if (flag === 'add') {
const result = await Api.addCustomizeVehicleType([data]);
if (!result || result.errorCode) return;
}
await getCustomizeVehicleType();
};
const bindDeviceHis = async (params: any) => {
const res = await Api.loadBoundDevices(params);
if (!res || (res.errorCode && res.errorCode !== 200)) return;
return res;
};
const deviceBindTimeLimit = async (deviceId: any) => {
const res = await Api.getDeviceTimeLimitApi({
deviceId: deviceId,
});
if (!res || res.errorCode) return;
if (res.length) {
return [
res[0].limitStartTime,
dayjs(new Date()).utc().format('YYYY-MM-DDTHH:mm:ss[Z]'),
];
} else {
return [null, dayjs(new Date()).utc().format('YYYY-MM-DDTHH:mm:ss[Z]')];
}
};
const saveFunctionVehicle = async (
params: any,
flag = false,
successFn: Function,
failFn: Function,
) => {
let result;
if (params.id) {
result = await Api.editVehicleNEW([params]);
} else {
result = await Api.addVehicleNEW([params]);
}
// 设备状态为1的需要切换为2
if (flag) {
const res = await Api.editDeviceNew([
{
id: params.deviceId,
content: {
deviceStatus: 2,
modifyTime: dayjs(new Date()).utc().format(),
activateTime: dayjs(new Date()).utc().format(),
},
},
]);
if (!res || res.errorCode) return failFn();
}
if (!result || result.errorCode) return failFn();
successFn();
refresh();
};
const saveFunctionTag = async (
params: any,
successFn: Function,
failFn: Function,
) => {
let result;
if (params.id) {
result = await Api.updateTagsList([params]);
} else {
result = await Api.saveTagsList([params]);
}
if (!result || result.errorCode) return failFn();
successFn();
handleClose();
};
const unitAllState = ref<any[]>([]);
const GOOGLE_MAP_API_KEY: string = 'xxxx;
const MAP_STYLES: any = {};
const searhTrimblemapAddress = async (params: any) => {
const res = await Api.searhTrimblemapAddress(params);
return res;
};
const saveFunctionLandmark = async (
params: any,
successFn: Function,
failFn: Function,
) => {
let result;
if (params.id) {
result = await Api.editLandmark([params]);
} else {
result = await Api.addLandmark([params]);
}
if (!result || result.errorCode) return failFn();
successFn();
initData();
};
📋 Props Reference
| 参数名称 | 描述 | 数据类型 | 默认值 |
|---|---|---|---|
| modelValue | 控制drawer的显示和隐藏 | Boolean |
false |
| readOnly | 是否只看 | Boolean |
false |
data |
接口返回的原始数据(包含数组:tagList,deviceStatusList,vehicleDeviceList,landmarkDeviceList,landmarkList,vehicleType,deviceList,vehicleList,landmarkGroupList) |
Object |
{} |
| itemData | 新增/编辑的share | Object |
{} |
| getShareHistoryUrl | 获取分享的历史logo | Function |
|
| uploadShareFile | 上传logo | Function |
|
| saveFunctionShare | share保存 | Function |
|
| loginInfo | 登录信息 | Object |
{} |
| assetTypeFn | 资产类型新增/删除方法 | Function |
|
| bindDeviceHis | 车辆绑定历史记录 | Function |
|
| deviceBindTimeLimit | 设备绑定时间限制 | Function |
|
| saveFunctionVehicle | 车辆保存 | Function |
|
| saveFunctionTag | tag保存 | Function |
|
| unitAllState | 城市列表 | Array |
[] |
| GOOGLE_MAP_API_KEY | 地图apikey | String |
|
| MAP_STYLES | 地图样式 | Array |
[] |
| searhTrimblemapAddress | 查询地址api | Function |
|
| saveFunctionLandmark | landmark保存 | Function |
|
| nowChooseVehicleId | 当前选择的车辆id | Number |
🧩 Component: <ImageDisplay>
图片预览弹窗,支持缩放、旋转、地图 MiniMap、AI 分析、图片列表懒加载、多帧图片轮播
📈 Maint 图表模式(maint 来源)
当 isComeFromMaint 为 true 时(图片来源于 maint 页面),imageCarousel / imageList 模式下会在图片状态区显示一个 line-chart 图标。点击图标会打开一个独立的多线折线图 dialog(基于 echarts,800×600,覆盖在图片预览上方,点击弹窗外部区域仅关闭图表、不关闭图片预览),展示 Gsensor X/Y/Z、Speed、Door Status、Human Detection 六条曲线。
图表数据由宿主通过 getChartData 方法提供,组件在点击图标时才发起请求,并按参数(userPackageId + beginTime + endTime)缓存结果——参数未变化不重复请求,图片列表变化导致参数变化后,下次点击才会重新请求。
// 宿主端实现示例:返回 echarts series 数据(结构见下方 getChartData 说明)
const getChartData = async (params: {
userPackageId: string; // String(deviceId)
beginTime: string; // 第一张图片的 takeTime
endTime: string; // 最后一张图片的 takeTime
}) => {
const res = await Api.getTelemetryChartData(params);
if (!res || res.errorCode) return null; // 返回 null/空 显示 No Data
return res;
};
<ImageDisplay
:isComeFromMaint="true"
:getChartData="getChartData"
...
/>
<ImageDisplay
ref="imageDisplayRef"
v-model="visible"
:data="imageData"
:vehicleId="vehicleId"
:deviceId="deviceId"
:curImgList="imgList"
:imageInterval=imageInterval
:curPicWH="curPicWH"
:haveRotate="haveRotate"
:saveRotation="saveRotation"
:haveMap="haveMap"
:needPath="needPath"
:mapJson="mapJson"
:google-map-key="googleMapKey"
:on-get-history-trail="fetchHistoryTrail"
:on-get-history-trail-point="fetchHistoryTrailPoint"
:get-img-list="fetchImgList"
:getImageVideo="getImageVideo"
@handle-close="onClose"
@handle-download="onDownload"
@update-cur-img="onUpdateCurImg"
@changeCurPicWH="changeCurPicWH"
@to-history="onToHistory"
/>
🔧 Example Setup
const imageDisplayRef = ref();
const visible = ref(false)
const imageData = ref<any>({})
const previewItem = ref<any>({})//当前设备
const imgList = ref<any[]>([]);
const imageInterval = ref<number>(15);//当前最新的设置Interval,默认是15
/* 图片宽高 */
const curPicWH = ref<any>({
imgW: 800,
imgH: 600,
});
const showImageDisplay = () => {
imageDisplayParams.value.curImg = {};
imageDisplayRef.value?.loadImageListFun();
visible.value = true;
}
/* 2310图片轮播,triggerType为CargoHandling或者eventType为9且uuid,index相同归为一项,新增 imageList 字段存,按 takeTime 最新排序 */
// 2310视频,uuid一样,含有media字段
export const groupCargoHandlingImages = (list: any[]): any[] => {
const groupMap = new Map<string, any[]>();
const mergedSet = new Set<any>();
// 第1步:先合并 triggerType 为 CargoHandling 或 eventType 为 9 且 uuid 相同的图片组
list.forEach((item: any) => {
if ((item.triggerType === 'CargoHandling' || item.eventType === 9) && item.uuid) {
const key = `picture-${item.uuid}-${item.index}`;
if (!groupMap.has(key)) groupMap.set(key, []);
groupMap.get(key)!.push(item);
}
});
// 第2步:处理带 media 的项,uuid + index 匹配到图片组则并入该组并记录为已合并,否则走 videoList 分组
list.forEach((item: any) => {
if (!item.media || !item.uuid) return;
if (item.triggerType === 'CargoHandling' || item.eventType === 9) {
// 自身既是 CargoHandling 又带 media:按 CargoHandling 算,已在图片组中,不重复处理
mergedSet.add(item);
return;
}
const pictureKey = `picture-${item.uuid}-${item.index}`;
if (groupMap.has(pictureKey)) {
groupMap.get(pictureKey)!.push(item);
mergedSet.add(item);
} else {
const key = `media-${item.uuid}`;
if (!groupMap.has(key)) groupMap.set(key, []);
groupMap.get(key)!.push(item);
}
});
const result: any[] = [];
groupMap.forEach((items) => {
items.sort((a: any, b: any) => new Date(a.takeTime).getTime() - new Date(b.takeTime).getTime());
const mediaItems = items.filter((i: any) => i.media);
const imageItems = items.filter((i: any) => !i.media);
const isPureMediaGroup = !!mediaItems.length && !imageItems.length;
let timeStampGap = 0;
if (isPureMediaGroup) {
timeStampGap = new Date(items[items.length - 1].endTime).getTime() - new Date(items[0].startTime).getTime();
} else {
timeStampGap = new Date(items[items.length - 1].takeTime).getTime() - new Date(items[0].takeTime).getTime();
}
let timeGap = formatDuration(timeStampGap, { d: true, h: true, m: true, s: true });
let list = items.slice(0);
if (import.meta.env.MODE === 'development') {
list = list.map((l: any) => {
const obj: any = {
...l,
}
obj.media && (obj.media = obj.media.replace('https://oss.anytrek.app', '/oss-anytrek-app'));
obj.media && (obj.media = obj.media.replace('https://oss.anytrek.com', '/oss-anytrek-com'));
obj.url && (obj.url = obj.url.replace('https://oss.anytrek.app', '/oss-anytrek-app'));
obj.url && (obj.url = obj.url.replace('https://oss.anytrek.com', '/oss-anytrek-com'));
return {
...obj,
}
})
}
if (isPureMediaGroup) {
result.push({ ...items[0], videoList: [...list], timeGap });
} else {
const merged = list.find((i: any) => i.media);
const hasMethodType = list.find((i: any) => i.methodType?.includes('final'));
const imageList = list.filter((i: any) => !i.media);
timeGap = timeGap === '0s' ? `${imageList.length}s` : timeGap;
const obj: any = { ...items[0], imageList: [...imageList], timeGap };
if (merged) obj.media = merged.media;
if (!hasMethodType) obj.isCarousel = true;
result.push(obj);
}
});
list.forEach((item: any) => {
// 已并入图片组的 media 项不再重复添加
if (mergedSet.has(item)) return;
const isNotMutiplePicture = !item.media && (!item.uuid || (item.triggerType !== 'CargoHandling' && item.eventType !== 9));
const isNotMergeMedia = item.media && !item.uuid;
if (isNotMutiplePicture) {
result.push(item);
}
if (isNotMergeMedia) {
let timeStampGap = 0;
timeStampGap = new Date(item.endTime).getTime() - new Date(item.startTime).getTime();
let timeGap = formatDuration(timeStampGap, { d: true, h: true, m: true, s: true });
result.push({ ...item, videoList: [item], timeGap });
}
});
result.sort((a, b) => new Date(b.takeTime).getTime() - new Date(a.takeTime).getTime());
return result;
}
function formatDuration(
ms: number,
options: any = {
d: true,
h: true,
m: true,
s: true,
}
) {
let remain = Math.floor(ms / 1000)
const result = []
const units: [string, number][] = [
['d', 86400],
['h', 3600],
['m', 60],
['s', 1],
]
for (const [key, value] of units) {
if (!options[key]) continue
const count = Math.floor(remain / value)
if (count > 0) {
result.push(`${count}${key}`)
}
remain %= value
}
return result.join('') || '0s'
}
const fetchImgList = async (successFun: Function) => {
/* 请求图片列表 */
const res = await getImgList();
if (!res || res.errorCode) {
return;
}
const list = groupCargoHandlingImages(res);
if (list[0]) {
imageData.value = { ...list[0] };
}
imgList.value = list;
successFun();
}
const getImageVideo = async(item: any) => {
const params = `userPackageId=${item.deviceId}&uuid=${item.uuid}`;
const res = await Api.getCargoHandlingVideo(params);
const index = curImgList.value.findIndex((l: any) => l.url === curImg.value.url);
if (index === -1) return;
let url = '';
if (!res || res.errorCode || !res.list?.[0]?.media) {
url = '';
} else {
url = res.list[0].media;
}
if (import.meta.env.MODE === 'development') {
url = url.replace('https://oss.anytrek.app', '/oss-anytrek-app');
url = url.replace('https://oss.anytrek.com', '/oss-anytrek-com');
}
curImg.value.media = url;
}
const onDownload = async(data: any) => {
imageDisplayRef.value?.changeDownloadImageLoading(true);
/* 下载图片,自行根据data数组长度执行下载方法 */
//await .....
imageDisplayRef.value?.changeDownloadImageLoading(false);
}
const onUpdateCurImg = (index: number) => {
const img = { ...imgList.value[index] };
imageData.value = img;
}
const changeCurPicWH = (data: any) => {
if (curPicWH.value.imgW !== data.imgW || curPicWH.value.imgH !== data.imgH) {
curPicWH.value = data;
}
}
const fetchHistoryTrail = async () => {
const res = await getHistoryTrail();
if (!res || res.errorCode) {
return [];
}
return res;
}
const fetchHistoryTrailPoint = async () => {
const res = await getHistoryTrailPoint();
if (!res || res.errorCode) {
return [];
}
return res;
}
const onToHistory = (data: any) => {
const { startTime, endTime, takeTime, vehicleId, deviceId, } = data;
let url =
getOrigin() +
`/#/history-trail?startTime=${startTime}&endTime=${endTime}&takeTime=${takeTime}`;
if (vehicleId) {
url += `&vehicleId=${vehicleId}`;
}
if (deviceId) {
url += `&deviceId=${deviceId}`;
}
console.log(url);
location.href = url;
}
📋 Props Reference
| 参数 | 描述 | 类型 | 默认值 |
|---|---|---|---|
modelValue |
弹窗显示/隐藏 | Boolean |
false |
data |
当前图片数据 | Object |
'' |
vehicleId |
车辆 ID | String | Number |
'' |
deviceId |
设备 ID | String | Number |
'' |
curImgList |
图片列表 | Object[] |
[] |
imageInterval |
cargoCamSetting接口的loading_snapshot_interval_timed | Number |
15 |
curPicWH |
图片宽高 | Object |
{imgW: 800,imgH: 600} |
haveRotate |
是否可以旋转角度 | Boolean |
false |
saveRotation |
是否持久化旋转角度 | Boolean |
false |
haveMp |
是否显示地图 | Boolean |
false |
needPath |
是否显示轨迹 | Boolean |
false |
zoom |
地图放大倍数 | Number |
8.5 |
mapJson |
Google Maps style | Object |
`` |
googleMapKey |
Google Maps API Key | String |
`` |
onGetHistoryTrail |
获取历史轨迹 | Function |
— |
onGetHistoryTrailPoint |
获取轨迹点详情 | Function |
— |
getImgList |
获取图片列表(弹窗打开且列表为空时调用) | Function |
— |
getImageVideo |
获取视频列表 | Function |
— |
isComeFromMaint |
图片是否来源于 maint 页面;为 true 时 imageCarousel / imageList 模式下状态区显示折线图图标,点击可打开遥测数据图表 |
Boolean |
false |
getChartData |
获取图表数据的方法,点击图标时才调用并按参数缓存;参数为 { userPackageId: String(deviceId), beginTime: 第一张图片takeTime, endTime: 最后一张图片takeTime },返回 null/空时图表显示 No Data |
Function |
— |
📋 Events Reference
| 事件 | 描述 | 参数 |
|---|---|---|
update:modelValue |
弹窗开关变化 | Boolean |
handleClose |
弹窗关闭 | — |
handleDownload |
下载图片,始终返回数组,父组件通过数组长度判断下载方式 | data: Object[] |
updateCurImg |
切换图片 | index: Number |
toHistory |
跳转轨迹历史 | params: Object |
changeCurPicWH |
图片加载后会返回原图宽高 | params: Object |
📋 Methods Reference (通过 ref 调用)
| 方法名 | 描述 | 参数 |
|---|---|---|
loadImageListFun |
请求图片列表,它会调传进来的getImgList方法 | — |
changeDownloadImageLoading |
改变图片下载的加载状态 | — |
🧩 Component: <SelectList>
列表选择
<SelectList
v-model:visible="isShowAllList"
v-model:selected="selectedIds"
:items="allTagsList"
id-key="id"
name-key="name"
:search-placeholder="placeholderText"
:batch-mode="hasMoreInput"
:single-mode-label="
eleType === 'vehicle' ? t('vehicleName') : t('device')
"
:batch-mode-label="t('batchDeviceID')"
batch-match-field="deviceUpid"
:create-label="wordAboutAdd"
@confirm="handleConfirm"
@jump="handleJumpEdit"
@create="handleClickAdd"
>
<template #item-icon="{ item }">
<anSvgIcon
:name="
addtagType === 'vehicle'
? getVehicleIconById(item.id)
: setTagSvg(addtagType, item)
"
font-size="24"
/>
</template>
</SelectList>
🔧 Example Setup
import { ref, computed } from 'vue';
// 面板控制
const isShowAllList = ref(false);
const selectedIds = ref<any[]>([]);
// 数据源(示例:标签列表)
const allTagsList = ref([
{ id: 1, name: '标签A' },
{ id: 2, name: '标签B' },
]);
// 搜索 & 输入模式
const placeholderText = ref('搜索标签');
const hasMoreInput = ref(false);
const eleType = ref('tag');
const addtagType = ref('tag');
const wordAboutAdd = ref('新建标签');
// 确认选中
function handleConfirm(items: any[]) {
// 将选中的 items 合并到表单数据
}
// 跳转编辑
function handleJumpEdit(item: any) {
console.log('edit', item);
}
// 新建
function handleClickAdd() {
console.log('create');
}
// 图标辅助函数(按项目类型返回对应 SVG)
function setTagSvg(type: string) {
return type === 'landmark'
? 'landmarkIcon'
: type === 'vehicle'
? 'vehicleIcon'
: 'tagIcon';
}
function getVehicleIconById(id: number) {
const vehicle = allVehicles.value.find((v: any) => v.id === id);
return vehicle ? 'vehicle-' + vehicle.content.newIcon : 'vehicleIcon';
}
📋 Props Reference
| 参数名称 | 描述 | 数据类型 | 默认值 | 必填 |
|---|---|---|---|---|
visible |
控制面板显隐 | Boolean |
— | ✅ |
selected |
已选中项的 ID 数组(双向绑定) | Array<string | number> |
[] |
— |
items |
数据源列表;tag时,需要过滤掉当前tag | Array |
[] |
— |
idKey |
唯一标识字段名 | String |
"id" |
— |
nameKey |
显示名称字段名 | String |
"name" |
— |
searchable |
是否显示搜索框 | Boolean |
true |
— |
searchPlaceholder |
搜索框占位文本,不传时默认使用多语言 | String |
"" |
— |
batchMode |
是否启用"单个/批量"输入模式切换 | Boolean |
false |
— |
singleModeLabel |
单个输入模式的标签文字 | String |
"" |
— |
batchModeLabel |
批量输入模式的标签文字 | String |
"" |
— |
batchMatchField |
批量模式下精确匹配的字段名,默认取 idKey;对于车辆和设备,需要传入deviceUpid字段,值为userPackageId |
String |
"" |
— |
showHeader |
是否显示头部 | Boolean |
true |
— |
showActions |
是否显示头部右侧按钮 | Boolean |
true |
— |
showSelectAll |
是否显示全选行 | Boolean |
true |
— |
showJump |
是否显示每项的跳转编辑按钮 | Boolean |
true |
— |
showCreate |
是否显示底部新建按钮 | Boolean |
true |
— |
createLabel |
新建按钮文字 | String |
"" |
— |
virtual |
是否启用虚拟滚动(大数据量推荐开启) | Boolean |
true |
— |
itemHeight |
虚拟滚动单项高度(px) | Number |
44 |
— |
maxHeight |
列表最大高度(px) | Number |
320 |
— |
top |
面板 CSS top 定位值 |
String |
"50%" |
— |
right |
面板 CSS right 定位值 |
String |
"525px" |
— |
zIndex |
面板 CSS z-index 层级 |
Number |
1000 |
— |
📤 Events
| 事件名 | 参数 | 描述 |
|---|---|---|
update:visible |
Boolean |
面板显隐状态变化 |
update:selected |
Array |
选中项 ID 数组变化 |
confirm |
Array |
点击确认按钮,返回当前选中的完整 item 数组 |
jump |
item |
点击某项的跳转编辑按钮,返回当前 item |
create |
— | 点击底部新建按钮 |
🧩 Slots
| 插槽名 | 作用域 | 描述 |
|---|---|---|
header-left |
— | 头部左侧区域,默认显示已选数量 |
header-actions |
— | 头部右侧操作按钮区域 |
item-icon |
{ item } |
每项左侧图标 |
item-label |
{ item } |
每项显示文本,默认取 item[nameKey] |
item-right |
{ item } |
每项右侧额外内容 |
empty |
— | 列表为空时的占位内容 |
footer |
— | 底部自定义区域,会覆盖默认的新建按钮 |
🧩 Component: <notificationOp>
通知/报警规则配置抽屉(drawer),支持新增和编辑报警通知。包含报警类型选择(低电量、静止、温度、TPMS、拆除、地理围栏、门磁等,不同类型有各自的阈值配置表单)、Apply To 绑定(tag/vehicle/landmark)、接收人配置(Email/Phone)、生效时间、TPMS 阈值弹窗等。宿主注入数据与保存方法,组件不写入任何全局 store。
<notificationOp
v-model:show="notificationShow"
:message="notificationMessage"
:is-edit="isEdit"
:data="notificationData"
:alarm-types="alarmTypes"
:vehicle-detail="notificationVehicleDetail"
:save-notification-fn="saveNotificationFn"
:edit-tpms-threshold-fn="editTpmsThresholdFn"
:edit-landmark-fn="editLandmarkFn"
:op-module-response="opModuleResponse"
:has-add="true"
@success="onNotificationSuccess"
@op-module="onOpModule"
@open-page="onOpenPage"
@refresh="onRefresh"
/>
🔧 Example Setup
const notificationShow = ref(false);
const notificationMessage = ref<any>({}); // 编辑时传入通知数据,新增传 {}
const isEdit = ref(false);
const notificationVehicleDetail = ref<any>(null); // 锁定车辆(可选)
// 宿主注入的数据(tag/vehicle/landmark 列表及用户信息等)
// deviceList + deviceStatusList + vehicleDeviceList 三份原始数据同传时,
// 组件内部会合并出 vehicle.deviceOne / deviceOne.deviceStatusOne,
// 供 Temperature(fwVer)/ TPMS(sensorConfig)等类型门槛过滤使用;
// 不传则原样使用 vehicleList。
const notificationData = ref<any>({
tagList: [],
vehicleList: [],
deviceList: [],
deviceStatusList: [],
vehicleDeviceList: [],
landmarkList: [],
landmarkGroupList: [],
loginInfo: {},
customerHabit: {},
allowSmsAlarm: false,
});
// 保存通知(新增/编辑),接收数组;返回 null 或带 errorCode 视为失败
const saveNotificationFn = async (list: any[]) => {
const res = await Api.saveNotification(list);
if (!res || res.errorCode) return res;
return res;
};
// 编辑 TPMS 阈值
const editTpmsThresholdFn = async (params: any) => {
return await Api.editTpmsThreshold(params);
};
// 编辑地标(保存星标用)
const editLandmarkFn = async (params: any[]) => {
return await Api.editLandmark(params);
};
// 宿主完成 tag/vehicle/landmark 数据刷新后,将结果传回 opModuleResponse 触发内部合并刷新
const opModuleResponse = ref<any>(null);
function onNotificationSuccess(payload: any) {
// payload: { type: 'add' | 'edit', data: 接口返回 }
console.log('notification success', payload);
}
// 组件内部跳转编辑 tag/vehicle/landmark 时触发,宿主打开对应编辑页
// payload: { opModule: 'tag' | 'vehicle' | 'landmark', type: 'edit', id: number }
function onOpModule(payload: any) {
console.log('opModule', payload);
}
// 组件请求宿主打开页面(如历史轨迹等)
function onOpenPage(url: string) {
location.href = url;
}
// 子模块(tag/vehicle/landmark/TPMS)保存成功后宿主刷新数据,配合 opModuleResponse 使用
function onRefresh(payload?: any) {
console.log('refresh', payload);
}
📋 Props Reference
| 参数名称 | 描述 | 数据类型 | 默认值 |
|---|---|---|---|
show |
drawer 的显示/隐藏(支持 v-model:show 双向绑定)。每次置为 true 时组件自动「重置内部状态 → 按 message 回填」,宿主无需 v-if 销毁重建 |
Boolean |
false |
message |
编辑时传入的通知数据(结构见下方 message 结构说明);新增时传 {}。宿主约定:先设置 message / vehicleDetail,再置 show = true |
Object |
{} |
isEdit |
是否为编辑模式(编辑模式下 vehicleDetail 锁定车辆不生效,完全走回填逻辑,标题切换为 Edit,且不显示右上角关闭按钮) |
Boolean |
false |
data |
宿主注入的数据(结构见下方 data 结构说明):tagList、vehicleList、deviceList、deviceStatusList、vehicleDeviceList、landmarkList、landmarkGroupList、loginInfo、customerHabit、allowSmsAlarm |
Object |
{} |
alarmTypes |
报警类型列表(结构见下方内置报警类型表);不传或传空数组则使用内置默认类型(随语言切换自动翻译) | Array |
内置默认 |
saveNotificationFn |
保存(新增/编辑)通知的方法,入参为通知数组(编辑时只传对比原始 message 后的变更字段 + id),需返回 Promise;返回 null 或带 errorCode 的结果视为失败,不触发 success |
Function |
— |
editTpmsThresholdFn |
编辑 TPMS 阈值的方法,由内部 tpmsThresholdSetting 弹窗调用 |
Function |
— |
editLandmarkFn |
编辑地标(保存星标用)的方法,由内部 landmark 相关弹层调用 | Function |
— |
opModuleResponse |
宿主完成 tag/vehicle/landmark 数据刷新后传入(deep watch),触发组件内部的合并刷新逻辑;结构:{ id, opModule: 'tag' | 'vehicle' | 'landmark', type: 'edit', opMessage },opMessage 为空或带 errorCode 时忽略 |
Object |
undefined |
vehicleDetail |
锁定车辆:非 null 时该车为唯一绑定车辆(Apply To 固定为 Manual 且不可切换、不能增删其他 tag/vehicle、报警类型只显示该车支持的类型);isEdit 为 true 时忽略;结构与 data.vehicleList 元素同构(需带 deviceOne / sensorConfig 才能做类型过滤;组件内部会优先从 data.vehicleList 中按 id 取全量元素,取不到则直接使用传入对象) |
Object |
null |
hasAdd |
是否显示「新增 tag / vehicle / landmark」入口按钮(影响 Apply To 添加下拉与地标选择弹层的新增按钮) | Boolean |
true |
📋 data 结构说明
{
tagList?: any[]; // tag 列表
vehicleList?: any[]; // 车辆列表
deviceList?: any[]; // 设备原始列表(与 vehicleDeviceList 同传时,组件内部合并出 vehicle.deviceOne)
deviceStatusList?: any[]; // 设备状态列表(按 userPackageId 关联到 device.deviceStatusOne,Temperature 门槛读 fwVer 依赖此合并)
vehicleDeviceList?: any[]; // 车辆-设备绑定关系([{ vehicleId, deviceId }])
landmarkList?: any[]; // landmark 列表
landmarkGroupList?: any[]; // landmark 分组列表
loginInfo?: Record<string, any>; // 用户信息;type 为 3 或 4 时组件表现为只读
customerHabit?: Record<string, any>; // 用户偏好(tempUnit、speedUnit、distanceUnit、localTimezone、timeZone 等)
allowSmsAlarm?: boolean; // 是否允许短信告警;false 时非 Door Status(20)/loadingUnloading(24) 类型会过滤掉 Phone 接收人选项
}
📋 message 结构说明(编辑时传入)
{
id: number; // 通知 id(编辑保存时回传)
type: number; // 报警类型 value(见下方内置报警类型表)
name: string;
content: {
selectVehicles: { allTags: 0 | 1; tags: number[]; vehicles: number[] };
time: { anyTime: 0 | 1; timeList: any[] }; // anyTime: 1=任何时间,0=自定义时间
attachToReminder: boolean;
createTime?: string;
};
trigger: any; // 各报警类型结构不同(alarmCompare / alarmLimit / alarmConditions / onlyIdle 等)
email: { remark: string; list: { type: 1 | 2; country: string; value: string }[] }; // type: 1=Email 2=Phone
}
// 特殊:message 无 type 但带 content.selectVehicles 时,视为"别的页面带了车辆 id 跳转过来",只回填绑定车辆
📋 内置报警类型(alarmTypes 默认值)
| value | 类型 | 分组(parent) | 阈值表单 | 适用车型(model) |
|---|---|---|---|---|
| 1 | Low Battery | deviceStatus | 电量百分比(1-100,快捷 10/15/20/30/50) | — |
| 3 | ACC | vehicle | on / off 单选 | VT1508D,VT2208,VT2216,VS2308,VT2508 |
| 4 | Geofence | vehicle | inside/outside/entering/exiting + landmark(outside 可填 for 距离),可多条 | — |
| 5 | Speed | vehicle | 速度阈值(按 customerHabit.speedUnit 显示单位) | — |
| 6 | Temperature | device | 高于 / 低于两个可选温度阈值(按 customerHabit.tempUnit 显示单位,需 fwVer ≥ 11309) | VT2002,VS2310 |
| 8 | Idle | vehicle | 静止时长 hr(快捷 1/6/24/168/720) | — |
| 11 | Offline | device | 无 | — |
| 14 | Device Dismantled | device | 无 | AT2215 |
| 17 | Landmark Vehicle | statistics | inside/outside + landmark + over/below + 数量,可多条 | — |
| 18 | landmarkDetention | vehicle | (Only idle) 勾选 + inside/outside + landmark + over/below + 滞留时长(hr,步进 0.5),可多条 | — |
| 19 | Status Change | vehicle | 无 | VT2002,VT2211 |
| 20 | Door Status | vehicle | open/close 多选 + 不触发区域(landmark 多选,需至少选一个) | VS2310 |
| 21 | Idle engine on | vehicle | 分钟数(最小 15,步进 15,可左右加减) | VT2208 |
| 23 | TPMS | device | 无(可打开 TPMS Threshold 弹窗配置胎压阈值,需车辆绑 AS2530) | VT2211,VS2310 |
| 24 | loadingUnloading | vehicle | 无 | VS2310 |
| 25 | Container Status | vehicle | loaded / unloaded / loadedOrUnloaded 单选(需集装箱传感器 AS2521) | VT2211 |
📤 Events
| 事件名 | 描述 | 参数 |
|---|---|---|
update:show |
drawer 显隐状态变化(关闭按钮 / 取消 / 保存成功后触发) | Boolean |
success |
保存成功后触发 | { type: 'add' | 'edit', data: 接口返回数据 } |
opModule |
组件内部点击编辑 tag/vehicle/landmark 时触发,宿主打开对应编辑页 | payload: { opModule: 'tag' | 'vehicle' | 'landmark', type: 'edit', id: number } |
openPage |
组件请求宿主打开页面时触发 | url: String |
refresh |
子模块(tag/vehicle/landmark/TPMS)保存成功后触发,宿主刷新对应数据后可配合 opModuleResponse 回传 |
payload?: Object |
📋 Methods Reference
无对外暴露方法(script setup 未 defineExpose)。每次 show 变为 true 时组件自动执行「重置全部内部状态 → 按 message 回填」,等价于销毁重建,宿主无需再用 v-if + 延时销毁来重置组件。
备注:
- 只读用户(
data.loginInfo.type === 3或4)下组件表现为只读,不可保存。 - 未传
alarmTypes时使用内置默认报警类型(见上表),各类型有对应的阈值配置表单。 vehicleDetail锁定车辆仅在新增模式(isEdit为false)下生效。- 宿主约定:打开前先设置
message/vehicleDetail,再置show = true(组件使用 pre-flush watch,同 tick 内设置即可生效)。 - 编辑保存时组件会用
compareObjects2对比原始message,只把变更字段传给saveNotificationFn;无变更时不调用保存方法。 - 保存时若时间选择弹层 / tag-vehicle 选择弹层尚未关闭,第一次点击保存会先确认弹层内容,第二次点击才真正提交。
allowSmsAlarm为false时,非 Door Status(20) / loadingUnloading(24) 类型的告警会过滤掉 Phone 接收人选项。
Related Packages
webpack plugin to find duplicated dependencies from your bundle
Version History
| Version | Released | License |
|---|---|---|
| 1.15.15 | ISC | |
| 1.15.14 | ISC | |
| 1.15.13 | ISC | |
| 1.15.12 | ISC | |
| 1.15.11 | ISC | |
| 1.15.10 | ISC | |
| 1.15.9 | ISC | |
| 1.15.8 | ISC | |
| 1.15.7 | ISC | |
| 1.15.6 | ISC | |
| 1.15.5 | ISC | |
| 1.15.4 | ISC | |
| 1.15.3 | ISC | |
| 1.15.2 | ISC | |
| 1.15.1 | ISC | |
| 1.14.8 | ISC | |
| 1.14.7 | ISC | |
| 1.14.6 | ISC | |
| 1.14.5 | ISC | |
| 1.14.4 | ISC | |
| 1.14.3 | ISC | |
| 1.14.2 | ISC | |
| 1.14.1 | ISC | |
| 1.13.33 | ISC | |
| 1.13.32 | ISC | |
| 1.13.31 | ISC | |
| 1.13.30 | ISC | |
| 1.13.29 | ISC | |
| 1.13.28 | ISC | |
| 1.13.27 | ISC |
Showing the latest 30 of 444. See every release on npm