This commit is contained in:
2026-04-14 15:54:15 +08:00
parent 0657117ec2
commit 3a38c34ea0
8 changed files with 288 additions and 22 deletions
@@ -5,7 +5,7 @@ import { useI18n } from 'vue-i18n'
import { useToastStore } from '@/stores/toast'
import { usePageTitle } from '@/composables/usePageTitle'
import { purchaseApi } from '@/api/purchase'
import { IconPlus, IconChevronLeftPipe, IconChevronRightPipe, IconChevronsLeft, IconChevronsRight, IconSearch } from '@tabler/icons-vue'
import { IconPlus, IconChevronLeftPipe, IconChevronRightPipe, IconChevronsLeft, IconChevronsRight } from '@tabler/icons-vue'
usePageTitle('appname.purchase')
const { t, locale } = useI18n()
@@ -16,9 +16,19 @@ const orders = ref([])
const totalCount = ref(0)
const pageSize = ref(10)
const currentPage = ref(1)
const searchQuery = ref('')
const statusFilter = ref('')
const loading = ref(false)
const statusOptions = [
{ value: '', labelKey: 'purchase.filter_all' },
{ value: 'pending', labelKey: 'purchase.status_pending' },
{ value: 'ordered', labelKey: 'purchase.status_ordered' },
{ value: 'arrived', labelKey: 'purchase.status_arrived' },
{ value: 'received', labelKey: 'purchase.status_received' },
{ value: 'lost', labelKey: 'purchase.status_lost' },
{ value: 'returned', labelKey: 'purchase.status_returned' },
]
const totalPages = computed(() => Math.ceil(totalCount.value / pageSize.value) || 1)
const pageRange = computed(() => {
@@ -34,7 +44,7 @@ async function fetchOrders() {
loading.value = true
try {
const { errCode, data } = await purchaseApi.getOrders({
search: searchQuery.value,
status: statusFilter.value,
entries: pageSize.value,
page: currentPage.value,
})
@@ -107,11 +117,16 @@ onMounted(fetchOrders)
<IconPlus :size="16" />
{{ t('purchase.add_part') }}
</RouterLink>
<button class="rounded-lg border border-gray-300 px-3 py-1.5 text-sm text-gray-600 transition-colors hover:bg-gray-50 dark:border-dk-muted dark:text-gray-400">{{ t('purchase.exp_report') }}</button>
</div>
<div class="flex items-center gap-2">
<label class="text-sm text-gray-500">{{ t('purchase.search') }}</label>
<input v-model="searchQuery" type="text" class="w-48 rounded-lg border border-gray-300 bg-white px-3 py-1.5 text-sm outline-none transition-colors focus:border-blue-500 focus:ring-2 focus:ring-blue-500/20 dark:border-dk-muted dark:bg-dk-base dark:text-white" @keydown.enter="currentPage=1;fetchOrders()" />
<!-- 状态下拉筛选 -->
<select
v-model="statusFilter"
class="rounded-lg border border-gray-300 bg-white px-3 py-1.5 text-sm dark:border-dk-muted dark:bg-dk-base dark:text-white"
@change="currentPage = 1; fetchOrders()"
>
<option v-for="opt in statusOptions" :key="opt.value" :value="opt.value">
{{ t(opt.labelKey) }}
</option>
</select>
</div>
</div>
@@ -17,6 +17,7 @@ import { useValidation } from "@/composables";
import { purchaseApi } from "@/api/purchase";
import tagadder from "@/components/tagadder.vue";
import useDropzone from "@/components/useDropzone.vue";
import ConfirmDialog from "@/components/ConfirmDialog.vue";
usePageTitle("purchase_addorder.edit_order");
@@ -96,6 +97,7 @@ watch(
// ==================== 图片上传 ====================
const dropzoneRef = ref(null);
const showDeleteConfirm = ref(false);
function getPhotoHashes() {
return dropzoneRef.value?.return_files().map((f) => f.hash) ?? [];
@@ -152,6 +154,30 @@ onMounted(async () => {
}
});
// ==================== 提交 ====================
// ==================== 删除订单 ====================
async function handleDelete() {
showDeleteConfirm.value = true;
}
async function doDelete() {
loading.value = true;
try {
const res = await purchaseApi.deleteOrder(orderId);
if (res.errCode === 0) {
toast.success(t("message.delete_ok"));
router.replace("/purchase");
} else {
toast.error(t("message.server_error"));
}
} catch {
toast.error(t("message.server_error"));
} finally {
loading.value = false;
}
}
// ==================== 提交 ====================
async function handleSubmit() {
clearErrors();
@@ -241,11 +267,24 @@ async function handleSubmit() {
<h4 class="text-sm font-semibold text-gray-900 dark:text-white">
{{ t("purchase_addorder.edit_order") }}
</h4>
<!-- 返回按钮 -->
<button
class="flex items-center gap-1.5 rounded-lg px-3 py-1.5 text-sm text-gray-500 hover:bg-gray-100 dark:text-gray-400 dark:hover:bg-dk-base"
@click="router.back()"
>
<!-- 操作按钮 -->
<div class="flex items-center gap-2">
<!-- 删除按钮 -->
<button
class="flex items-center gap-1.5 rounded-lg px-3 py-1.5 text-sm text-red-500 hover:bg-red-50 dark:hover:bg-red-900/20"
:disabled="loading"
@click="handleDelete"
>
<svg class="h-4 w-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
{{ t("purchase.delete_order") }}
</button>
<!-- 返回按钮 -->
<button
class="flex items-center gap-1.5 rounded-lg px-3 py-1.5 text-sm text-gray-500 hover:bg-gray-100 dark:text-gray-400 dark:hover:bg-dk-base"
@click="router.back()"
>
<svg
class="h-4 w-4"
fill="none"
@@ -259,8 +298,9 @@ async function handleSubmit() {
d="M15 19l-7-7 7-7"
/>
</svg>
{{ t("purchase.back_to_list") }}
{{ t("purchase.back") }}
</button>
</div>
</div>
<!-- 错误提示字段验证 -->
@@ -532,4 +572,12 @@ async function handleSubmit() {
</div>
</div>
</div>
<!-- 通用确认弹窗 -->
<ConfirmDialog
v-model="showDeleteConfirm"
:title="t('purchase.confirm_delete')"
danger
@confirm="doDelete"
/>
</template>