add product

This commit is contained in:
Timi
2026-09-03 14:27:40 +08:00
parent 7f669621fb
commit 333999fe21
63 changed files with 5662 additions and 2210 deletions
@@ -0,0 +1,86 @@
<template>
<div v-can="permissionCodes.fundReceive.create" class="sales-order-operation">
<t-cell-group v-if="order" title="销售订单" theme="card"><t-cell title="订单号" :note="order.orderNo || order.id" /><t-cell title="剩余待收款" :note="formatPackageAmount(order.unpaidAmount)" /></t-cell-group>
<t-form v-if="order" class="form" label-align="left" label-width="6rem">
<t-form-item label="收款金额(元)" required-mark><t-input v-model.trim="amountText" type="number" align="right" borderless placeholder="请输入本次收款金额" /></t-form-item>
<t-form-item label="收款时间" arrow @click="pickerVisible = true"><span class="picker-text" v-text="occurredAtText || '使用当前时间'" /></t-form-item>
<t-form-item label="备注"><t-textarea v-model.trim="remark" borderless autosize maxlength="500" /></t-form-item>
</t-form>
<package-photo-field v-if="order" v-model="photoItems" required title="收款凭证照片" />
<t-empty v-if="!order" :description="loading ? '正在加载订单' : '订单不存在'" />
<t-popup v-model="pickerVisible" placement="bottom">
<t-date-time-picker v-model="draftTime" format="YYYY-MM-DD HH:mm" :mode="['date', 'minute']" title="收款时间" @cancel="pickerVisible = false" @confirm="confirmTime" />
</t-popup>
</div>
</template>
<script setup lang="ts">
import { useUserStore, type Attachment } from "timi-web-api";
import PackagePhotoField from "@/components/PackagePhotoField.vue";
import SalesOrderAPI from "@/api/SalesOrderAPI";
import { useNavBarStore } from "@/store/navBarStore";
import type { SalesOrder } from "@/types/SalesOrder";
import type { PhotoItem } from "@/types/Photo";
import { createPackageRequestNo, formatPackageAmount, parsePackageAmount } from "@/utils/package";
import { getErrorMessage } from "@/utils/error";
import { Message } from "@/utils/message";
import { permissionCodes } from "@/utils/permission";
defineOptions({ name: "SalesOrderPayment" });
const route = useRoute();
const router = useRouter();
const navBarStore = useNavBarStore();
const userStore = useUserStore();
const order = ref<SalesOrder>();
const loading = ref(false);
const saving = ref(false);
const amountText = ref("");
const occurredAtText = ref("");
const draftTime = ref("");
const pickerVisible = ref(false);
const remark = ref("");
const photoItems = ref<PhotoItem[]>([]);
const requestNo = ref("");
function confirmTime(value: string | number): void { occurredAtText.value = String(value); pickerVisible.value = false; }
function resolveTimestamp(value: string): number | undefined { if (!value) return undefined; const timestamp = new Date(value.replace(" ", "T")).getTime(); return Number.isFinite(timestamp) ? timestamp : undefined; }
function buildAttachments(): Partial<Attachment>[] { return photoItems.value.flatMap(item => item.tempFileId ? [{ tempFileId: item.tempFileId, attachType: "PHOTO" }] : []); }
async function loadData(): Promise<void> {
const id = String(route.params.id || "");
if (!id) return;
loading.value = true;
try { order.value = await SalesOrderAPI.detail(id); }
catch (error) { Message.error(getErrorMessage(error, "订单加载失败")); }
finally { loading.value = false; }
}
async function save(): Promise<void> {
if (saving.value || !order.value?.id) return;
const amount = parsePackageAmount(amountText.value);
const attachmentList = buildAttachments();
if (amount === undefined || amount === 0) { Message.warning("请输入合法的正金额"); return; }
if (!attachmentList.length) { Message.warning("收款必须上传至少一张照片"); return; }
if (!requestNo.value) requestNo.value = createPackageRequestNo(); saving.value = true;
try {
await SalesOrderAPI.receivePayment({
salesOrderId: order.value.id,
requestNo: requestNo.value,
amount,
occurredAt: resolveTimestamp(occurredAtText.value),
attachmentList,
remark: remark.value.trim() || null
});
Message.success("收款登记已提交");
await router.replace(`/management/sales-orders/${order.value.id}`);
} catch (error) { Message.error(getErrorMessage(error, "收款登记失败,重试会复用同一请求号")); }
finally { saving.value = false; }
}
onMounted(async () => { if (!userStore.hasPermissions(permissionCodes.fundReceive.create)) return; navBarStore.setRightText("提交收款", "var(--td-brand-color)", save); await loadData(); });
onUnmounted(() => navBarStore.clearRight());
</script>
<style scoped lang="less">
.sales-order-operation { gap: 1rem; padding: calc(var(--app-nav-height) + 1rem) 0 2rem; display: flex; flex-direction: column;
.form { margin: 0 1rem; padding: 0 .5rem; background: var(--td-bg-color-container); box-shadow: var(--td-shadow-1); border-radius: 1rem; overflow: hidden; }
.picker-text { width: 100%; text-align: right; }
.package-photo-field { padding: 0 1rem; }
}
</style>