优化
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
VITE_API_BASE_URL=/api
|
||||
VITE_OVERSEAS_API_BASE_URL=/overseas-api
|
||||
OVERSEAS_API_PROXY_TARGET=http://127.0.0.1:9300
|
||||
API_PROXY_TARGET=http://8.163.30.128:9000
|
||||
VITE_ADMIN_WEB_ORIGIN=http://localhost:5173
|
||||
|
||||
+2
-2
@@ -57,7 +57,7 @@ function redirectToLoginAfterSessionExpiry() {
|
||||
window.location.replace(`/login?redirect=${encodeURIComponent(redirect)}`)
|
||||
}
|
||||
|
||||
async function refreshSession() {
|
||||
export async function refreshAuthenticatedSession() {
|
||||
const observedRefreshToken = getRefreshToken()
|
||||
if (!observedRefreshToken) throw new ApiError('登录已过期,请重新登录', 'A0401', 401)
|
||||
if (!refreshPromise) {
|
||||
@@ -103,7 +103,7 @@ transport.interceptors.response.use(
|
||||
|
||||
request._retriedAfterRefresh = true
|
||||
try {
|
||||
const accessToken = await refreshSession()
|
||||
const accessToken = await refreshAuthenticatedSession()
|
||||
request.headers.Authorization = `Bearer ${accessToken}`
|
||||
return await transport.request(request)
|
||||
} catch (refreshError) {
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
import axios, { type AxiosRequestConfig, type InternalAxiosRequestConfig } from 'axios'
|
||||
import { parseApiJsonResponse } from '@chongchong/shared'
|
||||
import { ApiError, isApiEnvelope, type ApiEnvelope } from '@/api/types'
|
||||
import { getOverseasAccessToken } from '@/api/session'
|
||||
import { refreshAuthenticatedSession } from '@/api/http'
|
||||
|
||||
interface RetryableRequest extends InternalAxiosRequestConfig { _retriedOverseasAuth?: boolean }
|
||||
|
||||
const transport = axios.create({
|
||||
baseURL: import.meta.env.VITE_OVERSEAS_API_BASE_URL || '/overseas-api',
|
||||
timeout: 15_000,
|
||||
transformResponse: [(data: unknown) => parseApiJsonResponse(data)],
|
||||
})
|
||||
|
||||
async function requireToken() {
|
||||
const current = getOverseasAccessToken()
|
||||
if (current) return current
|
||||
await refreshAuthenticatedSession()
|
||||
const refreshed = getOverseasAccessToken()
|
||||
if (!refreshed) throw new ApiError('当前环境未签发海外访问令牌', 'OVERSEAS_TOKEN_UNAVAILABLE')
|
||||
return refreshed
|
||||
}
|
||||
|
||||
transport.interceptors.request.use(async (config) => {
|
||||
config.headers.Authorization = `Bearer ${await requireToken()}`
|
||||
return config
|
||||
})
|
||||
|
||||
transport.interceptors.response.use((response) => response, async (error) => {
|
||||
const request = error.config as RetryableRequest | undefined
|
||||
if (error.response?.status !== 401 || !request || request._retriedOverseasAuth) throw error
|
||||
request._retriedOverseasAuth = true
|
||||
await refreshAuthenticatedSession()
|
||||
request.headers.Authorization = `Bearer ${await requireToken()}`
|
||||
return transport.request(request)
|
||||
})
|
||||
|
||||
export async function overseasApiRequest<T>(config: AxiosRequestConfig) {
|
||||
try {
|
||||
const response = await transport.request<ApiEnvelope<T>>(config)
|
||||
if (!isApiEnvelope(response.data) || response.data.code !== '200') {
|
||||
throw new ApiError(isApiEnvelope(response.data) ? response.data.message : '海外接口响应格式错误', isApiEnvelope(response.data) ? response.data.code : 'INVALID_RESPONSE')
|
||||
}
|
||||
return response.data.data
|
||||
} catch (error) {
|
||||
if (error instanceof ApiError) throw error
|
||||
if (axios.isAxiosError(error)) {
|
||||
const body = error.response?.data
|
||||
if (isApiEnvelope(body)) throw new ApiError(body.message, body.code, error.response?.status)
|
||||
throw new ApiError(error.code === 'ERR_NETWORK' ? '无法连接海外服务' : error.message, error.code || 'OVERSEAS_HTTP_ERROR', error.response?.status)
|
||||
}
|
||||
throw new ApiError(error instanceof Error ? error.message : '海外接口请求失败')
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
export const ACCESS_TOKEN_KEY = 'cc-client-access-token'
|
||||
export const REFRESH_TOKEN_KEY = 'cc-client-refresh-token'
|
||||
export const OVERSEAS_ACCESS_TOKEN_KEY = 'cc-client-overseas-access-token'
|
||||
export const OVERSEAS_EXPIRES_AT_KEY = 'cc-client-overseas-expires-at'
|
||||
|
||||
export interface TokenPair {
|
||||
tokenType: string
|
||||
@@ -7,6 +9,8 @@ export interface TokenPair {
|
||||
expiresIn: number
|
||||
refreshToken: string
|
||||
refreshExpiresIn: number
|
||||
overseasAccessToken?: string | null
|
||||
overseasExpiresIn?: number
|
||||
}
|
||||
|
||||
function activeStorage() {
|
||||
@@ -22,11 +26,22 @@ export function getRefreshToken() {
|
||||
return localStorage.getItem(REFRESH_TOKEN_KEY) || sessionStorage.getItem(REFRESH_TOKEN_KEY) || ''
|
||||
}
|
||||
|
||||
export function getOverseasAccessToken() {
|
||||
const storage = activeStorage()
|
||||
const expiresAt = Number(storage.getItem(OVERSEAS_EXPIRES_AT_KEY) || 0)
|
||||
if (expiresAt <= Date.now() + 30_000) return ''
|
||||
return storage.getItem(OVERSEAS_ACCESS_TOKEN_KEY) || ''
|
||||
}
|
||||
|
||||
export function persistTokenPair(tokens: TokenPair, remember?: boolean) {
|
||||
const storage = remember === undefined ? activeStorage() : remember ? localStorage : sessionStorage
|
||||
clearSessionTokens()
|
||||
storage.setItem(ACCESS_TOKEN_KEY, tokens.accessToken)
|
||||
storage.setItem(REFRESH_TOKEN_KEY, tokens.refreshToken)
|
||||
if (tokens.overseasAccessToken && Number(tokens.overseasExpiresIn) > 0) {
|
||||
storage.setItem(OVERSEAS_ACCESS_TOKEN_KEY, tokens.overseasAccessToken)
|
||||
storage.setItem(OVERSEAS_EXPIRES_AT_KEY, String(Date.now() + Number(tokens.overseasExpiresIn) * 1000))
|
||||
}
|
||||
}
|
||||
|
||||
export function persistTemporaryAccessToken(accessToken: string) {
|
||||
@@ -38,6 +53,8 @@ export function clearSessionTokens() {
|
||||
for (const storage of [localStorage, sessionStorage]) {
|
||||
storage.removeItem(ACCESS_TOKEN_KEY)
|
||||
storage.removeItem(REFRESH_TOKEN_KEY)
|
||||
storage.removeItem(OVERSEAS_ACCESS_TOKEN_KEY)
|
||||
storage.removeItem(OVERSEAS_EXPIRES_AT_KEY)
|
||||
storage.removeItem('cc-client-token')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
import { overseasApiRequest } from '@/api/overseasHttp'
|
||||
|
||||
export interface PageResult<T> { records: T[]; page: number; size: number; total: number; pages: number }
|
||||
export interface LastlegOrderSummary { id: string; orderNo: string; inboundCode: string; orderType: string; packageCount: number; overseasWarehouseId?: string }
|
||||
export interface TransferSummary { id: string; transferNo: string; inboundCode: string; lastlegOrderId: string; lastlegOrderNo: string; operatingWarehouseId: string; fbaWarehouseCode: string; packageCount: number; deliveryMethod: string; needRelabel: boolean; relabelNo?: string; status: string; estimatedArrivalAt: string; createdAt: string }
|
||||
export interface RelabelSummary { id: string; relabelNo: string; lastlegOrderId: string; lastlegOrderNo: string; customerOrderNo: string; overseasWarehouseId: string; status: string; itemCount: number; totalPackageCount: number; notifiedPackageCount: number; skuCodes: string; createdAt: string }
|
||||
export interface DiscardSummary { id: string; discardNo: string; inboundCodes: string; overseasWarehouseId: string; overseasWarehouseName: string; totalPackages: number; totalFee: number; currency: string; status: string; createdAt: string }
|
||||
export interface DiscardCandidate { orderId: string; inboundCode: string; inboundOrderNo: string; overseasWarehouseId: string; overseasWarehouseName: string; availablePackages: number; skus: Array<{ skuCode: string; productName: string; availablePackages: number }> }
|
||||
|
||||
export interface TransferCreate {
|
||||
inboundCode: string; sourceLastlegOrderIds: string[]; operatingWarehouseId: string; fbaWarehouseCode: string
|
||||
deliveryAddress: string; packageCount: number; volumeCbm: number; deliveryMethod: string; estimatedArrivalAt: string
|
||||
fbaShipmentNo: string; trackingNo?: string; fbaBoxNos?: string; remark?: string; needRelabel: boolean; idempotencyKey: string
|
||||
items: Array<{ skuCode: string; productNameZh: string; packageCount: number; shippingMark?: string }>
|
||||
}
|
||||
export interface RelabelCreate {
|
||||
customerOrderNo: string; overseasWarehouseId: string; labelDeadline: string; contactName: string; contactPhone: string
|
||||
remark?: string; idempotencyKey: string
|
||||
items: Array<{ skuCode: string; productNameZh: string; totalPackageCount: number; notifiedPackageCount: number; shipmentNo: string; trackingCode?: string; newSkuCode: string; labelRequirement?: string }>
|
||||
}
|
||||
|
||||
export function listLastlegOrders(inboundCode: string) {
|
||||
return overseasApiRequest<PageResult<LastlegOrderSummary>>({ method: 'GET', url: '/merchant/v1/last-leg/orders', params: { page: 1, size: 200, inboundCode } })
|
||||
}
|
||||
export function listTransfers(params: Record<string, unknown>) {
|
||||
return overseasApiRequest<PageResult<TransferSummary>>({ method: 'GET', url: '/merchant/v1/last-leg/warehouse-transfers', params })
|
||||
}
|
||||
export function getTransfer(transferNo: string) {
|
||||
return overseasApiRequest<Record<string, unknown>>({ method: 'GET', url: `/merchant/v1/last-leg/warehouse-transfers/${encodeURIComponent(transferNo)}` })
|
||||
}
|
||||
export function createTransfer(payload: TransferCreate) {
|
||||
return overseasApiRequest<Record<string, unknown>>({ method: 'POST', url: '/merchant/v1/last-leg/warehouse-transfers', data: payload })
|
||||
}
|
||||
export function listRelabels(params: Record<string, unknown>) {
|
||||
return overseasApiRequest<PageResult<RelabelSummary>>({ method: 'GET', url: '/merchant/v1/last-leg/relabel-applications', params })
|
||||
}
|
||||
export function getRelabel(relabelNo: string) {
|
||||
return overseasApiRequest<Record<string, unknown>>({ method: 'GET', url: `/merchant/v1/last-leg/relabel-applications/${encodeURIComponent(relabelNo)}` })
|
||||
}
|
||||
export function createRelabel(payload: RelabelCreate) {
|
||||
return overseasApiRequest<Record<string, unknown>>({ method: 'POST', url: '/merchant/v1/last-leg/relabel-applications', data: payload })
|
||||
}
|
||||
export function listDiscards(params: Record<string, unknown>) {
|
||||
return overseasApiRequest<PageResult<DiscardSummary>>({ method: 'GET', url: '/merchant/v1/last-leg/cargo-discards', params })
|
||||
}
|
||||
export function getDiscard(discardNo: string) {
|
||||
return overseasApiRequest<Record<string, unknown>>({ method: 'GET', url: `/merchant/v1/last-leg/cargo-discards/${encodeURIComponent(discardNo)}` })
|
||||
}
|
||||
export function listDiscardCandidates(inboundCode?: string) {
|
||||
return overseasApiRequest<DiscardCandidate[]>({ method: 'GET', url: '/merchant/v1/last-leg/cargo-discards/inventory-candidates', params: { inboundCode: inboundCode || undefined } })
|
||||
}
|
||||
export function createDiscard(payload: { items: Array<{ orderId: string; skuCode: string; discardPackages: number }>; idempotencyKey: string; remark?: string }) {
|
||||
return overseasApiRequest<Record<string, unknown>>({ method: 'POST', url: '/merchant/v1/last-leg/cargo-discards', data: payload })
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Package, Tags, Truck, Warehouse } from '@lucide/vue'
|
||||
import { PackageX, RefreshCcw, Tags, Truck, Warehouse } from '@lucide/vue'
|
||||
import type { ClientModuleManifest } from '../types.ts'
|
||||
|
||||
export const overseasWarehouseModule = {
|
||||
@@ -6,36 +6,28 @@ export const overseasWarehouseModule = {
|
||||
navigationGroup: { title: '海外仓', icon: Warehouse },
|
||||
routes: [
|
||||
{
|
||||
path: 'overseas-warehouse/labeling',
|
||||
name: 'overseas-labeling',
|
||||
component: () => import('./views/LabelingView.vue'),
|
||||
release: 'prototype',
|
||||
meta: { title: '贴标申请', description: '提交海外仓贴标申请', permission: 'merchant:overseas-warehouse:labeling' },
|
||||
path: 'overseas-warehouse/labeling', name: 'overseas-labeling',
|
||||
component: () => import('./views/WarehouseServiceView.vue'), props: { service: 'labeling' }, release: 'active',
|
||||
meta: { title: '贴标申请', description: '查询并管理海外仓贴标申请', permission: 'dashboard:view' },
|
||||
navigation: { key: 'overseas-labeling', icon: Tags },
|
||||
},
|
||||
{
|
||||
path: 'overseas-warehouse/return-inbound',
|
||||
name: 'overseas-return-inbound',
|
||||
component: () => import('./views/ReturnInboundView.vue'),
|
||||
release: 'prototype',
|
||||
meta: { title: '退货入库', description: '登记海外仓退货入库', permission: 'merchant:overseas-warehouse:return-inbound' },
|
||||
navigation: { key: 'overseas-return-inbound', icon: Package },
|
||||
path: 'overseas-warehouse/storage-transfer', name: 'overseas-storage-transfer',
|
||||
component: () => import('./views/WarehouseServiceView.vue'), props: { service: 'transfer' }, release: 'active',
|
||||
meta: { title: '仓储转运', description: '查询并管理海外仓转运订单', permission: 'dashboard:view' },
|
||||
navigation: { key: 'overseas-storage-transfer', icon: Truck },
|
||||
},
|
||||
{
|
||||
path: 'overseas-warehouse/return-relabel',
|
||||
name: 'overseas-return-relabel',
|
||||
component: () => import('./views/ReturnRelabelView.vue'),
|
||||
release: 'prototype',
|
||||
meta: { title: '退货换标', description: '提交退货换标申请', permission: 'merchant:overseas-warehouse:return-relabel' },
|
||||
navigation: { key: 'overseas-return-relabel', icon: Tags },
|
||||
path: 'overseas-warehouse/cargo-disposal', name: 'overseas-cargo-disposal',
|
||||
component: () => import('./views/WarehouseServiceView.vue'), props: { service: 'disposal' }, release: 'active',
|
||||
meta: { title: '货物废弃', description: '查询并管理海外仓货物废弃申请', permission: 'dashboard:view' },
|
||||
navigation: { key: 'overseas-cargo-disposal', icon: PackageX },
|
||||
},
|
||||
{
|
||||
path: 'overseas-warehouse/return-outbound',
|
||||
name: 'overseas-return-outbound',
|
||||
component: () => import('./views/ReturnOutboundView.vue'),
|
||||
release: 'prototype',
|
||||
meta: { title: '退货出库', description: '创建海外仓退货出库单', permission: 'merchant:overseas-warehouse:return-outbound' },
|
||||
navigation: { key: 'overseas-return-outbound', icon: Truck },
|
||||
path: 'overseas-warehouse/return-relabel', name: 'overseas-return-relabel',
|
||||
component: () => import('./views/WarehouseServiceView.vue'), props: { service: 'relabel' }, release: 'active',
|
||||
meta: { title: '退货换标', description: '查询并管理退货换标订单', permission: 'dashboard:view' },
|
||||
navigation: { key: 'overseas-return-relabel', icon: RefreshCcw },
|
||||
},
|
||||
],
|
||||
} satisfies ClientModuleManifest
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, ref, watch } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { ArrowLeft, Eye, Plus, Search, Trash2 } from '@lucide/vue'
|
||||
import {
|
||||
createDiscard, createRelabel, createTransfer, getDiscard, getRelabel, getTransfer,
|
||||
listDiscardCandidates, listDiscards, listLastlegOrders, listRelabels, listTransfers,
|
||||
} from '../api/warehouseServices'
|
||||
|
||||
type ServiceType = 'labeling' | 'transfer' | 'disposal' | 'relabel'
|
||||
type PageMode = 'list' | 'create' | 'detail'
|
||||
interface CargoRow { id: number; sku: string; name: string; quantity: number; availableQuantity?: number; sourceOrderId?: string; fnsku: string; shipmentNo: string; newSkuCode: string; shippingMark: string }
|
||||
interface ServiceConfig { title: string; createTitle: string; numberLabel: string; emptyText: string; columns: string[] }
|
||||
|
||||
const props = defineProps<{ service: ServiceType }>()
|
||||
const configs: Record<ServiceType, ServiceConfig> = {
|
||||
labeling: { title: '贴标申请', createTitle: '新增贴标申请', numberLabel: '贴标申请单号', emptyText: '暂无贴标申请', columns: ['贴标申请单号', '客户单号', '目的海外仓', '贴标要求', 'SKU 数', '总箱数', '状态', '创建时间', '操作'] },
|
||||
transfer: { title: '仓储转运单', createTitle: '新增仓储转运单', numberLabel: '转运单号', emptyText: '暂无仓储转运订单', columns: ['转运单号', '进仓编号', '发货箱数', '操作仓', 'FBA 目的仓', '派送方式', '是否换标', '状态', '创建时间', '操作'] },
|
||||
disposal: { title: '货物废弃', createTitle: '新增货物废弃申请', numberLabel: '废弃申请单号', emptyText: '暂无货物废弃申请', columns: ['废弃申请单号', '进仓编号', '操作仓', 'SKU 汇总', '废弃数量', '预估费用', '状态', '创建时间', '操作'] },
|
||||
relabel: { title: '退货换标', createTitle: '新建退货换标单', numberLabel: '退货换标单号', emptyText: '暂无退货换标订单', columns: ['退货换标单号', '来源 FBA 仓', '目的 FBA 仓', 'SKU 汇总', '退件总数', '是否换标', '换标状态', '尾程状态', '创建时间', '操作'] },
|
||||
}
|
||||
const config = computed(() => configs[props.service])
|
||||
const statusOptions = computed(() => props.service === 'labeling'
|
||||
? [{ label: '待处理', value: 'PENDING_PROCESSING' }, { label: '处理中', value: 'PROCESSING' }, { label: '已完成', value: 'COMPLETED' }, { label: '已拒绝', value: 'REJECTED' }, { label: '已取消', value: 'CANCELLED' }]
|
||||
: props.service === 'disposal'
|
||||
? [{ label: '待审核', value: 'PENDING_REVIEW' }, { label: '已通过', value: 'APPROVED' }, { label: '已拒绝', value: 'REJECTED' }]
|
||||
: [{ label: '待审核', value: 'PENDING_REVIEW' }, { label: '已通过', value: 'APPROVED' }, { label: '待分配', value: 'PENDING_ASSIGNMENT' }, { label: '已分配', value: 'ALLOCATED' }, { label: '已出库', value: 'OUTBOUNDED' }, { label: '部分收货', value: 'PARTIALLY_RECEIVED' }, { label: '已收货', value: 'RECEIVED' }, { label: '已拒绝', value: 'REJECTED' }])
|
||||
const mode = ref<PageMode>('list')
|
||||
const filters = reactive({ number: '', inboundNo: '', warehouse: '', destination: '', status: '', start: '', end: '' })
|
||||
const form = reactive({ customerNo: '', inboundNo: '', sourceWarehouse: '', warehouse: '', destination: '', address: '', contact: '', phone: '', deliveryMethod: '', trackingNo: '', reason: '', requirement: '', relabel: false, remark: '', labelDeadline: '', estimatedArrivalAt: '', fbaShipmentNo: '', volumeCbm: 0.01 })
|
||||
const cargoRows = ref<CargoRow[]>([])
|
||||
const submittedRows = ref<Record<string, string>[]>([])
|
||||
const selectedDetail = ref<Record<string, string> | null>(null)
|
||||
const discardCandidates = ref<import('../api/warehouseServices').DiscardCandidate[]>([])
|
||||
const selectedDiscardOrderId = ref('')
|
||||
const discardCandidatesLoading = ref(false)
|
||||
const loading = ref(false)
|
||||
const saving = ref(false)
|
||||
let cargoSequence = 0
|
||||
|
||||
watch(() => props.service, () => { mode.value = 'list'; resetFilters(); resetForm(); void load() })
|
||||
onMounted(() => { void load() })
|
||||
function resetFilters() { Object.assign(filters, { number: '', inboundNo: '', warehouse: '', destination: '', status: '', start: '', end: '' }) }
|
||||
function resetForm() {
|
||||
Object.assign(form, { customerNo: '', inboundNo: '', sourceWarehouse: '', warehouse: '', destination: '', address: '', contact: '', phone: '', deliveryMethod: '', trackingNo: '', reason: '', requirement: '', relabel: false, remark: '', labelDeadline: '', estimatedArrivalAt: '', fbaShipmentNo: '', volumeCbm: 0.01 })
|
||||
cargoRows.value = []
|
||||
discardCandidates.value = []
|
||||
selectedDiscardOrderId.value = ''
|
||||
}
|
||||
async function openCreate() {
|
||||
resetForm(); mode.value = 'create'
|
||||
if (props.service !== 'disposal') { addCargoRow(); return }
|
||||
discardCandidatesLoading.value = true
|
||||
try { discardCandidates.value = await listDiscardCandidates() }
|
||||
catch (error) { ElMessage.error(error instanceof Error ? error.message : '可废弃入库单加载失败') }
|
||||
finally { discardCandidatesLoading.value = false }
|
||||
}
|
||||
function addCargoRow() { cargoRows.value.push({ id: ++cargoSequence, sku: '', name: '', quantity: 1, fnsku: '', shipmentNo: '', newSkuCode: '', shippingMark: '' }) }
|
||||
function removeCargoRow(id: number) { cargoRows.value = cargoRows.value.filter((row) => row.id !== id) }
|
||||
function selectDiscardInbound(orderId: string) {
|
||||
const candidate = discardCandidates.value.find((item) => item.orderId === orderId)
|
||||
if (!candidate) { cargoRows.value = []; return }
|
||||
form.inboundNo = candidate.inboundCode
|
||||
form.warehouse = candidate.overseasWarehouseId
|
||||
cargoRows.value = candidate.skus.map((sku) => ({
|
||||
id: ++cargoSequence, sku: sku.skuCode, name: sku.productName, quantity: 0,
|
||||
availableQuantity: sku.availablePackages, sourceOrderId: candidate.orderId,
|
||||
fnsku: '', shipmentNo: '', newSkuCode: '', shippingMark: '',
|
||||
}))
|
||||
}
|
||||
function dateTime(value: string, end = false) { return value ? `${value}T${end ? '23:59:59' : '00:00:00'}` : undefined }
|
||||
function row(values: Array<string | number | undefined | null>) { return Object.fromEntries(values.map((value, index) => [String(index), value == null || value === '' ? '-' : String(value)])) }
|
||||
async function load() {
|
||||
loading.value = true
|
||||
try {
|
||||
if (props.service === 'labeling') {
|
||||
const result = await listRelabels({ page: 1, size: 20, relabelNo: filters.number || undefined, overseasWarehouseId: filters.warehouse || undefined, status: filters.status || undefined, createdFrom: dateTime(filters.start), createdTo: dateTime(filters.end, true) })
|
||||
submittedRows.value = result.records.map((item) => row([item.relabelNo, item.customerOrderNo, item.overseasWarehouseId, '-', item.itemCount, item.totalPackageCount, item.status, item.createdAt]))
|
||||
} else if (props.service === 'disposal') {
|
||||
const result = await listDiscards({ page: 1, size: 20, discardNo: filters.number || undefined, inboundCode: filters.inboundNo || undefined, overseasWarehouseId: filters.warehouse || undefined, status: filters.status || undefined, createdFrom: dateTime(filters.start), createdTo: dateTime(filters.end, true) })
|
||||
submittedRows.value = result.records.map((item) => row([item.discardNo, item.inboundCodes, item.overseasWarehouseName || item.overseasWarehouseId, '-', item.totalPackages, `${item.currency} ${item.totalFee}`, item.status, item.createdAt]))
|
||||
} else {
|
||||
const result = await listTransfers({ page: 1, size: 20, transferNo: filters.number || undefined, inboundCode: filters.inboundNo || undefined, operatingWarehouseId: filters.warehouse || undefined, fbaWarehouseCode: filters.destination || undefined, status: filters.status || undefined, needRelabel: props.service === 'relabel', createdFrom: dateTime(filters.start), createdTo: dateTime(filters.end, true) })
|
||||
submittedRows.value = result.records.map((item) => props.service === 'transfer'
|
||||
? row([item.transferNo, item.inboundCode, item.packageCount, item.operatingWarehouseId, item.fbaWarehouseCode, item.deliveryMethod, item.needRelabel ? '是' : '否', item.status, item.createdAt])
|
||||
: row([item.transferNo, item.operatingWarehouseId, item.fbaWarehouseCode, '-', item.packageCount, '是', item.relabelNo ? '已生成' : '待生成', item.status, item.createdAt]))
|
||||
}
|
||||
} catch (error) { ElMessage.error(error instanceof Error ? error.message : '列表加载失败') }
|
||||
finally { loading.value = false }
|
||||
}
|
||||
function query() { void load() }
|
||||
async function submit() {
|
||||
const primaryWarehouse = props.service === 'relabel' ? form.sourceWarehouse : form.warehouse
|
||||
const invalidCargo = props.service === 'disposal'
|
||||
? !selectedDiscardOrderId.value || cargoRows.value.every((item) => item.quantity < 1)
|
||||
|| cargoRows.value.some((item) => item.quantity < 0 || item.quantity > Number(item.availableQuantity || 0))
|
||||
: cargoRows.value.length === 0 || cargoRows.value.some((item) => !item.sku || !item.name || item.quantity < 1)
|
||||
if ((!form.customerNo && !form.inboundNo) || !primaryWarehouse || invalidCargo) {
|
||||
ElMessage.warning('请填写必填信息和完整的货物明细'); return
|
||||
}
|
||||
if (props.service === 'labeling' && (!form.labelDeadline || !form.contact || !form.phone || cargoRows.value.some((item) => !item.shipmentNo || !item.newSkuCode))) {
|
||||
ElMessage.warning('请填写贴标截止时间、联系人以及货物的货件编号和新 SKU'); return
|
||||
}
|
||||
if (props.service === 'disposal' && !form.reason) {
|
||||
ElMessage.warning('请选择废弃原因'); return
|
||||
}
|
||||
if ((props.service === 'transfer' || props.service === 'relabel') && (!form.destination || !form.address || !form.deliveryMethod || !form.estimatedArrivalAt || !form.fbaShipmentNo || form.volumeCbm <= 0)) {
|
||||
ElMessage.warning('请填写完整的派送信息、体积、预计到达时间和 FBA 货件号'); return
|
||||
}
|
||||
try { await ElMessageBox.confirm(`确认提交${config.value.title}吗?`, '提交确认', { confirmButtonText: '提交', cancelButtonText: '取消' }) } catch { return }
|
||||
saving.value = true
|
||||
try {
|
||||
const idempotencyKey = crypto.randomUUID()
|
||||
if (props.service === 'labeling') {
|
||||
await createRelabel({ customerOrderNo: form.customerNo, overseasWarehouseId: form.warehouse, labelDeadline: form.labelDeadline, contactName: form.contact, contactPhone: form.phone, remark: form.remark || undefined, idempotencyKey, items: cargoRows.value.map((item) => ({ skuCode: item.sku, productNameZh: item.name, totalPackageCount: item.quantity, notifiedPackageCount: item.quantity, shipmentNo: item.shipmentNo, trackingCode: form.trackingNo || undefined, newSkuCode: item.newSkuCode, labelRequirement: form.requirement || undefined })) })
|
||||
} else if (props.service === 'disposal') {
|
||||
const items = cargoRows.value.filter((item) => item.quantity > 0).map((item) => {
|
||||
if (!item.sourceOrderId || item.sourceOrderId !== selectedDiscardOrderId.value || item.quantity > Number(item.availableQuantity || 0)) {
|
||||
throw new Error(`SKU ${item.sku} 的可废弃库存不足`)
|
||||
}
|
||||
return { orderId: item.sourceOrderId, skuCode: item.sku, discardPackages: item.quantity }
|
||||
})
|
||||
await createDiscard({ items, idempotencyKey, remark: [form.reason, form.remark].filter(Boolean).join(';') || undefined })
|
||||
} else {
|
||||
const sources = await listLastlegOrders(form.inboundNo)
|
||||
if (sources.records.length === 0) throw new Error('未找到该入仓码对应的尾程订单')
|
||||
const packageCount = cargoRows.value.reduce((sum, item) => sum + item.quantity, 0)
|
||||
await createTransfer({ inboundCode: form.inboundNo, sourceLastlegOrderIds: sources.records.map((item) => item.id), operatingWarehouseId: primaryWarehouse, fbaWarehouseCode: form.destination, deliveryAddress: form.address, packageCount, volumeCbm: form.volumeCbm, deliveryMethod: form.deliveryMethod, estimatedArrivalAt: form.estimatedArrivalAt, fbaShipmentNo: form.fbaShipmentNo, trackingNo: form.trackingNo || undefined, remark: form.remark || undefined, needRelabel: props.service === 'relabel' || form.relabel, idempotencyKey, items: cargoRows.value.map((item) => ({ skuCode: item.sku, productNameZh: item.name, packageCount: item.quantity, shippingMark: item.shippingMark || undefined })) })
|
||||
}
|
||||
ElMessage.success(`${config.value.title}提交成功`); mode.value = 'list'; await load()
|
||||
} catch (error) { ElMessage.error(error instanceof Error ? error.message : '提交失败') }
|
||||
finally { saving.value = false }
|
||||
}
|
||||
async function openDetail(value: Record<string, string>) {
|
||||
try {
|
||||
const detail = props.service === 'labeling' ? await getRelabel(value['0']) : props.service === 'disposal' ? await getDiscard(value['0']) : await getTransfer(value['0'])
|
||||
selectedDetail.value = { ...value, raw: JSON.stringify(detail) }; mode.value = 'detail'
|
||||
} catch (error) { ElMessage.error(error instanceof Error ? error.message : '详情加载失败') }
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="warehouse-service-page">
|
||||
<template v-if="mode === 'list'">
|
||||
<div class="warehouse-breadcrumb"><span>首页</span><b>/</b><strong>{{ config.title }}</strong></div>
|
||||
<div class="warehouse-page-heading"><h2>{{ config.title }}</h2><el-button type="primary" @click="openCreate"><Plus :size="16" />{{ props.service === 'relabel' ? '新建退货换标单' : props.service === 'transfer' ? '新增订单' : `新增${config.title}` }}</el-button></div>
|
||||
<div class="warehouse-card warehouse-filter-card">
|
||||
<div class="warehouse-filter-grid">
|
||||
<label><span>{{ config.numberLabel }}</span><el-input v-model="filters.number" :placeholder="`请输入${config.numberLabel}`" clearable /></label>
|
||||
<label v-if="props.service !== 'labeling' && props.service !== 'relabel'"><span>进仓编号</span><el-input v-model="filters.inboundNo" placeholder="请输入进仓编号" clearable /></label>
|
||||
<label><span>{{ props.service === 'relabel' ? '来源 FBA 仓' : '操作仓' }}</span><el-select v-model="filters.warehouse" placeholder="全部"><el-option label="美西洛杉矶仓" value="LAX" /><el-option label="美东新泽西仓" value="EWR" /><el-option label="美中芝加哥仓" value="ORD" /></el-select></label>
|
||||
<label v-if="props.service === 'transfer' || props.service === 'relabel'"><span>目的 FBA 仓</span><el-input v-model="filters.destination" placeholder="请输入仓代码" clearable /></label>
|
||||
<label><span>订单状态</span><el-select v-model="filters.status" placeholder="全部状态" clearable><el-option v-for="option in statusOptions" :key="option.value" :label="option.label" :value="option.value" /></el-select></label>
|
||||
<label><span>创建时间</span><el-date-picker v-model="filters.start" type="date" placeholder="开始日期" value-format="YYYY-MM-DD" /></label>
|
||||
<label><span> </span><el-date-picker v-model="filters.end" type="date" placeholder="结束日期" value-format="YYYY-MM-DD" /></label>
|
||||
</div>
|
||||
<div class="warehouse-filter-actions"><el-button type="primary" @click="query"><Search :size="16" />查询</el-button><el-button @click="resetFilters(); query()">重置</el-button></div>
|
||||
</div>
|
||||
<div v-loading="loading" class="warehouse-card warehouse-table-card">
|
||||
<div class="warehouse-table-scroll"><table><thead><tr><th v-for="column in config.columns" :key="column">{{ column }}</th></tr></thead><tbody><tr v-for="(row, rowIndex) in submittedRows" :key="rowIndex"><td v-for="(_, index) in config.columns.slice(0, -1)" :key="index">{{ row[String(index)] }}</td><td><button class="warehouse-link" type="button" @click="openDetail(row)"><Eye :size="15" />查看</button></td></tr><tr v-if="submittedRows.length === 0"><td :colspan="config.columns.length"><div class="warehouse-empty">{{ config.emptyText }}</div></td></tr></tbody></table></div>
|
||||
<div class="warehouse-pagination"><span>共 <strong>{{ submittedRows.length }}</strong> 条</span><el-button size="small" disabled>上一页</el-button><button type="button" class="warehouse-page-number">1</button><el-button size="small" disabled>下一页</el-button></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-else-if="mode === 'create'">
|
||||
<div class="warehouse-breadcrumb"><button type="button" @click="mode = 'list'">{{ config.title }}</button><b>/</b><strong>新增</strong></div>
|
||||
<div class="warehouse-page-heading"><h2>{{ config.createTitle }}</h2><el-button @click="mode = 'list'"><ArrowLeft :size="16" />返回列表</el-button></div>
|
||||
<form @submit.prevent="submit">
|
||||
<div class="warehouse-card warehouse-form-section"><h3>{{ props.service === 'relabel' ? '退货信息' : props.service === 'labeling' ? '订单信息' : '基础与派送信息' }}</h3>
|
||||
<div class="warehouse-form-grid">
|
||||
<label v-if="props.service === 'labeling'"><span><i>*</i>客户单号</span><el-input v-model="form.customerNo" placeholder="请输入客户单号" /></label>
|
||||
<label v-if="props.service === 'disposal'" class="warehouse-span-2"><span><i>*</i>选择入库单</span><el-select v-model="selectedDiscardOrderId" filterable :loading="discardCandidatesLoading" placeholder="请选择有可用库存的入库单" @change="selectDiscardInbound"><el-option v-for="candidate in discardCandidates" :key="candidate.orderId" :label="`${candidate.inboundOrderNo || candidate.inboundCode}|${candidate.inboundCode}|${candidate.overseasWarehouseName || candidate.overseasWarehouseId}|可用 ${candidate.availablePackages} 箱`" :value="candidate.orderId" /></el-select><small class="warehouse-hint">仅显示一件代发中仍有可用箱数的入库单。</small></label>
|
||||
<label v-if="props.service === 'transfer' || props.service === 'relabel'"><span><i>*</i>进仓编号</span><el-input v-model="form.inboundNo" placeholder="请输入已有入仓编号" /></label>
|
||||
<label v-if="props.service === 'relabel'"><span><i>*</i>来源 FBA 仓库</span><el-select v-model="form.sourceWarehouse" placeholder="请选择"><el-option label="ONT8" value="ONT8" /><el-option label="ABE8" value="ABE8" /><el-option label="CLT2" value="CLT2" /></el-select></label>
|
||||
<label v-else-if="props.service === 'disposal'"><span>进仓编号</span><el-input v-model="form.inboundNo" readonly /></label>
|
||||
<label v-else><span><i>*</i>{{ props.service === 'labeling' ? '目的海外仓' : '操作仓' }}</span><el-select v-model="form.warehouse" placeholder="请选择"><el-option label="美西海外仓-ONT8" value="ONT8" /><el-option label="美东海外仓-ABE8" value="ABE8" /><el-option label="美中海外仓-MDW2" value="MDW2" /></el-select></label>
|
||||
<label v-if="props.service === 'disposal'"><span>操作仓</span><el-input :model-value="discardCandidates.find((item) => item.orderId === selectedDiscardOrderId)?.overseasWarehouseName || form.warehouse" readonly /></label>
|
||||
<label v-if="props.service === 'relabel' || props.service === 'transfer'"><span><i>*</i>目的 FBA 仓</span><el-input v-model="form.destination" placeholder="请输入仓代码" /></label>
|
||||
<label v-if="props.service === 'transfer' || props.service === 'relabel'"><span><i>*</i>派送方式</span><el-select v-model="form.deliveryMethod" placeholder="请选择"><el-option label="卡车派送" value="卡车派送" /><el-option label="快递派送" value="快递派送" /></el-select></label>
|
||||
<label v-if="props.service === 'relabel' || props.service === 'disposal'"><span><i>*</i>{{ props.service === 'relabel' ? '退货原因' : '废弃原因' }}</span><el-select v-model="form.reason" placeholder="请选择"><el-option label="客户退货" value="客户退货" /><el-option label="亚马逊移除" value="亚马逊移除" /><el-option label="残损不可售" value="残损不可售" /></el-select></label>
|
||||
<label v-if="props.service === 'labeling'"><span><i>*</i>联系人</span><el-input v-model="form.contact" placeholder="请输入联系人" /></label>
|
||||
<label v-if="props.service === 'labeling'"><span><i>*</i>联系电话</span><el-input v-model="form.phone" placeholder="请输入联系电话" /></label>
|
||||
<label v-if="props.service === 'labeling'"><span><i>*</i>贴标截止时间</span><el-date-picker v-model="form.labelDeadline" type="datetime" value-format="YYYY-MM-DDTHH:mm:ss" placeholder="请选择截止时间" /></label>
|
||||
<label v-if="props.service === 'transfer' || props.service === 'relabel'" class="warehouse-span-2"><span><i>*</i>详细派送地址</span><el-input v-model="form.address" placeholder="请输入派送地址" /></label>
|
||||
<label v-if="props.service === 'transfer' || props.service === 'relabel'"><span><i>*</i>总体积(CBM)</span><el-input-number v-model="form.volumeCbm" :min="0.0001" :precision="4" controls-position="right" /></label>
|
||||
<label v-if="props.service === 'transfer' || props.service === 'relabel'"><span><i>*</i>预计到达时间</span><el-date-picker v-model="form.estimatedArrivalAt" type="datetime" value-format="YYYY-MM-DDTHH:mm:ss" placeholder="请选择预计到达时间" /></label>
|
||||
<label v-if="props.service === 'transfer' || props.service === 'relabel'"><span><i>*</i>FBA 货件编号</span><el-input v-model="form.fbaShipmentNo" placeholder="请输入 FBA 货件编号" /></label>
|
||||
<label v-if="props.service === 'labeling'"><span>贴标要求</span><el-input v-model="form.requirement" /></label>
|
||||
<label v-else-if="props.service !== 'disposal'"><span>物流跟踪号</span><el-input v-model="form.trackingNo" /></label>
|
||||
<label class="warehouse-span-2"><span>订单备注</span><el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入备注信息" /></label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="warehouse-card warehouse-form-section"><div class="warehouse-section-heading"><h3>货物明细</h3><div v-if="props.service !== 'disposal'"><el-button type="primary" size="small" @click="addCargoRow"><Plus :size="15" />添加行</el-button><el-button size="small" @click="cargoRows = []">清空</el-button></div></div>
|
||||
<div class="warehouse-table-scroll"><table class="warehouse-edit-table"><thead><tr><th>商品 SKU *</th><th>中文品名 *</th><th v-if="props.service === 'disposal'">可用箱数</th><th v-else>{{ props.service === 'labeling' ? '货件编号 *' : 'FNSKU / 箱唛' }}</th><th v-if="props.service === 'labeling'">换标新 SKU *</th><th>{{ props.service === 'disposal' ? '本次废弃箱数' : props.service === 'relabel' ? '退件数量' : '箱数' }} *</th><th v-if="props.service !== 'disposal'">操作</th></tr></thead><tbody><tr v-for="row in cargoRows" :key="row.id"><td><el-input v-model="row.sku" :readonly="props.service === 'disposal'" /></td><td><el-input v-model="row.name" :readonly="props.service === 'disposal'" /></td><td v-if="props.service === 'disposal'"><strong>{{ row.availableQuantity }} 箱</strong></td><td v-else><el-input v-if="props.service === 'labeling'" v-model="row.shipmentNo" /><el-input v-else v-model="row.shippingMark" /></td><td v-if="props.service === 'labeling'"><el-input v-model="row.newSkuCode" /></td><td><el-input-number v-model="row.quantity" :min="props.service === 'disposal' ? 0 : 1" :max="props.service === 'disposal' ? row.availableQuantity : undefined" controls-position="right" /></td><td v-if="props.service !== 'disposal'"><button type="button" class="warehouse-delete" title="删除" @click="removeCargoRow(row.id)"><Trash2 :size="16" /></button></td></tr><tr v-if="cargoRows.length === 0"><td :colspan="props.service === 'labeling' ? 6 : 5"><div class="warehouse-empty">{{ props.service === 'disposal' ? '请先选择入库单,系统将自动带出 SKU 和可用箱数' : '暂无货物明细,请点击“添加行”' }}</div></td></tr></tbody></table></div>
|
||||
</div>
|
||||
<div v-if="props.service === 'transfer' || props.service === 'relabel'" class="warehouse-card warehouse-form-section"><h3>换标设置</h3><el-checkbox v-model="form.relabel">是否换标</el-checkbox><p v-if="form.relabel" class="warehouse-hint">请确认 SKU 与 FNSKU 信息准确,提交后海外仓将按明细执行换标。</p></div>
|
||||
<div class="warehouse-submit-bar"><el-button @click="resetForm">重置</el-button><el-button type="primary" native-type="submit" :loading="saving">提交{{ config.title }}</el-button></div>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<template v-else><div class="warehouse-breadcrumb"><button type="button" @click="mode = 'list'">{{ config.title }}</button><b>/</b><strong>订单详情</strong></div><div class="warehouse-page-heading"><h2>{{ config.title }}详情</h2><el-button @click="mode = 'list'"><ArrowLeft :size="16" />返回列表</el-button></div><div class="warehouse-card warehouse-form-section"><h3>订单概览</h3><div class="warehouse-detail-grid"><div v-for="(column, index) in config.columns.slice(0, -1)" :key="column"><span>{{ column }}</span><strong>{{ selectedDetail?.[String(index)] || '-' }}</strong></div></div></div></template>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.warehouse-service-page{color:#25324a}.warehouse-breadcrumb{display:flex;align-items:center;gap:9px;margin:0 0 14px;color:#9aa3b2;font-size:12px}.warehouse-breadcrumb b{font-weight:400}.warehouse-breadcrumb strong{color:#606b7c}.warehouse-breadcrumb button{padding:0;border:0;background:transparent;color:#4376b8;cursor:pointer}.warehouse-page-heading{display:flex;align-items:center;justify-content:space-between;margin-bottom:16px}.warehouse-page-heading h2{margin:0;color:#17233a;font-size:22px;letter-spacing:0}.warehouse-card{margin-bottom:12px;border:1px solid #e7ebf1;border-radius:4px;background:#fff;box-shadow:0 1px 3px rgba(25,42,70,.04)}.warehouse-filter-card{padding:20px 22px 16px}.warehouse-filter-grid{display:grid;grid-template-columns:repeat(4,minmax(180px,1fr));gap:16px 20px}.warehouse-filter-grid label,.warehouse-form-grid label{display:flex;min-width:0;flex-direction:column;gap:7px}.warehouse-filter-grid label>span,.warehouse-form-grid label>span{color:#596579;font-size:12px}.warehouse-filter-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:18px}.warehouse-table-card{overflow:hidden}.warehouse-table-scroll{overflow-x:auto}table{width:100%;min-width:940px;border-collapse:collapse;font-size:12px}th{height:44px;padding:0 14px;border-bottom:1px solid #e8edf3;background:#f7f9fc;color:#4d5a6e;font-weight:650;text-align:left;white-space:nowrap}td{height:48px;padding:8px 14px;border-bottom:1px solid #eef1f5;color:#526176;white-space:nowrap}.warehouse-empty{display:grid;height:150px;color:#a0a9b7;place-items:center}.warehouse-link,.warehouse-delete{display:inline-flex;align-items:center;gap:5px;padding:4px;border:0;background:transparent;color:#2176d2;cursor:pointer}.warehouse-delete{color:#d64545}.warehouse-pagination{display:flex;align-items:center;justify-content:flex-end;gap:8px;padding:14px 18px;color:#778398;font-size:12px}.warehouse-pagination span{margin-right:8px}.warehouse-page-number{width:28px;height:28px;border:1px solid #2176d2;border-radius:3px;background:#2176d2;color:#fff}.warehouse-form-section{padding:22px}.warehouse-form-section h3{margin:0 0 18px;padding-bottom:12px;border-bottom:1px solid #edf0f4;color:#26364f;font-size:15px;letter-spacing:0}.warehouse-form-grid{display:grid;grid-template-columns:repeat(2,minmax(240px,1fr));gap:18px 28px}.warehouse-form-grid i{margin-right:3px;color:#e24b4b;font-style:normal}.warehouse-span-2{grid-column:1/-1}.warehouse-section-heading{display:flex;align-items:center;justify-content:space-between}.warehouse-section-heading h3{flex:1}.warehouse-edit-table td{padding:9px 10px}.warehouse-edit-table :deep(.el-input-number){width:130px}.warehouse-hint{margin:12px 0 0;color:#8792a2;font-size:12px}.warehouse-submit-bar{display:flex;justify-content:center;gap:10px;padding:12px 0 30px}.warehouse-detail-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:24px}.warehouse-detail-grid div{display:flex;flex-direction:column;gap:7px}.warehouse-detail-grid span{color:#8a95a5;font-size:12px}.warehouse-detail-grid strong{color:#334157;font-size:13px}:deep(.el-button){border-radius:4px}:deep(.el-button span){display:inline-flex;align-items:center;gap:6px}:deep(.el-select),:deep(.el-date-editor.el-input){width:100%}@media(max-width:1100px){.warehouse-filter-grid{grid-template-columns:repeat(2,1fr)}.warehouse-detail-grid{grid-template-columns:repeat(2,1fr)}}
|
||||
</style>
|
||||
@@ -53,6 +53,10 @@ test('keeps only implemented navigation groups visible', () => {
|
||||
title: '头程物流',
|
||||
children: ['订单列表', '快速下单'],
|
||||
},
|
||||
{
|
||||
title: '海外仓',
|
||||
children: ['贴标申请', '仓储转运', '货物废弃', '退货换标'],
|
||||
},
|
||||
{
|
||||
title: '财务结算',
|
||||
children: ['客户账单'],
|
||||
@@ -87,6 +91,10 @@ test('flattens visible leaves with unique paths', () => {
|
||||
'/',
|
||||
'/orders/first-leg',
|
||||
'/first-leg/quick-order',
|
||||
'/overseas-warehouse/labeling',
|
||||
'/overseas-warehouse/storage-transfer',
|
||||
'/overseas-warehouse/cargo-disposal',
|
||||
'/overseas-warehouse/return-relabel',
|
||||
'/finance/bills',
|
||||
'/company/profile',
|
||||
'/organization/members',
|
||||
@@ -304,7 +312,7 @@ test('keeps the dashboard route permission synchronized with navigation', () =>
|
||||
assert.match(dashboardRoute, /permission: 'dashboard:view'/)
|
||||
})
|
||||
|
||||
test('keeps prototype views cataloged while allowing the implemented quick-order page', () => {
|
||||
test('keeps prototype views cataloged while allowing implemented order and overseas warehouse pages', () => {
|
||||
const routerSource = [
|
||||
'first-leg',
|
||||
'cross-border',
|
||||
@@ -317,17 +325,17 @@ test('keeps prototype views cataloged while allowing the implemented quick-order
|
||||
['cross-border/procurement', 'ProcurementView.vue'],
|
||||
['cross-border/domestic-trucking', 'DomesticTruckingView.vue'],
|
||||
['cross-border/pallet-shipping', 'PalletShippingView.vue'],
|
||||
['overseas-warehouse/labeling', 'LabelingView.vue'],
|
||||
['overseas-warehouse/return-inbound', 'ReturnInboundView.vue'],
|
||||
['overseas-warehouse/return-relabel', 'ReturnRelabelView.vue'],
|
||||
['overseas-warehouse/return-outbound', 'ReturnOutboundView.vue'],
|
||||
['overseas-warehouse/labeling', 'WarehouseServiceView.vue'],
|
||||
['overseas-warehouse/storage-transfer', 'WarehouseServiceView.vue'],
|
||||
['overseas-warehouse/cargo-disposal', 'WarehouseServiceView.vue'],
|
||||
['overseas-warehouse/return-relabel', 'WarehouseServiceView.vue'],
|
||||
['orders/my', 'MyOrdersView.vue'],
|
||||
['orders/inbound', 'InboundOrdersView.vue'],
|
||||
['orders/delivery', 'DeliveryOrdersView.vue'],
|
||||
]
|
||||
|
||||
for (const [path, view] of routes) {
|
||||
if (path === 'first-leg/quick-order') {
|
||||
if (path === 'first-leg/quick-order' || path.startsWith('overseas-warehouse/')) {
|
||||
assert.match(routerSource, new RegExp(`path: '${path}'[\\s\\S]*?\\./views/${view.replace('.', '\\.')}[\\s\\S]*?release: 'active'[\\s\\S]*?permission:`))
|
||||
assert.equal(getNavigationLeaves(clientNavigation).some((item) => item.path === `/${path}`), true)
|
||||
continue
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,174 @@
|
||||
{
|
||||
"hash": "54b831dc",
|
||||
"configHash": "35e57476",
|
||||
"lockfileHash": "938bcfb6",
|
||||
"browserHash": "314fd125",
|
||||
"optimized": {
|
||||
"@lucide/vue": {
|
||||
"src": "../../../node_modules/.pnpm/@lucide+vue@1.27.0_vue@3.5.40_typescript@5.9.3_/node_modules/@lucide/vue/dist/esm/lucide-vue.mjs",
|
||||
"file": "@lucide_vue.js",
|
||||
"fileHash": "ecff1ff6",
|
||||
"needsInterop": false
|
||||
},
|
||||
"axios": {
|
||||
"src": "../../../node_modules/.pnpm/axios@1.18.1_debug@4.4.3/node_modules/axios/index.js",
|
||||
"file": "axios.js",
|
||||
"fileHash": "842b2a8e",
|
||||
"needsInterop": false
|
||||
},
|
||||
"element-plus": {
|
||||
"src": "../../../node_modules/.pnpm/element-plus@2.14.3_vue@3.5.40_typescript@5.9.3_/node_modules/element-plus/es/index.mjs",
|
||||
"file": "element-plus.js",
|
||||
"fileHash": "77832b0a",
|
||||
"needsInterop": false
|
||||
},
|
||||
"element-plus/es": {
|
||||
"src": "../../../node_modules/.pnpm/element-plus@2.14.3_vue@3.5.40_typescript@5.9.3_/node_modules/element-plus/es/index.mjs",
|
||||
"file": "element-plus_es.js",
|
||||
"fileHash": "958756a0",
|
||||
"needsInterop": false
|
||||
},
|
||||
"element-plus/es/components/alert/style/css": {
|
||||
"src": "../../../node_modules/.pnpm/element-plus@2.14.3_vue@3.5.40_typescript@5.9.3_/node_modules/element-plus/es/components/alert/style/css.mjs",
|
||||
"file": "element-plus_es_components_alert_style_css.js",
|
||||
"fileHash": "c0e71419",
|
||||
"needsInterop": false
|
||||
},
|
||||
"element-plus/es/components/base/style/css": {
|
||||
"src": "../../../node_modules/.pnpm/element-plus@2.14.3_vue@3.5.40_typescript@5.9.3_/node_modules/element-plus/es/components/base/style/css.mjs",
|
||||
"file": "element-plus_es_components_base_style_css.js",
|
||||
"fileHash": "4a865f94",
|
||||
"needsInterop": false
|
||||
},
|
||||
"element-plus/es/components/button/style/css": {
|
||||
"src": "../../../node_modules/.pnpm/element-plus@2.14.3_vue@3.5.40_typescript@5.9.3_/node_modules/element-plus/es/components/button/style/css.mjs",
|
||||
"file": "element-plus_es_components_button_style_css.js",
|
||||
"fileHash": "bce131cb",
|
||||
"needsInterop": false
|
||||
},
|
||||
"element-plus/es/components/checkbox/style/css": {
|
||||
"src": "../../../node_modules/.pnpm/element-plus@2.14.3_vue@3.5.40_typescript@5.9.3_/node_modules/element-plus/es/components/checkbox/style/css.mjs",
|
||||
"file": "element-plus_es_components_checkbox_style_css.js",
|
||||
"fileHash": "6b0a507e",
|
||||
"needsInterop": false
|
||||
},
|
||||
"element-plus/es/components/date-picker/style/css": {
|
||||
"src": "../../../node_modules/.pnpm/element-plus@2.14.3_vue@3.5.40_typescript@5.9.3_/node_modules/element-plus/es/components/date-picker/style/css.mjs",
|
||||
"file": "element-plus_es_components_date-picker_style_css.js",
|
||||
"fileHash": "4b977f35",
|
||||
"needsInterop": false
|
||||
},
|
||||
"element-plus/es/components/dropdown-item/style/css": {
|
||||
"src": "../../../node_modules/.pnpm/element-plus@2.14.3_vue@3.5.40_typescript@5.9.3_/node_modules/element-plus/es/components/dropdown-item/style/css.mjs",
|
||||
"file": "element-plus_es_components_dropdown-item_style_css.js",
|
||||
"fileHash": "4799bc75",
|
||||
"needsInterop": false
|
||||
},
|
||||
"element-plus/es/components/dropdown-menu/style/css": {
|
||||
"src": "../../../node_modules/.pnpm/element-plus@2.14.3_vue@3.5.40_typescript@5.9.3_/node_modules/element-plus/es/components/dropdown-menu/style/css.mjs",
|
||||
"file": "element-plus_es_components_dropdown-menu_style_css.js",
|
||||
"fileHash": "581fb572",
|
||||
"needsInterop": false
|
||||
},
|
||||
"element-plus/es/components/dropdown/style/css": {
|
||||
"src": "../../../node_modules/.pnpm/element-plus@2.14.3_vue@3.5.40_typescript@5.9.3_/node_modules/element-plus/es/components/dropdown/style/css.mjs",
|
||||
"file": "element-plus_es_components_dropdown_style_css.js",
|
||||
"fileHash": "f8974261",
|
||||
"needsInterop": false
|
||||
},
|
||||
"element-plus/es/components/form-item/style/css": {
|
||||
"src": "../../../node_modules/.pnpm/element-plus@2.14.3_vue@3.5.40_typescript@5.9.3_/node_modules/element-plus/es/components/form-item/style/css.mjs",
|
||||
"file": "element-plus_es_components_form-item_style_css.js",
|
||||
"fileHash": "10fac181",
|
||||
"needsInterop": false
|
||||
},
|
||||
"element-plus/es/components/form/style/css": {
|
||||
"src": "../../../node_modules/.pnpm/element-plus@2.14.3_vue@3.5.40_typescript@5.9.3_/node_modules/element-plus/es/components/form/style/css.mjs",
|
||||
"file": "element-plus_es_components_form_style_css.js",
|
||||
"fileHash": "c02b30d1",
|
||||
"needsInterop": false
|
||||
},
|
||||
"element-plus/es/components/input-number/style/css": {
|
||||
"src": "../../../node_modules/.pnpm/element-plus@2.14.3_vue@3.5.40_typescript@5.9.3_/node_modules/element-plus/es/components/input-number/style/css.mjs",
|
||||
"file": "element-plus_es_components_input-number_style_css.js",
|
||||
"fileHash": "84d9dce9",
|
||||
"needsInterop": false
|
||||
},
|
||||
"element-plus/es/components/input/style/css": {
|
||||
"src": "../../../node_modules/.pnpm/element-plus@2.14.3_vue@3.5.40_typescript@5.9.3_/node_modules/element-plus/es/components/input/style/css.mjs",
|
||||
"file": "element-plus_es_components_input_style_css.js",
|
||||
"fileHash": "40e03fd6",
|
||||
"needsInterop": false
|
||||
},
|
||||
"element-plus/es/components/loading/style/css": {
|
||||
"src": "../../../node_modules/.pnpm/element-plus@2.14.3_vue@3.5.40_typescript@5.9.3_/node_modules/element-plus/es/components/loading/style/css.mjs",
|
||||
"file": "element-plus_es_components_loading_style_css.js",
|
||||
"fileHash": "38e8dbc7",
|
||||
"needsInterop": false
|
||||
},
|
||||
"element-plus/es/components/message-box/style/css": {
|
||||
"src": "../../../node_modules/.pnpm/element-plus@2.14.3_vue@3.5.40_typescript@5.9.3_/node_modules/element-plus/es/components/message-box/style/css.mjs",
|
||||
"file": "element-plus_es_components_message-box_style_css.js",
|
||||
"fileHash": "9a5704ea",
|
||||
"needsInterop": false
|
||||
},
|
||||
"element-plus/es/components/message/style/css": {
|
||||
"src": "../../../node_modules/.pnpm/element-plus@2.14.3_vue@3.5.40_typescript@5.9.3_/node_modules/element-plus/es/components/message/style/css.mjs",
|
||||
"file": "element-plus_es_components_message_style_css.js",
|
||||
"fileHash": "9f49f2f4",
|
||||
"needsInterop": false
|
||||
},
|
||||
"element-plus/es/components/option/style/css": {
|
||||
"src": "../../../node_modules/.pnpm/element-plus@2.14.3_vue@3.5.40_typescript@5.9.3_/node_modules/element-plus/es/components/option/style/css.mjs",
|
||||
"file": "element-plus_es_components_option_style_css.js",
|
||||
"fileHash": "70f9e743",
|
||||
"needsInterop": false
|
||||
},
|
||||
"element-plus/es/components/select/style/css": {
|
||||
"src": "../../../node_modules/.pnpm/element-plus@2.14.3_vue@3.5.40_typescript@5.9.3_/node_modules/element-plus/es/components/select/style/css.mjs",
|
||||
"file": "element-plus_es_components_select_style_css.js",
|
||||
"fileHash": "71e60af0",
|
||||
"needsInterop": false
|
||||
},
|
||||
"pinia": {
|
||||
"src": "../../../node_modules/.pnpm/pinia@4.0.2_@vue+devtools-a_b92da3656e156e155ff60832373d6152/node_modules/pinia/dist/pinia.js",
|
||||
"file": "pinia.js",
|
||||
"fileHash": "6456294f",
|
||||
"needsInterop": false
|
||||
},
|
||||
"vue-router": {
|
||||
"src": "../../../node_modules/.pnpm/vue-router@5.2.0_@vue+compi_b4a151216dd2fb3ddc479aca26abbdbb/node_modules/vue-router/dist/vue-router.js",
|
||||
"file": "vue-router.js",
|
||||
"fileHash": "a813a767",
|
||||
"needsInterop": false
|
||||
},
|
||||
"vue": {
|
||||
"src": "../../../node_modules/.pnpm/vue@3.5.40_typescript@5.9.3/node_modules/vue/dist/vue.runtime.esm-bundler.js",
|
||||
"file": "vue.js",
|
||||
"fileHash": "61472233",
|
||||
"needsInterop": false
|
||||
}
|
||||
},
|
||||
"chunks": {
|
||||
"css-B_3m1e-S": {
|
||||
"file": "css-B_3m1e-S.js",
|
||||
"isDynamicEntry": false
|
||||
},
|
||||
"dist-HCG7bkn3": {
|
||||
"file": "dist-HCG7bkn3.js",
|
||||
"isDynamicEntry": false
|
||||
},
|
||||
"es-ChUZ6qgj": {
|
||||
"file": "es-ChUZ6qgj.js",
|
||||
"isDynamicEntry": false
|
||||
},
|
||||
"rolldown-runtime-B-1-B7_t": {
|
||||
"file": "rolldown-runtime-B-1-B7_t.js",
|
||||
"isDynamicEntry": false
|
||||
},
|
||||
"vue.runtime.esm-bundler-DtwC7h4l": {
|
||||
"file": "vue.runtime.esm-bundler-DtwC7h4l.js",
|
||||
"isDynamicEntry": false
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -0,0 +1,3 @@
|
||||
import "./element-plus_es_components_base_style_css.js";
|
||||
import "D:/project/html/client-web/node_modules/.pnpm/element-plus@2.14.3_vue@3.5.40_typescript@5.9.3_/node_modules/element-plus/theme-chalk/el-scrollbar.css";
|
||||
import "D:/project/html/client-web/node_modules/.pnpm/element-plus@2.14.3_vue@3.5.40_typescript@5.9.3_/node_modules/element-plus/theme-chalk/el-popper.css";
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,2 @@
|
||||
import "./element-plus_es_components_base_style_css.js";
|
||||
import "D:/project/html/client-web/node_modules/.pnpm/element-plus@2.14.3_vue@3.5.40_typescript@5.9.3_/node_modules/element-plus/theme-chalk/el-alert.css";
|
||||
@@ -0,0 +1 @@
|
||||
import "D:/project/html/client-web/node_modules/.pnpm/element-plus@2.14.3_vue@3.5.40_typescript@5.9.3_/node_modules/element-plus/theme-chalk/base.css";
|
||||
@@ -0,0 +1,2 @@
|
||||
import "./element-plus_es_components_base_style_css.js";
|
||||
import "D:/project/html/client-web/node_modules/.pnpm/element-plus@2.14.3_vue@3.5.40_typescript@5.9.3_/node_modules/element-plus/theme-chalk/el-button.css";
|
||||
@@ -0,0 +1,2 @@
|
||||
import "./element-plus_es_components_base_style_css.js";
|
||||
import "D:/project/html/client-web/node_modules/.pnpm/element-plus@2.14.3_vue@3.5.40_typescript@5.9.3_/node_modules/element-plus/theme-chalk/el-checkbox.css";
|
||||
@@ -0,0 +1,6 @@
|
||||
import "./element-plus_es_components_base_style_css.js";
|
||||
import "./element-plus_es_components_input_style_css.js";
|
||||
import "./css-B_3m1e-S.js";
|
||||
import "./element-plus_es_components_button_style_css.js";
|
||||
import "D:/project/html/client-web/node_modules/.pnpm/element-plus@2.14.3_vue@3.5.40_typescript@5.9.3_/node_modules/element-plus/theme-chalk/el-date-picker-panel.css";
|
||||
import "D:/project/html/client-web/node_modules/.pnpm/element-plus@2.14.3_vue@3.5.40_typescript@5.9.3_/node_modules/element-plus/theme-chalk/el-date-picker.css";
|
||||
@@ -0,0 +1,2 @@
|
||||
import "./element-plus_es_components_base_style_css.js";
|
||||
import "D:/project/html/client-web/node_modules/.pnpm/element-plus@2.14.3_vue@3.5.40_typescript@5.9.3_/node_modules/element-plus/theme-chalk/el-dropdown-item.css";
|
||||
@@ -0,0 +1,2 @@
|
||||
import "./element-plus_es_components_base_style_css.js";
|
||||
import "D:/project/html/client-web/node_modules/.pnpm/element-plus@2.14.3_vue@3.5.40_typescript@5.9.3_/node_modules/element-plus/theme-chalk/el-dropdown-menu.css";
|
||||
@@ -0,0 +1,5 @@
|
||||
import "./element-plus_es_components_base_style_css.js";
|
||||
import "./css-B_3m1e-S.js";
|
||||
import "./element-plus_es_components_button_style_css.js";
|
||||
import "D:/project/html/client-web/node_modules/.pnpm/element-plus@2.14.3_vue@3.5.40_typescript@5.9.3_/node_modules/element-plus/theme-chalk/el-button-group.css";
|
||||
import "D:/project/html/client-web/node_modules/.pnpm/element-plus@2.14.3_vue@3.5.40_typescript@5.9.3_/node_modules/element-plus/theme-chalk/el-dropdown.css";
|
||||
@@ -0,0 +1,2 @@
|
||||
import "./element-plus_es_components_base_style_css.js";
|
||||
import "D:/project/html/client-web/node_modules/.pnpm/element-plus@2.14.3_vue@3.5.40_typescript@5.9.3_/node_modules/element-plus/theme-chalk/el-form-item.css";
|
||||
@@ -0,0 +1,2 @@
|
||||
import "./element-plus_es_components_base_style_css.js";
|
||||
import "D:/project/html/client-web/node_modules/.pnpm/element-plus@2.14.3_vue@3.5.40_typescript@5.9.3_/node_modules/element-plus/theme-chalk/el-form.css";
|
||||
@@ -0,0 +1,3 @@
|
||||
import "./element-plus_es_components_base_style_css.js";
|
||||
import "./element-plus_es_components_input_style_css.js";
|
||||
import "D:/project/html/client-web/node_modules/.pnpm/element-plus@2.14.3_vue@3.5.40_typescript@5.9.3_/node_modules/element-plus/theme-chalk/el-input-number.css";
|
||||
@@ -0,0 +1,2 @@
|
||||
import "./element-plus_es_components_base_style_css.js";
|
||||
import "D:/project/html/client-web/node_modules/.pnpm/element-plus@2.14.3_vue@3.5.40_typescript@5.9.3_/node_modules/element-plus/theme-chalk/el-input.css";
|
||||
@@ -0,0 +1,2 @@
|
||||
import "./element-plus_es_components_base_style_css.js";
|
||||
import "D:/project/html/client-web/node_modules/.pnpm/element-plus@2.14.3_vue@3.5.40_typescript@5.9.3_/node_modules/element-plus/theme-chalk/el-loading.css";
|
||||
@@ -0,0 +1,5 @@
|
||||
import "./element-plus_es_components_base_style_css.js";
|
||||
import "./element-plus_es_components_input_style_css.js";
|
||||
import "./element-plus_es_components_button_style_css.js";
|
||||
import "D:/project/html/client-web/node_modules/.pnpm/element-plus@2.14.3_vue@3.5.40_typescript@5.9.3_/node_modules/element-plus/theme-chalk/el-overlay.css";
|
||||
import "D:/project/html/client-web/node_modules/.pnpm/element-plus@2.14.3_vue@3.5.40_typescript@5.9.3_/node_modules/element-plus/theme-chalk/el-message-box.css";
|
||||
@@ -0,0 +1,3 @@
|
||||
import "./element-plus_es_components_base_style_css.js";
|
||||
import "D:/project/html/client-web/node_modules/.pnpm/element-plus@2.14.3_vue@3.5.40_typescript@5.9.3_/node_modules/element-plus/theme-chalk/el-badge.css";
|
||||
import "D:/project/html/client-web/node_modules/.pnpm/element-plus@2.14.3_vue@3.5.40_typescript@5.9.3_/node_modules/element-plus/theme-chalk/el-message.css";
|
||||
@@ -0,0 +1,2 @@
|
||||
import "./element-plus_es_components_base_style_css.js";
|
||||
import "D:/project/html/client-web/node_modules/.pnpm/element-plus@2.14.3_vue@3.5.40_typescript@5.9.3_/node_modules/element-plus/theme-chalk/el-option.css";
|
||||
@@ -0,0 +1,6 @@
|
||||
import "./element-plus_es_components_base_style_css.js";
|
||||
import "./element-plus_es_components_option_style_css.js";
|
||||
import "./css-B_3m1e-S.js";
|
||||
import "D:/project/html/client-web/node_modules/.pnpm/element-plus@2.14.3_vue@3.5.40_typescript@5.9.3_/node_modules/element-plus/theme-chalk/el-tag.css";
|
||||
import "D:/project/html/client-web/node_modules/.pnpm/element-plus@2.14.3_vue@3.5.40_typescript@5.9.3_/node_modules/element-plus/theme-chalk/el-option-group.css";
|
||||
import "D:/project/html/client-web/node_modules/.pnpm/element-plus@2.14.3_vue@3.5.40_typescript@5.9.3_/node_modules/element-plus/theme-chalk/el-select.css";
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"type": "module"
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -0,0 +1,33 @@
|
||||
//#region \0rolldown/runtime.js
|
||||
var __create = Object.create;
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __getProtoOf = Object.getPrototypeOf;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
|
||||
var __exportAll = (all, no_symbols) => {
|
||||
let target = {};
|
||||
for (var name in all) __defProp(target, name, {
|
||||
get: all[name],
|
||||
enumerable: true
|
||||
});
|
||||
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
||||
return target;
|
||||
};
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
||||
key = keys[i];
|
||||
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
||||
get: ((k) => from[k]).bind(null, key),
|
||||
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
||||
});
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
||||
value: mod,
|
||||
enumerable: true
|
||||
}) : target, mod));
|
||||
//#endregion
|
||||
export { __exportAll as n, __toESM as r, __commonJSMin as t };
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -0,0 +1,2 @@
|
||||
import { $ as createTextVNode, $n as toRefs, $t as resolveTransitionHooks, A as ErrorCodes, An as customRef, At as onBeforeMount, B as callWithErrorHandling, Bn as markRaw, Bt as onUpdated, C as vShow, Cn as withDirectives, Ct as isRuntimeOnly, D as BaseTransitionPropsValidators, Dn as ReactiveEffect, Dt as mergeProps, E as BaseTransition, En as EffectScope, Et as mergeModels, F as Suspense, Fn as isProxy, Ft as onMounted, G as createBlock, Gn as readonly, Gt as queuePostFlushCb, H as compatUtils, Hn as onWatcherCleanup, Ht as popScopeId, I as Teleport, In as isReactive, It as onRenderTracked, J as createHydrationRenderer, Jn as shallowReadonly, Jt as renderSlot, K as createCommentVNode, Kn as ref, Kt as registerRuntimeCompiler, L as Text, Ln as isReadonly, Lt as onRenderTriggered, M as Fragment, Mn as effectScope, Mt as onBeforeUpdate, N as KeepAlive, Nn as getCurrentScope, Nt as onDeactivated, O as Comment, On as TrackOpTypes, Ot as nextTick, P as Static, Pn as getCurrentWatcher, Pt as onErrorCaptured, Q as createStaticVNode, Qn as toRef, Qt as resolveFilter, R as assertNumber, Rn as isRef, Rt as onServerPrefetch, S as vModelText, Sn as withDefaults, St as isMemoSame, T as withModifiers, Tn as withScopeId, Tt as mergeDefaults, U as computed, Un as proxyRefs, Ut as provide, V as cloneVNode, Vn as onScopeDispose, Vt as openBlock, W as createBaseVNode, Wn as reactive, Wt as pushScopeId, X as createRenderer, Xn as stop, Xt as resolveDirective, Y as createPropsRestProxy, Yn as shallowRef, Yt as resolveComponent, Z as createSlots, Zn as toRaw, Zt as resolveDynamicComponent, _ as useShadowRoot, _n as watchEffect, _r as normalizeStyle, _t as hydrateOnInteraction, a as createApp, an as toHandlers, ar as capitalize, at as defineModel, b as vModelRadio, bn as withAsyncContext, bt as initCustomFormatter, c as defineSSRCustomElement, cn as useId, ct as defineSlots, d as nodeOps, dn as useSlots, dt as getTransitionRawChildren, en as setBlockTracking, er as toValue, et as createVNode, f as patchProp, fn as useTemplateRef, ft as guardReactiveProps, g as useHost, gn as watch, gr as normalizeProps, gt as hydrateOnIdle, h as useCssVars, hn as warn, hr as normalizeClass, ht as hasInjectionContext, i as VueElement, in as ssrUtils, ir as camelize, it as defineExpose, j as ErrorTypeStrings, jn as effect, jt as onBeforeUnmount, k as DeprecationTypes, kn as TriggerOpTypes, kt as onActivated, l as hydrate, ln as useModel, lt as devtools, m as useCssModule, mn as version, mt as handleError, n as Transition, nn as setTransitionHooks, nr as unref, nt as defineComponent, o as createSSRApp, on as transformVNodeArgs, ot as defineOptions, p as render, pn as useTransitionState, pt as h, q as createElementBlock, qn as shallowReactive, qt as renderList, r as TransitionGroup, rn as ssrContextKey, rt as defineEmits, s as defineCustomElement, sn as useAttrs, st as defineProps, t as compile, tn as setDevtoolsHook, tr as triggerRef, tt as defineAsyncComponent, u as initDirectivesForSSR, un as useSSRContext, ut as getCurrentInstance, v as vModelCheckbox, vn as watchPostEffect, vr as toDisplayString, vt as hydrateOnMediaQuery, w as withKeys, wn as withMemo, wt as isVNode, x as vModelSelect, xn as withCtx, xt as inject, y as vModelDynamic, yn as watchSyncEffect, yr as toHandlerKey, yt as hydrateOnVisible, z as callWithAsyncErrorHandling, zn as isShallow, zt as onUnmounted } from "./vue.runtime.esm-bundler-DtwC7h4l.js";
|
||||
export { BaseTransition, BaseTransitionPropsValidators, Comment, DeprecationTypes, EffectScope, ErrorCodes, ErrorTypeStrings, Fragment, KeepAlive, ReactiveEffect, Static, Suspense, Teleport, Text, TrackOpTypes, Transition, TransitionGroup, TriggerOpTypes, VueElement, assertNumber, callWithAsyncErrorHandling, callWithErrorHandling, camelize, capitalize, cloneVNode, compatUtils, compile, computed, createApp, createBlock, createCommentVNode, createElementBlock, createBaseVNode as createElementVNode, createHydrationRenderer, createPropsRestProxy, createRenderer, createSSRApp, createSlots, createStaticVNode, createTextVNode, createVNode, customRef, defineAsyncComponent, defineComponent, defineCustomElement, defineEmits, defineExpose, defineModel, defineOptions, defineProps, defineSSRCustomElement, defineSlots, devtools, effect, effectScope, getCurrentInstance, getCurrentScope, getCurrentWatcher, getTransitionRawChildren, guardReactiveProps, h, handleError, hasInjectionContext, hydrate, hydrateOnIdle, hydrateOnInteraction, hydrateOnMediaQuery, hydrateOnVisible, initCustomFormatter, initDirectivesForSSR, inject, isMemoSame, isProxy, isReactive, isReadonly, isRef, isRuntimeOnly, isShallow, isVNode, markRaw, mergeDefaults, mergeModels, mergeProps, nextTick, nodeOps, normalizeClass, normalizeProps, normalizeStyle, onActivated, onBeforeMount, onBeforeUnmount, onBeforeUpdate, onDeactivated, onErrorCaptured, onMounted, onRenderTracked, onRenderTriggered, onScopeDispose, onServerPrefetch, onUnmounted, onUpdated, onWatcherCleanup, openBlock, patchProp, popScopeId, provide, proxyRefs, pushScopeId, queuePostFlushCb, reactive, readonly, ref, registerRuntimeCompiler, render, renderList, renderSlot, resolveComponent, resolveDirective, resolveDynamicComponent, resolveFilter, resolveTransitionHooks, setBlockTracking, setDevtoolsHook, setTransitionHooks, shallowReactive, shallowReadonly, shallowRef, ssrContextKey, ssrUtils, stop, toDisplayString, toHandlerKey, toHandlers, toRaw, toRef, toRefs, toValue, transformVNodeArgs, triggerRef, unref, useAttrs, useCssModule, useCssVars, useHost, useId, useModel, useSSRContext, useShadowRoot, useSlots, useTemplateRef, useTransitionState, vModelCheckbox, vModelDynamic, vModelRadio, vModelSelect, vModelText, vShow, version, warn, watch, watchEffect, watchPostEffect, watchSyncEffect, withAsyncContext, withCtx, withDefaults, withDirectives, withKeys, withMemo, withModifiers, withScopeId };
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -8,8 +8,10 @@ import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
|
||||
export default defineConfig(({ mode }) => {
|
||||
const env = loadEnv(mode, process.cwd(), '')
|
||||
const proxyTarget = process.env.API_PROXY_TARGET || env.API_PROXY_TARGET || 'http://127.0.0.1:9000'
|
||||
const overseasProxyTarget = process.env.OVERSEAS_API_PROXY_TARGET || env.OVERSEAS_API_PROXY_TARGET || 'http://127.0.0.1:9300'
|
||||
|
||||
return {
|
||||
cacheDir: 'tmp/vite-cache',
|
||||
plugins: [
|
||||
vue(),
|
||||
AutoImport({
|
||||
@@ -38,6 +40,16 @@ export default defineConfig(({ mode }) => {
|
||||
})
|
||||
},
|
||||
},
|
||||
'/overseas-api': {
|
||||
target: overseasProxyTarget,
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/overseas-api/, '/api'),
|
||||
configure(proxy) {
|
||||
proxy.on('proxyReq', (proxyRequest) => {
|
||||
proxyRequest.removeHeader('origin')
|
||||
})
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user