add ConsumptionRecordList.vue
This commit is contained in:
@@ -0,0 +1,11 @@
|
|||||||
|
import { axios } from "timi-web-api";
|
||||||
|
import type { ConsumptionRecordPage, ConsumptionRecordPageResult } from "@/types/ConsumptionRecord";
|
||||||
|
import { withCurrentStoreId } from "@/utils/store";
|
||||||
|
|
||||||
|
const BASE_URI = "/gao/customer/consumption-record";
|
||||||
|
|
||||||
|
async function list(page: ConsumptionRecordPage): Promise<ConsumptionRecordPageResult> {
|
||||||
|
return await axios.post(`${BASE_URI}/list`, withCurrentStoreId(page));
|
||||||
|
}
|
||||||
|
|
||||||
|
export default { list };
|
||||||
@@ -112,7 +112,7 @@ import type { PhotoItem } from "@/types/Photo";
|
|||||||
import type { PointLedgerChangeType } from "@/types/Point";
|
import type { PointLedgerChangeType } from "@/types/Point";
|
||||||
import { getAttachmentReadURL } from "@/utils/attachment";
|
import { getAttachmentReadURL } from "@/utils/attachment";
|
||||||
import { buildEventOptionSelectionMap } from "@/utils/eventOption";
|
import { buildEventOptionSelectionMap } from "@/utils/eventOption";
|
||||||
import { getEventBizTypeText, isPackagePurchaseEvent } from "@/utils/event";
|
import { getEventBizTypeText, isRetiredEvent } from "@/utils/event";
|
||||||
import { getErrorMessage } from "@/utils/error";
|
import { getErrorMessage } from "@/utils/error";
|
||||||
import { formatEventNumberValue } from "@/utils/eventValue";
|
import { formatEventNumberValue } from "@/utils/eventValue";
|
||||||
import { Message } from "@/utils/message";
|
import { Message } from "@/utils/message";
|
||||||
@@ -135,10 +135,10 @@ const isVerifying = ref(false);
|
|||||||
const isDeleting = ref(false);
|
const isDeleting = ref(false);
|
||||||
const verifyDialogVisible = ref(false);
|
const verifyDialogVisible = ref(false);
|
||||||
|
|
||||||
const isPackagePurchaseRecord = computed(() => isPackagePurchaseEvent(record.value?.event));
|
const isRetiredRecord = computed(() => isRetiredEvent(record.value?.event));
|
||||||
const canUpdateRecord = computed(() => !!record.value && !isPackagePurchaseRecord.value && userStore.hasPermissions(permissionCodes.eventRecord.update));
|
const canUpdateRecord = computed(() => !!record.value && !isRetiredRecord.value && userStore.hasPermissions(permissionCodes.eventRecord.update));
|
||||||
const canVerifyRecord = computed(() => !!record.value && !isPackagePurchaseRecord.value && userStore.hasPermissions(permissionCodes.eventRecord.verify));
|
const canVerifyRecord = computed(() => !!record.value && !isRetiredRecord.value && userStore.hasPermissions(permissionCodes.eventRecord.verify));
|
||||||
const canDeleteRecord = computed(() => !!record.value && !isPackagePurchaseRecord.value && userStore.hasPermissions(permissionCodes.eventRecord.delete));
|
const canDeleteRecord = computed(() => !!record.value && !isRetiredRecord.value && userStore.hasPermissions(permissionCodes.eventRecord.delete));
|
||||||
const canReadEvent = computed(() => userStore.hasPermissions(permissionCodes.event.read));
|
const canReadEvent = computed(() => userStore.hasPermissions(permissionCodes.event.read));
|
||||||
const recordPhotoItemList = computed(() => (record.value?.attachmentList || [])
|
const recordPhotoItemList = computed(() => (record.value?.attachmentList || [])
|
||||||
.filter(item => !!item.id)
|
.filter(item => !!item.id)
|
||||||
@@ -152,7 +152,7 @@ const recordPhotoList = computed(() => recordPhotoItemList.value.map(item => ite
|
|||||||
|
|
||||||
async function submitPhotoItems(photoItems: PhotoItem[]): Promise<void> {
|
async function submitPhotoItems(photoItems: PhotoItem[]): Promise<void> {
|
||||||
const currentRecord = record.value;
|
const currentRecord = record.value;
|
||||||
if (!currentRecord?.id || isPackagePurchaseRecord.value) {
|
if (!currentRecord?.id || isRetiredRecord.value) {
|
||||||
throw new Error("登记记录不存在");
|
throw new Error("登记记录不存在");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -263,7 +263,7 @@ async function confirmVerifyRecord(): Promise<void> {
|
|||||||
|
|
||||||
async function submitVerifyRecord(record: EventRecord, attachmentList: Attachment[]): Promise<void> {
|
async function submitVerifyRecord(record: EventRecord, attachmentList: Attachment[]): Promise<void> {
|
||||||
const id = record.id;
|
const id = record.id;
|
||||||
if (!id || isPackagePurchaseRecord.value) {
|
if (!id || isRetiredRecord.value) {
|
||||||
throw new Error("登记事件记录不存在");
|
throw new Error("登记事件记录不存在");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -316,7 +316,7 @@ function confirmRemoveRecord(): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function removeRecord(id: string): Promise<void> {
|
async function removeRecord(id: string): Promise<void> {
|
||||||
if (isDeleting.value || isPackagePurchaseRecord.value) {
|
if (isDeleting.value || isRetiredRecord.value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,323 @@
|
|||||||
|
<template>
|
||||||
|
<div v-can="permissionCodes.salesOrder.read" class="consumption-record-list">
|
||||||
|
<div class="filters">
|
||||||
|
<t-input
|
||||||
|
:value="customerDisplayText"
|
||||||
|
class="customer-input"
|
||||||
|
readonly
|
||||||
|
borderless
|
||||||
|
placeholder="选择或扫码客户"
|
||||||
|
@click="customerSelectorVisible = true"
|
||||||
|
>
|
||||||
|
<template #suffix-icon>
|
||||||
|
<t-button size="small" variant="text" :content="scannerVisible ? '关闭扫码' : '扫码'" @click.stop="toggleScanner" />
|
||||||
|
</template>
|
||||||
|
</t-input>
|
||||||
|
<t-dropdown-menu>
|
||||||
|
<t-dropdown-item v-model="sourceType" label="消费来源" :options="sourceTypeOptions" @change="reloadRecords" />
|
||||||
|
<t-dropdown-item v-model="status" label="消费状态" :options="statusOptions" @change="reloadRecords" />
|
||||||
|
</t-dropdown-menu>
|
||||||
|
<code-scanner
|
||||||
|
v-if="scannerVisible"
|
||||||
|
visible
|
||||||
|
:status-text="scanStatus"
|
||||||
|
active-text="正在识别客户编码,请将条码或二维码移入取景框。"
|
||||||
|
@decode="handleDecodedText"
|
||||||
|
@status-change="handleScanStatusChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<t-pull-down-refresh
|
||||||
|
v-model="isRefreshing"
|
||||||
|
:refresh-timeout="30000"
|
||||||
|
@refresh="reloadRecords"
|
||||||
|
@scrolltolower="loadRecords"
|
||||||
|
>
|
||||||
|
<div class="list-wrap">
|
||||||
|
<t-cell-group class="summary" theme="card">
|
||||||
|
<t-cell title="记录数" :note="`共 ${total} 条`" />
|
||||||
|
<t-cell title="消费金额合计" :note="`¥${formatPackageAmount(amountTotal)}`" />
|
||||||
|
</t-cell-group>
|
||||||
|
<t-cell-group v-if="recordList.length" theme="card">
|
||||||
|
<t-cell
|
||||||
|
v-for="(item, index) in recordList"
|
||||||
|
:key="item.id"
|
||||||
|
v-entrance-transition="index * 30"
|
||||||
|
:title="getRecordTitle(item)"
|
||||||
|
:note="getRecordNote(item)"
|
||||||
|
:arrow="canOpenSource(item)"
|
||||||
|
@click="openSource(item)"
|
||||||
|
>
|
||||||
|
<template #description>
|
||||||
|
<div class="description">
|
||||||
|
<span v-text="getCustomerText(item)" />
|
||||||
|
<span v-text="getEffectiveAtText(item)" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #rightIcon>
|
||||||
|
<div class="right">
|
||||||
|
<t-tag :content="getSourceText(item.sourceType)" size="small" theme="primary" variant="light-outline" />
|
||||||
|
<t-tag :content="getStatusText(item.status)" size="small" :theme="item.status === 'ACTIVE' ? 'success' : 'danger'" variant="light-outline" />
|
||||||
|
<strong v-text="`¥${formatPackageAmount(item.amount)}`" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</t-cell>
|
||||||
|
</t-cell-group>
|
||||||
|
<div v-else class="center-tips" v-text="isLoading ? '正在加载消费记录' : '暂无消费记录'" />
|
||||||
|
<div v-if="recordList.length" class="center-tips" v-text="loadStateText" />
|
||||||
|
</div>
|
||||||
|
</t-pull-down-refresh>
|
||||||
|
<customer-selector
|
||||||
|
v-model="customerId"
|
||||||
|
v-model:visible="customerSelectorVisible"
|
||||||
|
@change="handleCustomerChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { DropdownValue } from "tdesign-mobile-vue";
|
||||||
|
import { Time } from "timi-web";
|
||||||
|
import { useUserStore } from "timi-web-api";
|
||||||
|
import ConsumptionRecordAPI from "@/api/ConsumptionRecordAPI";
|
||||||
|
import CustomerAPI from "@/api/CustomerAPI";
|
||||||
|
import CodeScanner from "@/components/CodeScanner.vue";
|
||||||
|
import CustomerSelector from "@/components/CustomerSelector.vue";
|
||||||
|
import { useNavBarStore } from "@/store/navBarStore";
|
||||||
|
import type { Customer } from "@/types/Customer";
|
||||||
|
import type { ConsumptionRecord, ConsumptionRecordPage, ConsumptionRecordSourceType, ConsumptionRecordStatus } from "@/types/ConsumptionRecord";
|
||||||
|
import { getErrorMessage } from "@/utils/error";
|
||||||
|
import { Message } from "@/utils/message";
|
||||||
|
import { formatPackageAmount } from "@/utils/package";
|
||||||
|
import { permissionCodes } from "@/utils/permission";
|
||||||
|
|
||||||
|
defineOptions({ name: "ConsumptionRecordList" });
|
||||||
|
|
||||||
|
const pageSize = 20;
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
const navBarStore = useNavBarStore();
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const sourceType = ref<ConsumptionRecordSourceType | "ALL">("ALL");
|
||||||
|
const status = ref<ConsumptionRecordStatus | "ALL">("ALL");
|
||||||
|
const customerId = ref("");
|
||||||
|
const selectedCustomer = ref<Customer>();
|
||||||
|
const customerSelectorVisible = ref(false);
|
||||||
|
const scannerVisible = ref(false);
|
||||||
|
const scanStatus = ref("点击扫码开始识别。");
|
||||||
|
let latestDecodedText = "";
|
||||||
|
const recordList = ref<ConsumptionRecord[]>([]);
|
||||||
|
const page = ref(0);
|
||||||
|
const total = ref(0);
|
||||||
|
const amountTotal = ref(0);
|
||||||
|
const isLoading = ref(false);
|
||||||
|
const isRefreshing = ref(false);
|
||||||
|
const isNoMore = ref(false);
|
||||||
|
const sourceTypeOptions = [{ label: "全部来源", value: "ALL" }, { label: "销售订单", value: "SALES_ORDER" }, { label: "历史消费", value: "LEGACY_EVENT" }];
|
||||||
|
const statusOptions = [{ label: "全部状态", value: "ALL" }, { label: "有效", value: "ACTIVE" }, { label: "已失效", value: "REVOKED" }];
|
||||||
|
const canReadEventRecord = computed(() => userStore.hasPermissions(permissionCodes.eventRecord.read));
|
||||||
|
const loadStateText = computed(() => isLoading.value ? "正在加载更多" : isNoMore.value ? "没有更多记录" : "上拉加载更多");
|
||||||
|
const customerDisplayText = computed(() => selectedCustomer.value?.name || selectedCustomer.value?.code || "");
|
||||||
|
|
||||||
|
function applyRouteQuery(): void {
|
||||||
|
const getText = (value: unknown): string => Array.isArray(value) ? String(value[0] || "") : String(value || "");
|
||||||
|
const routeSourceType = getText(route.query.sourceType);
|
||||||
|
const routeStatus = getText(route.query.status);
|
||||||
|
customerId.value = getText(route.query.customerId);
|
||||||
|
sourceType.value = ["ALL", "LEGACY_EVENT", "SALES_ORDER"].includes(routeSourceType) ? routeSourceType as ConsumptionRecordSourceType | "ALL" : "ALL";
|
||||||
|
status.value = ["ALL", "ACTIVE", "REVOKED"].includes(routeStatus) ? routeStatus as ConsumptionRecordStatus | "ALL" : "ALL";
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleCustomerChange(value: string): Promise<void> {
|
||||||
|
customerId.value = value;
|
||||||
|
selectedCustomer.value = value ? await CustomerAPI.detail(value) : undefined;
|
||||||
|
await reloadRecords();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadSelectedCustomer(): Promise<void> {
|
||||||
|
selectedCustomer.value = customerId.value ? await CustomerAPI.detail(customerId.value) : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function toggleScanner(): Promise<void> {
|
||||||
|
scannerVisible.value = !scannerVisible.value;
|
||||||
|
if (scannerVisible.value) {
|
||||||
|
latestDecodedText = "";
|
||||||
|
scanStatus.value = "正在打开相机。";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleDecodedText(rawText: string): Promise<void> {
|
||||||
|
const code = rawText.trim();
|
||||||
|
if (!code || code === latestDecodedText) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
latestDecodedText = code;
|
||||||
|
try {
|
||||||
|
const customer = await CustomerAPI.findByCode(code);
|
||||||
|
if (!customer?.id) {
|
||||||
|
scanStatus.value = `客户 ${code} 不存在。`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
customerId.value = customer.id;
|
||||||
|
selectedCustomer.value = customer;
|
||||||
|
scanStatus.value = `已识别 ${customer.name || customer.code || code}。`;
|
||||||
|
scannerVisible.value = false;
|
||||||
|
await reloadRecords();
|
||||||
|
} catch (error) {
|
||||||
|
scanStatus.value = getErrorMessage(error, "客户查询失败,请重试。");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleScanStatusChange(value: string): void {
|
||||||
|
scanStatus.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildPage(): ConsumptionRecordPage {
|
||||||
|
return {
|
||||||
|
customerId: customerId.value || undefined,
|
||||||
|
sourceType: sourceType.value === "ALL" ? undefined : sourceType.value,
|
||||||
|
status: status.value === "ALL" ? undefined : status.value,
|
||||||
|
index: page.value,
|
||||||
|
size: pageSize
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function reloadRecords(value?: DropdownValue): Promise<void> {
|
||||||
|
if (value && Array.isArray(value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
isRefreshing.value = true;
|
||||||
|
page.value = 0;
|
||||||
|
total.value = 0;
|
||||||
|
amountTotal.value = 0;
|
||||||
|
recordList.value = [];
|
||||||
|
isNoMore.value = false;
|
||||||
|
try {
|
||||||
|
await loadRecords();
|
||||||
|
} catch (error) {
|
||||||
|
Message.error(getErrorMessage(error, "消费记录加载失败"));
|
||||||
|
} finally {
|
||||||
|
isRefreshing.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadRecords(): Promise<void> {
|
||||||
|
if (isLoading.value || isNoMore.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
isLoading.value = true;
|
||||||
|
try {
|
||||||
|
const result = await ConsumptionRecordAPI.list(buildPage());
|
||||||
|
const list = result.list || [];
|
||||||
|
recordList.value = recordList.value.concat(list);
|
||||||
|
total.value = result.total || 0;
|
||||||
|
amountTotal.value = result.amountTotal || 0;
|
||||||
|
page.value += 1;
|
||||||
|
isNoMore.value = list.length < pageSize || total.value <= recordList.value.length;
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRecordTitle(record: ConsumptionRecord): string {
|
||||||
|
if (record.sourceType === "LEGACY_EVENT") {
|
||||||
|
return record.sourcePayload?.eventName || "历史消费事件";
|
||||||
|
}
|
||||||
|
return record.orderType === "PACKAGE" ? "套餐订单消费" : "产品订单消费";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRecordNote(record: ConsumptionRecord): string {
|
||||||
|
if (record.status === "REVOKED") {
|
||||||
|
return record.revokeReason || "消费事实已失效";
|
||||||
|
}
|
||||||
|
return record.sourceType === "LEGACY_EVENT" ? "查看原始登记记录" : "查看销售订单";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCustomerText(record: ConsumptionRecord): string {
|
||||||
|
return record.customer?.name || record.customer?.code || record.customerId || "未知客户";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getEffectiveAtText(record: ConsumptionRecord): string {
|
||||||
|
return record.effectiveAt ? Time.toDateTime(record.effectiveAt) : "未知生效时间";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSourceText(value?: ConsumptionRecordSourceType): string {
|
||||||
|
return value === "LEGACY_EVENT" ? "历史消费" : "销售订单";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStatusText(value?: ConsumptionRecordStatus): string {
|
||||||
|
return value === "REVOKED" ? "已失效" : "有效";
|
||||||
|
}
|
||||||
|
|
||||||
|
function canOpenSource(record: ConsumptionRecord): boolean {
|
||||||
|
return record.sourceType === "SALES_ORDER" ? !!record.salesOrderId : canReadEventRecord.value && !!record.sourceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function openSource(record: ConsumptionRecord): Promise<void> {
|
||||||
|
if (!canOpenSource(record)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (record.sourceType === "SALES_ORDER" && record.salesOrderId) {
|
||||||
|
await router.push({ name: "SalesOrderDetail", params: { id: record.salesOrderId } });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (record.sourceType === "LEGACY_EVENT" && record.sourceId) {
|
||||||
|
await router.push({ name: "EventRecordDetail", params: { id: record.sourceId } });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
navBarStore.clearRight();
|
||||||
|
applyRouteQuery();
|
||||||
|
await loadSelectedCustomer();
|
||||||
|
await reloadRecords();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.consumption-record-list {
|
||||||
|
min-height: 100vh;
|
||||||
|
padding: var(--app-nav-height) 0 1rem;
|
||||||
|
|
||||||
|
.filters {
|
||||||
|
top: var(--app-nav-height);
|
||||||
|
z-index: 1;
|
||||||
|
position: sticky;
|
||||||
|
background: var(--td-bg-color-page);
|
||||||
|
box-shadow: var(--td-shadow-1);
|
||||||
|
|
||||||
|
.customer-input {
|
||||||
|
--td-input-vertical-padding: .5rem 1rem;
|
||||||
|
|
||||||
|
margin: .5rem 1rem;
|
||||||
|
background: var(--td-bg-color-container);
|
||||||
|
border: 1px solid var(--td-component-border);
|
||||||
|
border-radius: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-wrap {
|
||||||
|
padding: 1rem 0;
|
||||||
|
|
||||||
|
.description {
|
||||||
|
gap: .5rem;
|
||||||
|
display: flex;
|
||||||
|
color: var(--td-text-color-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right {
|
||||||
|
gap: .35rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
strong {
|
||||||
|
color: var(--td-error-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -99,6 +99,12 @@ const menuGroupList = computed<MenuGroup[]>(() => [
|
|||||||
path: "/management/order-fulfillments",
|
path: "/management/order-fulfillments",
|
||||||
title: "统一履约",
|
title: "统一履约",
|
||||||
visible: userStore.hasPermissions(permissionCodes.orderFulfillment.read)
|
visible: userStore.hasPermissions(permissionCodes.orderFulfillment.read)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: "money",
|
||||||
|
path: "/management/consumption-records",
|
||||||
|
title: "消费记录",
|
||||||
|
visible: userStore.hasPermissions(permissionCodes.salesOrder.read)
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import SalesOrderCancel from "@/pages/salesOrder/SalesOrderCancel.vue";
|
|||||||
import FundRecordList from "@/pages/salesOrder/FundRecordList.vue";
|
import FundRecordList from "@/pages/salesOrder/FundRecordList.vue";
|
||||||
import FundRecordDetail from "@/pages/salesOrder/FundRecordDetail.vue";
|
import FundRecordDetail from "@/pages/salesOrder/FundRecordDetail.vue";
|
||||||
import FundRecordRevoke from "@/pages/salesOrder/FundRecordRevoke.vue";
|
import FundRecordRevoke from "@/pages/salesOrder/FundRecordRevoke.vue";
|
||||||
|
import ConsumptionRecordList from "@/pages/salesOrder/ConsumptionRecordList.vue";
|
||||||
|
|
||||||
function meta(title: string, depth = 3, background = "#F5F5F8"): RouteRecordRaw["meta"] {
|
function meta(title: string, depth = 3, background = "#F5F5F8"): RouteRecordRaw["meta"] {
|
||||||
return { depth, navBarVisible: true, navBarCanBack: true, navBarTitle: title, tabBarVisible: false, bodyBackground: background, darkBodyBackground: "#10161f" };
|
return { depth, navBarVisible: true, navBarCanBack: true, navBarTitle: title, tabBarVisible: false, bodyBackground: background, darkBodyBackground: "#10161f" };
|
||||||
@@ -16,6 +17,7 @@ function meta(title: string, depth = 3, background = "#F5F5F8"): RouteRecordRaw[
|
|||||||
|
|
||||||
export default [
|
export default [
|
||||||
{ path: "/management/sales-orders", name: "SalesOrderList", component: SalesOrderList, meta: meta("销售订单") },
|
{ path: "/management/sales-orders", name: "SalesOrderList", component: SalesOrderList, meta: meta("销售订单") },
|
||||||
|
{ path: "/management/consumption-records", name: "ConsumptionRecordList", component: ConsumptionRecordList, meta: meta("消费记录") },
|
||||||
{
|
{
|
||||||
path: "/management/sales-orders/create",
|
path: "/management/sales-orders/create",
|
||||||
name: "SalesOrderCreate",
|
name: "SalesOrderCreate",
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import type { Model, PageResult } from "timi-web-api";
|
||||||
|
import type { Customer } from "@/types/Customer";
|
||||||
|
import type { GaoSalesOrderType } from "@/types/SalesOrder";
|
||||||
|
|
||||||
|
export type ConsumptionRecordSourceType = "LEGACY_EVENT" | "SALES_ORDER";
|
||||||
|
export type ConsumptionRecordStatus = "ACTIVE" | "REVOKED";
|
||||||
|
|
||||||
|
export type ConsumptionRecord = {
|
||||||
|
storeId?: string;
|
||||||
|
customerId?: string;
|
||||||
|
sourceType?: ConsumptionRecordSourceType;
|
||||||
|
sourceId?: string;
|
||||||
|
salesOrderId?: string;
|
||||||
|
orderType?: GaoSalesOrderType;
|
||||||
|
amount?: number;
|
||||||
|
effectiveAt?: number;
|
||||||
|
status?: ConsumptionRecordStatus;
|
||||||
|
revokedAt?: number;
|
||||||
|
revokeReason?: string | null;
|
||||||
|
sourcePayload?: {
|
||||||
|
eventId?: string;
|
||||||
|
eventName?: string;
|
||||||
|
eventRecordId?: string;
|
||||||
|
migrationBatch?: string;
|
||||||
|
registeredAt?: number;
|
||||||
|
};
|
||||||
|
customer?: Customer;
|
||||||
|
} & Model;
|
||||||
|
|
||||||
|
export type ConsumptionRecordPage = {
|
||||||
|
storeId?: string;
|
||||||
|
customerId?: string;
|
||||||
|
sourceType?: ConsumptionRecordSourceType;
|
||||||
|
status?: ConsumptionRecordStatus;
|
||||||
|
beginAt?: number;
|
||||||
|
endAt?: number;
|
||||||
|
index: number;
|
||||||
|
size: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ConsumptionRecordPageResult = PageResult<ConsumptionRecord> & {
|
||||||
|
amountTotal?: number;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user