Merge branch 'deploy-1.0' into deploy-2.1
| | |
| | | return method |
| | | } |
| | | |
| | | // 导出活动订单Excel |
| | | export function exportActivityOrderExcel(params: any) { |
| | | const method = request.Post<Service.ResponseResult<any>>('/order/api/v1/order/exportActivityOrderExcel', params, { responseType: 'blob' }) |
| | | method.meta = { ...method.meta, isBlob: true } |
| | | return method |
| | | } |
| | | |
| | | // 改派订单 |
| | | export function reassignOrder(params: any) { |
| | | return request.Post<Service.ResponseResult<any>>('/order/api/v1/order/reassignById', params) |
| | |
| | | <script setup lang="tsx"> |
| | | import { useBoolean } from '@/hooks' |
| | | import { addOpenCity, editOpenCity, getRegionCascader } from '@/service/api/operations' |
| | | import { addOpenCity, editOpenCity, getRegionCascader, getRegionCascaderNoArea } from '@/service/api/operations' |
| | | |
| | | interface Props { |
| | | modalName?: string |
| | |
| | | } |
| | | |
| | | async function fetchCascaderData() { |
| | | const res = await getRegionCascader() |
| | | const res = await getRegionCascaderNoArea() |
| | | if (res.code === 200 && res.data) { |
| | | pcaOptions.value = formatCascaderData(res.data) |
| | | } |
| | |
| | | </script> |
| | | |
| | | <template> |
| | | <n-modal v-model:show="modalVisible" :segmented="{ |
| | | content: true, |
| | | action: true, |
| | | }" :content-style="{ |
| | | <n-modal |
| | | v-model:show="modalVisible" :segmented="{ |
| | | content: true, |
| | | action: true, |
| | | }" :content-style="{ |
| | | padding: '16px 24px 0px 24px', |
| | | }" :mask-closable="false" preset="card" title="开通城市" class="w-400px table-modal"> |
| | | <n-form ref="formRef" :rules="rules" :model="formModel" :disabled="modalType === 'view'" |
| | | require-mark-placement="left"> |
| | | }" :mask-closable="false" preset="card" title="开通城市" class="w-400px table-modal" |
| | | > |
| | | <n-form |
| | | ref="formRef" :rules="rules" :model="formModel" :disabled="modalType === 'view'" |
| | | require-mark-placement="left" |
| | | > |
| | | <n-form-item label="开通城市" path="pca"> |
| | | <n-cascader v-model:value="formModel.pca" :options="pcaOptions" value-field="code" label-field="name" |
| | | check-strategy="all" @update:value="handlePcaChange" @update:show="isRoleSelectOpen = $event"> |
| | | <n-cascader |
| | | v-model:value="formModel.pca" :options="pcaOptions" value-field="code" label-field="name" |
| | | check-strategy="all" @update:value="handlePcaChange" @update:show="isRoleSelectOpen = $event" |
| | | > |
| | | <template #arrow> |
| | | <img |
| | | src="https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/e120f81c-9f15-4e1a-9938-22252898edf9.png" |
| | | class="arrow-icon" :class="{ 'arrow-icon--open': isRoleSelectOpen }" style="width: 16px;height: 16px;" |
| | | alt="arrow" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }"> |
| | | alt="arrow" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }" |
| | | > |
| | | </template> |
| | | </n-cascader> |
| | | </n-form-item> |
| | |
| | | const baseItems = [ |
| | | { label: "启用中", value: getStatNumber(source?.startingUp), color: "success" }, |
| | | { label: "待生效", value: getStatNumber(source?.pendingLaunch), color: "warning" }, |
| | | // { label: "草稿", value: getStatNumber(source?.draft), color: "muted" }, |
| | | { label: "已停用", value: getStatNumber(source?.disabled), color: "muted" }, |
| | | { label: "已失效", value: getStatNumber(source?.expired), color: "muted" }, |
| | | ]; |
| | | |
| | | if (type === "station") return baseItems; |
| | | if (type === "station") { |
| | | return [ |
| | | ...baseItems.slice(0, 2), |
| | | { label: "草稿", value: getStatNumber(source?.draft), color: "muted" }, |
| | | ...baseItems.slice(2), |
| | | ]; |
| | | } |
| | | |
| | | return [ |
| | | ...baseItems, |
| | | { label: "可运营", value: getStatNumber(source?.canOperate), color: "success" }, |
| | | { label: "待测试", value: getStatNumber(source?.toBeTested), color: "info" }, |
| | | { label: "测试中", value: getStatNumber(source?.testing), color: "info" }, |
| | | { |
| | |
| | | import sortAsc from '@/assets/geofence/sort2.png' |
| | | import sortDesc from '@/assets/geofence/sort3.png' |
| | | import moreIcon from '@/assets/element-img/more.png' |
| | | import noDataIcon from '@/assets/element-img/no-data.png' |
| | | |
| | | const { hasPermission } = usePermission() |
| | | |
| | |
| | | electronicAreaId: null as string | null, |
| | | statusList: null as number[] | null, |
| | | type: null as number | null, |
| | | typeStr: null as number[] | null, |
| | | lifecycleRange: null as [number, number] | null, |
| | | startTime: '', |
| | | endTime: '', |
| | |
| | | const isPointAreaSelectOpen = ref(false) |
| | | const isFenceStatusListOpen = ref(false) |
| | | const isFenceElectronicAreaIdOpen = ref(false) |
| | | const isFenceStrategyTypeOpen = ref(false) |
| | | const isFenceStrategyWeekOpen = ref(false) |
| | | const isStationStatusOpen = ref(false) |
| | | const isStationPointNumOpen = ref(false) |
| | | const isFenceCityOpen = ref(false) |
| | |
| | | return labels.length ? labels.join('、') : '-' |
| | | } |
| | | return fenceStrategyDateTypeOptions.find(option => option.value === Number(item?.type))?.label || '-' |
| | | } |
| | | |
| | | function normalizeFenceStrategyWeekFilter(value: Array<number | string> | null | undefined) { |
| | | const normalized = (Array.isArray(value) ? value : []) |
| | | .map(item => Number(item)) |
| | | .filter(item => Number.isFinite(item)) |
| | | |
| | | if (normalized.includes(0)) |
| | | return [0] |
| | | |
| | | return Array.from(new Set(normalized)).sort((a, b) => a - b) |
| | | } |
| | | |
| | | function handleFenceStrategyTypeFilterChange(value: number | null) { |
| | | fenceModel.value.type = value |
| | | if (value !== 3) |
| | | fenceModel.value.typeStr = null |
| | | } |
| | | |
| | | function handleFenceStrategyWeekFilterChange(value: Array<number | string> | null) { |
| | | const normalized = normalizeFenceStrategyWeekFilter(value) |
| | | fenceModel.value.typeStr = normalized.length ? normalized : null |
| | | } |
| | | |
| | | function formatFenceStrategyTimeList(item: Record<string, any>) { |
| | |
| | | electronicNumberOrName: fenceModel.value.electronicNumberOrName, |
| | | statusList: fenceModel.value.statusList, |
| | | type: fenceModel.value.type, |
| | | typeStr: fenceModel.value.type === 3 ? fenceModel.value.typeStr : null, |
| | | 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, |
| | |
| | | return ( |
| | | <div class="flex-center py-12px"> |
| | | <img |
| | | src="/src/assets/element-img/no-data.png" |
| | | src={noDataIcon} |
| | | style="width: 123px; height: 125px" |
| | | alt="no-data" |
| | | /> |
| | |
| | | {{ empty: ( |
| | | <div class="flex-center"> |
| | | <img |
| | | src="/src/assets/element-img/no-data.png" |
| | | src={noDataIcon} |
| | | style="width: 123px; height: 125px" |
| | | alt="no-data" |
| | | /> |
| | |
| | | path: 'type', |
| | | render: () => ( |
| | | <NSelect |
| | | v-model:value={fenceModel.value.type} |
| | | value={fenceModel.value.type} |
| | | options={fenceStrategyTypeOptions} |
| | | placeholder="请选择" |
| | | clearable |
| | | /> |
| | | onUpdate:value={handleFenceStrategyTypeFilterChange} |
| | | onUpdate:show={(val: boolean) => isFenceStrategyTypeOpen.value = val} |
| | | > |
| | | {{ |
| | | arrow: () => ( |
| | | <img |
| | | src="https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/e120f81c-9f15-4e1a-9938-22252898edf9.png" |
| | | class={`arrow-icon ${isFenceStrategyTypeOpen.value ? 'arrow-icon--open' : ''}`} |
| | | style={{ width: '16px', height: '16px' }} |
| | | alt="arrow" |
| | | /> |
| | | ), |
| | | }} |
| | | </NSelect> |
| | | ), |
| | | }, |
| | | { |
| | |
| | | <NDatePicker v-model:value={fenceModel.value.lifecycleRange} type="daterange" clearable actions={[]} close-on-select={true} format="yyyy/MM/dd" /> |
| | | ), |
| | | }, |
| | | ...(fenceModel.value.type === 3 |
| | | ? [{ |
| | | label: '自定义周', |
| | | path: 'typeStr', |
| | | render: () => ( |
| | | <NSelect |
| | | value={fenceModel.value.typeStr} |
| | | options={fenceStrategyWeekOptions} |
| | | placeholder="请选择自定义周" |
| | | clearable |
| | | multiple |
| | | max-tag-count={1} |
| | | onUpdate:show={(val: boolean) => isFenceStrategyWeekOpen.value = val} |
| | | onUpdate:value={handleFenceStrategyWeekFilterChange} |
| | | > |
| | | {{ |
| | | arrow: () => ( |
| | | <img |
| | | src="https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/e120f81c-9f15-4e1a-9938-22252898edf9.png" |
| | | class={`arrow-icon ${isFenceStrategyWeekOpen.value ? 'arrow-icon--open' : ''}`} |
| | | style={{ width: '16px', height: '16px' }} |
| | | alt="arrow" |
| | | /> |
| | | ), |
| | | }} |
| | | </NSelect> |
| | | ), |
| | | }] |
| | | : []), |
| | | { |
| | | label: '运营时间', |
| | | path: 'operationTime', |
| | |
| | | <template #empty> |
| | | <div class="flex-center"> |
| | | <img |
| | | src="/src/assets/element-img/no-data.png" |
| | | :src="noDataIcon" |
| | | style="width: 123px; height: 125px" |
| | | alt="no-data" |
| | | > |
| | |
| | | () => !fenceCreateMode.value && Number(fenceDetailData.value?.status) === 2 |
| | | ); |
| | | |
| | | // 围栏保存前的统一阻断原因。 |
| | | // 这里不直接执行 message,而是返回首个阻断文案,供按钮禁用态和保存提示复用同一套校验结果。 |
| | | const fenceSaveBlockReason = computed(() => { |
| | | if (!isFenceFormMode.value) return ""; |
| | | if (isLockedFenceStatusEdit.value) return ""; |
| | |
| | | if (Number(strategy.type) === 3 && !strategy.typeStr.length) |
| | | return `请为 ${strategy.name || "策略组"} 选择生效星期`; |
| | | let typeKey = ""; |
| | | // 非自定义策略按固定类型去重;自定义策略按“星期组合”去重。 |
| | | // 这样既能防止出现两个“工作日”策略,也允许不同星期组合并存。 |
| | | if (Number(strategy.type) === 0) typeKey = "workday"; |
| | | else if (Number(strategy.type) === 1) typeKey = "statutoryHoliday"; |
| | | else if (Number(strategy.type) === 2) typeKey = "holiday"; |
| | |
| | | if (typeKey && strategyTypeKeys.has(typeKey)) |
| | | return `运营策略存在重复:${strategy.name || "策略组"} 的类型已存在`; |
| | | if (typeKey) strategyTypeKeys.add(typeKey); |
| | | // “每天”覆盖全部日期范围,因此不允许再和其他策略组并存。 |
| | | if (typeKey.includes("custom-0") && fenceStrategyList.value.length > 1) |
| | | return "选择“每天”时,不能添加其他运营策略"; |
| | | if (!strategy.time.length) |
| | |
| | | } |
| | | for (const area of fenceRestrictedAreas.value) { |
| | | if (!area.areaName.trim()) return "请填写禁行区名称"; |
| | | // 禁行区只要开始绘制,就必须形成完整 polygon,避免保存半成品区域。 |
| | | if (area.points.length > 0 && area.points.length < 3) |
| | | return `${area.areaName || "禁行区"} 还未完成绘制`; |
| | | if (!area.points.length) return `${area.areaName || "禁行区"} 还未完成绘制`; |
| | |
| | | return ""; |
| | | }); |
| | | |
| | | // 场站在“自定义时间”模式下的时间段校验。 |
| | | // 这里和围栏策略校验拆开,便于场站表单保存、联动校验及错误提示单独复用。 |
| | | function getStationCustomTimeErrorMessage() { |
| | | if (stationForm.value.operationType !== "2") return ""; |
| | | const ranges = Array.isArray(stationForm.value.customTimeRanges) |
| | |
| | | return ""; |
| | | } |
| | | |
| | | // 编辑态下通过快照判断场站表单是否被修改,用于离开编辑态时提示是否放弃更改。 |
| | | const stationEditPayloadSnapshot = computed(() => { |
| | | return JSON.stringify({ |
| | | stationName: stationForm.value.stationName.trim(), |
| | |
| | | }); |
| | | }); |
| | | |
| | | // 场站保存前的统一阻断原因。 |
| | | // 与围栏类似,返回首个错误文案,供按钮禁用态、保存前提示、局部联动共用。 |
| | | const stationSaveBlockReason = computed(() => { |
| | | if (!isStationFormMode.value) return ""; |
| | | if (!stationForm.value.stationName.trim()) return "请输入场站名称"; |
| | |
| | | allowOutsideOperationArea: isStationEditWorkflow.value, |
| | | }); |
| | | if (geometryError) return geometryError; |
| | | // 编辑已有场站时允许它暂时落在围栏外,方便修复历史数据或承接围栏变更后的残留场站; |
| | | // 但只要当前没有匹配到任何运营区,就不能把状态切回“启用”。 |
| | | if ( |
| | | isStationEditWorkflow.value && |
| | | !resolveStationOperationAreaByPolygon(stationDraftPoints.value) && |
| | |
| | | .filter((item) => item.points.length >= 3); |
| | | } |
| | | |
| | | // 根据整个场站 polygon 反查所属运营区。 |
| | | // 这里不使用中心点命中,而是要求“场站整体完全落在某个运营区内”, |
| | | // 以便和保存校验、围栏解绑逻辑保持一致。 |
| | | function resolveStationOperationAreaByPolygon(points: LngLatTuple[]) { |
| | | if (points.length < 3) return null; |
| | | return ( |
| | |
| | | ); |
| | | } |
| | | |
| | | // 统一处理场站几何合法性校验: |
| | | // 1. 必须构成合法 polygon |
| | | // 2. 新建时必须在运营区内,编辑时允许临时落在围栏外 |
| | | // 3. 不允许与其他场站重叠/包含 |
| | | // 4. 不允许与启用中的禁行区重叠 |
| | | function getStationGeometryInvalidReason( |
| | | points: LngLatTuple[], |
| | | options: { |
| | |
| | | const otherStations = getOtherStations(); |
| | | const geometryUtil = (window as any).AMap?.GeometryUtil; |
| | | if (geometryUtil) { |
| | | // 先拦截相交,再拦截包含/被包含,提示更符合用户的实际绘制问题。 |
| | | const crossStation = otherStations.find((station) => |
| | | geometryUtil.doesRingRingIntersect(points, station.points) |
| | | ); |
| | |
| | | return ""; |
| | | } |
| | | |
| | | // 地图交互层的场站几何校验封装。 |
| | | // 与纯函数校验分开,专门负责在用户绘制/拖拽时弹出 warning。 |
| | | function validateStationGeometry(points: LngLatTuple[]) { |
| | | const errorMessage = getStationGeometryInvalidReason(points, { |
| | | allowOutsideOperationArea: isStationEditWorkflow.value, |
| | |
| | | return true; |
| | | } |
| | | |
| | | // 按场站 polygon 自动同步框选到的点位列表。 |
| | | // 保留已匹配点位的原有顺序,只把新增命中的点位追加到末尾,避免用户编辑时列表频繁跳动。 |
| | | function syncStationPointListByPolygon(path: LngLatTuple[]) { |
| | | const nextMatchedList = getPointItemsWithinPolygon(path).map( |
| | | mapListItemToStationPoint |
| | |
| | | }); |
| | | } |
| | | |
| | | // 基于当前草稿点位重建地图上的场站 polygon。 |
| | | // 只负责地图图形本身,不负责同步表单/点位列表,避免图形重建时产生额外副作用。 |
| | | function createStationDraftPolygon(points: LngLatTuple[]) { |
| | | if (!map.value || points.length < 3) return; |
| | | if (stationDraftPolygon.value) map.value.remove(stationDraftPolygon.value); |
| | |
| | | map.value.add(stationDraftPolygon.value); |
| | | } |
| | | |
| | | // 首次应用一组完整的场站草稿几何信息。 |
| | | // 用于详情回显、新建绘制完成后落稿,同时按最新 polygon 自动同步场站内点位。 |
| | | function applyStationDraftGeometry(points: LngLatTuple[], fitView = false) { |
| | | stationDraftPoints.value = clonePolygonPath(points); |
| | | stationForm.value.polygonPath = clonePolygonPath(points); |
| | |
| | | map.value.setFitView([stationDraftPolygon.value], false, [100, 100, 100, 100]); |
| | | } |
| | | |
| | | // 编辑器拖拽/加点/删点过程中的轻量同步。 |
| | | // 这里只更新内存态,不重建 polygon,避免编辑过程出现闪烁。 |
| | | function syncStationDraftGeometryState(points: LngLatTuple[]) { |
| | | stationDraftPoints.value = clonePolygonPath(points); |
| | | stationForm.value.polygonPath = clonePolygonPath(points); |
| | | syncStationPointListByPolygon(points); |
| | | } |
| | | |
| | | // 当编辑后的几何不合法时,回滚到上一次合法草稿,并重新挂回编辑器。 |
| | | // 这里通过 rollbacking 标记和 suppress 标记避免 close/open 编辑器时触发重复 end 事件。 |
| | | async function rollbackStationDraftGeometry() { |
| | | if (!map.value || stationDraftPoints.value.length < 3 || stationEditorRollbacking) |
| | | return; |
| | |
| | | enableStationDraftEdit(); |
| | | } |
| | | |
| | | // 打开场站 polygon 的可编辑态,并把 AMap 编辑事件同步回表单草稿。 |
| | | // adjust/addnode/removenode 过程中先实时校验,若非法则立即回滚,确保草稿始终保持可保存状态。 |
| | | function enableStationDraftEdit() { |
| | | if (!map.value || !stationDraftPolygon.value || stationEditorRollbacking) return; |
| | | if (stationPolyEditor.value) suppressStationEditorEnd.value = true; |
| | |
| | | editOptions: { fillColor: "#A034FF" }, |
| | | } |
| | | ); |
| | | // 统一从 polygon 实例回读最新 path,避免不同编辑事件各自维护一套取值逻辑。 |
| | | const syncStationGeometry = () => { |
| | | if (!stationDraftPolygon.value || stationEditorRollbacking) return; |
| | | const points = stationDraftPolygon.value |
| | |
| | | } |
| | | syncStationDraftGeometryState(points); |
| | | }; |
| | | // 主动 close 编辑器时会触发 end,这里用 suppress 标记过滤掉非用户操作的结束事件。 |
| | | const handleStationGeometryEditEnd = () => { |
| | | if (suppressStationEditorEnd.value) { |
| | | suppressStationEditorEnd.value = false; |
| | |
| | | if (!value) |
| | | return '' |
| | | if (typeof value === 'string') { |
| | | const direct = parseAvatarUrl(value) |
| | | if (direct) |
| | | return direct |
| | | const list = getValidImageList(value) |
| | | if (list.length) |
| | | return list[0]?.url || '' |
| | | return value |
| | | return /^https?:\/\//.test(value) ? value : '' |
| | | } |
| | | if (typeof value === 'object') { |
| | | const direct = (value as any)?.url |
| | |
| | | import { computed, onMounted, ref } from 'vue' |
| | | import { useRoute, useRouter } from 'vue-router' |
| | | import { NButton, NCascader, NCheckbox, NDataTable, NDatePicker, NForm, NGrid, NInput, NSpace, NTag, useDialog, useMessage } from 'naive-ui' |
| | | import { getRegionCascader } from '@/service/api/operations' |
| | | import { getRegionCascader, getRegionCascaderNoArea } from '@/service/api/operations' |
| | | import { addDirectService, editDirectService, getDirectServiceDetail, getTargetedVehiclePageList } from '@/service/api/directService' |
| | | import { useTabStore } from '@/store' |
| | | import { useBoolean } from '@/hooks' |
| | |
| | | } |
| | | |
| | | async function fetchCascaderData() { |
| | | const res = await getRegionCascader() |
| | | const res = await getRegionCascaderNoArea() |
| | | if (res.code === 200 && res.data) { |
| | | pcaOptions.value = formatCascaderData(res.data) |
| | | } |
| | |
| | | useMessage, |
| | | } from 'naive-ui' |
| | | import type { DataTableColumns } from 'naive-ui' |
| | | import { exportOrderExcel, getFleetList, getOrderList } from '@/service/api/transaction' |
| | | import { exportActivityOrderExcel, getFleetList, getOrderList } from '@/service/api/transaction' |
| | | import { operationPageList } from '@/service/api/vehicleConvoy' |
| | | import SearchPanel from '@/components/custom/SearchPanel.vue' |
| | | import Pagination from '@/components/common/Pagination.vue' |
| | |
| | | |
| | | async function handleExport() { |
| | | let params: any = {} |
| | | if (checkedRowKeys.value.length > 0) { |
| | | params.ids = checkedRowKeys.value |
| | | if (!checkedRowKeys.value.length) { |
| | | return |
| | | } |
| | | else { |
| | | params = { |
| | | pageNum: currentPage.value, |
| | | pageSize: pageSize.value, |
| | | activityId: props.activityId, |
| | | status: searchModel.value.status, |
| | | orderNumber: searchModel.value.orderNumber, |
| | | nickName: searchModel.value.nickName, |
| | | plateNumber: searchModel.value.plateNumber, |
| | | fleetId: searchModel.value.fleetId, |
| | | orderSource: searchModel.value.orderSource, |
| | | cityCode: searchModel.value.cityCode, |
| | | orderTime: searchModel.value.orderTime, |
| | | startTime: searchModel.value.orderTime ? dayjs(searchModel.value.orderTime?.[0]).format('YYYY-MM-DD') : null, |
| | | endTime: searchModel.value.orderTime ? dayjs(searchModel.value.orderTime?.[1]).format('YYYY-MM-DD') : null, |
| | | } |
| | | params = { |
| | | pageNum: currentPage.value, |
| | | pageSize: pageSize.value, |
| | | activityId: props.activityId, |
| | | status: searchModel.value.status, |
| | | orderNumber: searchModel.value.orderNumber, |
| | | nickName: searchModel.value.nickName, |
| | | plateNumber: searchModel.value.plateNumber, |
| | | fleetId: searchModel.value.fleetId, |
| | | orderSource: searchModel.value.orderSource, |
| | | cityCode: searchModel.value.cityCode, |
| | | orderTime: searchModel.value.orderTime, |
| | | startTime: searchModel.value.orderTime ? dayjs(searchModel.value.orderTime?.[0]).format('YYYY-MM-DD') : null, |
| | | endTime: searchModel.value.orderTime ? dayjs(searchModel.value.orderTime?.[1]).format('YYYY-MM-DD') : null, |
| | | } |
| | | const response = await exportOrderExcel(params) |
| | | const response = await exportActivityOrderExcel(params) |
| | | if (response instanceof Blob) { |
| | | downloadFile(response, `活动订单列表_${new Date().getTime()}.xlsx`) |
| | | message.success(`导出成功,共${checkedRowKeys.value.length > 0 ? checkedRowKeys.value.length : total.value}条数据`) |
| | | message.success(`导出成功,共${checkedRowKeys.value.length}条数据`) |
| | | } |
| | | } |
| | | |
| | |
| | | <NCard class="flex-1 robotaxi-table-card" :bordered="false"> |
| | | <template #header> |
| | | <div class="flex items-center gap-16px"> |
| | | <div class="flex gap-2px items-center"> |
| | | <div class="flex gap-8px items-center"> |
| | | <span |
| | | v-if="checkedRowKeys.length > 0" |
| | | style="color: rgba(23, 31, 38, 0.6); font-weight: 400" |
| | |
| | | }) |
| | | }} |
| | | </span> |
| | | <NButton @click="handleExport"> |
| | | <NButton :disabled="checkedRowKeys.length === 0" @click="handleExport"> |
| | | 批量导出订单 |
| | | </NButton> |
| | | </div> |
| | |
| | | getList() |
| | | } |
| | | |
| | | function resetModalState() { |
| | | searchModel.value = { |
| | | account: '', |
| | | whitelistSource: null, |
| | | userType: null, |
| | | status: null, |
| | | } |
| | | checkedRowKeys.value = [] |
| | | currentPage.value = 1 |
| | | pageSize.value = 10 |
| | | } |
| | | |
| | | const isNormalStatus = (status: unknown) => status === 1 || status === true |
| | | |
| | | const columns: DataTableColumns<any> = [ |
| | |
| | | checkedRowKeys.value = [] |
| | | getList() |
| | | } |
| | | else { |
| | | resetModalState() |
| | | } |
| | | }) |
| | | |
| | | function handlePageChange(page: number, size: number) { |
| | |
| | | |
| | | <!-- 编辑模式:单行输入 --> |
| | | <NFormItem v-if="mode === 'edit'" label="邀请手机号" path="account"> |
| | | <NInput v-model:value="formModel.account" placeholder="请输入手机号" /> |
| | | <NInput v-model:value="formModel.account" disabled placeholder="请输入手机号" /> |
| | | </NFormItem> |
| | | |
| | | <!-- 编辑模式:用户名称 --> |