anytrek-front-public-component

some common component
43,591
Install
npm install anytrek-front-public-component
Latest Version:1.15.15
Vue:3
License:ISC
Last Updated:Oct 22, 2026
Links: npm
Maintainer: anytrek-one

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的使用

  1. 必须有modleList才能生效;
  2. 用于对于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 + 延时销毁来重置组件。

备注:

  1. 只读用户(data.loginInfo.type === 3 或 4)下组件表现为只读,不可保存。
  2. 未传 alarmTypes 时使用内置默认报警类型(见上表),各类型有对应的阈值配置表单。
  3. vehicleDetail 锁定车辆仅在新增模式(isEdit 为 false)下生效。
  4. 宿主约定:打开前先设置 message / vehicleDetail,再置 show = true(组件使用 pre-flush watch,同 tick 内设置即可生效)。
  5. 编辑保存时组件会用 compareObjects2 对比原始 message,只把变更字段传给 saveNotificationFn;无变更时不调用保存方法。
  6. 保存时若时间选择弹层 / tag-vehicle 选择弹层尚未关闭,第一次点击保存会先确认弹层内容,第二次点击才真正提交。
  7. allowSmsAlarm 为 false 时,非 Door Status(20) / loadingUnloading(24) 类型的告警会过滤掉 Phone 接收人选项。

Related Packages

@21cnfe/onekit-mp-cli

小程序框架开发工具

1,063
duplicate-dependencies-webpack-plugin

webpack plugin to find duplicated dependencies from your bundle

332,104 6
@debut/plugin-report

Report generation for strategy backtesting in debut

4,011 18
@amilochau/core-vue3

Core plugin for vue.js (v3) projects

3,802 0
@lg2/nuxt3-plugin-link-resolver

link resolver plugin fro nuxt3

3,544 0

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