feat(coupon, directService): 新增优惠券编辑功能,优化车辆选择组件及相关API

完善优惠券管理全流程,添加编辑按钮与编辑页面逻辑,支持提交优惠券ID完成编辑操作。新增定向服务车辆分页查询接口,重构车辆选择弹窗组件以支持自定义列表查询接口并复用至定向服务页面。同时优化优惠券编辑页的提示文案与交互判断逻辑。
6个文件已修改
69 ■■■■■ 已修改文件
src/service/api/coupon.ts 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/service/api/directService.ts 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/assetManagement/fleetManagement/components/VehicleSelectModal.vue 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/transaction/directServiceManagement/add.vue 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/userContent/active/coupon/add.vue 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/userContent/active/coupon/index.vue 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/service/api/coupon.ts
@@ -28,6 +28,17 @@
  return request.Post<Service.ResponseResult<any>>('/operation/v1/api/xp-coupon/add', data)
}
export function editCoupon(data: {
  id: number | string
  amount?: number // 金额
  conditionalAmount?: number // 条件金额
  couponDescription: string // 描述
  couponName: string // 优惠券名称
  couponType: number // 类型 1=抵扣 2=满减 3=抹零
}) {
  return request.Post<Service.ResponseResult<any>>('/v1/api/xp-coupon/editCoupon', data)
}
/**
 * 发放优惠券
 * @param data - 发放参数
src/service/api/directService.ts
@@ -25,6 +25,11 @@
  return request.Post<Service.ResponseResult<any>>(`/operation/api/v1/targetedService/deleteByIds`, { ids })
}
/** 定向服务选择车辆分页 */
export function getTargetedVehiclePageList(params: any) {
  return request.Post<Service.ResponseResult<any>>('/api/v1/vehicle/getTargetedVehiclePageList', params)
}
/** 启用/禁用定向服务 */
export function toggleDirectServiceStatus(id: any, status: any) {
  return request.Post<Service.ResponseResult<any>>(`/operation/api/v1/targetedService/updateStatus?id=${id}&status=${status}`)
src/views/assetManagement/fleetManagement/components/VehicleSelectModal.vue
@@ -15,10 +15,18 @@
interface Props {
  existingVehicles?: VehicleItem[]
  requestList?: (params: {
    vehicleCode?: string
    plateNumber?: string
    fleetName?: string
    pageNum: number
    pageSize: number
  }) => Promise<any>
}
const props = withDefaults(defineProps<Props>(), {
  existingVehicles: () => [],
  requestList: undefined,
})
const emit = defineEmits<{
@@ -101,7 +109,9 @@
async function getVehicleList() {
  startLoading()
  try {
    const res = await fetchVehiclePageList({
    const requestList = props.requestList || fetchVehiclePageList
    const res = await requestList({
      vehicleCode: searchModel.value.vehicleCode,
      fleetName: searchModel.value.fleetName,
      plateNumber: searchModel.value.plateNumber,
      pageNum: currentPage.value,
src/views/transaction/directServiceManagement/add.vue
@@ -4,7 +4,7 @@
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 { addDirectService, editDirectService, getDirectServiceDetail } from '@/service/api/directService'
import { addDirectService, editDirectService, getDirectServiceDetail, getTargetedVehiclePageList } from '@/service/api/directService'
import { useTabStore } from '@/store'
import { useBoolean } from '@/hooks'
import VehicleSelectModal from '@/views/assetManagement/fleetManagement/components/VehicleSelectModal.vue'
@@ -830,6 +830,7 @@
    <VehicleSelectModal
      v-model:visible="vehicleSelectModalVisible" :existing-vehicles="selectedVehicles"
      :request-list="getTargetedVehiclePageList"
      @confirm="handleVehicleSelectConfirm"
    />
src/views/userContent/active/coupon/add.vue
@@ -4,7 +4,7 @@
import type { FormInst, FormRules } from 'naive-ui'
import { useDialog, useMessage } from 'naive-ui'
import AiRichTextEditor from '@/components/custom/Editor/AiRichTextEditor/index.vue'
import { addCoupon, fetchCouponDetail } from '@/service/api/coupon'
import { addCoupon, editCoupon, fetchCouponDetail } from '@/service/api/coupon'
import { useTabStore } from '@/store'
const tabStore = useTabStore()
@@ -20,6 +20,7 @@
const isSelectOpen = ref(false)
const isReadonly = computed(() => route.query.mode === 'detail')
const isEditMode = computed(() => route.query.mode === 'edit')
const couponId = computed(() => {
  const raw = route.query.id
  const value = Array.isArray(raw) ? raw[0] : raw
@@ -330,7 +331,7 @@
  loading.value = true
  try {
    const res = await addCoupon({
    const submitParams = {
      couponName: formModel.value.name,
      couponType: formModel.value.type as unknown as number,
      conditionalAmount: Number(formModel.value.conditionAmount || 0),
@@ -342,10 +343,16 @@
            : formModel.value.amount || 0,
      ),
      couponDescription: encodeURIComponent(formModel.value.description || ''),
    })
    }
    const res = isEditMode.value && couponId.value
      ? await editCoupon({
          id: couponId.value,
          ...submitParams,
        })
      : await addCoupon(submitParams)
    if (res.code === 200) {
      message.success('保存成功')
      message.success(isEditMode.value ? '编辑成功' : '保存成功')
      window.sessionStorage.setItem('couponListNeedRefresh', '1')
      tabStore.closeTab(route.fullPath)
      router.back()
@@ -368,7 +375,7 @@
  }
  dialog.warning({
    title: '确定保存该优惠券?',
    title: isEditMode.value ? '确定保存优惠券修改?' : '确定保存该优惠券?',
    content: '',
    positiveText: '确定',
    negativeText: '取消',
@@ -387,8 +394,8 @@
  }
  dialog.warning({
    title: '确认取消创建吗?',
    content: '当前优惠券未保存,确认取消创建吗?',
    title: isEditMode.value ? '确认取消编辑吗?' : '确认取消创建吗?',
    content: isEditMode.value ? '当前优惠券修改未保存,确认取消编辑吗?' : '当前优惠券未保存,确认取消创建吗?',
    positiveText: '确定取消',
    negativeText: '继续编辑',
    maskClosable: false,
@@ -400,7 +407,7 @@
}
async function getDetail() {
  if (!isReadonly.value || !couponId.value)
  if ((!isReadonly.value && !isEditMode.value) || !couponId.value)
    return
  loading.value = true
src/views/userContent/active/coupon/index.vue
@@ -95,6 +95,16 @@
  })
}
function handleEdit(row: any) {
  router.push({
    path: '/userContent/active/coupon/add',
    query: {
      mode: 'edit',
      id: String(row.id ?? ''),
    },
  })
}
function handleDelete(row: any) {
  dialog.warning({
    title: '确认删除',
@@ -350,6 +360,11 @@
            详情
          </NButton>
          {status === 1 && (
            <NButton text type="primary" onClick={() => handleEdit(row)}>
              编辑
            </NButton>
          )}
          {status === 1 && (
            <NButton text type="primary" v-permission="202642906" onClick={() => handlePublish(row)}>
              发布
            </NButton>