fix FundRecordList.vue style
This commit is contained in:
@@ -1,248 +0,0 @@
|
||||
<template>
|
||||
<div v-can="permissionCodes.fundRecord.read" class="package-record-detail">
|
||||
<template v-if="record">
|
||||
<section class="hero" v-entrance-transition>
|
||||
<span class="eyebrow" v-text="'资金流水'" />
|
||||
<div class="hero-title">
|
||||
<h1 v-text="getFundOperationText(record.operationType)" />
|
||||
<t-tag :content="getFundStatusText(record.status)" :theme="getStatusTheme(record.status)" variant="light-outline" />
|
||||
</div>
|
||||
<strong class="amount" v-text="formatPackageAmount(record.amount)" />
|
||||
</section>
|
||||
<t-cell-group title="资金记录" theme="card" v-entrance-transition>
|
||||
<t-cell title="操作类型" :note="getFundOperationText(record.operationType)" />
|
||||
<t-cell title="状态"><template #note><t-tag :content="getFundStatusText(record.status)" :theme="getStatusTheme(record.status)" variant="light-outline" /></template></t-cell>
|
||||
<t-cell title="金额" :note="formatPackageAmount(record.amount)" />
|
||||
<t-cell title="销售订单" :note="record.bizId || '未知'" />
|
||||
<t-cell title="发生时间" :note="record.occurredAt ? Time.toDateTime(record.occurredAt) : '未知'" />
|
||||
<t-cell title="提交人" :note="record.operator?.name || record.operator?.nick || record.operatorUserId || '未知'" />
|
||||
<t-cell title="处理人" :note="record.verifier?.name || record.verifier?.nick || record.verifierUserId || '未处理'" />
|
||||
<t-cell v-if="record.sourceRecordId" title="原资金记录" :note="record.sourceRecordId" />
|
||||
<t-cell v-if="record.reason" title="原因" :note="record.reason" />
|
||||
<t-cell v-if="record.remark" title="备注" :note="record.remark" />
|
||||
</t-cell-group>
|
||||
<photo-scroller :model-value="photoURLList" :photo-items="photoItems" title="业务照片" :show-add="false" :show-remove="false" :show-viewer-actions="false" />
|
||||
<section v-if="isPendingReceive || canRevoke" class="actions" v-entrance-transition="70">
|
||||
<t-button v-if="canVerifyReceive" theme="primary" @click="verifyReceive">核销收款</t-button>
|
||||
<t-button v-if="canVerifyReceive" theme="default" variant="outline" @click="openReasonDialog('reject')">拒绝收款</t-button>
|
||||
<t-button v-if="canCancelReceive" theme="default" variant="outline" @click="openReasonDialog('cancel')">撤销收款</t-button>
|
||||
<t-button v-if="canRevoke" theme="danger" variant="outline" @click="toRevoke">资金冲正</t-button>
|
||||
</section>
|
||||
</template>
|
||||
<t-empty v-else :description="isLoading ? '正在加载资金记录' : '资金记录不存在'" />
|
||||
<t-dialog v-model:visible="reasonDialogVisible" :title="reasonTitle" :confirm-btn="reasonActionText" @confirm="submitReason">
|
||||
<t-textarea v-model.trim="reasonText" borderless autosize placeholder="请输入原因" />
|
||||
</t-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { DialogPlugin } from "tdesign-mobile-vue";
|
||||
import { Time } from "timi-web";
|
||||
import { useUserStore } from "timi-web-api";
|
||||
import SalesOrderAPI from "@/api/SalesOrderAPI";
|
||||
import PhotoScroller from "@/components/PhotoScroller.vue";
|
||||
import { useNavBarStore } from "@/store/navBarStore";
|
||||
import type { FundRecord, FundRecordStatus } from "@/types/SalesOrder";
|
||||
import type { PhotoItem } from "@/types/Photo";
|
||||
import { getAttachmentReadURL } from "@/utils/attachment";
|
||||
import { getErrorMessage } from "@/utils/error";
|
||||
import { formatPackageAmount } from "@/utils/package";
|
||||
import { getFundOperationText, getFundStatusText } from "@/utils/salesOrder";
|
||||
import { Message } from "@/utils/message";
|
||||
import { permissionCodes } from "@/utils/permission";
|
||||
|
||||
type ReasonAction = "reject" | "cancel";
|
||||
|
||||
defineOptions({ name: "FundRecordDetail" });
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const userStore = useUserStore();
|
||||
const navBarStore = useNavBarStore();
|
||||
const record = ref<FundRecord>();
|
||||
const isLoading = ref(false);
|
||||
const isOperating = ref(false);
|
||||
const reasonDialogVisible = ref(false);
|
||||
const reasonText = ref("");
|
||||
const reasonAction = ref<ReasonAction>("reject");
|
||||
const isPendingReceive = computed(() => record.value?.operationType === "RECEIVE" && record.value.status === "PENDING");
|
||||
const canVerifyReceive = computed(() => isPendingReceive.value && userStore.hasPermissions(permissionCodes.fundReceive.verify));
|
||||
const canCancelReceive = computed(() => isPendingReceive.value && userStore.hasPermissions(permissionCodes.fundReceive.create));
|
||||
const canRevoke = computed(() => record.value?.status === "VERIFIED" && record.value.operationType !== "REVOKE" && userStore.hasPermissions(permissionCodes.fund.revoke));
|
||||
const reasonTitle = computed(() => reasonAction.value === "reject" ? "拒绝收款" : "撤销收款");
|
||||
const reasonActionText = computed(() => reasonAction.value === "reject" ? "拒绝" : "撤销");
|
||||
const photoItems = computed<PhotoItem[]>(() => (record.value?.attachmentList || [])
|
||||
.filter(item => !!item.id)
|
||||
.map(item => ({
|
||||
attachment: item,
|
||||
attachType: item.attachType || "PHOTO",
|
||||
url: getAttachmentReadURL(item.id || "")
|
||||
})));
|
||||
const photoURLList = computed(() => photoItems.value.map(item => item.url));
|
||||
|
||||
function getStatusTheme(status?: FundRecordStatus): "success" | "warning" | "danger" | "default" {
|
||||
return status === "VERIFIED" ? "success" : status === "PENDING" ? "warning" : status === "REJECTED" ? "danger" : "default";
|
||||
}
|
||||
|
||||
async function loadData(): Promise<void> {
|
||||
const id = String(route.params.id || "");
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
isLoading.value = true;
|
||||
try {
|
||||
record.value = await SalesOrderAPI.fundRecordDetail(id);
|
||||
} catch (error) {
|
||||
Message.error(getErrorMessage(error, "资金记录加载失败"));
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshRelatedPackage(): Promise<void> {
|
||||
if (record.value?.bizId) {
|
||||
await SalesOrderAPI.detail(record.value.bizId);
|
||||
}
|
||||
}
|
||||
|
||||
async function verifyReceive(): Promise<void> {
|
||||
if (!record.value?.id || !canVerifyReceive.value || isOperating.value) {
|
||||
return;
|
||||
}
|
||||
const dialog = DialogPlugin.confirm({
|
||||
title: "核销收款",
|
||||
content: "核销后该金额才会计入套餐的已收款。确定继续?",
|
||||
confirmBtn: "核销",
|
||||
onConfirm: async () => {
|
||||
isOperating.value = true;
|
||||
try {
|
||||
await SalesOrderAPI.verifyPayment(record.value!.id!);
|
||||
await refreshRelatedPackage();
|
||||
Message.success("收款已核销");
|
||||
await loadData();
|
||||
} catch (error) {
|
||||
Message.error(getErrorMessage(error, "收款核销失败,请刷新后确认状态"));
|
||||
} finally {
|
||||
isOperating.value = false;
|
||||
dialog.destroy();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function openReasonDialog(action: ReasonAction): void {
|
||||
if (!isPendingReceive.value || isOperating.value) {
|
||||
return;
|
||||
}
|
||||
reasonAction.value = action;
|
||||
reasonText.value = "";
|
||||
reasonDialogVisible.value = true;
|
||||
}
|
||||
|
||||
async function submitReason(): Promise<void> {
|
||||
if (!record.value?.id || !reasonText.value.trim() || isOperating.value) {
|
||||
Message.warning("请输入原因");
|
||||
return;
|
||||
}
|
||||
isOperating.value = true;
|
||||
try {
|
||||
if (reasonAction.value === "reject") {
|
||||
await SalesOrderAPI.rejectPayment({ fundRecordId: record.value.id, reason: reasonText.value.trim() });
|
||||
} else {
|
||||
await SalesOrderAPI.cancelPayment({ fundRecordId: record.value.id, reason: reasonText.value.trim() });
|
||||
}
|
||||
reasonDialogVisible.value = false;
|
||||
await refreshRelatedPackage();
|
||||
Message.success(reasonAction.value === "reject" ? "收款已拒绝" : "收款已撤销");
|
||||
await loadData();
|
||||
} catch (error) {
|
||||
Message.error(getErrorMessage(error, "收款状态操作失败,请刷新后确认状态"));
|
||||
} finally {
|
||||
isOperating.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function toRevoke(): Promise<void> {
|
||||
if (record.value?.id && canRevoke.value) {
|
||||
await router.push(`/management/fund-records/${record.value.id}/revoke`);
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(loadData);
|
||||
onUnmounted(() => navBarStore.clearRight());
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.package-record-detail {
|
||||
gap: 1rem;
|
||||
padding: calc(var(--app-nav-height) + 1rem) 0 2rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.hero {
|
||||
margin: 0 1rem;
|
||||
padding: 1rem;
|
||||
background: linear-gradient(135deg, #eff6ff, #f0fdf4);
|
||||
box-shadow: var(--td-shadow-1);
|
||||
border-radius: 1.25rem;
|
||||
|
||||
.eyebrow {
|
||||
color: #2563eb;
|
||||
display: block;
|
||||
font-size: .6875rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: .08em;
|
||||
}
|
||||
|
||||
.hero-title {
|
||||
gap: .5rem;
|
||||
display: flex;
|
||||
margin-top: .35rem;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 0;
|
||||
font-size: 1.2rem;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.amount {
|
||||
color: #0f766e;
|
||||
display: block;
|
||||
margin-top: .75rem;
|
||||
font-size: 1.5rem;
|
||||
line-height: 1.2;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.photo-scroller) {
|
||||
padding: 0 1rem;
|
||||
}
|
||||
|
||||
.actions {
|
||||
gap: .5rem;
|
||||
padding: 0 1rem;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.t-button {
|
||||
flex: 1 1 8rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:global(html.theme-dark) .package-record-detail .hero {
|
||||
background: linear-gradient(135deg, rgba(37, 99, 235, .2), rgba(15, 118, 110, .18));
|
||||
|
||||
.eyebrow {
|
||||
color: #93c5fd;
|
||||
}
|
||||
|
||||
.amount {
|
||||
color: #99f6e4;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,202 +0,0 @@
|
||||
<template>
|
||||
<div v-can="permissionCodes.fundRecord.read" class="package-record-list">
|
||||
<div class="filters">
|
||||
<t-dropdown-menu>
|
||||
<t-dropdown-item v-model="operationType" label="操作类型" :options="operationOptions" @change="selectOperation" />
|
||||
<t-dropdown-item v-model="status" label="状态" :options="statusOptions" @change="selectStatus" />
|
||||
</t-dropdown-menu>
|
||||
</div>
|
||||
<t-pull-down-refresh v-model="isRefreshing" :refresh-timeout="30000" @refresh="reload" @scrolltolower="loadMore">
|
||||
<t-cell-group v-if="recordList.length" theme="card">
|
||||
<t-cell
|
||||
v-for="(item, index) in recordList"
|
||||
:key="item.id"
|
||||
:title="`${getFundOperationText(item.operationType)} · ${getFundStatusText(item.status)}`"
|
||||
:description="getDescription(item)"
|
||||
arrow
|
||||
hover
|
||||
v-entrance-transition="index * 30"
|
||||
@click="toDetail(item)"
|
||||
>
|
||||
<template #title>
|
||||
<div class="title-row">
|
||||
<span v-text="getFundOperationText(item.operationType)" />
|
||||
<t-tag :content="getFundStatusText(item.status)" variant="light-outline" :theme="getStatusTheme(item.status)" />
|
||||
</div>
|
||||
</template>
|
||||
<template #note><span class="amount" v-text="formatPackageAmount(item.amount)" /></template>
|
||||
</t-cell>
|
||||
</t-cell-group>
|
||||
<div v-else class="empty" v-text="loading ? '正在加载资金流水' : '暂无资金流水'" />
|
||||
<div class="center-tips" v-text="loadStateText" />
|
||||
</t-pull-down-refresh>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { type DropdownValue } from "tdesign-mobile-vue";
|
||||
import { Time } from "timi-web";
|
||||
import SalesOrderAPI from "@/api/SalesOrderAPI";
|
||||
import { useNavBarStore } from "@/store/navBarStore";
|
||||
import type { FundOperationType, FundRecord, FundRecordStatus } from "@/types/SalesOrder";
|
||||
import { getErrorMessage } from "@/utils/error";
|
||||
import { formatPackageAmount } from "@/utils/package";
|
||||
import { getFundOperationText, getFundStatusText } from "@/utils/salesOrder";
|
||||
import { Message } from "@/utils/message";
|
||||
import { permissionCodes } from "@/utils/permission";
|
||||
|
||||
defineOptions({ name: "FundRecordList" });
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const navBarStore = useNavBarStore();
|
||||
const recordList = ref<FundRecord[]>([]);
|
||||
const operationType = ref(route.query.operationType === "RECEIVE" ? "RECEIVE" : "ALL");
|
||||
const status = ref(route.query.status === "PENDING" ? "PENDING" : "ALL");
|
||||
const pageIndex = ref(0);
|
||||
const total = ref(0);
|
||||
const loading = ref(false);
|
||||
const isRefreshing = ref(false);
|
||||
const noMore = ref(false);
|
||||
const operationOptions = [
|
||||
{ label: "全部类型", value: "ALL" },
|
||||
{ label: "线下收款", value: "RECEIVE" },
|
||||
{ label: "退款", value: "REFUND" },
|
||||
{ label: "确认无需退款", value: "CONFIRM_NO_REFUND" },
|
||||
{ label: "资金冲正", value: "REVOKE" }
|
||||
];
|
||||
const statusOptions = [
|
||||
{ label: "全部状态", value: "ALL" },
|
||||
{ label: "待核销", value: "PENDING" },
|
||||
{ label: "已核销", value: "VERIFIED" },
|
||||
{ label: "已拒绝", value: "REJECTED" },
|
||||
{ label: "已撤销", value: "CANCELLED" }
|
||||
];
|
||||
const loadStateText = computed(() => loading.value ? "正在加载" : noMore.value ? "已经到底了" : "上拉加载更多");
|
||||
|
||||
async function loadMore(): Promise<void> {
|
||||
if (loading.value || noMore.value) {
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
try {
|
||||
const result = await SalesOrderAPI.fundRecordList({
|
||||
index: pageIndex.value,
|
||||
size: 15,
|
||||
salesOrderId: String(route.query.salesOrderId || "") || undefined,
|
||||
operationType: operationType.value === "ALL" ? undefined : operationType.value as FundOperationType,
|
||||
status: status.value === "ALL" ? undefined : status.value as FundRecordStatus
|
||||
});
|
||||
const list = result.list || [];
|
||||
recordList.value = recordList.value.concat(list);
|
||||
total.value = result.total || 0;
|
||||
pageIndex.value += 1;
|
||||
noMore.value = list.length < 15 || total.value <= recordList.value.length;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function reload(): Promise<void> {
|
||||
if (loading.value) {
|
||||
return;
|
||||
}
|
||||
pageIndex.value = 0;
|
||||
recordList.value = [];
|
||||
total.value = 0;
|
||||
noMore.value = false;
|
||||
await loadMore();
|
||||
}
|
||||
|
||||
async function selectOperation(value: DropdownValue): Promise<void> {
|
||||
if (!Array.isArray(value)) {
|
||||
operationType.value = String(value);
|
||||
await reload();
|
||||
}
|
||||
}
|
||||
|
||||
async function selectStatus(value: DropdownValue): Promise<void> {
|
||||
if (!Array.isArray(value)) {
|
||||
status.value = String(value);
|
||||
await reload();
|
||||
}
|
||||
}
|
||||
|
||||
function getDescription(item: FundRecord): string {
|
||||
return `${item.customer?.name || item.customerId || "未知客户"}|订单 ${item.bizId || "未知"}|${item.occurredAt ? Time.toDateTime(item.occurredAt) : "时间未知"}`;
|
||||
}
|
||||
|
||||
function getStatusTheme(statusValue?: FundRecordStatus): "success" | "warning" | "danger" | "default" {
|
||||
return statusValue === "VERIFIED" ? "success" : statusValue === "PENDING" ? "warning" : statusValue === "REJECTED" ? "danger" : "default";
|
||||
}
|
||||
|
||||
async function toDetail(item: FundRecord): Promise<void> {
|
||||
if (item.id) {
|
||||
await router.push({
|
||||
path: `/management/fund-records/${item.id}`,
|
||||
query: { transitionDepth: String(getCurrentRouteDepth() + 1) }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getCurrentRouteDepth(): number {
|
||||
const transitionDepth = Array.isArray(route.query.transitionDepth)
|
||||
? route.query.transitionDepth[0]
|
||||
: route.query.transitionDepth;
|
||||
const depth = Number(transitionDepth);
|
||||
if (transitionDepth && Number.isInteger(depth) && 0 <= depth && depth <= 20) {
|
||||
return depth;
|
||||
}
|
||||
return Number(route.meta.depth ?? 0);
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
navBarStore.clearRight();
|
||||
try {
|
||||
await reload();
|
||||
} catch (error) {
|
||||
Message.error(getErrorMessage(error, "资金流水加载失败"));
|
||||
}
|
||||
});
|
||||
|
||||
onUnmounted(() => navBarStore.clearRight());
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.package-record-list {
|
||||
padding: var(--app-nav-height) 0 1rem;
|
||||
|
||||
.filters {
|
||||
top: var(--app-nav-height);
|
||||
z-index: 10;
|
||||
padding: .75rem 1rem .5rem;
|
||||
position: sticky;
|
||||
background: var(--td-bg-color-page);
|
||||
box-shadow: var(--td-shadow-1);
|
||||
|
||||
:deep(.t-dropdown-menu) {
|
||||
border-radius: .875rem;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.t-pull-down-refresh) {
|
||||
padding-top: .75rem;
|
||||
}
|
||||
|
||||
.title-row {
|
||||
gap: .5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.amount {
|
||||
color: var(--td-brand-color);
|
||||
}
|
||||
|
||||
.empty {
|
||||
color: var(--td-text-color-secondary);
|
||||
padding: 2rem 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,172 +0,0 @@
|
||||
<template>
|
||||
<div v-can="permissionCodes.fund.revoke" class="package-operation-edit">
|
||||
<t-cell-group v-if="record" title="原资金记录" theme="card" v-entrance-transition>
|
||||
<t-cell title="操作类型" :note="getFundOperationText(record.operationType)" />
|
||||
<t-cell title="金额" :note="formatPackageAmount(record.amount)" />
|
||||
<t-cell title="原状态" :note="getFundStatusText(record.status)" />
|
||||
<t-cell title="销售订单" :note="record.bizId || '未知'" />
|
||||
</t-cell-group>
|
||||
<t-form v-if="record" class="form" label-align="left" label-width="6rem" v-entrance-transition="70">
|
||||
<t-form-item label="冲正原因" required-mark><t-textarea v-model.trim="reason" borderless autosize maxlength="500" 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" placeholder="备注(可选)" /></t-form-item>
|
||||
</t-form>
|
||||
<photo-scroller
|
||||
v-if="record"
|
||||
:model-value="photoUrlList"
|
||||
v-model:photo-items="photoItems"
|
||||
class="photo-field"
|
||||
:upload="uploadPhotoList"
|
||||
:edit-mode="true"
|
||||
required
|
||||
title="冲正凭证照片"
|
||||
/>
|
||||
<t-empty v-if="!record" :description="isLoading ? '正在加载资金记录' : '资金记录不存在'" />
|
||||
<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 SalesOrderAPI from "@/api/SalesOrderAPI";
|
||||
import PhotoScroller from "@/components/PhotoScroller.vue";
|
||||
import { useNavBarStore } from "@/store/navBarStore";
|
||||
import type { FundRecord } from "@/types/SalesOrder";
|
||||
import type { PhotoItem } from "@/types/Photo";
|
||||
import { createPackageRequestNo, formatPackageAmount } from "@/utils/package";
|
||||
import { getFundOperationText, getFundStatusText } from "@/utils/salesOrder";
|
||||
import { getErrorMessage } from "@/utils/error";
|
||||
import { Message } from "@/utils/message";
|
||||
import { permissionCodes } from "@/utils/permission";
|
||||
import { uploadPhotoList } from "@/utils/photo";
|
||||
|
||||
defineOptions({ name: "FundRecordRevoke" });
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const userStore = useUserStore();
|
||||
const navBarStore = useNavBarStore();
|
||||
const record = ref<FundRecord>();
|
||||
const reason = ref("");
|
||||
const remark = ref("");
|
||||
const occurredAtText = ref("");
|
||||
const draftTime = ref("");
|
||||
const pickerVisible = ref(false);
|
||||
const photoItems = ref<PhotoItem[]>([]);
|
||||
const isLoading = ref(false);
|
||||
const isSaving = ref(false);
|
||||
const requestNo = ref("");
|
||||
const photoUrlList = computed(() => photoItems.value.map(item => item.url));
|
||||
|
||||
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;
|
||||
}
|
||||
isLoading.value = true;
|
||||
try {
|
||||
record.value = await SalesOrderAPI.fundRecordDetail(id);
|
||||
} catch (error) {
|
||||
Message.error(getErrorMessage(error, "资金记录加载失败"));
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function save(): Promise<void> {
|
||||
if (isSaving.value) {
|
||||
return;
|
||||
}
|
||||
if (!record.value?.id || record.value.status !== "VERIFIED" || record.value.operationType === "REVOKE") {
|
||||
Message.warning("只有已核销且非冲正的资金记录可以冲正");
|
||||
return;
|
||||
}
|
||||
if (!reason.value.trim()) {
|
||||
Message.warning("请输入冲正原因");
|
||||
return;
|
||||
}
|
||||
if (!photoItems.value.some(item => item.tempFileId)) {
|
||||
Message.warning("资金冲正必须上传至少一张照片");
|
||||
return;
|
||||
}
|
||||
isSaving.value = true;
|
||||
try {
|
||||
if (!requestNo.value) {
|
||||
requestNo.value = createPackageRequestNo();
|
||||
}
|
||||
const result = await SalesOrderAPI.fundRecordRevoke({
|
||||
requestNo: requestNo.value,
|
||||
fundRecordId: record.value.id,
|
||||
occurredAt: resolveTimestamp(occurredAtText.value),
|
||||
reason: reason.value.trim(),
|
||||
attachmentList: buildAttachments(),
|
||||
remark: remark.value.trim() || null
|
||||
});
|
||||
Message.success("资金记录已冲正");
|
||||
if (record.value.bizId) {
|
||||
await router.replace(`/management/sales-orders/${record.value.bizId}`);
|
||||
return;
|
||||
}
|
||||
await router.replace(`/management/fund-records/${result.id || record.value.id}`);
|
||||
} catch (error) {
|
||||
Message.error(getErrorMessage(error, "资金冲正失败,请刷新后确认状态"));
|
||||
} finally {
|
||||
isSaving.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
if (userStore.hasPermissions(permissionCodes.fund.revoke)) {
|
||||
navBarStore.setRightText("提交冲正", "var(--td-danger-color)", save);
|
||||
}
|
||||
await loadData();
|
||||
});
|
||||
|
||||
onUnmounted(() => navBarStore.clearRight());
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.package-operation-edit {
|
||||
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;
|
||||
}
|
||||
|
||||
.photo-field {
|
||||
padding: 0 1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user