up
This commit is contained in:
BIN
Binary file not shown.
@@ -7,32 +7,65 @@ yarn-error.log*
|
|||||||
pnpm-debug.log*
|
pnpm-debug.log*
|
||||||
lerna-debug.log*
|
lerna-debug.log*
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
node_modules
|
node_modules
|
||||||
.DS_Store
|
.pnp
|
||||||
|
.pnp.js
|
||||||
|
|
||||||
|
# Build output
|
||||||
dist
|
dist
|
||||||
dist-ssr
|
dist-ssr
|
||||||
coverage
|
|
||||||
*.local
|
*.local
|
||||||
|
|
||||||
# Editor directories and files
|
# Testing
|
||||||
.vscode/*
|
coverage
|
||||||
!.vscode/extensions.json
|
.nyc_output
|
||||||
.idea
|
__screenshots__/
|
||||||
*.suo
|
|
||||||
*.ntvs*
|
|
||||||
*.njsproj
|
|
||||||
*.sln
|
|
||||||
*.sw?
|
|
||||||
|
|
||||||
*.tsbuildinfo
|
|
||||||
|
|
||||||
.eslintcache
|
|
||||||
|
|
||||||
# Cypress
|
# Cypress
|
||||||
/cypress/videos/
|
/cypress/videos/
|
||||||
/cypress/screenshots/
|
/cypress/screenshots/
|
||||||
|
|
||||||
# Vitest
|
# Editor directories and files
|
||||||
__screenshots__/
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# TypeScript
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# ESLint
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Environment variables
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
|
||||||
|
# Temporary files
|
||||||
|
*.tmp
|
||||||
|
*.temp
|
||||||
|
.cache
|
||||||
|
.parcel-cache
|
||||||
|
|
||||||
|
# OS files
|
||||||
|
Thumbs.db
|
||||||
|
Desktop.ini
|
||||||
|
|
||||||
|
# Documentation (temporary/generated)
|
||||||
|
*_SUMMARY.md
|
||||||
|
SEARCH_FIX_SUMMARY.md
|
||||||
|
|
||||||
|
# Project specific
|
||||||
.workbuddy
|
.workbuddy
|
||||||
|
|||||||
@@ -356,6 +356,8 @@
|
|||||||
"no_contents": "No contents",
|
"no_contents": "No contents",
|
||||||
"type_container": "Container",
|
"type_container": "Container",
|
||||||
"type_item": "Item",
|
"type_item": "Item",
|
||||||
|
"item": "Item",
|
||||||
|
"view_detail": "View Detail",
|
||||||
"linked_customers": "Linked Customers",
|
"linked_customers": "Linked Customers",
|
||||||
"linked_customer_placeholder": "Search customer by name or phone...",
|
"linked_customer_placeholder": "Search customer by name or phone...",
|
||||||
"linked_customer_not_found": "No matching customers found",
|
"linked_customer_not_found": "No matching customers found",
|
||||||
|
|||||||
@@ -356,6 +356,8 @@
|
|||||||
"no_contents": "暂无内容",
|
"no_contents": "暂无内容",
|
||||||
"type_container": "容器",
|
"type_container": "容器",
|
||||||
"type_item": "物品",
|
"type_item": "物品",
|
||||||
|
"item": "物品",
|
||||||
|
"view_detail": "查看详情",
|
||||||
"linked_customers": "关联客户",
|
"linked_customers": "关联客户",
|
||||||
"linked_customer_placeholder": "搜索客户姓名或电话...",
|
"linked_customer_placeholder": "搜索客户姓名或电话...",
|
||||||
"linked_customer_not_found": "未找到匹配的客户",
|
"linked_customer_not_found": "未找到匹配的客户",
|
||||||
|
|||||||
@@ -68,16 +68,53 @@ const pageRange = computed(() => {
|
|||||||
async function fetchContainers() {
|
async function fetchContainers() {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
const { errCode, data } = await warehouseApi.getContainers({
|
// 如果有搜索词,同时搜索容器和物品
|
||||||
search: search.value,
|
if (search.value.trim()) {
|
||||||
entries: pageSize.value,
|
const [containersRes, itemsRes] = await Promise.all([
|
||||||
page: currentPage.value,
|
warehouseApi.getContainers({
|
||||||
})
|
search: search.value,
|
||||||
if (errCode === 0) {
|
entries: pageSize.value,
|
||||||
containers.value = data.containers ?? []
|
page: currentPage.value,
|
||||||
totalCount.value = data.all_count ?? 0
|
}),
|
||||||
|
warehouseApi.getItems({
|
||||||
|
search: search.value,
|
||||||
|
entries: pageSize.value,
|
||||||
|
page: currentPage.value,
|
||||||
|
})
|
||||||
|
])
|
||||||
|
|
||||||
|
if (containersRes.errCode === 0 && itemsRes.errCode === 0) {
|
||||||
|
// 合并容器和物品结果,添加类型标识
|
||||||
|
const containerList = (containersRes.data.containers ?? []).map(c => ({
|
||||||
|
...c,
|
||||||
|
_type: 'container'
|
||||||
|
}))
|
||||||
|
const itemList = (itemsRes.data.items ?? []).map(i => ({
|
||||||
|
...i,
|
||||||
|
_type: 'item'
|
||||||
|
}))
|
||||||
|
|
||||||
|
containers.value = [...containerList, ...itemList]
|
||||||
|
totalCount.value = (containersRes.data.all_count ?? 0) + (itemsRes.data.all_count ?? 0)
|
||||||
|
} else {
|
||||||
|
toast.error(t('message.server_error'))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
toast.error(t('message.server_error'))
|
// 没有搜索词时,只显示容器
|
||||||
|
const { errCode, data } = await warehouseApi.getContainers({
|
||||||
|
search: search.value,
|
||||||
|
entries: pageSize.value,
|
||||||
|
page: currentPage.value,
|
||||||
|
})
|
||||||
|
if (errCode === 0) {
|
||||||
|
containers.value = (data.containers ?? []).map(c => ({
|
||||||
|
...c,
|
||||||
|
_type: 'container'
|
||||||
|
}))
|
||||||
|
totalCount.value = data.all_count ?? 0
|
||||||
|
} else {
|
||||||
|
toast.error(t('message.server_error'))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
// 拦截器已处理
|
// 拦截器已处理
|
||||||
@@ -121,9 +158,13 @@ function handleSearch() {
|
|||||||
fetchContainers()
|
fetchContainers()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── 跳转到子容器 ──
|
// ── 跳转到容器或物品详情 ──
|
||||||
function jumpToContainer(id) {
|
function jumpToDetail(item) {
|
||||||
router.push(`/warehouse/container/${id}`)
|
if (item._type === 'item') {
|
||||||
|
router.push(`/warehouse/item/${item.ID}`)
|
||||||
|
} else {
|
||||||
|
router.push(`/warehouse/container/${item.ID}`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── 打开新增 ──
|
// ── 打开新增 ──
|
||||||
@@ -323,41 +364,49 @@ onMounted(() => {
|
|||||||
<tr
|
<tr
|
||||||
v-else
|
v-else
|
||||||
v-for="c in containers"
|
v-for="c in containers"
|
||||||
:key="c.ID"
|
:key="`${c._type}-${c.ID}`"
|
||||||
class="border-b border-gray-100 cursor-pointer transition-colors hover:bg-gray-50 dark:border-dk-muted dark:hover:bg-dk-base"
|
class="border-b border-gray-100 cursor-pointer transition-colors hover:bg-gray-50 dark:border-dk-muted dark:hover:bg-dk-base"
|
||||||
@click="jumpToContainer(c.ID)"
|
@click="jumpToDetail(c)"
|
||||||
>
|
>
|
||||||
<td class="px-6 py-3 text-gray-500 dark:text-gray-400">{{ c.ID }}</td>
|
<td class="px-6 py-3 text-gray-500 dark:text-gray-400">{{ c.ID }}</td>
|
||||||
<td class="px-6 py-3">
|
<td class="px-6 py-3">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<IconFolder class="text-blue-500 flex-shrink-0" :size="18" />
|
<IconFolder v-if="c._type === 'container'" class="text-blue-500 flex-shrink-0" :size="18" />
|
||||||
<span class="font-medium text-gray-900 dark:text-white max-w-[10rem] truncate">{{ c.Title }}</span>
|
<IconPackage v-else class="text-green-500 flex-shrink-0" :size="18" />
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<span class="font-medium text-gray-900 dark:text-white max-w-[10rem] truncate">{{ c.Title }}</span>
|
||||||
|
<span v-if="c._type === 'item'" class="text-xs text-gray-500 dark:text-gray-400">{{ t('warehouse.item') }}</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="px-6 py-3 text-gray-500 dark:text-gray-400 max-w-xs truncate">{{ c.Remark || '—' }}</td>
|
<td class="px-6 py-3 text-gray-500 dark:text-gray-400 max-w-xs truncate">{{ c.Remark || '—' }}</td>
|
||||||
<td class="px-6 py-3 text-center">
|
<td class="px-6 py-3 text-center">
|
||||||
<span
|
<span v-if="c._type === 'container'"
|
||||||
class="inline-flex items-center gap-1 rounded-full bg-purple-100 px-2 py-0.5 text-xs font-medium text-purple-700 dark:bg-purple-900/40 dark:text-purple-400"
|
class="inline-flex items-center gap-1 rounded-full bg-purple-100 px-2 py-0.5 text-xs font-medium text-purple-700 dark:bg-purple-900/40 dark:text-purple-400"
|
||||||
>
|
>
|
||||||
<IconFolders :size="12" />
|
<IconFolders :size="12" />
|
||||||
{{ c.ChildCount }}
|
{{ c.ChildCount }}
|
||||||
</span>
|
</span>
|
||||||
|
<span v-else class="text-gray-400">—</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="px-6 py-3 text-center">
|
<td class="px-6 py-3 text-center">
|
||||||
<span
|
<span v-if="c._type === 'container'"
|
||||||
class="inline-flex items-center gap-1 rounded-full bg-green-100 px-2 py-0.5 text-xs font-medium text-green-700 dark:bg-green-900/40 dark:text-green-400"
|
class="inline-flex items-center gap-1 rounded-full bg-green-100 px-2 py-0.5 text-xs font-medium text-green-700 dark:bg-green-900/40 dark:text-green-400"
|
||||||
>
|
>
|
||||||
<IconPackage :size="12" />
|
<IconPackage :size="12" />
|
||||||
{{ c.ItemCount }}
|
{{ c.ItemCount }}
|
||||||
</span>
|
</span>
|
||||||
|
<span v-else-if="c.Quantity !== undefined" class="text-gray-500 dark:text-gray-400">x{{ c.Quantity }}</span>
|
||||||
|
<span v-else class="text-gray-400">—</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="px-6 py-3 text-center">
|
<td class="px-6 py-3 text-center">
|
||||||
<span
|
<span v-if="c.WorkOrderCount !== undefined"
|
||||||
class="inline-flex items-center gap-1 rounded-full bg-orange-100 px-2 py-0.5 text-xs font-medium text-orange-700 dark:bg-orange-900/40 dark:text-orange-400"
|
class="inline-flex items-center gap-1 rounded-full bg-orange-100 px-2 py-0.5 text-xs font-medium text-orange-700 dark:bg-orange-900/40 dark:text-orange-400"
|
||||||
>
|
>
|
||||||
<IconTool :size="12" />
|
<IconTool :size="12" />
|
||||||
{{ c.WorkOrderCount }}
|
{{ c.WorkOrderCount }}
|
||||||
</span>
|
</span>
|
||||||
|
<span v-else class="text-gray-400">—</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="px-6 py-3">
|
<td class="px-6 py-3">
|
||||||
<div v-if="c.Customers && c.Customers.length > 0" class="flex flex-wrap gap-1">
|
<div v-if="c.Customers && c.Customers.length > 0" class="flex flex-wrap gap-1">
|
||||||
@@ -376,7 +425,7 @@ onMounted(() => {
|
|||||||
<span v-else class="text-gray-400">—</span>
|
<span v-else class="text-gray-400">—</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="px-6 py-3 text-right" @click.stop>
|
<td class="px-6 py-3 text-right" @click.stop>
|
||||||
<div class="flex items-center justify-end gap-1">
|
<div v-if="c._type === 'container'" class="flex items-center justify-end gap-1">
|
||||||
<button
|
<button
|
||||||
v-if="c.ChildCount === 0 && c.ItemCount === 0"
|
v-if="c.ChildCount === 0 && c.ItemCount === 0"
|
||||||
class="rounded p-1.5 text-gray-400 hover:bg-gray-100 hover:text-red-500 dark:hover:bg-dk-muted"
|
class="rounded p-1.5 text-gray-400 hover:bg-gray-100 hover:text-red-500 dark:hover:bg-dk-muted"
|
||||||
@@ -395,7 +444,16 @@ onMounted(() => {
|
|||||||
<button
|
<button
|
||||||
class="rounded p-1.5 text-gray-400 hover:bg-gray-100 hover:text-blue-500 dark:hover:bg-dk-muted"
|
class="rounded p-1.5 text-gray-400 hover:bg-gray-100 hover:text-blue-500 dark:hover:bg-dk-muted"
|
||||||
:title="t('warehouse.view_items')"
|
:title="t('warehouse.view_items')"
|
||||||
@click="jumpToContainer(c.ID)"
|
@click="jumpToDetail(c)"
|
||||||
|
>
|
||||||
|
<IconChevronRight :size="15" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div v-else class="flex items-center justify-end gap-1">
|
||||||
|
<button
|
||||||
|
class="rounded p-1.5 text-gray-400 hover:bg-gray-100 hover:text-blue-500 dark:hover:bg-dk-muted"
|
||||||
|
:title="t('warehouse.view_detail')"
|
||||||
|
@click="jumpToDetail(c)"
|
||||||
>
|
>
|
||||||
<IconChevronRight :size="15" />
|
<IconChevronRight :size="15" />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
Reference in New Issue
Block a user