fix SalesOrderPayment.vue style
This commit is contained in:
@@ -1,89 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="package-photo-field">
|
|
||||||
<photo-scroller
|
|
||||||
v-model="photoUrlList"
|
|
||||||
v-model:photo-items="photoItems"
|
|
||||||
:title="title"
|
|
||||||
:required="required"
|
|
||||||
:loading="isUploading"
|
|
||||||
:edit-mode="true"
|
|
||||||
@add="openFilePicker"
|
|
||||||
/>
|
|
||||||
<input ref="fileInput" class="file-input" type="file" accept="image/*" multiple @change="handleFileChange" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { AttachmentAPI } from "timi-web-api";
|
|
||||||
import PhotoScroller from "@/components/PhotoScroller.vue";
|
|
||||||
import type { PhotoItem } from "@/types/Photo";
|
|
||||||
import { compressUploadImageList } from "@/utils/imageCompression";
|
|
||||||
import { Message } from "@/utils/message";
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
required?: boolean;
|
|
||||||
title?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
withDefaults(defineProps<Props>(), {
|
|
||||||
required: false,
|
|
||||||
title: "业务照片"
|
|
||||||
});
|
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
name: "PackagePhotoField"
|
|
||||||
});
|
|
||||||
|
|
||||||
const photoItems = defineModel<PhotoItem[]>({ required: true });
|
|
||||||
const fileInput = ref<HTMLInputElement>();
|
|
||||||
const isUploading = ref(false);
|
|
||||||
const photoUrlList = computed<string[]>({
|
|
||||||
get: () => photoItems.value.map(item => item.url),
|
|
||||||
set: value => {
|
|
||||||
const urlSet = new Set(value);
|
|
||||||
photoItems.value = photoItems.value.filter(item => urlSet.has(item.url));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function openFilePicker(): void {
|
|
||||||
fileInput.value?.click();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleFileChange(event: Event): Promise<void> {
|
|
||||||
const input = event.target as HTMLInputElement;
|
|
||||||
const fileList = Array.from(input.files || []);
|
|
||||||
if (!fileList.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
isUploading.value = true;
|
|
||||||
try {
|
|
||||||
const tempFileList = await AttachmentAPI.upload(await compressUploadImageList(fileList));
|
|
||||||
for (const tempFile of tempFileList) {
|
|
||||||
if (!tempFile.id) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
photoItems.value.push({
|
|
||||||
tempFileId: tempFile.id,
|
|
||||||
attachType: "PHOTO",
|
|
||||||
url: AttachmentAPI.getTempReadURL(tempFile.id)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
Message.error("照片上传失败");
|
|
||||||
} finally {
|
|
||||||
isUploading.value = false;
|
|
||||||
input.value = "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="less">
|
|
||||||
.package-photo-field {
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
.file-input {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -44,8 +44,8 @@
|
|||||||
theme="primary"
|
theme="primary"
|
||||||
variant="dashed"
|
variant="dashed"
|
||||||
class="add"
|
class="add"
|
||||||
:loading="loading"
|
:loading="isBusy"
|
||||||
:disabled="loading"
|
:disabled="isBusy"
|
||||||
@click="handleAddAction(addActionList[0])"
|
@click="handleAddAction(addActionList[0])"
|
||||||
>
|
>
|
||||||
<span v-text="addActionList[0].text" />
|
<span v-text="addActionList[0].text" />
|
||||||
@@ -60,8 +60,8 @@
|
|||||||
class="add"
|
class="add"
|
||||||
theme="primary"
|
theme="primary"
|
||||||
variant="dashed"
|
variant="dashed"
|
||||||
:loading="loading"
|
:loading="isBusy"
|
||||||
:disabled="loading"
|
:disabled="isBusy"
|
||||||
>
|
>
|
||||||
<span v-text="addText" />
|
<span v-text="addText" />
|
||||||
</t-button>
|
</t-button>
|
||||||
@@ -73,7 +73,7 @@
|
|||||||
variant="text"
|
variant="text"
|
||||||
:theme="action.theme"
|
:theme="action.theme"
|
||||||
class="add-action"
|
class="add-action"
|
||||||
:disabled="loading"
|
:disabled="isBusy"
|
||||||
@click="handleAddAction(action)"
|
@click="handleAddAction(action)"
|
||||||
>
|
>
|
||||||
<template #icon v-if="action.icon">
|
<template #icon v-if="action.icon">
|
||||||
@@ -86,6 +86,15 @@
|
|||||||
</t-popover>
|
</t-popover>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<input
|
||||||
|
v-if="props.upload"
|
||||||
|
ref="fileInput"
|
||||||
|
class="file-input"
|
||||||
|
type="file"
|
||||||
|
accept="image/*"
|
||||||
|
multiple
|
||||||
|
@change="handleFileChange"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<t-image-viewer
|
<t-image-viewer
|
||||||
v-model:visible="photoViewerVisible"
|
v-model:visible="photoViewerVisible"
|
||||||
@@ -163,6 +172,7 @@ interface Props {
|
|||||||
modelValue: string[];
|
modelValue: string[];
|
||||||
photoItems?: readonly PhotoItem[];
|
photoItems?: readonly PhotoItem[];
|
||||||
required?: boolean;
|
required?: boolean;
|
||||||
|
upload?: (fileList: File[]) => Promise<PhotoItem[]>;
|
||||||
showViewerActions?: boolean;
|
showViewerActions?: boolean;
|
||||||
showAdd?: boolean;
|
showAdd?: boolean;
|
||||||
showRemove?: boolean;
|
showRemove?: boolean;
|
||||||
@@ -217,12 +227,15 @@ const photoViewerItems = ref<ViewerPhotoItem[]>([]);
|
|||||||
const photoViewerIndex = ref(0);
|
const photoViewerIndex = ref(0);
|
||||||
const photoActionVisible = ref(false);
|
const photoActionVisible = ref(false);
|
||||||
const isSubmitting = ref(false);
|
const isSubmitting = ref(false);
|
||||||
|
const isUploading = ref(false);
|
||||||
const addPopoverVisible = ref(false);
|
const addPopoverVisible = ref(false);
|
||||||
|
const fileInput = ref<HTMLInputElement>();
|
||||||
const addActionList = computed<readonly AddAction[]>(() => props.addActions.length ? props.addActions : [{
|
const addActionList = computed<readonly AddAction[]>(() => props.addActions.length ? props.addActions : [{
|
||||||
emit: "add",
|
emit: "add",
|
||||||
text: props.addText,
|
text: props.addText,
|
||||||
theme: "default"
|
theme: "default"
|
||||||
}]);
|
}]);
|
||||||
|
const isBusy = computed(() => props.loading || isSubmitting.value || isUploading.value);
|
||||||
const displayPhotoList = computed<PhotoItem[]>(() => {
|
const displayPhotoList = computed<PhotoItem[]>(() => {
|
||||||
if (props.photoItems?.length) {
|
if (props.photoItems?.length) {
|
||||||
return props.photoItems.map(normalizePhotoItem);
|
return props.photoItems.map(normalizePhotoItem);
|
||||||
@@ -250,9 +263,37 @@ function handleAddAction(action?: AddAction): void {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
addPopoverVisible.value = false;
|
addPopoverVisible.value = false;
|
||||||
|
if (action.emit === "add" && props.upload) {
|
||||||
|
fileInput.value?.click();
|
||||||
|
return;
|
||||||
|
}
|
||||||
emit(action.emit);
|
emit(action.emit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleFileChange(event: Event): Promise<void> {
|
||||||
|
const input = event.target as HTMLInputElement;
|
||||||
|
const fileList = Array.from(input.files || []);
|
||||||
|
if (!fileList.length || !props.upload) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isUploading.value = true;
|
||||||
|
try {
|
||||||
|
const uploadedPhotoItems = await props.upload(fileList);
|
||||||
|
if (!uploadedPhotoItems.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const nextPhotoItems = [...displayPhotoList.value, ...uploadedPhotoItems];
|
||||||
|
emit("update:photoItems", nextPhotoItems);
|
||||||
|
emit("update:modelValue", nextPhotoItems.map(item => item.url));
|
||||||
|
} catch {
|
||||||
|
Message.error("照片上传失败");
|
||||||
|
} finally {
|
||||||
|
isUploading.value = false;
|
||||||
|
input.value = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function removePhoto(index: number): Promise<void> {
|
async function removePhoto(index: number): Promise<void> {
|
||||||
if (index < 0 || displayPhotoList.value.length <= index) {
|
if (index < 0 || displayPhotoList.value.length <= index) {
|
||||||
return;
|
return;
|
||||||
@@ -341,6 +382,10 @@ function normalizePhotoItem(item: PhotoItem): PhotoItem {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
display: grid;
|
display: grid;
|
||||||
|
|
||||||
|
.file-input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.photo-shell {
|
.photo-shell {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: .75rem;
|
gap: .75rem;
|
||||||
|
|||||||
@@ -17,7 +17,15 @@
|
|||||||
<t-form-item label="履约时间" content-align="right"><t-button theme="primary" variant="outline" size="small" :content="deliveredAtText || '此刻'" @click="pickerVisible = true" /></t-form-item>
|
<t-form-item label="履约时间" content-align="right"><t-button theme="primary" variant="outline" size="small" :content="deliveredAtText || '此刻'" @click="pickerVisible = true" /></t-form-item>
|
||||||
<t-form-item label="备注"><t-textarea v-model.trim="remark" borderless autosize maxlength="500" /></t-form-item>
|
<t-form-item label="备注"><t-textarea v-model.trim="remark" borderless autosize maxlength="500" /></t-form-item>
|
||||||
</t-form>
|
</t-form>
|
||||||
<package-photo-field v-if="order" v-model="photoItems" required title="履约凭证照片" />
|
<photo-scroller
|
||||||
|
v-if="order"
|
||||||
|
:model-value="photoUrlList"
|
||||||
|
v-model:photo-items="photoItems"
|
||||||
|
:upload="uploadPhotoList"
|
||||||
|
:edit-mode="true"
|
||||||
|
required
|
||||||
|
title="履约凭证照片"
|
||||||
|
/>
|
||||||
<t-popup v-model="pickerVisible" placement="bottom">
|
<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-date-time-picker v-model="draftTime" format="YYYY-MM-DD HH:mm" :mode="['date', 'minute']" title="履约时间" @cancel="pickerVisible = false" @confirm="confirmTime" />
|
||||||
</t-popup>
|
</t-popup>
|
||||||
@@ -45,7 +53,7 @@
|
|||||||
import PackageAPI from "@/api/PackageAPI";
|
import PackageAPI from "@/api/PackageAPI";
|
||||||
import SalesOrderAPI from "@/api/SalesOrderAPI";
|
import SalesOrderAPI from "@/api/SalesOrderAPI";
|
||||||
import FulfillmentAPI from "@/api/FulfillmentAPI";
|
import FulfillmentAPI from "@/api/FulfillmentAPI";
|
||||||
import PackagePhotoField from "@/components/PackagePhotoField.vue";
|
import PhotoScroller from "@/components/PhotoScroller.vue";
|
||||||
import { useNavBarStore } from "@/store/navBarStore";
|
import { useNavBarStore } from "@/store/navBarStore";
|
||||||
import type { CustomerPackageItem, GaoCustomerPackage } from "@/types/Package";
|
import type { CustomerPackageItem, GaoCustomerPackage } from "@/types/Package";
|
||||||
import type { OrderFulfillmentItemRequest } from "@/types/Fulfillment";
|
import type { OrderFulfillmentItemRequest } from "@/types/Fulfillment";
|
||||||
@@ -56,6 +64,7 @@ import { createPackageRequestNo, formatPackageAmount, getPackageQuantityText, no
|
|||||||
import { getErrorMessage } from "@/utils/error";
|
import { getErrorMessage } from "@/utils/error";
|
||||||
import { Message } from "@/utils/message";
|
import { Message } from "@/utils/message";
|
||||||
import { permissionCodes } from "@/utils/permission";
|
import { permissionCodes } from "@/utils/permission";
|
||||||
|
import { uploadPhotoList } from "@/utils/photo";
|
||||||
|
|
||||||
type SourceItem = SalesOrderItem | CustomerPackageItem;
|
type SourceItem = SalesOrderItem | CustomerPackageItem;
|
||||||
defineOptions({ name: "OrderFulfillmentCreate" });
|
defineOptions({ name: "OrderFulfillmentCreate" });
|
||||||
@@ -72,6 +81,7 @@ const remark = ref("");
|
|||||||
const requestNo = ref("");
|
const requestNo = ref("");
|
||||||
const isSaving = ref(false);
|
const isSaving = ref(false);
|
||||||
const photoItems = ref<PhotoItem[]>([]);
|
const photoItems = ref<PhotoItem[]>([]);
|
||||||
|
const photoUrlList = computed(() => photoItems.value.map(item => item.url));
|
||||||
const orderPickerVisible = ref(false);
|
const orderPickerVisible = ref(false);
|
||||||
const orderLoading = ref(false);
|
const orderLoading = ref(false);
|
||||||
const orderOptions = ref<SalesOrder[]>([]);
|
const orderOptions = ref<SalesOrder[]>([]);
|
||||||
|
|||||||
@@ -52,7 +52,14 @@
|
|||||||
<t-form-item label="冲正原因" required-mark><t-textarea v-model.trim="revokeReason" borderless autosize /></t-form-item>
|
<t-form-item label="冲正原因" required-mark><t-textarea v-model.trim="revokeReason" borderless autosize /></t-form-item>
|
||||||
<t-form-item label="库存是否退回门店"><t-switch v-model="returnedToStore" /></t-form-item>
|
<t-form-item label="库存是否退回门店"><t-switch v-model="returnedToStore" /></t-form-item>
|
||||||
</t-form>
|
</t-form>
|
||||||
<package-photo-field v-model="photoItems" required title="冲正凭证照片" />
|
<photo-scroller
|
||||||
|
:model-value="photoUrlList"
|
||||||
|
v-model:photo-items="photoItems"
|
||||||
|
:upload="uploadPhotoList"
|
||||||
|
:edit-mode="true"
|
||||||
|
required
|
||||||
|
title="冲正凭证照片"
|
||||||
|
/>
|
||||||
</bottom-popup>
|
</bottom-popup>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -62,7 +69,7 @@ import { BottomPopup } from "timi-tdesign-mobile";
|
|||||||
import { Time } from "timi-web";
|
import { Time } from "timi-web";
|
||||||
import { useUserStore } from "timi-web-api";
|
import { useUserStore } from "timi-web-api";
|
||||||
import FulfillmentAPI from "@/api/FulfillmentAPI";
|
import FulfillmentAPI from "@/api/FulfillmentAPI";
|
||||||
import PackagePhotoField from "@/components/PackagePhotoField.vue";
|
import PhotoScroller from "@/components/PhotoScroller.vue";
|
||||||
import { useNavBarStore } from "@/store/navBarStore";
|
import { useNavBarStore } from "@/store/navBarStore";
|
||||||
import type { OrderFulfillment } from "@/types/Fulfillment";
|
import type { OrderFulfillment } from "@/types/Fulfillment";
|
||||||
import type { PhotoItem } from "@/types/Photo";
|
import type { PhotoItem } from "@/types/Photo";
|
||||||
@@ -72,6 +79,7 @@ import { getErrorMessage } from "@/utils/error";
|
|||||||
import { getFulfillmentStatusText, getFulfillmentTypeText, getStatusTheme } from "@/utils/salesOrder";
|
import { getFulfillmentStatusText, getFulfillmentTypeText, getStatusTheme } from "@/utils/salesOrder";
|
||||||
import { Message } from "@/utils/message";
|
import { Message } from "@/utils/message";
|
||||||
import { permissionCodes } from "@/utils/permission";
|
import { permissionCodes } from "@/utils/permission";
|
||||||
|
import { uploadPhotoList } from "@/utils/photo";
|
||||||
|
|
||||||
type ReasonAction = "reject" | "cancel";
|
type ReasonAction = "reject" | "cancel";
|
||||||
defineOptions({ name: "OrderFulfillmentDetail" });
|
defineOptions({ name: "OrderFulfillmentDetail" });
|
||||||
@@ -90,6 +98,7 @@ const revokeReason = ref("");
|
|||||||
const returnedToStore = ref(true);
|
const returnedToStore = ref(true);
|
||||||
const revokeRequestNo = ref("");
|
const revokeRequestNo = ref("");
|
||||||
const photoItems = ref<PhotoItem[]>([]);
|
const photoItems = ref<PhotoItem[]>([]);
|
||||||
|
const photoUrlList = computed(() => photoItems.value.map(item => item.url));
|
||||||
const isWaitingStock = computed(() => fulfillment.value?.status === "WAITING_STOCK");
|
const isWaitingStock = computed(() => fulfillment.value?.status === "WAITING_STOCK");
|
||||||
const isPending = computed(() => fulfillment.value?.status === "PENDING");
|
const isPending = computed(() => fulfillment.value?.status === "PENDING");
|
||||||
const isVerified = computed(() => fulfillment.value?.status === "VERIFIED");
|
const isVerified = computed(() => fulfillment.value?.status === "VERIFIED");
|
||||||
|
|||||||
@@ -11,7 +11,16 @@
|
|||||||
<t-form-item label="冲正时间" arrow @click="pickerVisible = true"><span class="picker-text" v-text="occurredAtText || '使用当前时间'" /></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-item label="备注"><t-textarea v-model.trim="remark" borderless autosize maxlength="500" placeholder="备注(可选)" /></t-form-item>
|
||||||
</t-form>
|
</t-form>
|
||||||
<package-photo-field v-if="record" v-model="photoItems" required title="冲正凭证照片" />
|
<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-empty v-if="!record" :description="isLoading ? '正在加载资金记录' : '资金记录不存在'" />
|
||||||
<t-popup v-model="pickerVisible" placement="bottom">
|
<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-date-time-picker v-model="draftTime" format="YYYY-MM-DD HH:mm" :mode="['date', 'minute']" title="冲正时间" @cancel="pickerVisible = false" @confirm="confirmTime" />
|
||||||
@@ -22,7 +31,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useUserStore, type Attachment } from "timi-web-api";
|
import { useUserStore, type Attachment } from "timi-web-api";
|
||||||
import SalesOrderAPI from "@/api/SalesOrderAPI";
|
import SalesOrderAPI from "@/api/SalesOrderAPI";
|
||||||
import PackagePhotoField from "@/components/PackagePhotoField.vue";
|
import PhotoScroller from "@/components/PhotoScroller.vue";
|
||||||
import { useNavBarStore } from "@/store/navBarStore";
|
import { useNavBarStore } from "@/store/navBarStore";
|
||||||
import type { FundRecord } from "@/types/SalesOrder";
|
import type { FundRecord } from "@/types/SalesOrder";
|
||||||
import type { PhotoItem } from "@/types/Photo";
|
import type { PhotoItem } from "@/types/Photo";
|
||||||
@@ -31,6 +40,7 @@ import { getFundOperationText, getFundStatusText } from "@/utils/salesOrder";
|
|||||||
import { getErrorMessage } from "@/utils/error";
|
import { getErrorMessage } from "@/utils/error";
|
||||||
import { Message } from "@/utils/message";
|
import { Message } from "@/utils/message";
|
||||||
import { permissionCodes } from "@/utils/permission";
|
import { permissionCodes } from "@/utils/permission";
|
||||||
|
import { uploadPhotoList } from "@/utils/photo";
|
||||||
|
|
||||||
defineOptions({ name: "FundRecordRevoke" });
|
defineOptions({ name: "FundRecordRevoke" });
|
||||||
|
|
||||||
@@ -48,6 +58,7 @@ const photoItems = ref<PhotoItem[]>([]);
|
|||||||
const isLoading = ref(false);
|
const isLoading = ref(false);
|
||||||
const isSaving = ref(false);
|
const isSaving = ref(false);
|
||||||
const requestNo = ref("");
|
const requestNo = ref("");
|
||||||
|
const photoUrlList = computed(() => photoItems.value.map(item => item.url));
|
||||||
|
|
||||||
function confirmTime(value: string | number): void {
|
function confirmTime(value: string | number): void {
|
||||||
occurredAtText.value = String(value);
|
occurredAtText.value = String(value);
|
||||||
@@ -154,7 +165,7 @@ onUnmounted(() => navBarStore.clearRight());
|
|||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.package-photo-field {
|
.photo-field {
|
||||||
padding: 0 1rem;
|
padding: 0 1rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item label-width="0">
|
<t-form-item label-width="0">
|
||||||
<package-photo-field v-model="photoItems" title="操作照片" :required="true" />
|
<photo-scroller
|
||||||
|
:model-value="photoUrlList"
|
||||||
|
v-model:photo-items="photoItems"
|
||||||
|
:upload="uploadPhotoList"
|
||||||
|
:edit-mode="true"
|
||||||
|
title="操作照片"
|
||||||
|
required
|
||||||
|
/>
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
</t-form>
|
</t-form>
|
||||||
</div>
|
</div>
|
||||||
@@ -60,6 +67,7 @@
|
|||||||
import { watch } from "vue";
|
import { watch } from "vue";
|
||||||
import { BottomPopup } from "timi-tdesign-mobile";
|
import { BottomPopup } from "timi-tdesign-mobile";
|
||||||
import ProductAPI from "@/api/ProductAPI";
|
import ProductAPI from "@/api/ProductAPI";
|
||||||
|
import PhotoScroller from "@/components/PhotoScroller.vue";
|
||||||
import type {
|
import type {
|
||||||
Product,
|
Product,
|
||||||
ProductStock,
|
ProductStock,
|
||||||
@@ -71,6 +79,7 @@ import { getErrorMessage } from "@/utils/error";
|
|||||||
import { createProductRequestNo, getStockOperationText } from "@/utils/product";
|
import { createProductRequestNo, getStockOperationText } from "@/utils/product";
|
||||||
import { Message } from "@/utils/message";
|
import { Message } from "@/utils/message";
|
||||||
import type { PhotoItem } from "@/types/Photo";
|
import type { PhotoItem } from "@/types/Photo";
|
||||||
|
import { uploadPhotoList } from "@/utils/photo";
|
||||||
|
|
||||||
type ManualStockOperation = Extract<StockOperationType, "MANUAL_IN" | "MANUAL_OUT" | "STOCKTAKE_GAIN" | "STOCKTAKE_LOSS">;
|
type ManualStockOperation = Extract<StockOperationType, "MANUAL_IN" | "MANUAL_OUT" | "STOCKTAKE_GAIN" | "STOCKTAKE_LOSS">;
|
||||||
type PickerValue = string | number;
|
type PickerValue = string | number;
|
||||||
@@ -91,6 +100,7 @@ const quantity = ref("");
|
|||||||
const remark = ref("");
|
const remark = ref("");
|
||||||
const requestNo = ref("");
|
const requestNo = ref("");
|
||||||
const photoItems = ref<PhotoItem[]>([]);
|
const photoItems = ref<PhotoItem[]>([]);
|
||||||
|
const photoUrlList = computed(() => photoItems.value.map(item => item.url));
|
||||||
const selectedUnitId = ref("");
|
const selectedUnitId = ref("");
|
||||||
const unitPickerVisible = ref(false);
|
const unitPickerVisible = ref(false);
|
||||||
const unitDraft = ref<PickerValue[]>([]);
|
const unitDraft = ref<PickerValue[]>([]);
|
||||||
|
|||||||
@@ -107,7 +107,14 @@
|
|||||||
<t-textarea v-model="revokeRemark" borderless autosize maxlength="500" placeholder="请输入冲正备注" />
|
<t-textarea v-model="revokeRemark" borderless autosize maxlength="500" placeholder="请输入冲正备注" />
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
</t-form>
|
</t-form>
|
||||||
<package-photo-field v-model="revokePhotoItems" required title="冲正凭证照片" />
|
<photo-scroller
|
||||||
|
:model-value="revokePhotoUrlList"
|
||||||
|
v-model:photo-items="revokePhotoItems"
|
||||||
|
:upload="uploadPhotoList"
|
||||||
|
:edit-mode="true"
|
||||||
|
required
|
||||||
|
title="冲正凭证照片"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</bottom-popup>
|
</bottom-popup>
|
||||||
</div>
|
</div>
|
||||||
@@ -128,6 +135,7 @@ import { getErrorMessage } from "@/utils/error";
|
|||||||
import { createProductRequestNo, getProductBaseUnitName, getProductQuantityText, getStockOperationText } from "@/utils/product";
|
import { createProductRequestNo, getProductBaseUnitName, getProductQuantityText, getStockOperationText } from "@/utils/product";
|
||||||
import { Message } from "@/utils/message";
|
import { Message } from "@/utils/message";
|
||||||
import { permissionCodes } from "@/utils/permission";
|
import { permissionCodes } from "@/utils/permission";
|
||||||
|
import { uploadPhotoList } from "@/utils/photo";
|
||||||
|
|
||||||
defineOptions({ name: "ProductStockRecordDetail" });
|
defineOptions({ name: "ProductStockRecordDetail" });
|
||||||
|
|
||||||
@@ -143,6 +151,7 @@ const revokeVisible = ref(false);
|
|||||||
const revokeRemark = ref("");
|
const revokeRemark = ref("");
|
||||||
const revokeRequestNo = ref("");
|
const revokeRequestNo = ref("");
|
||||||
const revokePhotoItems = ref<PhotoItem[]>([]);
|
const revokePhotoItems = ref<PhotoItem[]>([]);
|
||||||
|
const revokePhotoUrlList = computed(() => revokePhotoItems.value.map(item => item.url));
|
||||||
const canManualRevoke = computed(() => {
|
const canManualRevoke = computed(() => {
|
||||||
const operationType = record.value?.operationType;
|
const operationType = record.value?.operationType;
|
||||||
return (operationType === "MANUAL_IN" || operationType === "MANUAL_OUT")
|
return (operationType === "MANUAL_IN" || operationType === "MANUAL_OUT")
|
||||||
|
|||||||
@@ -143,7 +143,7 @@
|
|||||||
<t-textarea v-model.trim="remark" class="textarea" borderless autosize maxlength="500" placeholder="记录本单需要注意的内容" />
|
<t-textarea v-model.trim="remark" class="textarea" borderless autosize maxlength="500" placeholder="记录本单需要注意的内容" />
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<div class="photo-part">
|
<div class="photo-part">
|
||||||
<package-photo-field v-model="photoItems" title="订单照片(选填)" />
|
<photo-scroller :model-value="photoUrlList" v-model:photo-items="photoItems" :upload="uploadPhotoList" :edit-mode="true" title="订单照片(选填)" />
|
||||||
</div>
|
</div>
|
||||||
</t-form>
|
</t-form>
|
||||||
<div class="submit-wrap glass-white bg-white">
|
<div class="submit-wrap glass-white bg-white">
|
||||||
@@ -178,7 +178,7 @@ import SalesOrderAPI from "@/api/SalesOrderAPI";
|
|||||||
import CodeScanner from "@/components/CodeScanner.vue";
|
import CodeScanner from "@/components/CodeScanner.vue";
|
||||||
import CustomerProfileCard from "@/components/CustomerProfileCard.vue";
|
import CustomerProfileCard from "@/components/CustomerProfileCard.vue";
|
||||||
import CustomerSelector from "@/components/CustomerSelector.vue";
|
import CustomerSelector from "@/components/CustomerSelector.vue";
|
||||||
import PackagePhotoField from "@/components/PackagePhotoField.vue";
|
import PhotoScroller from "@/components/PhotoScroller.vue";
|
||||||
import type { Customer } from "@/types/Customer";
|
import type { Customer } from "@/types/Customer";
|
||||||
import type { Product, ProductUnit } from "@/types/Product";
|
import type { Product, ProductUnit } from "@/types/Product";
|
||||||
import type { PhotoItem } from "@/types/Photo";
|
import type { PhotoItem } from "@/types/Photo";
|
||||||
@@ -188,6 +188,7 @@ import { formatProductAmount } from "@/utils/product";
|
|||||||
import { getErrorMessage } from "@/utils/error";
|
import { getErrorMessage } from "@/utils/error";
|
||||||
import { Message } from "@/utils/message";
|
import { Message } from "@/utils/message";
|
||||||
import { permissionCodes } from "@/utils/permission";
|
import { permissionCodes } from "@/utils/permission";
|
||||||
|
import { uploadPhotoList } from "@/utils/photo";
|
||||||
import { useNavBarStore } from "@/store/navBarStore";
|
import { useNavBarStore } from "@/store/navBarStore";
|
||||||
import { useSettingStore } from "@/store/settingStore";
|
import { useSettingStore } from "@/store/settingStore";
|
||||||
|
|
||||||
@@ -208,6 +209,7 @@ const draftTime = ref("");
|
|||||||
const pickerVisible = ref(false);
|
const pickerVisible = ref(false);
|
||||||
const isSaving = ref(false);
|
const isSaving = ref(false);
|
||||||
const photoItems = ref<PhotoItem[]>([]);
|
const photoItems = ref<PhotoItem[]>([]);
|
||||||
|
const photoUrlList = computed(() => photoItems.value.map(item => item.url));
|
||||||
const maxQuantity = Number.MAX_SAFE_INTEGER;
|
const maxQuantity = Number.MAX_SAFE_INTEGER;
|
||||||
const productCache = new Map<string, Product>();
|
const productCache = new Map<string, Product>();
|
||||||
const requestNo = ref("");
|
const requestNo = ref("");
|
||||||
|
|||||||
@@ -2,29 +2,78 @@
|
|||||||
<div v-can="permissionCodes.fundReceive.create" class="sales-order-operation">
|
<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-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 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="收款金额" required-mark>
|
||||||
<t-form-item label="收款时间" arrow @click="pickerVisible = true"><span class="picker-text" v-text="occurredAtText || '使用当前时间'" /></t-form-item>
|
<t-input
|
||||||
|
v-model.trim="amountText"
|
||||||
|
type="number"
|
||||||
|
align="right"
|
||||||
|
borderless
|
||||||
|
placeholder="请输入本次收款金额"
|
||||||
|
/>
|
||||||
|
<template #help>
|
||||||
|
<div class="help">
|
||||||
|
<t-button
|
||||||
|
size="extra-small"
|
||||||
|
theme="primary"
|
||||||
|
variant="outline"
|
||||||
|
@click="fillUnpaidAmount"
|
||||||
|
>全额收款</t-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</t-form-item>
|
||||||
|
<t-form-item label="收款时间" content-align="right">
|
||||||
|
<div class="time-actions">
|
||||||
|
<t-button
|
||||||
|
theme="primary"
|
||||||
|
variant="outline"
|
||||||
|
size="extra-small"
|
||||||
|
:content="occurredAtText || '此刻'"
|
||||||
|
@click="openOccurredAtPicker"
|
||||||
|
/>
|
||||||
|
<t-button
|
||||||
|
v-if="occurredAtText"
|
||||||
|
theme="primary"
|
||||||
|
variant="text"
|
||||||
|
size="extra-small"
|
||||||
|
content="此刻"
|
||||||
|
@click="resetOccurredAt"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</t-form-item>
|
||||||
<t-form-item label="备注"><t-textarea v-model.trim="remark" borderless autosize maxlength="500" /></t-form-item>
|
<t-form-item label="备注"><t-textarea v-model.trim="remark" borderless autosize maxlength="500" /></t-form-item>
|
||||||
|
<t-form-item label-width="0">
|
||||||
|
<photo-scroller
|
||||||
|
v-if="order"
|
||||||
|
:model-value="photoUrlList"
|
||||||
|
v-model:photo-items="photoItems"
|
||||||
|
class="photo-field"
|
||||||
|
:upload="uploadPhotoList"
|
||||||
|
:edit-mode="true"
|
||||||
|
required
|
||||||
|
title="收款凭证照片"
|
||||||
|
/>
|
||||||
|
</t-form-item>
|
||||||
</t-form>
|
</t-form>
|
||||||
<package-photo-field v-if="order" v-model="photoItems" required title="收款凭证照片" />
|
|
||||||
<t-empty v-if="!order" :description="loading ? '正在加载订单' : '订单不存在'" />
|
<t-empty v-if="!order" :description="loading ? '正在加载订单' : '订单不存在'" />
|
||||||
<t-popup v-model="pickerVisible" placement="bottom">
|
<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-date-time-picker v-if="pickerVisible" v-model="draftTime" format="YYYY-MM-DD HH:mm" :mode="['date', 'minute']" title="收款时间" @cancel="pickerVisible = false" @confirm="confirmOccurredAt" />
|
||||||
</t-popup>
|
</t-popup>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useUserStore, type Attachment } from "timi-web-api";
|
import { useUserStore, type Attachment } from "timi-web-api";
|
||||||
import PackagePhotoField from "@/components/PackagePhotoField.vue";
|
import { Time } from "timi-web";
|
||||||
|
import PhotoScroller from "@/components/PhotoScroller.vue";
|
||||||
import SalesOrderAPI from "@/api/SalesOrderAPI";
|
import SalesOrderAPI from "@/api/SalesOrderAPI";
|
||||||
import { useNavBarStore } from "@/store/navBarStore";
|
import { useNavBarStore } from "@/store/navBarStore";
|
||||||
import type { SalesOrder } from "@/types/SalesOrder";
|
import type { SalesOrder } from "@/types/SalesOrder";
|
||||||
import type { PhotoItem } from "@/types/Photo";
|
import type { PhotoItem } from "@/types/Photo";
|
||||||
import { createPackageRequestNo, formatPackageAmount, parsePackageAmount } from "@/utils/package";
|
import { createPackageRequestNo, formatPackageAmount, formatPackageAmountInput, parsePackageAmount } from "@/utils/package";
|
||||||
import { getErrorMessage } from "@/utils/error";
|
import { getErrorMessage } from "@/utils/error";
|
||||||
import { Message } from "@/utils/message";
|
import { Message } from "@/utils/message";
|
||||||
import { permissionCodes } from "@/utils/permission";
|
import { permissionCodes } from "@/utils/permission";
|
||||||
|
import { uploadPhotoList } from "@/utils/photo";
|
||||||
|
|
||||||
defineOptions({ name: "SalesOrderPayment" });
|
defineOptions({ name: "SalesOrderPayment" });
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
@@ -40,25 +89,68 @@ const draftTime = ref("");
|
|||||||
const pickerVisible = ref(false);
|
const pickerVisible = ref(false);
|
||||||
const remark = ref("");
|
const remark = ref("");
|
||||||
const photoItems = ref<PhotoItem[]>([]);
|
const photoItems = ref<PhotoItem[]>([]);
|
||||||
|
const photoUrlList = computed(() => photoItems.value.map(item => item.url));
|
||||||
const requestNo = ref("");
|
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 openOccurredAtPicker(): void {
|
||||||
function buildAttachments(): Partial<Attachment>[] { return photoItems.value.flatMap(item => item.tempFileId ? [{ tempFileId: item.tempFileId, attachType: "PHOTO" }] : []); }
|
draftTime.value = occurredAtText.value || Time.formatDateTime(new Date());
|
||||||
|
pickerVisible.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function confirmOccurredAt(value?: string | number): void {
|
||||||
|
occurredAtText.value = String(value || draftTime.value);
|
||||||
|
pickerVisible.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetOccurredAt(): void {
|
||||||
|
occurredAtText.value = "";
|
||||||
|
draftTime.value = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function fillUnpaidAmount(): void {
|
||||||
|
amountText.value = formatPackageAmountInput(order.value?.unpaidAmount);
|
||||||
|
}
|
||||||
|
|
||||||
|
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> {
|
async function loadData(): Promise<void> {
|
||||||
const id = String(route.params.id || "");
|
const id = String(route.params.id || "");
|
||||||
if (!id) return;
|
if (!id) return;
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
try { order.value = await SalesOrderAPI.detail(id); }
|
try {
|
||||||
catch (error) { Message.error(getErrorMessage(error, "订单加载失败")); }
|
order.value = await SalesOrderAPI.detail(id);
|
||||||
finally { loading.value = false; }
|
} catch (error) {
|
||||||
|
Message.error(getErrorMessage(error, "订单加载失败"));
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function save(): Promise<void> {
|
async function save(): Promise<void> {
|
||||||
if (saving.value || !order.value?.id) return;
|
if (saving.value || !order.value?.id) return;
|
||||||
const amount = parsePackageAmount(amountText.value);
|
const amount = parsePackageAmount(amountText.value);
|
||||||
const attachmentList = buildAttachments();
|
const attachmentList = buildAttachments();
|
||||||
if (amount === undefined || amount === 0) { Message.warning("请输入合法的正金额"); return; }
|
if (amount === undefined || amount === 0) {
|
||||||
if (!attachmentList.length) { Message.warning("收款必须上传至少一张照片"); return; }
|
Message.warning("请输入合法的正金额");
|
||||||
if (!requestNo.value) requestNo.value = createPackageRequestNo(); saving.value = true;
|
return;
|
||||||
|
}
|
||||||
|
if (!attachmentList.length) {
|
||||||
|
Message.warning("收款必须上传至少一张照片");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!requestNo.value) requestNo.value = createPackageRequestNo();
|
||||||
|
saving.value = true;
|
||||||
try {
|
try {
|
||||||
await SalesOrderAPI.receivePayment({
|
await SalesOrderAPI.receivePayment({
|
||||||
salesOrderId: order.value.id,
|
salesOrderId: order.value.id,
|
||||||
@@ -70,17 +162,52 @@ async function save(): Promise<void> {
|
|||||||
});
|
});
|
||||||
Message.success("收款登记已提交");
|
Message.success("收款登记已提交");
|
||||||
await router.replace(`/management/sales-orders/${order.value.id}`);
|
await router.replace(`/management/sales-orders/${order.value.id}`);
|
||||||
} catch (error) { Message.error(getErrorMessage(error, "收款登记失败,重试会复用同一请求号")); }
|
} catch (error) {
|
||||||
finally { saving.value = false; }
|
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(); });
|
|
||||||
|
onMounted(async () => {
|
||||||
|
if (!userStore.hasPermissions(permissionCodes.fundReceive.create)) return;
|
||||||
|
navBarStore.setRightText("提交", "var(--td-brand-color)", save);
|
||||||
|
await loadData();
|
||||||
|
});
|
||||||
onUnmounted(() => navBarStore.clearRight());
|
onUnmounted(() => navBarStore.clearRight());
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
.sales-order-operation { gap: 1rem; padding: calc(var(--app-nav-height) + 1rem) 0 2rem; display: flex; flex-direction: column;
|
.sales-order-operation {
|
||||||
.form { margin: 0 1rem; padding: 0 .5rem; background: var(--td-bg-color-container); box-shadow: var(--td-shadow-1); border-radius: 1rem; overflow: hidden; }
|
gap: 1rem;
|
||||||
.picker-text { width: 100%; text-align: right; }
|
padding: calc(var(--app-nav-height) + 1rem) 0 2rem;
|
||||||
.package-photo-field { padding: 0 1rem; }
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.form {
|
||||||
|
|
||||||
|
.help {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
margin-top: .5rem;
|
||||||
|
justify-content: end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.picker-text {
|
||||||
|
width: 100%;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-actions {
|
||||||
|
gap: .5rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-field {
|
||||||
|
padding: 0 1rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -10,7 +10,16 @@
|
|||||||
<t-form-item label="退款时间" arrow @click="pickerVisible = true"><span class="picker-text" v-text="occurredAtText || '使用当前时间'" /></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-item label="备注"><t-textarea v-model.trim="remark" borderless autosize maxlength="500" /></t-form-item>
|
||||||
</t-form>
|
</t-form>
|
||||||
<package-photo-field v-if="order" v-model="photoItems" required title="退款凭证照片" />
|
<photo-scroller
|
||||||
|
v-if="order"
|
||||||
|
:model-value="photoUrlList"
|
||||||
|
v-model:photo-items="photoItems"
|
||||||
|
class="photo-field"
|
||||||
|
:upload="uploadPhotoList"
|
||||||
|
:edit-mode="true"
|
||||||
|
required
|
||||||
|
title="退款凭证照片"
|
||||||
|
/>
|
||||||
<t-empty v-if="!order" :description="loading ? '正在加载订单' : '订单不存在'" />
|
<t-empty v-if="!order" :description="loading ? '正在加载订单' : '订单不存在'" />
|
||||||
<t-popup v-model="pickerVisible" placement="bottom">
|
<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-date-time-picker v-model="draftTime" format="YYYY-MM-DD HH:mm" :mode="['date', 'minute']" title="退款时间" @cancel="pickerVisible = false" @confirm="confirmTime" />
|
||||||
@@ -20,7 +29,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useUserStore, type Attachment } from "timi-web-api";
|
import { useUserStore, type Attachment } from "timi-web-api";
|
||||||
import PackagePhotoField from "@/components/PackagePhotoField.vue";
|
import PhotoScroller from "@/components/PhotoScroller.vue";
|
||||||
import SalesOrderAPI from "@/api/SalesOrderAPI";
|
import SalesOrderAPI from "@/api/SalesOrderAPI";
|
||||||
import { useNavBarStore } from "@/store/navBarStore";
|
import { useNavBarStore } from "@/store/navBarStore";
|
||||||
import type { SalesOrder } from "@/types/SalesOrder";
|
import type { SalesOrder } from "@/types/SalesOrder";
|
||||||
@@ -29,6 +38,7 @@ import { createPackageRequestNo, formatPackageAmount, parsePackageAmount } from
|
|||||||
import { getErrorMessage } from "@/utils/error";
|
import { getErrorMessage } from "@/utils/error";
|
||||||
import { Message } from "@/utils/message";
|
import { Message } from "@/utils/message";
|
||||||
import { permissionCodes } from "@/utils/permission";
|
import { permissionCodes } from "@/utils/permission";
|
||||||
|
import { uploadPhotoList } from "@/utils/photo";
|
||||||
|
|
||||||
defineOptions({ name: "SalesOrderRefund" });
|
defineOptions({ name: "SalesOrderRefund" });
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
@@ -45,6 +55,7 @@ const draftTime = ref("");
|
|||||||
const pickerVisible = ref(false);
|
const pickerVisible = ref(false);
|
||||||
const remark = ref("");
|
const remark = ref("");
|
||||||
const photoItems = ref<PhotoItem[]>([]);
|
const photoItems = ref<PhotoItem[]>([]);
|
||||||
|
const photoUrlList = computed(() => photoItems.value.map(item => item.url));
|
||||||
const requestNo = ref("");
|
const requestNo = ref("");
|
||||||
function confirmTime(value: string | number): void { occurredAtText.value = String(value); pickerVisible.value = false; }
|
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 resolveTimestamp(value: string): number | undefined { if (!value) return undefined; const timestamp = new Date(value.replace(" ", "T")).getTime(); return Number.isFinite(timestamp) ? timestamp : undefined; }
|
||||||
@@ -86,6 +97,6 @@ onUnmounted(() => navBarStore.clearRight());
|
|||||||
.sales-order-operation { gap: 1rem; padding: calc(var(--app-nav-height) + 1rem) 0 2rem; display: flex; flex-direction: column;
|
.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; }
|
.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; }
|
.picker-text { width: 100%; text-align: right; }
|
||||||
.package-photo-field { padding: 0 1rem; }
|
.photo-field { padding: 0 1rem; }
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { AttachmentAPI } from "timi-web-api";
|
||||||
|
import type { PhotoItem } from "@/types/Photo";
|
||||||
|
import { compressUploadImageList } from "@/utils/imageCompression";
|
||||||
|
|
||||||
|
/** 压缩并上传照片,返回可直接交给照片滚动组件展示的临时附件。 */
|
||||||
|
export async function uploadPhotoList(fileList: File[]): Promise<PhotoItem[]> {
|
||||||
|
const tempFileList = await AttachmentAPI.upload(await compressUploadImageList(fileList));
|
||||||
|
return tempFileList
|
||||||
|
.filter(item => !!item.id)
|
||||||
|
.map(item => ({
|
||||||
|
tempFileId: item.id,
|
||||||
|
attachType: "PHOTO",
|
||||||
|
url: AttachmentAPI.getTempReadURL(item.id || "")
|
||||||
|
}));
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user