This commit is contained in:
2026-06-24 13:09:29 +08:00
parent 9d3c489302
commit 5406b14b5c
5 changed files with 133 additions and 38 deletions
BIN
View File
Binary file not shown.
+50 -17
View File
@@ -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
+2
View File
@@ -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",
+2
View File
@@ -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>