| | |
| | | <template> |
| | | <div ref="wrapperRef" class="search-panel"> |
| | | <n-grid :cols="cols" :collapsed="collapsed" :collapsed-rows="collapsedRows" x-gap="24" y-gap="12"> |
| | | <n-grid |
| | | ref="gridRef" |
| | | :cols="cols" |
| | | :collapsed="collapsed" |
| | | :collapsed-rows="collapsedRows" |
| | | x-gap="24" |
| | | y-gap="12" |
| | | > |
| | | <!-- 查询条件 --> |
| | | <slot /> |
| | | |
| | | <!-- 操作区 --> |
| | | <n-gi suffix> |
| | | <div class="search-panel__actions"> |
| | | <!-- 展开 / 收起 --> |
| | | <n-button v-if="showToggle" text size="small" @click="toggle"> |
| | | <template #icon> |
| | | <icon-park-outline-up v-if="!collapsed" /> |
| | |
| | | </template> |
| | | {{ collapsed ? t('common.expand') : t('common.collapse') }} |
| | | </n-button> |
| | | |
| | | <slot name="action" /> |
| | | </div> |
| | | </n-gi> |
| | |
| | | </template> |
| | | |
| | | <script setup lang="ts"> |
| | | import { ref, onMounted, onBeforeUnmount, watch } from 'vue' |
| | | import { ref, onMounted, onBeforeUnmount, nextTick } from 'vue' |
| | | |
| | | const { t } = useI18n() |
| | | |
| | |
| | | }) |
| | | |
| | | interface Props { |
| | | /** 收起时显示行数 */ |
| | | collapsedRows?: number |
| | | /** 是否默认收起 */ |
| | | defaultCollapsed?: boolean |
| | | /** 是否显示展开收起按钮 */ |
| | | showToggle?: boolean |
| | | } |
| | | |
| | | export interface SearchItem { |
| | | key: string |
| | | label?: string |
| | | [key: string]: any |
| | | } |
| | | |
| | | const props = withDefaults(defineProps<Props>(), { |
| | | collapsedRows: 2, |
| | | defaultCollapsed: true, |
| | | showToggle: false, |
| | | defaultCollapsed: true |
| | | }) |
| | | |
| | | const wrapperRef = ref<any>(null) |
| | | const collapsed = ref<boolean>(props.defaultCollapsed) |
| | | const cols = ref<number>(2) |
| | | const wrapperRef = ref<HTMLElement | null>(null) |
| | | const gridRef = ref<any>(null) |
| | | |
| | | const collapsed = ref(props.defaultCollapsed) |
| | | const cols = ref(2) |
| | | const showToggle = ref(false) |
| | | |
| | | function toggle() { |
| | | collapsed.value = !collapsed.value |
| | | } |
| | | |
| | | /** |
| | | * 宽度 → 列数规则 |
| | | * 宽度 → 列数 |
| | | */ |
| | | function calcCols(width: number): number { |
| | | function calcCols(width: number) { |
| | | if (width >= 1756) return 6 |
| | | if (width >= 1516) return 5 |
| | | if (width >= 1112) return 4 |
| | | if (width >= 828) return 3 |
| | | return 2 |
| | | } |
| | | |
| | | /** |
| | | * 计算 n-gi 数量 |
| | | */ |
| | | function calcGiCount() { |
| | | nextTick(() => { |
| | | const grid = gridRef.value?.$el |
| | | if (!grid) return |
| | | |
| | | const items = grid.querySelectorAll( |
| | | '.n-grid-item' |
| | | ) |
| | | console.log(items) |
| | | const count = items.length |
| | | |
| | | const rows = Math.ceil(count / cols.value) |
| | | |
| | | showToggle.value = rows > props.collapsedRows |
| | | }) |
| | | } |
| | | |
| | | let observer: ResizeObserver | null = null |
| | |
| | | |
| | | observer = new ResizeObserver(entries => { |
| | | const width = entries[0].contentRect.width |
| | | |
| | | cols.value = calcCols(width) |
| | | |
| | | calcGiCount() |
| | | }) |
| | | |
| | | const element = wrapperRef.value.$el || wrapperRef.value |
| | | observer.observe(element) |
| | | observer.observe(wrapperRef.value) |
| | | |
| | | calcGiCount() |
| | | }) |
| | | |
| | | onBeforeUnmount(() => { |
| | | observer?.disconnect() |
| | | observer = null |
| | | }) |
| | | |
| | | watch( |
| | | () => props.defaultCollapsed, |
| | | val => { |
| | | collapsed.value = val |
| | | } |
| | | ) |
| | | </script> |
| | | |
| | | <style scoped> |
| | |
| | | const { VITE_MAP_KEY } = import.meta.env |
| | | |
| | | const NEVER_EXPIRE = -1 // -1 永不失效 |
| | | const IMMEDIATE_EFFECT = -2 // -1 立即失效 |
| | | const IMMEDIATE_EFFECT = -2 // -2 立即生效 |
| | | const MAX_VERTICES = 16 // 最大顶点数限制 |
| | | const MAX_AREA = 81 // 最大区域面积限制(单位:平方公里) |
| | | let centerPoint = [104.066301, 30.572961] |
| | |
| | | |
| | | let isDrawing = false // 是否正在绘制 |
| | | let isEditing = false // 是否正在编辑 |
| | | |
| | | let fenceEditorClosed = false // 围栏编辑器是否已关闭 |
| | | // ==================== 电子围栏相关 ==================== |
| | | |
| | | /** 初始化地图 */ |
| | |
| | | fenceEditor.open() |
| | | isEditing = true |
| | | console.log('开启编辑', fencePoints, fence) |
| | | fenceEditor.on('end', (e: any) => { |
| | | const path = fence.getPath() |
| | | if (path.length < 3) { |
| | | message.warning('运营区域至少需要3个点') |
| | | isEditing = false |
| | | setTimeout(() => { |
| | | drawPolygon() |
| | | }, 100) |
| | | } else { |
| | | // 更新围栏坐标 |
| | | fencePoints = path.map((p: any) => [p.lng, p.lat]) |
| | | } |
| | | }) |
| | | } |
| | | |
| | | /** 绘制禁行区域多边形围栏 */ |
| | |
| | | const path = e.obj.getPath().map((p: any) => [p.lng, p.lat]) |
| | | console.log('禁行区围栏坐标:', path) |
| | | if (path.length < 3) { |
| | | message.error('请绘制至少3个点的围栏') |
| | | message.error('请绘制至少3个点的禁行区') |
| | | map.remove(e.obj) |
| | | drawDisallowedAreaPolygon(id) |
| | | return |
| | | } |
| | | // 检查禁行区顶点数量是否超过限制 |
| | | if (path.length > MAX_VERTICES) { |
| | | message.warning(`禁行区域顶点数量不能超过 ${MAX_VERTICES} 个`) |
| | | message.warning(`禁行区最多支持设置 ${MAX_VERTICES} 个点位`) |
| | | map.remove(e.obj) |
| | | drawDisallowedAreaPolygon(id) |
| | | return |
| | |
| | | const area = calcPolygonArea(e.obj.getPath()) |
| | | console.log('禁行区面积:', area) |
| | | if (area > MAX_AREA) { |
| | | message.warning(`禁行区域面积不能超过 ${MAX_AREA} 平方公里`) |
| | | message.warning(`当前禁行区范围过大,请缩小区域`) |
| | | map.remove(e.obj) |
| | | drawDisallowedAreaPolygon(id) |
| | | return |
| | |
| | | |
| | | const operationPath = fence.getPath() |
| | | if (!isDisallowedAreaInOperationArea(e.obj.getPath(), operationPath)) { |
| | | message.warning('禁行区域必须在运营区域内') |
| | | message.warning('禁行区域必须完全处于运营区域内') |
| | | map.remove(e.obj) |
| | | drawDisallowedAreaPolygon(id) |
| | | return |
| | |
| | | function enableDisallowedAreaFenceEdit(id: number) { |
| | | const disallowedArea = disallowedAreaList.value.find((sub: any) => sub.id === id) |
| | | if (!disallowedArea.fence) return |
| | | // 先关闭现有的编辑器,防止事件监听器累积 |
| | | if (fenceEditor) { |
| | | fenceEditor.close() |
| | | } |
| | | // 开启当前禁行区多边形的编辑能力 |
| | | fenceEditor = new AMap.PolyEditor(map, disallowedArea.fence, { |
| | | controlPoint: { |
| | |
| | | fenceEditor.on('addnode', (e: any) => { |
| | | const path = e.target.getPath(); |
| | | if (path.length > MAX_VERTICES) { |
| | | message.warning(`禁行区域顶点数量不能超过 ${MAX_VERTICES} 个`); |
| | | message.warning(`禁行区域顶点数量不能超过 ${MAX_VERTICES} 个`); |
| | | // 移除操作的顶点 |
| | | path.pop() |
| | | e.target.setPath(path); |
| | |
| | | |
| | | // 检查面积限制 |
| | | if (area > MAX_AREA) { |
| | | message.warning(`禁行区域面积不能超过${MAX_AREA}平方公里`) |
| | | message.warning(`当前禁行区范围过大,请缩小区域`) |
| | | return |
| | | } |
| | | |
| | |
| | | if (fence) { |
| | | const operationPath = fence.getPath() |
| | | if (!isDisallowedAreaInOperationArea(path, operationPath)) { |
| | | message.warning('禁行区域必须在运营区域内') |
| | | message.warning('禁行区域必须完全处于运营区域内') |
| | | return |
| | | } |
| | | } |
| | |
| | | |
| | | /** 编辑完成后最终校验(兜底) **/ |
| | | fenceEditor.on('end', (e: any) => { |
| | | const path = e.target.getPath() |
| | | console.log('编辑完成事件', e, '11111111111111111111111111') |
| | | const path = e.target ? e.target.getPath() : disallowedArea.fence.getPath() |
| | | if (path.length < 3) { |
| | | if (!fenceEditorClosed) { |
| | | message.warning('请绘制至少3个点的禁行区') |
| | | } |
| | | isEditing = false |
| | | fenceEditorClosed = true |
| | | setTimeout(() => { |
| | | disallowedArea.isActive = true |
| | | drawDisallowedAreaPolygon(id) |
| | | }, 100) |
| | | } |
| | | const area = calcPolygonArea(path) |
| | | |
| | | console.log('禁行区面积:', path) |
| | | // 检查面积限制 |
| | | if (area > MAX_AREA) { |
| | | message.warning(`禁行区域面积不能超过${MAX_AREA}平方公里`) |
| | | message.warning(`当前禁行区范围过大,请缩小区域`) |
| | | return |
| | | } |
| | | |
| | |
| | | if (fence) { |
| | | const operationPath = fence.getPath() |
| | | if (!isDisallowedAreaInOperationArea(path, operationPath)) { |
| | | message.warning('禁行区域必须在运营区域内') |
| | | message.warning('禁行区域必须完全处于运营区域内') |
| | | return |
| | | } |
| | | } |
| | |
| | | function updateDisallowedAreaStyle(id?: number) { |
| | | const list = disallowedAreaList.value.filter((sub: any) => sub.id !== id) |
| | | if (!list.length) return |
| | | console.log('禁行区样式', list) |
| | | list.forEach((sub: any) => { |
| | | sub.fence.setOptions({ |
| | | strokeColor: "transparent", //轮廓线颜色 |
| | |
| | | /** 保存草稿/发布围栏 */ |
| | | const handleAddFence = (type: number) => { |
| | | fencePoints = fence.getPath().map((p: any) => [p.lng, p.lat]) |
| | | if (disallowedAreaList.value.some((item: any) => item.fence === null)) { |
| | | console.log(disallowedAreaList.value) |
| | | if (disallowedAreaList.value.some((item: any) => !item.fence || item.fence === null || item.fence.getPath().map((p: any) => (p.lng + '_' + p.lat)).length < 3)) { |
| | | message.error('请先绘制禁行区') |
| | | return |
| | | } |
| | |
| | | isEdit.value = modalType.value === 'edit' && fenceDetail.value.status === 2 |
| | | if (modalType.value === 'edit') { |
| | | const item = fenceDetail.value.electronicFenceAreas.filter((item: any) => item.areaType === 1)[0] |
| | | console.log(dayjs(fenceDetail.value.effectTime).valueOf(), 'eeeeee') |
| | | formModel.value = { |
| | | electronicName: fenceDetail.value.electronicName || '', |
| | | areaDescription: item.areaDescription || '', |
| | |
| | | id: index + 1 |
| | | } |
| | | }) |
| | | console.log(groupList.value) |
| | | } |
| | | } |
| | | }) |
| | |
| | | <div class="title items-center" v-if="fenceDetail"> |
| | | {{ fenceDetail.electronicName || '' }} |
| | | <NTag style="font-weight: 500;" |
| | | :type="fenceDetail.status === 0 || fenceDetail.status === 3 ? 'default' : fenceDetail.status === 2 ? 'success' : 'warning'" |
| | | :type="fenceDetail.status === 0 ? 'default' : fenceDetail.status === 2 ? 'success' : fenceDetail.status === 3 ? 'error' : 'warning'" |
| | | :bordered="false" class="h-24px mx-4px"> |
| | | {{ fenceDetail.status === 0 ? '草稿' : fenceDetail.status === 1 ? '待生效' : |
| | | fenceDetail.status === 2 |
| | |
| | | <!-- 禁行区 --> |
| | | <div class="box flex flex-col gap-8px"> |
| | | <div class="box-title">禁行区</div> |
| | | <div v-for="sub in disallowedAreaList" :key="sub.id" class="disallowed-area" |
| | | <div v-for="sub in disallowedAreaList" :key="sub.id" class="disallowed-area gap-4px" |
| | | :class="{ 'active': sub.isActive }" @click="() => { |
| | | sub.isActive = true |
| | | sub.fence.setOptions({ |
| | |
| | | } |
| | | }) |
| | | }"> |
| | | <div class="flex items-center"> |
| | | <div class="flex items-center flex-1"> |
| | | <img src="../../../assets/geofence/noEntry.png" class="w-[32px] h-[32px] mr-4px" /> |
| | | <div class="flex flex-col gap-4px"> |
| | | <div class="flex flex-col gap-4px flex-1"> |
| | | <div class="name">{{ sub.areaName }}</div> |
| | | <div class="area">{{ `覆盖面积:${sub.area || 0}km²` }}</div> |
| | | </div> |
| | |
| | | <div class="box flex flex-col"> |
| | | <div class="box-title mb-8px">基本信息</div> |
| | | <n-form-item label="区域名称" path="electronicName"> |
| | | <n-input v-model:value="formModel.electronicName" clearable maxLength="50" :disabled="isEdit" /> |
| | | <n-input v-model:value="formModel.electronicName" clearable maxlength="50" :disabled="isEdit" /> |
| | | </n-form-item> |
| | | <n-form-item label="区域类型" path="areaType"> |
| | | <n-select v-model:value="formModel.areaType" :options="regionTypeOptions" clearable |
| | |
| | | <div class="box-title">禁行区</div> |
| | | <div v-for="sub in disallowedAreaList" :key="sub.id" class="disallowed-area" |
| | | :class="{ 'active': sub.isActive }" @click="handleDisallowedAreaEdit(sub)"> |
| | | <div class="flex items-center"> |
| | | <div class="flex items-center flex-1"> |
| | | <img src="../../../assets/geofence/noEntry.png" class="w-[32px] h-[32px] mr-4px" /> |
| | | <div class="flex flex-col gap-4px"> |
| | | <div class="flex flex-col gap-4px flex-1"> |
| | | <div class="name" v-if="!sub.edit">{{ sub.areaName }}</div> |
| | | <n-input v-model:value="sub.areaName" v-else clearable @keydown.enter.stop="sub.edit = false" |
| | | @blur.stop="sub.edit = false" :autofocus="sub.edit"/> |
| | |
| | | import type { DataTableColumns, FormInst } from 'naive-ui' |
| | | import { useBoolean } from '@/hooks' |
| | | import { getFenceList, copyFence } from '@/service/api/operations' |
| | | import { NButton, NTag, NSpace } from 'naive-ui' |
| | | import { NButton, NTag, NSpace, NTooltip } from 'naive-ui' |
| | | import SearchPanel from '@/components/custom/SearchPanel.vue' |
| | | import { useRouter } from 'vue-router' |
| | | import sortDefault from '@/assets/geofence/sort1.png' |
| | |
| | | |
| | | function handleResetSearch() { |
| | | model.value = { ...initialModel } |
| | | getList() |
| | | } |
| | | |
| | | const formRef = ref<FormInst | null>() |
| | |
| | | title: '围栏ID', |
| | | key: 'electronicNumber', |
| | | resizable: true, |
| | | minWidth: 120, |
| | | width: 120, |
| | | }, |
| | | { |
| | | title: '围栏名称', |
| | | key: 'electronicName', |
| | | resizable: true, |
| | | minWidth: 150, |
| | | ellipsis: true, |
| | | width: 150, |
| | | render(row) { |
| | | return ( |
| | | <div |
| | | class="cursor-pointer text-ellipsis whitespace-nowrap w-full overflow-hidden text-[#0068FF]" |
| | | onClick={() => router.push({ |
| | | name: 'geofenceDetail', |
| | | query: { |
| | | id: row.id, |
| | | type: 'detail' |
| | | } |
| | | })}> |
| | | {row.electronicName} |
| | | </div> |
| | | ) |
| | | return h( |
| | | NTooltip, |
| | | { |
| | | trigger: "hover", |
| | | }, |
| | | { |
| | | trigger: () => |
| | | h( |
| | | "span", |
| | | { |
| | | style: { |
| | | display: "inline-block", |
| | | width: "100%", |
| | | overflow: "hidden", |
| | | textOverflow: "ellipsis", |
| | | whiteSpace: "nowrap", |
| | | color: '#0068FF', |
| | | cursor: 'pointer', |
| | | }, |
| | | onClick: () => router.push({ |
| | | path: '/businessOperations/geofenceManagement/detail', |
| | | query: { |
| | | id: row.id |
| | | } |
| | | }) |
| | | }, |
| | | row.electronicName |
| | | ), |
| | | default: () => row.electronicName, |
| | | } |
| | | ); |
| | | } |
| | | }, |
| | | { |
| | |
| | | key: 'operatingAreaCount', |
| | | resizable: true, |
| | | ellipsis: true, |
| | | minWidth: 180, |
| | | width: 180, |
| | | render(row) { |
| | | return ( |
| | | <div class='cursor-pointer text-ellipsis whitespace-nowrap w-full overflow-hidden'> |
| | |
| | | title: '状态', |
| | | key: 'status', |
| | | resizable: true, |
| | | minWidth: 80, |
| | | width: 80, |
| | | render(row) { |
| | | return h( |
| | | NTag, |
| | | { |
| | | type: row.status === 0 || row.status === 3 ? 'default' : row.status === 2 ? 'success' : 'warning', |
| | | type: row.status === 0 ? 'default' : row.status === 2 ? 'success' : row.status === 3 ? 'error' : 'warning', |
| | | bordered: false |
| | | }, |
| | | { |
| | |
| | | key: 'updateBy', |
| | | ellipsis: true, |
| | | resizable: true, |
| | | minWidth: 100, |
| | | width: 100, |
| | | }, |
| | | { |
| | | title: '更新时间', |
| | |
| | | ellipsis: true, |
| | | resizable: true, |
| | | sorter: true, |
| | | minWidth: 200, |
| | | width: 200, |
| | | renderSorterIcon({ order }) { |
| | | let src = sortDefault |
| | | if (order === 'ascend') src = sortAsc |
| | |
| | | key: 'createBy', |
| | | ellipsis: true, |
| | | resizable: true, |
| | | minWidth: 100, |
| | | width: 100, |
| | | }, |
| | | { |
| | | title: '创建时间', |
| | |
| | | ellipsis: true, |
| | | sorter: true, |
| | | resizable: true, |
| | | minWidth: 200, |
| | | width: 200, |
| | | renderSorterIcon({ order }) { |
| | | let src = sortDefault |
| | | if (order === 'ascend') src = sortAsc |
| | |
| | | text |
| | | v-permission="403" |
| | | type="primary" |
| | | disabled={row.status === 2} |
| | | disabled={row.status !== 3} |
| | | onClick={() => { |
| | | dialog.create({ |
| | | title: '确认复制围栏吗?', |
| | | content: '', |
| | | title: '是否确认复制?', |
| | | content: `复制位【${row.electronicName}_副本】`, |
| | | positiveText: '确定', |
| | | negativeText: '取消', |
| | | draggable: true, |
| | |
| | | </script> |
| | | |
| | | <template> |
| | | <n-flex class="roboTaxi"> |
| | | <NSpace vertical class="flex-1" :size="12"> |
| | | <div class="roboTaxi-form-card"> |
| | | <div class="roboTaxi w-full h-full"> |
| | | <div ref="formRef" class="roboTaxi-form-card mb-12px"> |
| | | <n-form ref="formRef" :model="model" label-placement="left" inline :show-feedback="false"> |
| | | <SearchPanel showToggle> |
| | | <n-gi v-for="item in filterItems" :key="item.path"> |
| | | <SearchPanel> |
| | | <n-gi v-for="item in filterItems" :key="item.path" class="n-grid-item"> |
| | | <n-form-item :label="item.label" :path="item.path"> |
| | | <component :is="item.render" /> |
| | | </n-form-item> |
| | |
| | | </n-form> |
| | | </div> |
| | | |
| | | <n-card class="flex-1"> |
| | | <n-card class="flex-1 robotaxi-table-card"> |
| | | <template #header> |
| | | <n-flex justify="flex-end" v-permission="404"> |
| | | <NButton type="primary" secondary @click="handleToDetail({}, 'add')"> |
| | |
| | | </NButton> |
| | | </n-flex> |
| | | </template> |
| | | <NSpace vertical> |
| | | <n-data-table :columns="columns" :data="listData" :row-class-name="getRowClass" :loading="loading" |
| | | <div class="flex-1 flex flex-col h-full"> |
| | | <n-data-table class="flex-1" :columns="columns" :data="listData" :row-class-name="getRowClass" :loading="loading" flex-height :scroll-x="1230" |
| | | :bordered="false" :header-height="18" :row-height="18" @update:sorter="handleSorterChange" /> |
| | | <Pagination :count="count" @change="changePage" /> |
| | | </NSpace> |
| | | <Pagination class="robotaxi-pagination mt-12px" :count="count" @change="changePage" /> |
| | | </div> |
| | | </n-card> |
| | | </NSpace> |
| | | </n-flex> |
| | | </div> |
| | | </template> |
| | | |
| | | <style lang="scss"> |
| | |
| | | <n-select v-model:value="formModel.openCityId" :options="cityList" /> |
| | | </n-form-item-grid-item> |
| | | <n-form-item-grid-item :span="2" label="备注" path="remarks"> |
| | | <n-input v-model:value="formModel.remarks" type="textarea" /> |
| | | <n-input v-model:value="formModel.remarks" type="textarea" maxlength="200" show-count /> |
| | | </n-form-item-grid-item> |
| | | <n-form-item-grid-item :span="2" label="线路规划规则" path="routePlanRule" label-placement="top"> |
| | | <n-radio-group v-model:value="formModel.routePlanRule" class="pl-8"> |
| | |
| | | const model = ref({ ...initialModel }) |
| | | function handleResetSearch() { |
| | | model.value = { ...initialModel } |
| | | getList() |
| | | } |
| | | |
| | | const formRef = ref<FormInst | null>() |
| | |
| | | { |
| | | title: '序号', |
| | | key: 'serialNumber', |
| | | resizable: true |
| | | resizable: true, |
| | | width: 80 |
| | | }, |
| | | { |
| | | title: '名称', |
| | | key: 'planName', |
| | | resizable: true |
| | | resizable: true, |
| | | ellipsis: { |
| | | tooltip: true |
| | | } |
| | | }, |
| | | { |
| | | title: '开通城市', |
| | | key: 'cityName', |
| | | resizable: true |
| | | resizable: true, |
| | | ellipsis: { |
| | | tooltip: true |
| | | }, |
| | | render: (row) => row.cityName + row.districtName || '-' |
| | | }, |
| | | { |
| | | title: '优先规则', |
| | | key: 'routePlanRule', |
| | | resizable: true, |
| | | ellipsis: { |
| | | tooltip: true |
| | | }, |
| | | render: (row) => { |
| | | return options.find(item => item.value === row.routePlanRule)?.label || '-' |
| | | } |
| | |
| | | { |
| | | title: '备注', |
| | | key: 'remarks', |
| | | resizable: true |
| | | resizable: true, |
| | | ellipsis: { |
| | | tooltip: true |
| | | } |
| | | }, |
| | | { |
| | | title: '添加时间', |
| | |
| | | { |
| | | title: '操作', |
| | | key: 'actions', |
| | | width: 260, |
| | | width: 120, |
| | | fixed: 'right', |
| | | render: (row) => { |
| | | return ( |
| | |
| | | type="error" |
| | | onClick={() => { |
| | | dialog.create({ |
| | | title: '是否确认删除?', |
| | | content: '', |
| | | title: '是否确认删除数据?', |
| | | content: '确认删除吗该路线规划配置?删除后不可恢复', |
| | | positiveText: '确定', |
| | | negativeText: '取消', |
| | | draggable: true, |
| | |
| | | <template #header> |
| | | <n-flex justify="flex-end" v-permission="6061"> |
| | | <NButton type="primary" secondary @click="modalRef.openModal('add')"> |
| | | 新增城市 |
| | | 新增 |
| | | </NButton> |
| | | </n-flex> |
| | | </template> |
| | |
| | | <Pagination :count="count" @change="changePage" /> |
| | | </NSpace> |
| | | |
| | | <TableModal ref="modalRef" modal-name="线路规划配置" @refresh="getList"/> |
| | | <TableModal ref="modalRef" modal-name="线路规划配置" @refresh="getList" /> |
| | | </n-card> |
| | | </NSpace> |
| | | </n-flex> |
| | |
| | | const model = ref({ ...initialModel }) |
| | | function handleResetSearch() { |
| | | model.value = { ...initialModel } |
| | | getList() |
| | | } |
| | | const pageParams = ref({ |
| | | pageNum: 1, |
| | |
| | | orderRating: model.value.orderRating, |
| | | phoneNumber: model.value.phoneNumber, |
| | | plateNumber: model.value.plateNumber, |
| | | startTime: model.value.time ? dayjs(model.value.time[0]).format('YYYY-MM-DD HH:mm:ss') : null, |
| | | endTime: model.value.time ? dayjs(model.value.time[1]).format('YYYY-MM-DD HH:mm:ss') : null, |
| | | startTime: model.value.time ? dayjs(model.value.time[0]).format('YYYY-MM-DD') : null, |
| | | endTime: model.value.time ? dayjs(model.value.time[1]).format('YYYY-MM-DD') : null, |
| | | } |
| | | getOrderEvaluateList(params).then((res) => { |
| | | listData.value = res.data.records || [] |
| | |
| | | <div class="roboTaxi w-full h-full"> |
| | | <div ref="formRef" class="roboTaxi-form-card mb-12px"> |
| | | <n-form :model="model" label-placement="left" inline :show-feedback="false"> |
| | | <SearchPanel show-toggle> |
| | | <n-gi v-for="item in filterItems" :key="item.path"> |
| | | <SearchPanel> |
| | | <n-gi v-for="item in filterItems" :key="item.path" class="n-grid-item"> |
| | | <n-form-item :label="item.label" :path="item.path"> |
| | | <component :is="item.render" /> |
| | | </n-form-item> |
| | |
| | | basicPricingJson1: { |
| | | num4: null, |
| | | num5: null, |
| | | num6: null, |
| | | num6: 0, |
| | | num7: null, |
| | | num8: null, |
| | | num9: null, |
| | |
| | | return |
| | | } |
| | | if (category.value === '1') { |
| | | console.log(model.value.basicPricingJson1) |
| | | if (hasNullValue(model.value.basicPricingJson1)) { |
| | | window.$message.error('请配置基础定价') |
| | | return |
| | | } else { |
| | | model.value.basicPricingJson1.num14 = model.value.basicPricingJson1.num14 + ' - ' + model.value.basicPricingJson1.num141 |
| | | model.value.basicPricingJson1.num21 = model.value.basicPricingJson1.num21 + ' - ' + model.value.basicPricingJson1.num211 |
| | | model.value.basicPricingJson1.num22 = model.value.basicPricingJson1.num22 + ' - ' + model.value.basicPricingJson1.num221 |
| | | delete model.value.basicPricingJson1.num141 |
| | | delete model.value.basicPricingJson1.num211 |
| | | delete model.value.basicPricingJson1.num221 |
| | | } |
| | | if (model.value.startingFareJson.length === 0) { |
| | | window.$message.error('请配置起步价') |
| | |
| | | return |
| | | } |
| | | startLoading() |
| | | |
| | | // 创建一个副本用于提交,避免修改原始数据 |
| | | let submitBasicPricingJson1 = { ...model.value.basicPricingJson1 }; |
| | | if (category.value === '1' && !hasNullValue(model.value.basicPricingJson1)) { |
| | | // 在副本上进行时间范围合并 |
| | | if (submitBasicPricingJson1.num14 && submitBasicPricingJson1.num141) { |
| | | submitBasicPricingJson1.num14 = submitBasicPricingJson1.num14 + ' - ' + submitBasicPricingJson1.num141; |
| | | delete submitBasicPricingJson1.num141; |
| | | } |
| | | if (submitBasicPricingJson1.num21 && submitBasicPricingJson1.num211) { |
| | | submitBasicPricingJson1.num21 = submitBasicPricingJson1.num21 + ' - ' + submitBasicPricingJson1.num211; |
| | | delete submitBasicPricingJson1.num211; |
| | | } |
| | | if (submitBasicPricingJson1.num22 && submitBasicPricingJson1.num221) { |
| | | submitBasicPricingJson1.num22 = submitBasicPricingJson1.num22 + ' - ' + submitBasicPricingJson1.num221; |
| | | delete submitBasicPricingJson1.num221; |
| | | } |
| | | } |
| | | |
| | | const params = { |
| | | basicPricingJson: category.value === '1' ? JSON.stringify(model.value.basicPricingJson1) : JSON.stringify(model.value.basicPricingJson2), |
| | | basicPricingJson: category.value === '1' ? JSON.stringify(submitBasicPricingJson1) : JSON.stringify(model.value.basicPricingJson2), |
| | | dynamicPricingJson: JSON.stringify(model.value.dynamicPricingJson), |
| | | startingFareJson: category.value === '1' ? JSON.stringify(model.value.startingFareJson) : '', |
| | | id: id.value, |
| | |
| | | model.value.isDynamicPrice = detail.value.isDynamicPrice || 0 |
| | | } |
| | | } |
| | | |
| | | watch(() => model.value.basicPricingJson1.num7, (newVal, oldVal) => { |
| | | if (newVal !== oldVal) { |
| | | model.value.basicPricingJson1.num9 = newVal |
| | | } |
| | | }) |
| | | |
| | | watch(() => model.value.basicPricingJson1.num10, (newVal, oldVal) => { |
| | | if (newVal !== oldVal) { |
| | | model.value.basicPricingJson1.num12 = newVal |
| | | } |
| | | }) |
| | | |
| | | onMounted(() => { |
| | | getPricingDetailData(id.value as string) |
| | |
| | | <n-form-item label="远途费设置" path="num6" required> |
| | | <NSpace vertical :size="16"> |
| | | <NSpace align="center"> |
| | | 总里程超过 |
| | | <!-- 总里程超过 |
| | | <n-input-number v-model:value="model.basicPricingJson1.num6" min="0" max="999" :precision="0" |
| | | :show-button="false" placeholder="最多3位数" class="mr-3" /> |
| | | 公里,不足 |
| | | :show-button="false" placeholder="最多3位数" class="mr-3" /> --> |
| | | 总里程超过起步包含公里数时,公里不足 |
| | | <n-input-number v-model:value="model.basicPricingJson1.num7" min="0" max="999" :precision="0" |
| | | :show-button="false" placeholder="最多3位数" class="mr-3" /> |
| | | 公里,超出部分每公里加收 |
| | |
| | | </NSpace> |
| | | <NSpace align="center"> |
| | | 超过 |
| | | <n-input-number v-model:value="model.basicPricingJson1.num9" min="0" max="999" :precision="0" |
| | | :show-button="false" placeholder="最多3位数" class="mr-3" /> |
| | | <!-- 公里数为num9,但是直接取num7 --> |
| | | <n-input-number v-model:value="model.basicPricingJson1.num7" min="0" max="999" :precision="0" |
| | | :show-button="false" placeholder="最多3位数" class="mr-3" disabled /> |
| | | 不足 |
| | | <n-input-number v-model:value="model.basicPricingJson1.num10" min="0" max="999" :precision="0" |
| | | :show-button="false" placeholder="最多3位数" class="mr-3" /> |
| | |
| | | </NSpace> |
| | | <NSpace align="center"> |
| | | 超过 |
| | | <n-input-number v-model:value="model.basicPricingJson1.num12" min="0" max="999" :precision="0" |
| | | :show-button="false" placeholder="最多3位数" class="mr-3" /> |
| | | <!-- 公里数为num12,但是直接取num10 --> |
| | | <n-input-number v-model:value="model.basicPricingJson1.num10" min="0" max="999" :precision="0" |
| | | :show-button="false" placeholder="最多3位数" class="mr-3" disabled/> |
| | | 公里以后,超出部分每公里加收 |
| | | <n-input-number v-model:value="model.basicPricingJson1.num13" min="0" max="999" :precision="2" |
| | | :show-button="false" placeholder="最多3位数" class="mr-3" /> |
| | |
| | | <n-form-item label="远途费设置" path="basicPricingJson1"> |
| | | <NSpace vertical> |
| | | <NSpace align="center"> |
| | | 总里程超过 {{ model.basicPricingJson1.num6 }} 公里不足 {{ model.basicPricingJson1.num7 }} 公里,超出部分每公里加收 {{ |
| | | <!-- 总里程超过 {{ model.basicPricingJson1.num6 }} 公里不足 {{ model.basicPricingJson1.num7 }} 公里,超出部分每公里加收 {{ |
| | | model.basicPricingJson1.num8 }} 元 --> |
| | | 总里程超过起步包含公里数时,公里不足 {{ model.basicPricingJson1.num7 }} 公里,超出部分每公里加收 {{ |
| | | model.basicPricingJson1.num8 }} 元 |
| | | </NSpace> |
| | | <NSpace align="center"> |