zhou zhou
2 天以前 33bd4dd1f0e41131cd8e5bbf87204a1f0b72bb08
#页面优化
2个文件已添加
11个文件已修改
961 ■■■■■ 已修改文件
rsf-design/.env.production 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-design/src/api/wave.js 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-design/src/locales/langs/en.json 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-design/src/locales/langs/zh.json 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-design/src/utils/http/error.js 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-design/src/utils/http/index.js 69 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-design/src/views/orders/wave/index.vue 241 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-design/src/views/orders/wave/modules/wave-detail-drawer.vue 78 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-design/src/views/orders/wave/modules/wave-item-panel.vue 191 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-design/src/views/orders/wave/modules/wave-order-rela-panel.vue 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-design/src/views/orders/wave/wavePage.helpers.js 60 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-design/src/views/orders/wave/waveTable.columns.js 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-design/vite.config.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-design/.env.production
@@ -4,7 +4,8 @@
VITE_BASE_URL = /
# API 地址前缀
VITE_API_URL = /rsf-server
# VITE_API_URL = /rsf-server
VITE_API_URL = http://127.0.0.1:8085/rsf-server
# Delete console
VITE_DROP_CONSOLE = true
rsf-design/src/api/wave.js
@@ -55,6 +55,13 @@
  })
}
export function fetchWaveOrderRelaPage(params = {}) {
  return request.post({
    url: '/waveOrderRela/page',
    params: buildWaveItemPageParams(params)
  })
}
export function fetchGetWaveDetail(id) {
  return request.get({
    url: `/wave/${id}`
@@ -103,6 +110,29 @@
  })
}
export function fetchSelectWaveTask(payload = {}) {
  return request.post({
    url: '/wave/selects/task',
    params: payload
  })
}
export function fetchWaveAutoExceFlag() {
  return request.get({
    url: '/config/flag/WaveAutoExce'
  })
}
export function fetchUpdateWaveAutoExceFlag(enabled) {
  return request.post({
    url: '/config/byFlag',
    params: {
      val: !!enabled,
      flag: 'WaveAutoExce'
    }
  })
}
export function fetchCreateOutStockWave(payload = {}) {
  return request.post({
    url: '/outStock/generate/wave',
rsf-design/src/locales/langs/en.json
@@ -12,7 +12,9 @@
    "requestCancelled": "Request cancelled",
    "networkError": "Network connection error, please check your connection",
    "requestFailed": "Request failed",
    "requestConfigError": "Request configuration error"
    "requestConfigError": "Request configuration error",
    "invalidResponseFormat": "Unexpected API response format, please check the server response",
    "invalidHtmlResponse": "The API returned an HTML page, please check proxy or deployment routing"
  },
  "topBar": {
    "search": {
@@ -2079,11 +2081,22 @@
          "codePlaceholder": "Enter wave No.",
          "type": "Wave Type",
          "exceStatus": "Wave Status",
          "anfme": "Wave Qty",
          "anfmePlaceholder": "Enter wave quantity",
          "qty": "Executed Qty",
          "qtyPlaceholder": "Enter executed quantity",
          "orderNum": "Order Count",
          "orderNumPlaceholder": "Enter order count",
          "status": "Status",
          "memo": "Remark",
          "memoPlaceholder": "Enter remark",
          "timeStart": "Start Time",
          "timeEnd": "End Time"
        },
        "buttons": {
          "batchPublicTask": "Batch Dispatch",
          "autoStart": "Enable Auto Run",
          "autoPause": "Pause Auto Run"
        },
        "status": {
          "type": {
@@ -2110,6 +2123,7 @@
          "type": "Wave Type",
          "exceStatus": "Wave Status",
          "anfme": "Expected Qty",
          "groupQty": "Merged Qty",
          "workQty": "Running Qty",
          "qty": "Completed Qty",
          "orderNum": "Document Count",
@@ -2117,6 +2131,13 @@
          "createTime": "Created At",
          "updateTime": "Updated At",
          "status": "Status"
        },
        "rela": {
          "asnCode": "Document No.",
          "platOrderCode": "Platform Order No.",
          "spec": "Spec",
          "model": "Model",
          "unitPlaceholder": "Enter unit"
        },
        "preview": {
          "waveCode": "Wave No.",
@@ -2150,7 +2171,8 @@
          "updateBy": "Updated By",
          "updateTime": "Updated At",
          "memo": "Remark",
          "previewTitle": "Wave Preview Items - Material Code"
          "previewTitle": "Wave Preview Items - Material Code",
          "itemTitle": "Wave Items"
        },
        "publicTask": {
          "title": "Dispatch Wave Task",
@@ -2171,7 +2193,15 @@
          "publicTaskTimeout": "Wave dispatch preview timed out and waiting has stopped",
          "publicTaskSuccess": "Wave dispatched",
          "publicTaskFailed": "Wave dispatch failed",
          "publicTaskWarning": "Wave preview data is unavailable. Please check location configuration first."
          "publicTaskWarning": "Wave preview data is unavailable. Please check location configuration first.",
          "batchPublicTaskSelect": "Please select waves first",
          "batchPublicTaskConfirm": "Are you sure you want to dispatch {count} selected waves?",
          "batchPublicTaskTitle": "Batch Dispatch Confirmation",
          "batchPublicTaskSuccess": "Waves dispatched in batch",
          "batchPublicTaskFailed": "Batch dispatch failed",
          "autoStartSuccess": "Auto run enabled",
          "autoPauseSuccess": "Auto run paused",
          "autoUpdateFailed": "Failed to update auto run settings"
        }
      },
      "waveItem": {
rsf-design/src/locales/langs/zh.json
@@ -12,7 +12,9 @@
    "requestCancelled": "请求已取消",
    "networkError": "网络连接异常,请检查网络连接",
    "requestFailed": "请求失败",
    "requestConfigError": "请求配置错误"
    "requestConfigError": "请求配置错误",
    "invalidResponseFormat": "接口响应格式异常,请检查服务端返回",
    "invalidHtmlResponse": "接口返回了 HTML 页面,请检查接口代理或部署路由配置"
  },
  "topBar": {
    "search": {
@@ -2087,11 +2089,22 @@
          "codePlaceholder": "请输入波次单号",
          "type": "波次类型",
          "exceStatus": "波次状态",
          "anfme": "波次数量",
          "anfmePlaceholder": "请输入波次数量",
          "qty": "已执行数量",
          "qtyPlaceholder": "请输入已执行数量",
          "orderNum": "订单数",
          "orderNumPlaceholder": "请输入订单数",
          "status": "状态",
          "memo": "备注",
          "memoPlaceholder": "请输入备注",
          "timeStart": "开始时间",
          "timeEnd": "结束时间"
        },
        "buttons": {
          "batchPublicTask": "批量下发",
          "autoStart": "开启自动执行",
          "autoPause": "暂停自动执行"
        },
        "status": {
          "type": {
@@ -2118,6 +2131,7 @@
          "type": "波次类型",
          "exceStatus": "波次状态",
          "anfme": "应盘数量",
          "groupQty": "合并数量",
          "workQty": "执行数量",
          "qty": "已盘数量",
          "orderNum": "单据数量",
@@ -2125,6 +2139,13 @@
          "createTime": "创建时间",
          "updateTime": "更新时间",
          "status": "状态"
        },
        "rela": {
          "asnCode": "单据号",
          "platOrderCode": "平台单号",
          "spec": "规格",
          "model": "型号",
          "unitPlaceholder": "请输入单位"
        },
        "preview": {
          "waveCode": "波次号",
@@ -2158,7 +2179,8 @@
          "updateBy": "更新人",
          "updateTime": "更新时间",
          "memo": "备注",
          "previewTitle": "波次预览明细 - 物料编码"
          "previewTitle": "波次预览明细 - 物料编码",
          "itemTitle": "波次明细"
        },
        "publicTask": {
          "title": "波次下发任务",
@@ -2179,7 +2201,15 @@
          "publicTaskTimeout": "波次下发预览加载超时,已停止等待",
          "publicTaskSuccess": "波次已下发",
          "publicTaskFailed": "波次下发失败",
          "publicTaskWarning": "波次预览数据不可用,请先检查库位配置"
          "publicTaskWarning": "波次预览数据不可用,请先检查库位配置",
          "batchPublicTaskSelect": "请先选择波次",
          "batchPublicTaskConfirm": "确定下发选中的 {count} 个波次吗?",
          "batchPublicTaskTitle": "批量下发确认",
          "batchPublicTaskSuccess": "波次已批量下发",
          "batchPublicTaskFailed": "波次批量下发失败",
          "autoStartSuccess": "已开启自动执行",
          "autoPauseSuccess": "已暂停自动执行",
          "autoUpdateFailed": "自动执行开关更新失败"
        }
      },
      "waveItem": {
rsf-design/src/utils/http/error.js
@@ -9,6 +9,8 @@
    this.timestamp = /* @__PURE__ */ new Date().toISOString()
    this.url = options?.url
    this.method = options?.method
    this.status = options?.status
    this.contentType = options?.contentType
  }
  toLogData() {
    return {
@@ -18,8 +20,50 @@
      timestamp: this.timestamp,
      url: this.url,
      method: this.method,
      status: this.status,
      contentType: this.contentType,
      stack: this.stack
    }
  }
}
function resolveRequestUrl(config) {
  const baseURL = String(config?.baseURL || '').trim()
  const requestUrl = String(config?.url || '').trim()
  if (!baseURL && !requestUrl) {
    return void 0
  }
  if (!baseURL) {
    return requestUrl || void 0
  }
  if (!requestUrl) {
    return baseURL
  }
  const isAbsoluteBase = /^https?:\/\//i.test(baseURL)
  if (!isAbsoluteBase) {
    const normalizedBase = baseURL.replace(/\/+$/, '')
    const normalizedUrl = requestUrl.replace(/^\/+/, '')
    if (!normalizedBase) {
      return requestUrl
    }
    if (!normalizedUrl) {
      return normalizedBase
    }
    return `${normalizedBase}/${normalizedUrl}`
  }
  try {
    const origin = globalThis?.location?.origin || 'http://localhost'
    const normalizedBase = new URL(baseURL, origin)
    return new URL(requestUrl, normalizedBase).toString()
  } catch {
    const normalizedBase = baseURL.replace(/\/+$/, '')
    const normalizedUrl = requestUrl.replace(/^\/+/, '')
    if (!normalizedBase) {
      return requestUrl
    }
    if (!normalizedUrl) {
      return normalizedBase
    }
    return `${normalizedBase}/${normalizedUrl}`
  }
}
const getErrorMessage = (status) => {
@@ -52,7 +96,7 @@
  const requestConfig = error.config
  if (isRequestCancelled(error)) {
    throw new HttpError($t('httpMsg.requestCancelled'), 'REQUEST_CANCELLED', {
      url: requestConfig?.url,
      url: resolveRequestUrl(requestConfig),
      method: requestConfig?.method?.toUpperCase()
    })
  }
@@ -60,7 +104,7 @@
  const errorMessage = error.response?.data?.msg || error.message
  if (!error.response) {
    throw new HttpError($t('httpMsg.networkError'), ApiStatus.error, {
      url: requestConfig?.url,
      url: resolveRequestUrl(requestConfig),
      method: requestConfig?.method?.toUpperCase()
    })
  }
@@ -69,8 +113,10 @@
    : errorMessage || $t('httpMsg.requestFailed')
  throw new HttpError(message, statusCode || ApiStatus.error, {
    data: error.response.data,
    url: requestConfig?.url,
    method: requestConfig?.method?.toUpperCase()
    url: resolveRequestUrl(requestConfig),
    method: requestConfig?.method?.toUpperCase(),
    status: statusCode,
    contentType: error.response?.headers?.['content-type']
  })
}
function showError(error, showMessage = true) {
@@ -90,4 +136,4 @@
const isHttpError = (error) => {
  return error instanceof HttpError
}
export { HttpError, handleError, isHttpError, showError, showSuccess }
export { HttpError, handleError, isHttpError, resolveRequestUrl, showError, showSuccess }
rsf-design/src/utils/http/index.js
@@ -1,7 +1,7 @@
import axios from 'axios'
import { useUserStore } from '@/store/modules/user'
import { ApiStatus } from './status'
import { HttpError, handleError, showError, showSuccess } from './error'
import { HttpError, handleError, resolveRequestUrl, showError, showSuccess } from './error'
import { $t } from '@/locales'
const REQUEST_TIMEOUT = 30e3
const LOGOUT_DELAY = 500
@@ -51,21 +51,40 @@
)
axiosInstance.interceptors.response.use(
  (response) => {
    if (!isStandardResponse(response.data)) {
      throw createInvalidResponseError(response)
    }
    const { code, msg } = response.data
    if (code === ApiStatus.success) return response
    if (code === ApiStatus.unauthorized) handleUnauthorizedError(msg)
    throw createHttpError(msg || $t('httpMsg.requestFailed'), code)
    if (code === ApiStatus.unauthorized) handleUnauthorizedError(msg, response.config)
    throw createHttpError(
      msg || $t('httpMsg.requestFailed'),
      code ?? ApiStatus.error,
      createErrorOptions(
        response.config,
        {
          data: response.data,
          status: response.status,
          contentType: response.headers?.['content-type']
        },
        response
      )
    )
  },
  (error) => {
    if (error.response?.status === ApiStatus.unauthorized) handleUnauthorizedError()
    return Promise.reject(handleError(error))
  }
)
function createHttpError(message, code) {
  return new HttpError(message, code)
function createHttpError(message, code, options) {
  return new HttpError(message, code, options)
}
function handleUnauthorizedError(message) {
  const error = createHttpError(message || $t('httpMsg.unauthorized'), ApiStatus.unauthorized)
function handleUnauthorizedError(message, config) {
  const error = createHttpError(
    message || $t('httpMsg.unauthorized'),
    ApiStatus.unauthorized,
    createErrorOptions(config)
  )
  if (!isUnauthorizedErrorShown) {
    isUnauthorizedErrorShown = true
    logOut()
@@ -94,6 +113,42 @@
    ApiStatus.gatewayTimeout
  ].includes(statusCode)
}
function createErrorOptions(config, extra = {}, response) {
  return {
    url: response?.request?.responseURL || resolveRequestUrl(config),
    method: config?.method?.toUpperCase(),
    ...extra
  }
}
function isStandardResponse(payload) {
  return payload !== null && typeof payload === 'object' && !Array.isArray(payload) && 'code' in payload
}
function createInvalidResponseError(response) {
  const responseData = response?.data
  const responseText =
    typeof responseData === 'string' ? responseData.slice(0, 300) : responseData
  const contentType = String(response?.headers?.['content-type'] || '')
  const looksLikeHtml =
    contentType.includes('text/html') ||
    (typeof responseData === 'string' &&
      /<(!doctype|html|head|body)\b/i.test(responseData.trim().slice(0, 120)))
  const message = looksLikeHtml
    ? $t('httpMsg.invalidHtmlResponse')
    : $t('httpMsg.invalidResponseFormat')
  return createHttpError(
    message,
    ApiStatus.error,
    createErrorOptions(
      response?.config,
      {
        data: responseText,
        status: response?.status,
        contentType
      },
      response
    )
  )
}
async function retryRequest(config, retries = MAX_RETRIES) {
  try {
    return await request(config)
rsf-design/src/views/orders/wave/index.vue
@@ -11,20 +11,44 @@
    <ElCard class="art-table-card">
      <ArtTableHeader v-model:columns="columnChecks" :loading="loading" @refresh="refreshData">
        <template #left>
          <ListExportPrint
            :preview-visible="previewVisible"
            @update:previewVisible="handlePreviewVisibleChange"
            :report-title="reportTitle"
            :selected-rows="selectedRows"
            :query-params="reportQueryParams"
            :columns="columns"
            :preview-rows="previewRows"
            :preview-meta="resolvedPreviewMeta"
            :total="pagination.total"
            :disabled="loading"
            @export="handleExport"
            @print="handlePrint"
          />
          <ElSpace wrap>
            <ElButton
              type="primary"
              :loading="batchTaskSubmitting"
              :disabled="loading || batchTaskSubmitting || !selectedRows.length"
              @click="handleBatchPublicTask"
            >
              {{ t('pages.orders.wave.buttons.batchPublicTask') }}
            </ElButton>
            <ElButton
              :loading="autoExceSubmitting"
              :disabled="loading || autoExceSubmitting || autoExce"
              @click="toggleAutoExce(true)"
            >
              {{ t('pages.orders.wave.buttons.autoStart') }}
            </ElButton>
            <ElButton
              :loading="autoExceSubmitting"
              :disabled="loading || autoExceSubmitting || !autoExce"
              @click="toggleAutoExce(false)"
            >
              {{ t('pages.orders.wave.buttons.autoPause') }}
            </ElButton>
            <ListExportPrint
              :preview-visible="previewVisible"
              @update:previewVisible="handlePreviewVisibleChange"
              :report-title="reportTitle"
              :selected-rows="selectedRows"
              :query-params="reportQueryParams"
              :columns="columns"
              :preview-rows="previewRows"
              :preview-meta="resolvedPreviewMeta"
              :total="pagination.total"
              :disabled="loading"
              @export="handleExport"
              @print="handlePrint"
            />
          </ElSpace>
        </template>
      </ArtTableHeader>
@@ -68,8 +92,8 @@
</template>
<script setup>
  import { computed, reactive, ref } from 'vue'
  import { ElMessage, ElMessageBox } from 'element-plus'
  import { computed, onMounted, reactive, ref } from 'vue'
  import { ElButton, ElMessage, ElMessageBox, ElSpace } from 'element-plus'
  import { useI18n } from 'vue-i18n'
  import { useUserStore } from '@/store/modules/user'
  import { useTable } from '@/hooks/core/useTable'
@@ -101,7 +125,10 @@
    fetchGetWaveMany,
    fetchPauseWave,
    fetchPublicWaveTask,
    fetchSelectWaveTask,
    fetchStopWave,
    fetchUpdateWaveAutoExceFlag,
    fetchWaveAutoExceFlag,
    fetchWavePage,
    fetchWavePreviewPage
  } from '@/api/wave'
@@ -125,6 +152,9 @@
  const publicTaskSubmitting = ref(false)
  const publicTaskWave = ref({})
  const publicTaskRows = ref([])
  const autoExce = ref(false)
  const batchTaskSubmitting = ref(false)
  const autoExceSubmitting = ref(false)
  const detailPagination = reactive({
    current: 1,
@@ -142,7 +172,9 @@
  const detailColumns = computed(() => createWaveDetailItemColumns(t))
  const publicTaskColumns = computed(() => createWavePreviewItemColumns(t))
  const publicTaskCanSubmit = computed(
    () => publicTaskRows.value.length > 0 && publicTaskRows.value.every((row) => row.stockLocsText && row.stockLocsText !== '[]')
    () =>
      publicTaskRows.value.length > 0 &&
      publicTaskRows.value.every((row) => row.stockLocsText && row.stockLocsText !== '[]')
  )
  const publicTaskWarningText = computed(() => t('pages.orders.wave.messages.publicTaskWarning'))
  const searchItems = computed(() => [
@@ -185,6 +217,36 @@
      }
    },
    {
      label: t('pages.orders.wave.search.anfme'),
      key: 'anfme',
      type: 'inputNumber',
      props: {
        clearable: true,
        controlsPosition: 'right',
        placeholder: t('pages.orders.wave.search.anfmePlaceholder')
      }
    },
    {
      label: t('pages.orders.wave.search.qty'),
      key: 'qty',
      type: 'inputNumber',
      props: {
        clearable: true,
        controlsPosition: 'right',
        placeholder: t('pages.orders.wave.search.qtyPlaceholder')
      }
    },
    {
      label: t('pages.orders.wave.search.orderNum'),
      key: 'orderNum',
      type: 'inputNumber',
      props: {
        clearable: true,
        controlsPosition: 'right',
        placeholder: t('pages.orders.wave.search.orderNumPlaceholder')
      }
    },
    {
      label: t('pages.orders.wave.search.status'),
      key: 'status',
      type: 'select',
@@ -202,8 +264,18 @@
      type: 'input',
      props: { clearable: true, placeholder: t('pages.orders.wave.search.memoPlaceholder') }
    },
    { label: t('pages.orders.wave.search.timeStart'), key: 'timeStart', type: 'date', props: { clearable: true, type: 'date', valueFormat: 'YYYY-MM-DD' } },
    { label: t('pages.orders.wave.search.timeEnd'), key: 'timeEnd', type: 'date', props: { clearable: true, type: 'date', valueFormat: 'YYYY-MM-DD' } }
    {
      label: t('pages.orders.wave.search.timeStart'),
      key: 'timeStart',
      type: 'date',
      props: { clearable: true, type: 'date', valueFormat: 'YYYY-MM-DD' }
    },
    {
      label: t('pages.orders.wave.search.timeEnd'),
      key: 'timeEnd',
      type: 'date',
      props: { clearable: true, type: 'date', valueFormat: 'YYYY-MM-DD' }
    }
  ])
  function updatePaginationState(target, response, fallbackCurrent, fallbackSize) {
@@ -254,11 +326,15 @@
        return
      }
      if (action.key === 'stop') {
        await ElMessageBox.confirm(t('pages.orders.wave.messages.stopConfirm', { code: row.code || '' }), t('pages.orders.wave.messages.stopTitle'), {
          confirmButtonText: t('common.confirm'),
          cancelButtonText: t('common.cancel'),
          type: 'warning'
        })
        await ElMessageBox.confirm(
          t('pages.orders.wave.messages.stopConfirm', { code: row.code || '' }),
          t('pages.orders.wave.messages.stopTitle'),
          {
            confirmButtonText: t('common.confirm'),
            cancelButtonText: t('common.cancel'),
            type: 'warning'
          }
        )
        await fetchStopWave(row.id)
        ElMessage.success(t('pages.orders.wave.messages.stopSuccess'))
        await refreshData()
@@ -288,7 +364,8 @@
      columnsFactory: () => createWaveTableColumns({ handleActionClick, t })
    },
    transform: {
      dataTransformer: (records) => (Array.isArray(records) ? records.map((item) => normalizeWaveRow(item, t)) : [])
      dataTransformer: (records) =>
        Array.isArray(records) ? records.map((item) => normalizeWaveRow(item, t)) : []
    }
  })
@@ -300,16 +377,33 @@
    detailLoading.value = true
    try {
      const [detailResponse, previewResponse] = await Promise.all([
        guardRequestWithMessage(fetchGetWaveDetail(activeWaveId.value), {}, { timeoutMessage: t('pages.orders.wave.messages.detailTimeout') }),
        guardRequestWithMessage(
          fetchWavePreviewPage(buildWaveDetailQueryParams({ waveId: activeWaveId.value, current: detailPagination.current, pageSize: detailPagination.size })),
          fetchGetWaveDetail(activeWaveId.value),
          {},
          { timeoutMessage: t('pages.orders.wave.messages.detailTimeout') }
        ),
        guardRequestWithMessage(
          fetchWavePreviewPage(
            buildWaveDetailQueryParams({
              waveId: activeWaveId.value,
              current: detailPagination.current,
              pageSize: detailPagination.size
            })
          ),
          { records: [], total: 0, current: detailPagination.current, size: detailPagination.size },
          { timeoutMessage: t('pages.orders.wave.messages.previewTimeout') }
        )
      ])
      detailData.value = normalizeWaveRow(detailResponse, t)
      detailTableData.value = Array.isArray(previewResponse?.records) ? previewResponse.records.map((item) => normalizeWaveItemRow(item, t)) : []
      updatePaginationState(detailPagination, previewResponse, detailPagination.current, detailPagination.size)
      detailTableData.value = Array.isArray(previewResponse?.records)
        ? previewResponse.records.map((item) => normalizeWaveItemRow(item, t))
        : []
      updatePaginationState(
        detailPagination,
        previewResponse,
        detailPagination.current,
        detailPagination.size
      )
    } finally {
      detailLoading.value = false
    }
@@ -323,12 +417,30 @@
    publicTaskLoading.value = true
    try {
      const previewResponse = await guardRequestWithMessage(
        fetchWavePreviewPage(buildWaveDetailQueryParams({ waveId: publicTaskWave.value.id, current: publicTaskPagination.current, pageSize: publicTaskPagination.size })),
        { records: [], total: 0, current: publicTaskPagination.current, size: publicTaskPagination.size },
        fetchWavePreviewPage(
          buildWaveDetailQueryParams({
            waveId: publicTaskWave.value.id,
            current: publicTaskPagination.current,
            pageSize: publicTaskPagination.size
          })
        ),
        {
          records: [],
          total: 0,
          current: publicTaskPagination.current,
          size: publicTaskPagination.size
        },
        { timeoutMessage: t('pages.orders.wave.messages.publicTaskTimeout') }
      )
      publicTaskRows.value = Array.isArray(previewResponse?.records) ? previewResponse.records.map((item) => normalizeWaveItemRow(item, t)) : []
      updatePaginationState(publicTaskPagination, previewResponse, publicTaskPagination.current, publicTaskPagination.size)
      publicTaskRows.value = Array.isArray(previewResponse?.records)
        ? previewResponse.records.map((item) => normalizeWaveItemRow(item, t))
        : []
      updatePaginationState(
        publicTaskPagination,
        previewResponse,
        publicTaskPagination.current,
        publicTaskPagination.size
      )
    } finally {
      publicTaskLoading.value = false
    }
@@ -355,6 +467,65 @@
      ElMessage.error(error?.message || t('pages.orders.wave.messages.publicTaskFailed'))
    } finally {
      publicTaskSubmitting.value = false
    }
  }
  async function handleBatchPublicTask() {
    if (!selectedRows.value.length) {
      ElMessage.warning(t('pages.orders.wave.messages.batchPublicTaskSelect'))
      return
    }
    try {
      await ElMessageBox.confirm(
        t('pages.orders.wave.messages.batchPublicTaskConfirm', {
          count: selectedRows.value.length
        }),
        t('pages.orders.wave.messages.batchPublicTaskTitle'),
        {
          confirmButtonText: t('common.confirm'),
          cancelButtonText: t('common.cancel'),
          type: 'warning'
        }
      )
      batchTaskSubmitting.value = true
      await fetchSelectWaveTask({
        ids: selectedRows.value.map((item) => item.id)
      })
      ElMessage.success(t('pages.orders.wave.messages.batchPublicTaskSuccess'))
      selectedRows.value = []
      await refreshData()
    } catch (error) {
      if (error === 'cancel' || error?.message === 'cancel') return
      ElMessage.error(error?.message || t('pages.orders.wave.messages.batchPublicTaskFailed'))
    } finally {
      batchTaskSubmitting.value = false
    }
  }
  async function toggleAutoExce(enabled) {
    try {
      autoExceSubmitting.value = true
      await fetchUpdateWaveAutoExceFlag(enabled)
      autoExce.value = !!enabled
      ElMessage.success(
        enabled
          ? t('pages.orders.wave.messages.autoStartSuccess')
          : t('pages.orders.wave.messages.autoPauseSuccess')
      )
    } catch (error) {
      ElMessage.error(error?.message || t('pages.orders.wave.messages.autoUpdateFailed'))
    } finally {
      autoExceSubmitting.value = false
    }
  }
  async function loadAutoExceFlag() {
    try {
      const response = await fetchWaveAutoExceFlag()
      const rawVal = response?.val ?? response?.value ?? false
      autoExce.value = rawVal === true || rawVal === 'true'
    } catch {
      autoExce.value = false
    }
  }
@@ -446,4 +617,8 @@
      t
    })
  )
  onMounted(() => {
    loadAutoExceFlag()
  })
</script>
rsf-design/src/views/orders/wave/modules/wave-detail-drawer.vue
@@ -8,32 +8,66 @@
    <ElScrollbar class="wave-detail-scroll">
      <div class="flex min-h-full flex-col gap-4 pr-2">
        <ElDescriptions :column="4" border>
          <ElDescriptionsItem :label="t('pages.orders.wave.detail.code')">{{ detail.code || '--' }}</ElDescriptionsItem>
          <ElDescriptionsItem :label="t('pages.orders.wave.detail.type')">{{ detail.typeLabel || '--' }}</ElDescriptionsItem>
          <ElDescriptionsItem :label="t('pages.orders.wave.detail.exceStatus')">{{ detail.exceStatusText || '--' }}</ElDescriptionsItem>
          <ElDescriptionsItem :label="t('pages.orders.wave.detail.code')">{{
            detail.code || '--'
          }}</ElDescriptionsItem>
          <ElDescriptionsItem :label="t('pages.orders.wave.detail.type')">{{
            detail.typeLabel || '--'
          }}</ElDescriptionsItem>
          <ElDescriptionsItem :label="t('pages.orders.wave.detail.exceStatus')">{{
            detail.exceStatusText || '--'
          }}</ElDescriptionsItem>
          <ElDescriptionsItem :label="t('pages.orders.wave.detail.status')">
            <ElTag :type="Number(detail.status) === 1 ? 'success' : 'danger'" effect="light">
              {{ detail.statusLabel || '--' }}
            </ElTag>
          </ElDescriptionsItem>
          <ElDescriptionsItem :label="t('pages.orders.wave.detail.anfme')">{{ detail.anfme ?? '--' }}</ElDescriptionsItem>
          <ElDescriptionsItem :label="t('pages.orders.wave.detail.workQty')">{{ detail.workQty ?? '--' }}</ElDescriptionsItem>
          <ElDescriptionsItem :label="t('pages.orders.wave.detail.qty')">{{ detail.qty ?? '--' }}</ElDescriptionsItem>
          <ElDescriptionsItem :label="t('pages.orders.wave.detail.orderNum')">{{ detail.orderNum ?? '--' }}</ElDescriptionsItem>
          <ElDescriptionsItem :label="t('pages.orders.wave.detail.groupQty')">{{ detail.groupQty ?? '--' }}</ElDescriptionsItem>
          <ElDescriptionsItem :label="t('pages.orders.wave.detail.targSite')">{{ detail.targSite || '--' }}</ElDescriptionsItem>
          <ElDescriptionsItem :label="t('pages.orders.wave.detail.stationId')">{{ detail.stationId || '--' }}</ElDescriptionsItem>
          <ElDescriptionsItem :label="t('pages.orders.wave.detail.locCode')">{{ detail.locCode || '--' }}</ElDescriptionsItem>
          <ElDescriptionsItem :label="t('pages.orders.wave.detail.createBy')">{{ detail.createByText || '--' }}</ElDescriptionsItem>
          <ElDescriptionsItem :label="t('pages.orders.wave.detail.createTime')">{{ detail.createTimeText || '--' }}</ElDescriptionsItem>
          <ElDescriptionsItem :label="t('pages.orders.wave.detail.updateBy')">{{ detail.updateByText || '--' }}</ElDescriptionsItem>
          <ElDescriptionsItem :label="t('pages.orders.wave.detail.updateTime')">{{ detail.updateTimeText || '--' }}</ElDescriptionsItem>
          <ElDescriptionsItem :label="t('pages.orders.wave.detail.memo')" :span="4">{{ detail.memo || '--' }}</ElDescriptionsItem>
          <ElDescriptionsItem :label="t('pages.orders.wave.detail.anfme')">{{
            detail.anfme ?? '--'
          }}</ElDescriptionsItem>
          <ElDescriptionsItem :label="t('pages.orders.wave.detail.workQty')">{{
            detail.workQty ?? '--'
          }}</ElDescriptionsItem>
          <ElDescriptionsItem :label="t('pages.orders.wave.detail.qty')">{{
            detail.qty ?? '--'
          }}</ElDescriptionsItem>
          <ElDescriptionsItem :label="t('pages.orders.wave.detail.orderNum')">{{
            detail.orderNum ?? '--'
          }}</ElDescriptionsItem>
          <ElDescriptionsItem :label="t('pages.orders.wave.detail.groupQty')">{{
            detail.groupQty ?? '--'
          }}</ElDescriptionsItem>
          <ElDescriptionsItem :label="t('pages.orders.wave.detail.targSite')">{{
            detail.targSite || '--'
          }}</ElDescriptionsItem>
          <ElDescriptionsItem :label="t('pages.orders.wave.detail.stationId')">{{
            detail.stationId || '--'
          }}</ElDescriptionsItem>
          <ElDescriptionsItem :label="t('pages.orders.wave.detail.locCode')">{{
            detail.locCode || '--'
          }}</ElDescriptionsItem>
          <ElDescriptionsItem :label="t('pages.orders.wave.detail.createBy')">{{
            detail.createByText || '--'
          }}</ElDescriptionsItem>
          <ElDescriptionsItem :label="t('pages.orders.wave.detail.createTime')">{{
            detail.createTimeText || '--'
          }}</ElDescriptionsItem>
          <ElDescriptionsItem :label="t('pages.orders.wave.detail.updateBy')">{{
            detail.updateByText || '--'
          }}</ElDescriptionsItem>
          <ElDescriptionsItem :label="t('pages.orders.wave.detail.updateTime')">{{
            detail.updateTimeText || '--'
          }}</ElDescriptionsItem>
          <ElDescriptionsItem :label="t('pages.orders.wave.detail.memo')" :span="4">{{
            detail.memo || '--'
          }}</ElDescriptionsItem>
        </ElDescriptions>
        <ElCard shadow="never" class="border border-[var(--art-border-color)]">
          <template #header>
            <div class="text-sm font-medium text-[var(--art-text-gray-800)]">{{ t('pages.orders.wave.detail.previewTitle') }}</div>
            <div class="text-sm font-medium text-[var(--art-text-gray-800)]">{{
              t('pages.orders.wave.detail.previewTitle')
            }}</div>
          </template>
          <ArtTable
            :loading="loading"
@@ -44,6 +78,15 @@
            @pagination:current-change="$emit('current-change', $event)"
          />
        </ElCard>
        <ElCard shadow="never" class="border border-[var(--art-border-color)]">
          <template #header>
            <div class="text-sm font-medium text-[var(--art-text-gray-800)]">
              {{ t('pages.orders.wave.detail.itemTitle') }}
            </div>
          </template>
          <WaveItemPanel :wave-id="detail.id" />
        </ElCard>
      </div>
    </ElScrollbar>
  </ElDrawer>
@@ -51,6 +94,7 @@
<script setup>
  import { useI18n } from 'vue-i18n'
  import WaveItemPanel from './wave-item-panel.vue'
  defineOptions({ name: 'WaveDetailDrawer' })
  const { t } = useI18n()
rsf-design/src/views/orders/wave/modules/wave-item-panel.vue
New file
@@ -0,0 +1,191 @@
<template>
  <div class="flex flex-col gap-4">
    <ArtSearchBar
      v-model="searchForm"
      :items="searchItems"
      :showExpand="true"
      @search="handleSearch"
      @reset="handleReset"
    />
    <ArtTable
      :loading="loading"
      :data="rows"
      :columns="columns"
      :pagination="pagination"
      @pagination:size-change="handleSizeChange"
      @pagination:current-change="handleCurrentChange"
    />
  </div>
</template>
<script setup>
  import { computed, reactive, ref, watch } from 'vue'
  import { useI18n } from 'vue-i18n'
  import { defaultResponseAdapter } from '@/utils/table/tableUtils'
  import { fetchWaveItemPage } from '@/api/wave-item'
  import {
    buildWaveItemPageQueryParams,
    createWaveItemSearchState,
    normalizeWaveItemRow
  } from '@/views/orders/wave-item/waveItemPage.helpers'
  import { createWaveItemTableColumns } from '@/views/orders/wave-item/waveItemTable.columns'
  const props = defineProps({
    waveId: { type: [Number, String], default: undefined }
  })
  const { t } = useI18n()
  const loading = ref(false)
  const rows = ref([])
  const searchForm = ref(createWaveItemSearchState())
  const pagination = reactive({
    current: 1,
    size: 20,
    total: 0
  })
  const columns = computed(() =>
    createWaveItemTableColumns({
      t,
      handleActionClick: () => {}
    }).filter((column) => column.prop !== 'operation')
  )
  const searchItems = computed(() => [
    {
      label: t('pages.orders.waveItem.search.condition'),
      key: 'condition',
      type: 'input',
      props: {
        clearable: true,
        placeholder: t('pages.orders.waveItem.search.conditionPlaceholder')
      }
    },
    {
      label: t('pages.orders.waveItem.search.waveCode'),
      key: 'waveCode',
      type: 'input',
      props: { clearable: true, placeholder: t('pages.orders.waveItem.search.waveCodePlaceholder') }
    },
    {
      label: t('pages.orders.waveItem.search.orderCode'),
      key: 'orderCode',
      type: 'input',
      props: {
        clearable: true,
        placeholder: t('pages.orders.waveItem.search.orderCodePlaceholder')
      }
    },
    {
      label: t('pages.orders.waveItem.search.matnrCode'),
      key: 'matnrCode',
      type: 'input',
      props: {
        clearable: true,
        placeholder: t('pages.orders.waveItem.search.matnrCodePlaceholder')
      }
    },
    {
      label: t('pages.orders.waveItem.search.maktx'),
      key: 'maktx',
      type: 'input',
      props: { clearable: true, placeholder: t('pages.orders.waveItem.search.maktxPlaceholder') }
    },
    {
      label: t('pages.orders.waveItem.search.batch'),
      key: 'batch',
      type: 'input',
      props: { clearable: true, placeholder: t('pages.orders.waveItem.search.batchPlaceholder') }
    },
    {
      label: t('pages.orders.waveItem.search.splrBatch'),
      key: 'splrBatch',
      type: 'input',
      props: {
        clearable: true,
        placeholder: t('pages.orders.waveItem.search.splrBatchPlaceholder')
      }
    },
    {
      label: t('table.unit'),
      key: 'unit',
      type: 'input',
      props: { clearable: true, placeholder: t('pages.orders.wave.rela.unitPlaceholder') }
    },
    {
      label: t('pages.orders.waveItem.search.fieldsIndex'),
      key: 'fieldsIndex',
      type: 'input',
      props: {
        clearable: true,
        placeholder: t('pages.orders.waveItem.search.fieldsIndexPlaceholder')
      }
    }
  ])
  function buildParams() {
    return buildWaveItemPageQueryParams({
      ...searchForm.value,
      waveId: props.waveId,
      current: pagination.current,
      pageSize: pagination.size
    })
  }
  async function loadRows() {
    if (props.waveId === undefined || props.waveId === null || props.waveId === '') {
      rows.value = []
      pagination.total = 0
      return
    }
    loading.value = true
    try {
      const response = await fetchWaveItemPage(buildParams())
      const normalized = defaultResponseAdapter(response)
      rows.value = Array.isArray(normalized.records)
        ? normalized.records.map((item) => normalizeWaveItemRow(item, t))
        : []
      pagination.total = Number(normalized.total || 0)
      pagination.current = Number(normalized.current || pagination.current || 1)
      pagination.size = Number(normalized.size || pagination.size || 20)
    } catch {
      rows.value = []
      pagination.total = 0
    } finally {
      loading.value = false
    }
  }
  function handleSearch(params) {
    searchForm.value = { ...searchForm.value, ...params }
    pagination.current = 1
    loadRows()
  }
  function handleReset() {
    searchForm.value = createWaveItemSearchState()
    pagination.current = 1
    loadRows()
  }
  function handleSizeChange(size) {
    pagination.size = size
    pagination.current = 1
    loadRows()
  }
  function handleCurrentChange(current) {
    pagination.current = current
    loadRows()
  }
  watch(
    () => props.waveId,
    () => {
      pagination.current = 1
      loadRows()
    },
    { immediate: true }
  )
</script>
rsf-design/src/views/orders/wave/modules/wave-order-rela-panel.vue
New file
@@ -0,0 +1,60 @@
<template>
  <div class="wave-order-rela-panel">
    <ArtTable :loading="loading" :data="rows" :columns="columns" />
  </div>
</template>
<script setup>
  import { computed, ref, watch } from 'vue'
  import { useI18n } from 'vue-i18n'
  import { defaultResponseAdapter } from '@/utils/table/tableUtils'
  import { fetchWaveOrderRelaPage } from '@/api/wave'
  import { normalizeWaveOrderRelaRow } from '../wavePage.helpers'
  import { createWaveOrderRelaColumns } from '../waveTable.columns'
  const props = defineProps({
    waveId: { type: [Number, String], default: undefined }
  })
  const loading = ref(false)
  const rows = ref([])
  const { t } = useI18n()
  const columns = computed(() => createWaveOrderRelaColumns(t))
  async function loadRows() {
    if (props.waveId === undefined || props.waveId === null || props.waveId === '') {
      rows.value = []
      return
    }
    loading.value = true
    try {
      const response = await fetchWaveOrderRelaPage({
        waveId: Number(props.waveId),
        current: 1,
        pageSize: 200
      })
      const records = defaultResponseAdapter(response).records
      rows.value = Array.isArray(records)
        ? records.map((item) => normalizeWaveOrderRelaRow(item))
        : []
    } catch {
      rows.value = []
    } finally {
      loading.value = false
    }
  }
  watch(
    () => props.waveId,
    () => {
      loadRows()
    },
    { immediate: true }
  )
</script>
<style scoped>
  .wave-order-rela-panel {
    padding: 12px 0;
  }
</style>
rsf-design/src/views/orders/wave/wavePage.helpers.js
@@ -80,16 +80,20 @@
    code: '',
    type: '',
    exceStatus: '',
    anfme: '',
    qty: '',
    orderNum: '',
    status: '',
    memo: '',
    timeStart: '',
    timeEnd: ''
    timeEnd: '',
    orderBy: 'create_time desc'
  }
}
export function buildWaveSearchParams(params = {}) {
  const result = {}
  ;['condition', 'code', 'memo', 'timeStart', 'timeEnd'].forEach((key) => {
  ;['condition', 'code', 'memo', 'timeStart', 'timeEnd', 'orderBy'].forEach((key) => {
    const value = normalizeText(params[key])
    if (value) {
      result[key] = value
@@ -108,6 +112,12 @@
    result.status = Number(params.status)
  }
  ;['anfme', 'qty', 'orderNum'].forEach((key) => {
    if (params[key] !== '' && params[key] !== undefined && params[key] !== null) {
      result[key] = Number(params[key])
    }
  })
  return result
}
@@ -115,6 +125,7 @@
  return {
    current: params.current || 1,
    pageSize: params.pageSize || params.size || 20,
    orderBy: normalizeText(params.orderBy) || 'create_time desc',
    ...buildWaveSearchParams(params)
  }
}
@@ -129,19 +140,30 @@
export function normalizeWaveRow(record = {}, t) {
  const statusConfig = getStatusConfig(record.exceStatus, record['exceStatus$'], t)
  const progress = normalizeNumber(record.anfme) > 0
    ? Math.min(Math.round((normalizeNumber(record.workQty) / normalizeNumber(record.anfme)) * 100), 100)
    : 0
  const progress =
    normalizeNumber(record.anfme) > 0
      ? Math.min(
          Math.round((normalizeNumber(record.workQty) / normalizeNumber(record.anfme)) * 100),
          100
        )
      : 0
  return {
    ...record,
    code: record.code || '-',
    typeLabel: record['type$'] || translate(t, `pages.orders.wave.status.type.${record.type}`) || record.type || '-',
    typeLabel:
      record['type$'] ||
      translate(t, `pages.orders.wave.status.type.${record.type}`) ||
      record.type ||
      '-',
    exceStatusText: statusConfig.label,
    exceStatusTagType: statusConfig.tagType,
    statusLabel:
      record['status$'] ||
      translate(t, Number(record.status) === 1 ? 'common.status.normal' : 'common.status.disabled') ||
      translate(
        t,
        Number(record.status) === 1 ? 'common.status.normal' : 'common.status.disabled'
      ) ||
      record.status ||
      '-',
    anfme: normalizeNumber(record.anfme),
@@ -169,13 +191,18 @@
  const statusConfig = getItemStatusConfig(record.exceStatus, record['exceStatus$'], t)
  return {
    ...record,
    id: record.id ?? null,
    waveId: record.waveId ?? '-',
    waveCode: record.waveCode || '-',
    orderCode: record.orderCode || '-',
    orderItemId: record.orderItemId ?? '-',
    matnrId: record.matnrId ?? '-',
    matnrCode: record.matnrCode || '-',
    maktx: record.maktx || '-',
    batch: record.batch || '-',
    splrBatch: record.splrBatch || '-',
    unit: record.unit || '-',
    memo: record.memo || '-',
    trackCode: record.trackCode || '-',
    fieldsIndex: record.fieldsIndex || '-',
    anfme: normalizeNumber(record.anfme),
@@ -183,6 +210,8 @@
    workQty: normalizeNumber(record.workQty),
    stockQty: normalizeNumber(record.stockQty),
    stockLocsText: normalizeStockLocs(record.stockLocs),
    updateByText: record['updateBy$'] || record.updateBy || '-',
    createByText: record['createBy$'] || record.createBy || '-',
    statusLabel: record['status$'] || record.status || '-',
    exceStatusText: statusConfig.label,
    exceStatusTagType: statusConfig.tagType,
@@ -319,3 +348,20 @@
    ...buildWaveItemSearchParams(params)
  }
}
export function normalizeWaveOrderRelaRow(record = {}) {
  return {
    ...record,
    id: record.id ?? null,
    asnCode: record.asnCode || record.orderCode || '-',
    matnrCode: record.matnrCode || '-',
    maktx: record.maktx || '-',
    splrBatch: record.splrBatch || '-',
    platOrderCode: record.platOrderCode || '-',
    spec: record.spec || '-',
    model: record.model || '-',
    stockUnit: record.stockUnit || '-',
    splrName: record.splrName || '-',
    anfme: normalizeNumber(record.anfme)
  }
}
rsf-design/src/views/orders/wave/waveTable.columns.js
@@ -3,12 +3,30 @@
import { $t } from '@/locales'
import ArtButtonMore from '@/components/core/forms/art-button-more/index.vue'
import { getWaveActionList } from './wavePage.helpers'
import WaveOrderRelaPanel from './modules/wave-order-rela-panel.vue'
export function createWaveTableColumns({ handleActionClick, t }) {
  const translate = typeof t === 'function' ? t : $t
  return [
    {
      type: 'expand',
      width: 56,
      formatter: (row) => ({
        render() {
          return h(WaveOrderRelaPanel, {
            waveId: row.id
          })
        }
      })
    },
    { type: 'selection', width: 48, align: 'center' },
    { type: 'globalIndex', label: translate('table.index'), width: 72, align: 'center' },
    {
      prop: 'id',
      label: translate('table.id'),
      width: 90,
      align: 'center'
    },
    {
      prop: 'code',
      label: translate('pages.orders.wave.table.code'),
@@ -34,6 +52,12 @@
    {
      prop: 'anfme',
      label: translate('pages.orders.wave.table.anfme'),
      width: 110,
      align: 'right'
    },
    {
      prop: 'groupQty',
      label: translate('pages.orders.wave.table.groupQty'),
      width: 110,
      align: 'right'
    },
@@ -81,6 +105,18 @@
      showOverflowTooltip: true
    },
    {
      prop: 'updateByText',
      label: translate('table.updateBy'),
      minWidth: 120,
      showOverflowTooltip: true
    },
    {
      prop: 'createByText',
      label: translate('table.createBy'),
      minWidth: 120,
      showOverflowTooltip: true
    },
    {
      prop: 'statusLabel',
      label: translate('pages.orders.wave.table.status'),
      width: 100,
@@ -90,6 +126,12 @@
          { type: Number(row.status) === 1 ? 'success' : 'danger', effect: 'light' },
          () => row.statusLabel
        )
    },
    {
      prop: 'memo',
      label: translate('table.memo'),
      minWidth: 150,
      showOverflowTooltip: true
    },
    {
      prop: 'operation',
@@ -181,3 +223,60 @@
export function createWaveDetailItemColumns(t) {
  return createWavePreviewItemColumns(t)
}
export function createWaveOrderRelaColumns(t) {
  const translate = typeof t === 'function' ? t : $t
  return [
    { type: 'globalIndex', label: translate('table.index'), width: 72, align: 'center' },
    {
      prop: 'asnCode',
      label: translate('pages.orders.wave.rela.asnCode'),
      minWidth: 160,
      showOverflowTooltip: true
    },
    {
      prop: 'matnrCode',
      label: translate('table.materialCode'),
      minWidth: 140,
      showOverflowTooltip: true
    },
    {
      prop: 'maktx',
      label: translate('table.materialName'),
      minWidth: 220,
      showOverflowTooltip: true
    },
    {
      prop: 'splrBatch',
      label: translate('table.supplierBatch'),
      minWidth: 140,
      showOverflowTooltip: true
    },
    {
      prop: 'platOrderCode',
      label: translate('pages.orders.wave.rela.platOrderCode'),
      minWidth: 140,
      showOverflowTooltip: true
    },
    {
      prop: 'spec',
      label: translate('pages.orders.wave.rela.spec'),
      minWidth: 120,
      showOverflowTooltip: true
    },
    {
      prop: 'model',
      label: translate('pages.orders.wave.rela.model'),
      minWidth: 120,
      showOverflowTooltip: true
    },
    { prop: 'anfme', label: translate('table.quantity'), width: 100, align: 'right' },
    { prop: 'stockUnit', label: translate('table.unit'), width: 90, align: 'center' },
    {
      prop: 'splrName',
      label: translate('table.supplier'),
      minWidth: 150,
      showOverflowTooltip: true
    }
  ]
}
rsf-design/vite.config.js
@@ -56,7 +56,7 @@
          manualChunks: createManualChunks
        }
      },
      minify: 'terser',
      minify: 'esbuild',
      terserOptions: {
        compress: {
          drop_console: true,