Merge branch 'deploy-1.0' of http://120.76.84.145:10101/gitblit/r/H5/xp-robotaxi-web into deploy-1.0
| | |
| | | <script setup lang="tsx"> |
| | | import type { DataTableColumns, FormInst } from 'naive-ui' |
| | | import { NButton, NDataTable, NDatePicker, NInput, NSelect, NSpace, NTag, NTooltip } from 'naive-ui' |
| | | import { NButton, NDataTable, NDatePicker, NInput, NSelect, NSpace, NTag, NTimePicker, NTooltip } from 'naive-ui' |
| | | import { computed, onActivated, onMounted, reactive, ref, watch } from 'vue' |
| | | import { onBeforeRouteLeave, useRoute, useRouter } from 'vue-router' |
| | | import dayjs from 'dayjs' |
| | |
| | | { label: '20以上禁行区域', value: '20' }, |
| | | ] |
| | | |
| | | const fenceStrategyTypeOptions = [ |
| | | { label: '工作日', value: 0 }, |
| | | { label: '法定节假日', value: 1 }, |
| | | { label: '非工作日', value: 2 }, |
| | | { label: '自定义时间', value: 3 }, |
| | | ] |
| | | |
| | | const stationStatusOptions = [ |
| | | { label: '草稿', value: 0 }, |
| | | { label: '待生效', value: 1 }, |
| | |
| | | electronicNumberOrName: '', |
| | | electronicAreaId: null as string | null, |
| | | statusList: null as number[] | null, |
| | | type: null as number | null, |
| | | lifecycleRange: null as [number, number] | null, |
| | | startTime: '', |
| | | endTime: '', |
| | | updateBy: '', |
| | | updateTime: null as [number, number] | null, |
| | | createBy: '', |
| | |
| | | return value ? dayjs(value[1]).endOf('day').format('YYYY-MM-DD HH:mm:ss') : undefined |
| | | } |
| | | |
| | | function parseTimePickerValue(value?: string | null) { |
| | | const normalizedValue = String(value || '').trim() |
| | | if (!normalizedValue) |
| | | return null |
| | | const parsed = dayjs(`2000-01-01 ${normalizedValue}`) |
| | | return parsed.isValid() ? parsed.valueOf() : null |
| | | } |
| | | |
| | | function formatTimePickerValue(value: number | null) { |
| | | return typeof value === 'number' ? dayjs(value).format('HH:mm') : '' |
| | | } |
| | | |
| | | function getCurrentTabModel() { |
| | | if (activeTab.value === 'fence') |
| | | return fenceModel.value |
| | |
| | | pageSize: pagination.pageSize, |
| | | electronicNumberOrName: fenceModel.value.electronicNumberOrName, |
| | | statusList: fenceModel.value.statusList, |
| | | type: fenceModel.value.type, |
| | | effectTime: fenceModel.value.lifecycleRange ? dayjs(fenceModel.value.lifecycleRange[0]).startOf('day').format('YYYY-MM-DD HH:mm:ss') : null, |
| | | failureTime: fenceModel.value.lifecycleRange ? dayjs(fenceModel.value.lifecycleRange[1]).endOf('day').format('YYYY-MM-DD HH:mm:ss') : null, |
| | | startTime: fenceModel.value.startTime || null, |
| | | endTime: fenceModel.value.endTime || null, |
| | | createBy: fenceModel.value.createBy, |
| | | updateBy: fenceModel.value.updateBy, |
| | | updateStartTime: fenceModel.value.updateTime ? dayjs(fenceModel.value.updateTime[0]).format('YYYY-MM-DD HH:mm:ss') : null, |
| | |
| | | ), |
| | | }} |
| | | </NSelect> |
| | | ), |
| | | }, |
| | | { |
| | | label: '运营策略', |
| | | path: 'type', |
| | | render: () => ( |
| | | <NSelect |
| | | v-model:value={fenceModel.value.type} |
| | | options={fenceStrategyTypeOptions} |
| | | placeholder="请选择" |
| | | clearable |
| | | /> |
| | | ), |
| | | }, |
| | | { |
| | | label: '生命周期', |
| | | path: 'lifecycleRange', |
| | | render: () => ( |
| | | <NDatePicker v-model:value={fenceModel.value.lifecycleRange} type="daterange" clearable actions={[]} close-on-select={true} format="yyyy/MM/dd" /> |
| | | ), |
| | | }, |
| | | { |
| | | label: '运营时间', |
| | | path: 'operationTime', |
| | | render: () => ( |
| | | <div style={{ display: 'flex', alignItems: 'center', gap: '8px', width: '100%' }}> |
| | | <NTimePicker |
| | | value={parseTimePickerValue(fenceModel.value.startTime)} |
| | | format="HH:mm" |
| | | placeholder="开始时间" |
| | | clearable |
| | | actions={['now']} |
| | | style={{ width: 'calc(50% - 12px)' }} |
| | | onUpdate:value={value => fenceModel.value.startTime = formatTimePickerValue(value)} |
| | | /> |
| | | <span>至</span> |
| | | <NTimePicker |
| | | value={parseTimePickerValue(fenceModel.value.endTime)} |
| | | format="HH:mm" |
| | | placeholder="结束时间" |
| | | clearable |
| | | actions={['now']} |
| | | style={{ width: 'calc(50% - 12px)' }} |
| | | onUpdate:value={value => fenceModel.value.endTime = formatTimePickerValue(value)} |
| | | /> |
| | | </div> |
| | | ), |
| | | }, |
| | | { |
| | |
| | | :disabled="checkedRowKeys.length === 0" |
| | | @click="handlePointBatchExport" |
| | | > |
| | | 批量导出 |
| | | 批量导出点位 |
| | | </NButton> |
| | | </template> |
| | | <template v-else-if="activeTab === 'station'"> |
| | |
| | | :disabled="checkedRowKeys.length === 0" |
| | | @click="handleStationBatchExport" |
| | | > |
| | | 批量导出 |
| | | 批量导出场站 |
| | | </NButton> |
| | | </template> |
| | | </n-flex> |
| | |
| | | return Number(operationArea?.areaStatus || 1); |
| | | } |
| | | |
| | | function getStationOriginalAreaStatus() { |
| | | const detail = stationDetailData.value; |
| | | return Number(detail?.areaStatus) === 2 || |
| | | Number(detail?.status) === 3 || |
| | | String(detail?.status || detail?.statusText || "").trim() === "已停用" |
| | | ? 2 |
| | | : 1; |
| | | } |
| | | |
| | | function openStationDisablePointStopDialog(pointCount: number) { |
| | | return new Promise<boolean | null>((resolve) => { |
| | | if (!window.$dialog) { |
| | | resolve(null); |
| | | return; |
| | | } |
| | | window.$dialog.warning({ |
| | | title: "提示", |
| | | content: `当前场站包含${pointCount}个点位,确认一起停用?`, |
| | | positiveText: "是", |
| | | negativeText: "否", |
| | | maskClosable: false, |
| | | autoFocus: false, |
| | | onPositiveClick: () => resolve(true), |
| | | onNegativeClick: () => resolve(false), |
| | | onClose: () => resolve(null), |
| | | }); |
| | | }); |
| | | } |
| | | |
| | | async function resolveFencePointRelationPayload() { |
| | | if (fenceCreateMode.value || !fenceDetailData.value) |
| | | return { addPointIdList: [], delPointIdList: [], disablePointIdList: [] }; |
| | |
| | | return; |
| | | } |
| | | const isCreatingStation = stationCreateMode.value; |
| | | const areaStatus = Number(stationForm.value.areaStatus || 1); |
| | | const pointCount = Array.isArray(stationForm.value.pointList) |
| | | ? stationForm.value.pointList.length |
| | | : 0; |
| | | let pointStop: boolean | undefined; |
| | | if ( |
| | | !isCreatingStation && |
| | | saveType === 1 && |
| | | areaStatus === 2 && |
| | | getStationOriginalAreaStatus() !== 2 && |
| | | pointCount > 0 |
| | | ) { |
| | | const confirmResult = await openStationDisablePointStopDialog(pointCount); |
| | | if (confirmResult === null) return; |
| | | pointStop = confirmResult; |
| | | } |
| | | stationSaving.value = true; |
| | | try { |
| | | const areaStatus = Number(stationForm.value.areaStatus || 1); |
| | | const nextStatus = |
| | | saveType === 0 ? "草稿" : areaStatus === 2 ? "已停用" : "启用中"; |
| | | const pointList = stationForm.value.pointList.map((item) => ({ |
| | |
| | | centerLon, |
| | | saveType, |
| | | areaStatus, |
| | | pointStop, |
| | | operationArea, |
| | | polygonPath, |
| | | operationType: stationForm.value.operationType, |
| | |
| | | centerLon?: number; |
| | | saveType: 0 | 1; |
| | | areaStatus: number; |
| | | pointStop?: boolean; |
| | | operationArea: any; |
| | | polygonPath: LngLatTuple[]; |
| | | operationType: "1" | "2"; |
| | |
| | | centerLon, |
| | | saveType, |
| | | areaStatus, |
| | | pointStop, |
| | | operationArea, |
| | | polygonPath, |
| | | operationType, |
| | |
| | | fencePoints: stringifyFencePoints(polygonPath) || undefined, |
| | | operationType: Number(operationType || 1), |
| | | parkingTimeLimit: Number(parkingTimeLimit || 0), |
| | | pointStop, |
| | | pointCount: pointList.length, |
| | | remark: description.trim() || undefined, |
| | | stationArea, |