董国庆
2026-03-14 1383f61a8fb13bfbfae87c898e36e45b1548ee58
修改
10个文件已修改
439 ■■■■■ 已修改文件
src/service/api/banner.ts 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/assetManagement/ruleConfig/index.vue 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/accountPermission/accountManagement/components/AccountModal.vue 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/accountPermission/accountManagement/components/ResetPasswordModal.vue 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/accountPermission/accountManagement/index.vue 264 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/accountPermission/permissionGroup/form.vue 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/accountPermission/permissionGroup/index.vue 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/userContent/active/coupon/add.vue 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/userContent/banner/components/BannerFormModal.vue 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/userContent/banner/index.vue 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/service/api/banner.ts
@@ -4,6 +4,7 @@
  id: number
  bannerTitle: string
  bannerPictures: string // JSON string: [{"id":"...","name":"...","url":"..."}]
  sort?: number
  startTime: string
  endTime: string
  status: number // 0=未开始, 1=进行中, 2=已结束 (展示用)
src/views/assetManagement/ruleConfig/index.vue
@@ -244,17 +244,17 @@
// --- Helper Functions ---
const formatTimeForAPI = (timestamp: number | null) => {
  if (!timestamp) return null
  return dayjs(timestamp).format('HH:mm:ss')
  return dayjs(timestamp).format('HH:mm')
}
const parseTimeFromAPI = (timeStr: string | null) => {
  if (!timeStr) return null
  const timeParts = timeStr.split(':')
  if (timeParts.length === 3) {
  if (timeParts.length === 2 || timeParts.length === 3) {
    const hour = parseInt(timeParts[0], 10)
    const minute = parseInt(timeParts[1], 10)
    const second = parseInt(timeParts[2], 10)
    const second = timeParts.length === 3 ? parseInt(timeParts[2], 10) : 0
    if (!isNaN(hour) && !isNaN(minute) && !isNaN(second)) {
      const now = dayjs()
@@ -590,7 +590,7 @@
                  </n-gi>
                  <n-gi>
                    <n-form-item label="新任务下放时间" path="dailyCheck.newTaskTime" required>
                      <n-time-picker v-model:value="regularForm.dailyCheck.newTaskTime" placeholder="请选择" style="width: 100%" :disabled="!regularForm.dailyCheck.enabled" />
                      <n-time-picker v-model:value="regularForm.dailyCheck.newTaskTime" format="HH:mm" placeholder="请选择" style="width: 100%" :disabled="!regularForm.dailyCheck.enabled" />
                    </n-form-item>
                  </n-gi>
                  <n-gi>
@@ -618,7 +618,7 @@
            <div class="pl-4">
              <n-form ref="vehicleReturnFormRef" :model="regularForm" :rules="regularRules" inline label-placement="left" :show-feedback="true" require-mark-placement="left">
                <n-form-item label="每日收车时间" path="vehicleReturn.returnTime" required>
                  <n-time-picker v-model:value="regularForm.vehicleReturn.returnTime" placeholder="请输入" style="width: 100%" :disabled="!regularForm.vehicleReturn.enabled" />
                  <n-time-picker v-model:value="regularForm.vehicleReturn.returnTime" format="HH:mm" placeholder="请输入" style="width: 100%" :disabled="!regularForm.vehicleReturn.enabled" />
                </n-form-item>
              </n-form>
            </div>
@@ -644,7 +644,7 @@
                  </n-gi>
                  <n-gi>
                    <n-form-item label="新任务下放时间" path="maintenance.newTaskTime" required>
                      <n-time-picker v-model:value="regularForm.maintenance.newTaskTime" placeholder="请选择" style="width: 100%" :disabled="!regularForm.maintenance.enabled" />
                      <n-time-picker v-model:value="regularForm.maintenance.newTaskTime" format="HH:mm" placeholder="请选择" style="width: 100%" :disabled="!regularForm.maintenance.enabled" />
                    </n-form-item>
                  </n-gi>
                  <n-gi>
@@ -720,7 +720,7 @@
                    </n-gi>
                    <n-gi>
                      <n-form-item label="任务下放时间" path="cleaning.newTaskTime" required>
                        <n-time-picker v-model:value="regularForm.cleaning.newTaskTime" placeholder="请选择" style="width: 100%" />
                        <n-time-picker v-model:value="regularForm.cleaning.newTaskTime" format="HH:mm" placeholder="请选择" style="width: 100%" />
                      </n-form-item>
                    </n-gi>
                    <n-gi>
@@ -752,7 +752,7 @@
                    </n-gi>
                    <n-gi>
                      <n-form-item label="任务下放时间" path="dailyPrep.newTaskTime" required>
                        <n-time-picker v-model:value="regularForm.dailyPrep.newTaskTime" placeholder="请选择" style="width: 100%" />
                        <n-time-picker v-model:value="regularForm.dailyPrep.newTaskTime" format="HH:mm" placeholder="请选择" style="width: 100%" />
                      </n-form-item>
                    </n-gi>
                    <n-gi>
src/views/system/accountPermission/accountManagement/components/AccountModal.vue
@@ -158,7 +158,7 @@
        closeModal()
      }
      else {
        message.error(res.message || '创建账号失败')
        // message.error(res.message || '创建账号失败')
      }
    }
    else if (props.mode === 'edit') {
src/views/system/accountPermission/accountManagement/components/ResetPasswordModal.vue
@@ -120,7 +120,7 @@
    }
  }
  catch (error) {
    console.error(error)
    // console.error(error)
  }
  finally {
    loading.value = false
src/views/system/accountPermission/accountManagement/index.vue
@@ -3,41 +3,63 @@
import { useBoolean } from '@/hooks'
import { deleteAccounts, fetchAccountList, switchStatus } from '@/service'
import { fetchRoleList } from '@/service/api/fleet'
import { NAvatar, NButton, NPopconfirm, NSpace, NTag, NSwitch, NGrid, NFormItemGi, NGi, NInput, NSelect } from 'naive-ui'
import SearchPanel from '@/components/custom/SearchPanel.vue'
import { NButton, NFormItemGi, NGi, NGrid, NInput, NSelect, NSwitch, NTag } from 'naive-ui'
import AccountModal from './components/AccountModal.vue'
import ResetPasswordModal from './components/ResetPasswordModal.vue'
const { bool: loading, setTrue: startLoading, setFalse: endLoading } = useBoolean(false)
const { t } = useI18n()
const message = useMessage()
const dialog = useDialog()
function isSuperAdmin(row: any) {
  const uniqueNumber = row?.uniqueNumber
  if (uniqueNumber !== undefined && uniqueNumber !== null)
    return String(uniqueNumber) === '1'
  return String(row?.id ?? '') === '1'
}
const initialModel = {
  nickName: '', // 用户名称
  phoneNumber: '', // 用户电话
  roleId: null, // 角色
  status: null, // 账号状态
  pageNum: 1,
  pageSize: 10,
}
const model = ref({ ...initialModel })
const pageParams = ref({
  pageNum: 1,
  pageSize: 10,
})
function handleResetSearch() {
  model.value = { ...initialModel }
  pageParams.value.pageNum = 1
  getAccountList()
}
function handleSearch() {
  pageParams.value.pageNum = 1
  getAccountList()
}
const formRef = ref<FormInst | null>()
const modalRef = ref()
const selectedRowKeys = ref<number[]>([])
const selectedRowKeys = ref<Array<string | number>>([])
const count = ref(0)
const listData = ref<any[]>([])
// 批量删除
function handleBatchDelete() {
  if (selectedRowKeys.value.length === 0) {
    return
  }
  const selectedRows = listData.value.filter(row => selectedRowKeys.value.includes(row.id))
  if (selectedRows.some(isSuperAdmin)) {
    message.error('超级管理员不允许删除')
    return
  }
  dialog.warning({
    title: '是否确认删除',
    content: '删除后数据不可恢复,请谨慎操作',
@@ -56,7 +78,7 @@
}
// 行选择
function handleCheck(rowKeys: number[]) {
function handleCheck(rowKeys: Array<string | number>) {
  selectedRowKeys.value = rowKeys
}
@@ -80,7 +102,7 @@
      // 如果取消,需要强制刷新一下列表以恢复Switch状态,或者手动重置该行状态
      // 这里简单处理,直接刷新列表,或者可以不做处理如果table没立即响应
      getAccountList()
    }
    },
  })
}
@@ -94,13 +116,16 @@
  {
    type: 'selection',
    width: 50,
    disabled: (row) => {
      return isSuperAdmin(row)
    },
  },
  {
    title: '序号',
    align: 'left',
    key: 'index',
    width: 80,
    render: (row, index) => <span>{(model.value.pageNum - 1) * model.value.pageSize + index + 1}</span>,
    render: (row, index) => <span>{(pageParams.value.pageNum - 1) * pageParams.value.pageSize + index + 1}</span>,
  },
  {
    title: '人员名称',
@@ -144,17 +169,18 @@
    width: 100,
    render: (row) => {
      const status = statusMap[row.status as keyof typeof statusMap]
      if (!status) return <NTag type="default" round>未知</NTag>
      if (!status)
        return <NTag type="default" round>未知</NTag>
      return (
        <NTag
          round
          bordered={false}
          style={{
          style={{
            borderRadius: '4px',
            border: `1px solid ${row.status === 0 ? '#B7EB8F' : '#E0E0E0'}`,
            color: row.status === 0 ? '#52C41A' : '#666666',
            backgroundColor: row.status === 0 ? '#F6FFED' : '#F5F5F5',
            color: row.status === 0 ? '#52C41A' : '#666666',
            backgroundColor: row.status === 0 ? '#F6FFED' : '#F5F5F5',
          }}
        >
          {status.text}
@@ -185,16 +211,16 @@
    render: (row) => {
      return (
        <div class="flex items-center justify-start gap-2">
          <span
            class="cursor-pointer text-primary"
          <span
            class="cursor-pointer text-primary"
            v-permission="20112"
            style={{ color: '#2d8cf0' }}
            onClick={() => handleEdit(row)}
          >
            编辑
          </span>
          <span
            class="cursor-pointer text-primary"
          <span
            class="cursor-pointer text-primary"
            v-permission="20114"
            style={{ color: '#2d8cf0' }}
            onClick={() => handleResetPassword(row)}
@@ -204,18 +230,22 @@
          <NSwitch
            v-permission="20113"
            value={row.status === 0}
            onUpdateValue={(val) => handleSwitchChange(val, row)}
            onUpdateValue={val => handleSwitchChange(val, row)}
          >
            {{
              checked: () => '启用',
              unchecked: () => '禁用'
              unchecked: () => '禁用',
            }}
          </NSwitch>
          <span
            class="cursor-pointer text-primary"
          <span
            class="cursor-pointer text-primary"
            v-permission="20115"
            style={{ color: '#2d8cf0' }}
            onClick={() => handleDelete(row)}
            style={{ color: isSuperAdmin(row) ? '#bfbfbf' : '#2d8cf0', cursor: isSuperAdmin(row) ? 'not-allowed' : 'pointer' }}
            onClick={() => {
              if (isSuperAdmin(row))
                return
              handleDelete(row)
            }}
          >
            删除
          </span>
@@ -232,41 +262,32 @@
const roleOptions = ref<any[]>([])
const count = ref(0)
const listData = ref<any[]>([])
// 分页配置
const pagination = computed(() => ({
  page: model.value.pageNum,
  pageSize: model.value.pageSize,
  itemCount: count.value,
  showSizePicker: true,
  pageSizes: [10, 20, 50, 100],
  prefix: ({ itemCount }) => `共 ${itemCount || 0} 条`,
  onChange: (page: number) => {
    model.value.pageNum = page
    getAccountList()
  },
  onUpdatePageSize: (pageSize: number) => {
    model.value.pageSize = pageSize
    model.value.pageNum = 1
    getAccountList()
  }
}))
async function getAccountList() {
  startLoading()
  const params = {
    ...model.value,
    roleId: model.value.roleId || '',
    status: model.value.status || '',
  try {
    const params = {
      ...model.value,
      pageNum: pageParams.value.pageNum,
      pageSize: pageParams.value.pageSize,
      roleId: model.value.roleId || '',
      status: model.value.status || '',
    }
    const res = await fetchAccountList(params)
    if (res.code === 200) {
      listData.value = res.data.records || []
      count.value = res.data.total || 0
    }
  }
  const res = await fetchAccountList(params)
  if (res.code === 200) {
    listData.value = res.data.records
    count.value = res.data.total
  finally {
    endLoading()
  }
  endLoading()
}
function changePage(page: number, size: number) {
  const sizeChanged = size !== pageParams.value.pageSize
  pageParams.value.pageSize = size
  pageParams.value.pageNum = sizeChanged ? 1 : page
  getAccountList()
}
const showModal = ref(false)
@@ -288,12 +309,6 @@
  showModal.value = true
}
function handleView(row: any) {
  modalMode.value = 'view'
  currentRow.value = row
  showModal.value = true
}
function handleResetPassword(row: any) {
  resetPasswordRow.value = row
  showResetPasswordModal.value = true
@@ -307,6 +322,10 @@
  getAccountList()
}
function handleDelete(row: any) {
  if (isSuperAdmin(row)) {
    message.error('超级管理员不允许删除')
    return
  }
  dialog.warning({
    title: '是否确认删除',
    content: '删除后数据不可恢复,请谨慎操作',
@@ -341,70 +360,83 @@
<template>
  <div class="h-full flex flex-col">
    <!-- 搜索区域 -->
    <n-card style="border-radius: 6px" content-style="padding: 12px">
      <n-form ref="formRef" :model="model" label-placement="left" :show-feedback="false">
        <n-grid :cols="4" :x-gap="24" :y-gap="12" responsive="screen">
          <n-form-item-gi label="用户姓名" path="nickName">
            <n-input v-model:value="model.nickName" placeholder="请输入" />
          </n-form-item-gi>
          <n-form-item-gi label="用户电话" path="phoneNumber">
            <n-input v-model:value="model.phoneNumber" placeholder="请输入" />
          </n-form-item-gi>
          <n-form-item-gi label="角色" path="roleId">
            <n-select v-model:value="model.roleId" :options="roleOptions" placeholder="请选择" clearable />
          </n-form-item-gi>
          <n-form-item-gi label="账号状态" path="status">
            <n-select v-model:value="model.status" :options="statusOptions" placeholder="请选择" clearable />
          </n-form-item-gi>
          <n-gi suffix>
    <NCard style="border-radius: 6px; margin-bottom: 12px;" content-style="padding: 12px">
      <NForm ref="formRef" :model="model" label-placement="left" :show-feedback="false">
        <NGrid :cols="4" :x-gap="24" :y-gap="12" responsive="screen">
          <NFormItemGi label="用户姓名" path="nickName">
            <NInput v-model:value="model.nickName" placeholder="请输入" />
          </NFormItemGi>
          <NFormItemGi label="用户电话" path="phoneNumber">
            <NInput v-model:value="model.phoneNumber" placeholder="请输入" />
          </NFormItemGi>
          <NFormItemGi label="角色" path="roleId">
            <NSelect v-model:value="model.roleId" :options="roleOptions" placeholder="请选择" clearable />
          </NFormItemGi>
          <NFormItemGi label="账号状态" path="status">
            <NSelect v-model:value="model.status" :options="statusOptions" placeholder="请选择" clearable />
          </NFormItemGi>
          <NGi suffix>
            <div class="flex justify-end gap-2">
              <NButton style="width: 84px; height: 32px" @click="handleResetSearch">
                {{ $t('common.reset') }}
              </NButton>
              <NButton type="primary" style="width: 84px; height: 32px" @click="getAccountList">
              <NButton type="primary" style="width: 84px; height: 32px" @click="handleSearch">
                搜索
              </NButton>
            </div>
          </n-gi>
        </n-grid>
      </n-form>
    </n-card>
          </NGi>
        </NGrid>
      </NForm>
    </NCard>
    <!-- 表格区域 -->
    <n-card class="flex-1" style="border-radius: 6px; margin-top: 12px;" content-style="padding: 12px">
      <div class="mb-4 flex justify-between items-center">
        <div class="flex gap-2 items-center">
          <span v-if="selectedRowKeys.length > 0" class="text-gray-500">
            已选择{{ selectedRowKeys.length }}条记录
          </span>
          <NButton
            v-if="selectedRowKeys.length > 0"
            style="width: 84px; height: 32px"
            @click="handleBatchDelete"
          >
            批量删除
          </NButton>
        </div>
        <div class="flex gap-2">
          <NButton
            v-permission="20111"
            @click="handleAdd"
            style="width: 110px; height: 32px; background-color: #E6F4FF; color: #0068FF; border: 1px solid #7ABDFF;"
          >
            新增账号
          </NButton>
        </div>
    <NCard class="flex-1 robotaxi-table-card">
      <template #header>
        <NFlex justify="space-between" align="center">
          <div class="flex gap-2 items-center">
            <span v-if="selectedRowKeys.length > 0" class="text-gray-500">
              已选择{{ selectedRowKeys.length }}条记录
            </span>
            <NButton
              v-if="selectedRowKeys.length > 0"
              style="width: 84px; height: 32px"
              @click="handleBatchDelete"
            >
              批量删除
            </NButton>
          </div>
          <div class="flex gap-2">
            <NButton
              v-permission="20111"
              @click="handleAdd"
              style="width: 110px; height: 32px; background-color: #E6F4FF; color: #0068FF; border: 1px solid #7ABDFF;"
            >
              新增账号
            </NButton>
          </div>
        </NFlex>
      </template>
      <div class="flex-1 flex flex-col h-full">
        <NDataTable
          class="flex-1"
          :columns="columns"
          :data="listData"
          :loading="loading"
          :row-key="(row) => row.id"
          flex-height
          :bordered="false"
          @update:checked-row-keys="handleCheck"
        />
        <Pagination
          :count="count"
          :page="pageParams.pageNum"
          :page-size="pageParams.pageSize"
          class="mt-12px"
          @change="changePage"
        />
      </div>
      <n-data-table
        :columns="columns"
        :data="listData"
        :loading="loading"
        :pagination="pagination"
        :row-key="(row) => row.id"
        @update:checked-row-keys="handleCheck"
      />
    </n-card>
    </NCard>
    <!-- 账号管理弹窗 -->
    <AccountModal
src/views/system/accountPermission/permissionGroup/form.vue
@@ -292,7 +292,7 @@
const rules: FormRules = {
  groupName: [
    { required: true, message: '请输入权限组名称', trigger: 'blur' },
    { min: 2, max: 50, message: '权限组名称长度为2-50个字符', trigger: 'blur' },
    { min: 1, max: 20, message: '权限组名称长度为1-20个字符', trigger: 'blur' },
  ],
}
src/views/system/accountPermission/permissionGroup/index.vue
@@ -9,13 +9,21 @@
const initialModel = {
  groupName: '', // 权限组名
  pageNum: 1,
  pageSize: 10,
}
const model = ref({ ...initialModel })
const pageParams = ref({
  pageNum: 1,
  pageSize: 10,
})
function handleResetSearch() {
  model.value = { ...initialModel }
  pageParams.value.pageNum = 1
  getPermissionGroupList()
}
function handleSearch() {
  pageParams.value.pageNum = 1
  getPermissionGroupList()
}
@@ -25,7 +33,7 @@
    align: 'left',
    key: 'index',
    width: 80,
    render: (_, index) => (model.value.pageNum - 1) * model.value.pageSize + index + 1,
    render: (_, index) => (pageParams.value.pageNum - 1) * pageParams.value.pageSize + index + 1,
  },
  {
    title: '权限组名称',
@@ -98,32 +106,13 @@
const count = ref(0)
const listData = ref<any[]>([])
// 分页配置
const pagination = computed(() => ({
  page: model.value.pageNum,
  pageSize: model.value.pageSize,
  itemCount: count.value,
  showSizePicker: true,
  pageSizes: [10, 20, 50, 100],
  prefix: ({ itemCount }) => `共 ${itemCount || 0} 条`,
  onChange: (page: number) => {
    model.value.pageNum = page
    getPermissionGroupList()
  },
  onUpdatePageSize: (pageSize: number) => {
    model.value.pageSize = pageSize
    model.value.pageNum = 1
    getPermissionGroupList()
  },
}))
async function getPermissionGroupList() {
  startLoading()
  try {
    const { data } = await fetchPermissionGroupPage({
      groupName: model.value.groupName,
      pageNum: model.value.pageNum,
      pageSize: model.value.pageSize,
      pageNum: pageParams.value.pageNum,
      pageSize: pageParams.value.pageSize,
    })
    if (data) {
      listData.value = data.records || []
@@ -133,6 +122,13 @@
  finally {
    endLoading()
  }
}
function changePage(page: number, size: number) {
  const sizeChanged = size !== pageParams.value.pageSize
  pageParams.value.pageSize = size
  pageParams.value.pageNum = sizeChanged ? 1 : page
  getPermissionGroupList()
}
function handleAdd() {
@@ -180,7 +176,7 @@
<template>
  <div class="h-full flex flex-col">
    <!-- 搜索区域 -->
    <n-card style="border-radius: 6px" content-style="padding: 12px">
    <n-card style="border-radius: 6px;margin-bottom: 12px;" content-style="padding: 12px">
      <NForm label-placement="left" :show-feedback="false">
        <NGrid :cols="4" :x-gap="24" :y-gap="12" responsive="screen">
          <NFormItemGi label="权限组名称">
@@ -191,7 +187,7 @@
              <NButton style="width: 84px; height: 32px" @click="handleResetSearch">
                {{ $t('common.reset') }}
              </NButton>
              <NButton type="primary" style="width: 84px; height: 32px" @click="getPermissionGroupList">
              <NButton type="primary" style="width: 84px; height: 32px" @click="handleSearch">
                搜索
              </NButton>
            </div>
@@ -201,9 +197,10 @@
    </n-card>
    <!-- 表格区域 -->
    <n-card class="flex-1" style="border-radius: 6px; margin-top: 12px;" content-style="padding: 12px">
      <div class="mb-4 flex justify-between items-center">
        <div class="flex gap-2 items-center">
    <n-card class="flex-1 robotaxi-table-card" >
      <template #header>
        <n-flex justify="space-between" align="center">
          <div class="flex gap-2 items-center">
          <!-- 左侧占位或批量操作 -->
        </div>
        <div class="flex gap-2">
@@ -215,14 +212,25 @@
            新增
          </NButton>
        </div>
      </div>
      <n-data-table
        :columns="columns"
        :data="listData"
        :loading="loading"
        :pagination="pagination"
      />
        </n-flex>
      </template>
      <div class="flex-1 flex flex-col h-full">
        <n-data-table
          class="flex-1"
          :columns="columns"
          :data="listData"
          :loading="loading"
          :bordered="false"
        />
        <Pagination
          :count="count"
          :page="pageParams.pageNum"
          :page-size="pageParams.pageSize"
          class="mt-12px"
          @change="changePage"
        />
        </div>
    </n-card>
  </div>
</template>
src/views/userContent/active/coupon/add.vue
@@ -17,7 +17,7 @@
  if (value === undefined || value === null)
    return null
  const str = String(value)
  return str ? str : null
  return str || null
})
const pageTitle = computed(() => isReadonly.value ? '优惠券详情' : '添加优惠券')
@@ -35,7 +35,16 @@
  { label: '满减券', value: 2 },
]
const handleSave = async () => {
function safeDecode(value: string) {
  try {
    return decodeURIComponent(value)
  }
  catch {
    return value
  }
}
async function handleSave() {
  if (isReadonly.value)
    return
@@ -55,7 +64,7 @@
    message.warning('请输入优惠金额')
    return
  }
  loading.value = true
  try {
    const res = await addCoupon({
@@ -63,23 +72,24 @@
      couponType: formModel.value.type,
      conditionalAmount: formModel.value.type === 1 ? 0 : (formModel.value.conditionAmount || 0),
      amount: formModel.value.amount || 0,
      couponDescription: formModel.value.description
      couponDescription: encodeURIComponent(formModel.value.description || ''),
    })
    if (res.code === 200) {
      message.success('保存成功')
      router.back()
    }
  } finally {
  }
  finally {
    loading.value = false
  }
}
const handleBack = () => {
function handleBack() {
  router.back()
}
const getDetail = async () => {
async function getDetail() {
  if (!isReadonly.value || !couponId.value)
    return
@@ -92,10 +102,11 @@
        type: res.data.couponType ?? null,
        conditionAmount: res.data.conditionalAmount ?? null,
        amount: res.data.amount ?? null,
        description: res.data.couponDescription || '',
        description: safeDecode(res.data.couponDescription || ''),
      }
    }
  } finally {
  }
  finally {
    loading.value = false
  }
}
@@ -111,7 +122,7 @@
      <div class="w-1 h-4 bg-blue-500 mr-2 rounded-sm"></div>
      <div class="text-lg font-bold">{{ pageTitle }}</div>
    </div>
    <div class="flex-1 overflow-y-auto px-6 pb-20">
      <n-form
        ref="formRef"
src/views/userContent/banner/components/BannerFormModal.vue
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { ref, watch, computed } from 'vue'
import { ref, watch } from 'vue'
import type { FormInst, FormRules, UploadFileInfo, UploadCustomRequestOptions } from 'naive-ui'
import { useMessage } from 'naive-ui'
import { addBanner, updateBanner, type Banner } from '@/service/api/banner'
@@ -30,6 +30,7 @@
interface FormModel {
  bannerTitle: string
  bannerPictures: string
  sort: number | null
  startTime: number | null
  endTime: number | null
  jumpType: number
@@ -42,6 +43,7 @@
const formModel = ref<FormModel>({
  bannerTitle: '',
  bannerPictures: '',
  sort: 0,
  startTime: null,
  endTime: null,
  jumpType: 1,
@@ -63,6 +65,24 @@
  ],
  bannerTitle: [
    { required: true, message: '请输入广告标题', trigger: 'blur' }
  ],
  sort: [
    {
      required: true,
      validator(_rule, value) {
        if (value === null || value === undefined || value === '') {
          return new Error('请输入排序')
        }
        if (typeof value !== 'number' || Number.isNaN(value)) {
          return new Error('请输入有效的排序数字')
        }
        if (value < 0) {
          return new Error('排序不能小于 0')
        }
        return true
      },
      trigger: 'change'
    }
  ],
  startTime: [
    { required: true, type: 'number', message: '请选择广告开始时间', trigger: 'change' }
@@ -154,6 +174,7 @@
    const params = {
      ...formModel.value,
      sort: formModel.value.sort ?? 0,
      bannerStatus: formModel.value.bannerStatus ? 1 : 2,
      startTime: dayjs(formModel.value.startTime).format('YYYY-MM-DD HH:mm:ss'),
      endTime: dayjs(formModel.value.endTime).format('YYYY-MM-DD HH:mm:ss')
@@ -210,6 +231,7 @@
      formModel.value = {
        bannerTitle: row.bannerTitle,
        bannerPictures: row.bannerPictures,
        sort: typeof row.sort === 'number' ? row.sort : 0,
        startTime: row.startTime ? dayjs(row.startTime).valueOf() : null,
        endTime: row.endTime ? dayjs(row.endTime).valueOf() : null,
        jumpType: row.jumpType || 1,
@@ -223,6 +245,7 @@
      formModel.value = {
        bannerTitle: '',
        bannerPictures: '',
        sort: 0,
        startTime: null,
        endTime: null,
        jumpType: 1,
@@ -333,6 +356,10 @@
        <n-input v-model:value="formModel.jumpUrl" placeholder="请输入" />
      </n-form-item>
      <n-form-item label="排序" path="sort" required>
        <n-input-number v-model:value="formModel.sort" placeholder="请输入" :min="0" style="width: 100%;" />
      </n-form-item>
      <n-form-item label="广告上架状态" required>
        <n-switch v-model:value="formModel.bannerStatus" />
      </n-form-item>
src/views/userContent/banner/index.vue
@@ -66,6 +66,12 @@
    width: 150
  },
  {
    title: '排序',
    key: 'sort',
    width: 80,
    render: (row) => row.sort ?? '-'
  },
  {
    title: '显示时间',
    key: 'time',
    width: 320,
@@ -176,7 +182,7 @@
  itemCount: totalCount.value,
  showSizePicker: true,
  pageSizes: [10, 20, 50, 100],
  prefix: ({ itemCount }) => `共 ${itemCount || 0} 条`,
  prefix: ({ itemCount }: { itemCount?: number }) => `共 ${itemCount || 0} 条`,
  onChange: (page: number) => {
    searchForm.value.pageNum = page
    fetchData()