702 lines
22 KiB
Vue
702 lines
22 KiB
Vue
<template>
|
|
<div v-can="permissionCodes.salesOrder.read" class="sales-order-detail">
|
|
<template v-if="order">
|
|
<customer-profile-card v-if="order.customer" class="customer" :customer="order.customer" v-entrance-transition />
|
|
<section class="hero" v-entrance-transition="35">
|
|
<div class="header">
|
|
<span class="eyebrow">销售订单</span>
|
|
<div class="tags">
|
|
<t-tag :content="getSalesOrderTypeText(order.orderType)" theme="primary" variant="light-outline" />
|
|
<t-tag :content="getSalesOrderStatusText(order.status)" variant="light-outline" :theme="getStatusTheme(order.status)" />
|
|
</div>
|
|
</div>
|
|
<h1 class="no selectable" v-text="order.orderNo" />
|
|
<div class="meta">
|
|
<pass-time-label class="time" :timestamp="order.purchasedAt" underline />
|
|
<div class="amount red text-bold">
|
|
<span>¥</span>
|
|
<strong class="value" v-text="formatPackageAmount(order.saleAmount)" />
|
|
</div>
|
|
</div>
|
|
</section>
|
|
<t-form class="info" title="订单信息" @submit="saveEdit" v-entrance-transition="70">
|
|
<t-form-item v-if="order.sourceEventRecord?.id" label="来源登记记录" arrow @click="toEventRecord">
|
|
<span>查看来源记录</span>
|
|
</t-form-item>
|
|
<t-form-item label="备注">
|
|
<t-textarea v-if="isEditing" v-model.trim="editRemark" class="remark-editor" borderless autosize maxlength="500" placeholder="记录本单需要注意的内容" />
|
|
<span v-else v-text="Text.display(order.remark)" />
|
|
</t-form-item>
|
|
<t-form-item label-width="0">
|
|
<div class="photo-wrap">
|
|
<photo-scroller
|
|
:model-value="photoURLList"
|
|
v-model:photo-items="photoItems"
|
|
:edit-mode="isEditing"
|
|
:show-add="isEditing"
|
|
:show-remove="isEditing"
|
|
:show-viewer-actions="isEditing"
|
|
:upload="isEditing ? uploadPhotoList : undefined"
|
|
title="订单照片"
|
|
v-entrance-transition="105"
|
|
/>
|
|
</div>
|
|
</t-form-item>
|
|
<t-form-item v-if="canUpdate" label-width="0">
|
|
<div class="edit-actions">
|
|
<t-button
|
|
v-if="!isEditing"
|
|
type="button"
|
|
size="extra-small"
|
|
theme="primary"
|
|
@click="toEdit"
|
|
>编辑</t-button>
|
|
<template v-else>
|
|
<t-button
|
|
type="button"
|
|
size="extra-small"
|
|
:disabled="isSaving"
|
|
@click="cancelEdit"
|
|
>取消</t-button>
|
|
<t-button
|
|
type="submit"
|
|
theme="primary"
|
|
size="extra-small"
|
|
:loading="isSaving"
|
|
:disabled="isSaving"
|
|
>保存</t-button>
|
|
</template>
|
|
</div>
|
|
</t-form-item>
|
|
</t-form>
|
|
<t-form
|
|
v-if="order.itemList?.length"
|
|
class="form product"
|
|
title="订单明细"
|
|
theme="card"
|
|
label-width="0"
|
|
v-entrance-transition="140"
|
|
>
|
|
<t-form-item class="form-item" v-for="item in order.itemList" :key="item.id">
|
|
<div class="item">
|
|
<div class="header">
|
|
<div class="left">
|
|
<t-image
|
|
v-if="getProductPhotoURL(item)"
|
|
:src="getProductPhotoURL(item)"
|
|
:alt="item.productNameSnapshot || '产品图片'"
|
|
fit="cover"
|
|
class="avatar"
|
|
/>
|
|
<div
|
|
v-else
|
|
class="avatar fallback"
|
|
v-text="(item.productNameSnapshot || '产').slice(0, 1)"
|
|
/>
|
|
</div>
|
|
<div class="main">
|
|
<div class="title">
|
|
<div class="name-gift">
|
|
<span class="name text-bold" v-text="item.productNameSnapshot || '未命名产品'" />
|
|
<t-tag v-if="item.gifted" size="small" theme="success" content="赠送" />
|
|
</div>
|
|
<t-tag
|
|
:content="getItemFulfillmentStatus(item).text"
|
|
:theme="getItemFulfillmentStatus(item).theme"
|
|
variant="light-outline"
|
|
/>
|
|
</div>
|
|
<div class="description">
|
|
<strong
|
|
v-if="item.lineAmount !== null && item.lineAmount !== undefined"
|
|
class="red"
|
|
v-text="`¥${formatPackageAmount(item.lineAmount)}`"
|
|
/>
|
|
<t-tag v-else size="small" content="套餐权益" />
|
|
<product-sku-snapshot-tags
|
|
:product-name="item.productNameSnapshot"
|
|
:snapshot="item.skuSpecSnapshot"
|
|
:fallback="item.specSummarySnapshot || item.skuNameSnapshot"
|
|
:unit="item.unitSnapshot"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="content">
|
|
<div class="metric">
|
|
<strong v-text="getPackageQuantityText(item.quantity)" />
|
|
<span>订单数量</span>
|
|
</div>
|
|
<div class="metric">
|
|
<strong :class="getAvailableQuantityClass(item)" v-text="getItemQuantityText(item, item.availableBaseQuantity)" />
|
|
<span>可履约</span>
|
|
</div>
|
|
<div class="metric">
|
|
<strong v-text="getItemQuantityText(item, item.pendingBaseQuantity)" />
|
|
<span>待核销</span>
|
|
</div>
|
|
<div class="metric">
|
|
<strong :class="getFulfilledQuantityClass(item)" v-text="getItemQuantityText(item, item.fulfilledBaseQuantity)" />
|
|
<span>已履约</span>
|
|
</div>
|
|
<div class="metric">
|
|
<strong :class="getRemainingQuantityClass(item)" v-text="getItemQuantityText(item, item.remainingBaseQuantity)" />
|
|
<span>剩余</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</t-form-item>
|
|
<t-form-item label="履约记录" arrow @click="toFulfillmentList">
|
|
查看本单所有履约单
|
|
</t-form-item>
|
|
</t-form>
|
|
<t-button
|
|
v-if="canCreateFulfillment && canFulfillByPayment && order.status === 'ACTIVE'"
|
|
class="cell-button"
|
|
theme="primary"
|
|
content="履约产品"
|
|
@click="toCreateFulfillment"
|
|
/>
|
|
<t-cell-group class="finance-panel" title="资金摘要" theme="card">
|
|
<t-cell title="成交金额">
|
|
<template #note><span :class="getPaymentAmountClass(order.paymentStatus)" v-text="formatPackageAmount(order.saleAmount)" /></template>
|
|
</t-cell>
|
|
<t-cell title="已生效收款">
|
|
<template #note><span :class="getAmountClass(order.paidAmount, 'green')" v-text="formatPackageAmount(order.paidAmount)" /></template>
|
|
</t-cell>
|
|
<t-cell title="待核销收款">
|
|
<template #note><span :class="getAmountClass(order.pendingPaidAmount, 'orange')" v-text="formatPackageAmount(order.pendingPaidAmount)" /></template>
|
|
</t-cell>
|
|
<t-cell title="剩余待收款">
|
|
<template #note><span :class="getRemainingAmountClass(order.unpaidAmount)" v-text="formatPackageAmount(order.unpaidAmount)" /></template>
|
|
</t-cell>
|
|
<t-cell title="有效退款">
|
|
<template #note><span :class="getAmountClass(order.refundedAmount, 'red')" v-text="formatPackageAmount(order.refundedAmount)" /></template>
|
|
</t-cell>
|
|
<t-cell title="收款状态" :note="getSalesPaymentStatusText(order.paymentStatus)" />
|
|
<t-cell v-if="order.paymentStatus === 'PAID'" title="全额实收时间" :note="getPaidAtText(order.paidAt)" />
|
|
<t-cell title="退款状态" :note="getSalesRefundStatusText(order.refundStatus)" />
|
|
<t-cell
|
|
v-if="canReadFund || canReadMyReceive"
|
|
:title="canReadFund ? '查看全部资金流水' : '查看我的收款申请'"
|
|
arrow
|
|
@click="toFundRecords"
|
|
/>
|
|
</t-cell-group>
|
|
<t-cell-group v-if="order.status === 'CANCELLED'" class="cancel-info" title="取消记录" theme="card">
|
|
<t-cell title="取消时间">
|
|
<template #note>
|
|
<pass-time-label :timestamp="order.cancelledAt" underline />
|
|
</template>
|
|
</t-cell>
|
|
<t-cell title="取消人" :note="order.cancelOperator?.name || order.cancelOperator?.nick || order.cancelOperatorUserId || '未知'" />
|
|
<t-cell title="取消原因" :note="order.cancelReason || '未填写'" />
|
|
</t-cell-group>
|
|
<section class="actions" v-entrance-transition="280">
|
|
<t-button
|
|
v-if="canReceive"
|
|
theme="primary"
|
|
@click="toPayment"
|
|
>登记收款</t-button>
|
|
<t-button
|
|
v-if="canRefund"
|
|
theme="danger"
|
|
@click="toRefund"
|
|
>登记退款</t-button>
|
|
<t-button
|
|
v-if="canConfirmNoRefund"
|
|
theme="default"
|
|
@click="toNoRefund"
|
|
>确认无需退款</t-button>
|
|
<t-button
|
|
v-if="canCancel"
|
|
theme="danger"
|
|
@click="toCancel"
|
|
>取消订单</t-button>
|
|
</section>
|
|
</template>
|
|
<t-empty v-else :description="loading ? '正在加载订单' : '订单不存在'" />
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { PassTimeLabel, Text, Time } from "timi-web";
|
|
import { type Attachment, useUserStore } from "timi-web-api";
|
|
import FulfillmentAPI from "@/api/FulfillmentAPI";
|
|
import SalesOrderAPI from "@/api/SalesOrderAPI";
|
|
import CustomerProfileCard from "@/components/CustomerProfileCard.vue";
|
|
import PhotoScroller from "@/components/PhotoScroller.vue";
|
|
import ProductSkuSnapshotTags from "@/components/ProductSkuSnapshotTags.vue";
|
|
import { useNavBarStore } from "@/store/navBarStore";
|
|
import type { GaoSalesOrderItem, GaoSalesPaymentStatus, SalesOrder } from "@/types/SalesOrder";
|
|
import type { PhotoItem } from "@/types/Photo";
|
|
import { convertPackageQuantity, formatPackageAmount, getPackageQuantityText } from "@/utils/package";
|
|
import { getAttachmentReadURL } from "@/utils/attachment";
|
|
import { getErrorMessage } from "@/utils/error";
|
|
import { getProductPhotoURL } from "@/utils/product";
|
|
import {
|
|
getSalesOrderStatusText,
|
|
getSalesOrderTypeText,
|
|
getSalesPaymentStatusText,
|
|
getSalesRefundStatusText,
|
|
getStatusTheme
|
|
} from "@/utils/salesOrder";
|
|
import { Message } from "@/utils/message";
|
|
import { gaoRoleCodes, permissionCodes } from "@/utils/permission";
|
|
import { uploadPhotoList } from "@/utils/photo";
|
|
|
|
defineOptions({ name: "SalesOrderDetail" });
|
|
const route = useRoute();
|
|
const router = useRouter();
|
|
const navBarStore = useNavBarStore();
|
|
const userStore = useUserStore();
|
|
const order = ref<SalesOrder>();
|
|
const hasPendingFulfillment = ref(false);
|
|
const loading = ref(false);
|
|
const isEditing = ref(false);
|
|
const isSaving = ref(false);
|
|
const editRemark = ref("");
|
|
const photoItems = ref<PhotoItem[]>([]);
|
|
const canReadFund = computed(() => userStore.hasPermissions(permissionCodes.fundRecord.read));
|
|
const canReadMyReceive = computed(() => userStore.hasPermissions(permissionCodes.fundReceive.create));
|
|
const canUpdate = computed(() => userStore.hasPermissions(permissionCodes.salesOrder.update));
|
|
const canCreateFulfillment = computed(() => userStore.hasPermissions(permissionCodes.orderFulfillment.create));
|
|
const canReceive = computed(() => !!order.value && order.value.status !== "CANCELLED" && order.value.paymentStatus !== "PAID" && userStore.hasPermissions(permissionCodes.fundReceive.create));
|
|
const canRefund = computed(() => !!order.value && order.value.status === "CANCELLED" && order.value.refundStatus === "PENDING" && userStore.hasPermissions(permissionCodes.fund.refund));
|
|
const canConfirmNoRefund = computed(() => !!order.value && order.value.status === "CANCELLED" && order.value.refundStatus === "PENDING" && userStore.hasPermissions(permissionCodes.fund.refund));
|
|
const canCancel = computed(() => !!order.value && order.value.status === "ACTIVE" && !hasPendingFulfillment.value && userStore.hasPermissions(permissionCodes.salesOrder.cancel));
|
|
const canFulfillByPayment = computed(() => order.value?.paymentStatus === "PAID" || userStore.hasRoles(gaoRoleCodes.storeManager));
|
|
const photoURLList = computed(() => photoItems.value.map(item => item.url));
|
|
|
|
function toPhotoItems(attachmentList?: readonly Partial<Attachment>[]): PhotoItem[] {
|
|
return (attachmentList || [])
|
|
.filter(item => !!item.id)
|
|
.map(item => ({
|
|
attachment: item,
|
|
attachType: item.attachType || "PHOTO",
|
|
previewUrl: getAttachmentReadURL(item.id || ""),
|
|
url: getAttachmentReadURL(item.id || "", 90)
|
|
}));
|
|
}
|
|
|
|
function buildAttachmentList(): Partial<Attachment>[] {
|
|
return photoItems.value.flatMap((item): Partial<Attachment>[] => {
|
|
const attachmentId = item.attachment?.id;
|
|
if (attachmentId) {
|
|
return [{ id: attachmentId, attachType: item.attachType || item.attachment?.attachType || "PHOTO" }];
|
|
}
|
|
return item.tempFileId ? [{ tempFileId: item.tempFileId, attachType: item.attachType || "PHOTO" }] : [];
|
|
});
|
|
}
|
|
|
|
function getPaidAtText(paidAt?: number | null): string {
|
|
return paidAt ? Time.toDateTime(paidAt) : "待后端回填";
|
|
}
|
|
|
|
function getPaymentAmountClass(status?: GaoSalesPaymentStatus): "green" | "orange" | "red" | "gray" {
|
|
if (status === "PAID") return "green";
|
|
if (status === "PARTIALLY_PAID") return "orange";
|
|
if (status === "UNPAID") return "red";
|
|
return "gray";
|
|
}
|
|
|
|
function getAmountClass(amount?: number | null, activeClass: "green" | "orange" | "red" = "green"): "green" | "orange" | "red" | "gray" {
|
|
if (amount === undefined || amount === null || amount === 0) return "gray";
|
|
return activeClass;
|
|
}
|
|
|
|
function getRemainingAmountClass(amount?: number | null): "green" | "red" | "gray" {
|
|
if (amount === undefined || amount === null) return "gray";
|
|
return amount === 0 ? "green" : "red";
|
|
}
|
|
|
|
function getItemUnitName(item: GaoSalesOrderItem): string {
|
|
return item.unitSnapshot || "单位";
|
|
}
|
|
|
|
function getBaseUnitName(item: GaoSalesOrderItem): string {
|
|
return item.baseUnitName || "基础单位";
|
|
}
|
|
|
|
function shouldDisplayBaseQuantity(item: GaoSalesOrderItem, baseQuantity?: number | string): boolean {
|
|
const isBaseUnit = Number(item.baseQuantityPerUnitSnapshot ?? 1) === 1;
|
|
return item.allowDecimalQuantity === false
|
|
&& !isBaseUnit
|
|
&& baseQuantity !== undefined
|
|
&& Number(baseQuantity) < Number(item.baseQuantityPerUnitSnapshot ?? 1);
|
|
}
|
|
|
|
function getItemQuantityText(item: GaoSalesOrderItem, baseQuantity?: number | string): string {
|
|
const quantity = convertPackageQuantity(
|
|
String(baseQuantity ?? "0"),
|
|
"1",
|
|
item.baseQuantityPerUnitSnapshot ?? "1"
|
|
);
|
|
const useBaseUnit = shouldDisplayBaseQuantity(item, baseQuantity);
|
|
const displayQuantity = useBaseUnit ? getPackageQuantityText(baseQuantity) : getPackageQuantityText(quantity ?? baseQuantity);
|
|
return `${displayQuantity} ${useBaseUnit ? getBaseUnitName(item) : getItemUnitName(item)}`;
|
|
}
|
|
|
|
function getAvailableQuantityClass(item: GaoSalesOrderItem): "green" | "orange" {
|
|
return Number(item.availableBaseQuantity || 0) === 0 ? "green" : "orange";
|
|
}
|
|
|
|
function getFulfilledQuantityClass(item: GaoSalesOrderItem): "green" | "orange" {
|
|
return Number(item.remainingBaseQuantity || 0) === 0 ? "green" : "orange";
|
|
}
|
|
|
|
function getRemainingQuantityClass(item: GaoSalesOrderItem): "green" | "orange" {
|
|
return Number(item.remainingBaseQuantity || 0) === 0 ? "green" : "orange";
|
|
}
|
|
|
|
function getItemFulfillmentStatus(item: GaoSalesOrderItem): { text: string; theme: "success" | "warning" | "danger" } {
|
|
const fulfilledQuantity = Number(item.fulfilledBaseQuantity || 0);
|
|
const remainingQuantity = Number(item.remainingBaseQuantity || 0);
|
|
if (0 < fulfilledQuantity && remainingQuantity === 0) {
|
|
return { text: "已完成", theme: "success" };
|
|
}
|
|
if (0 < fulfilledQuantity) {
|
|
return { text: "部分履约", theme: "warning" };
|
|
}
|
|
return { text: "未履约", theme: "danger" };
|
|
}
|
|
|
|
async function loadData(): Promise<void> {
|
|
const id = String(route.params.id || "");
|
|
isEditing.value = false;
|
|
navBarStore.clearRight();
|
|
if (!id) {
|
|
order.value = undefined;
|
|
hasPendingFulfillment.value = false;
|
|
photoItems.value = [];
|
|
return;
|
|
}
|
|
loading.value = true;
|
|
try {
|
|
order.value = await SalesOrderAPI.detail(id);
|
|
hasPendingFulfillment.value = false;
|
|
if (userStore.hasPermissions(permissionCodes.orderFulfillment.read)) {
|
|
try {
|
|
const [pendingResult, waitingStockResult] = await Promise.all([
|
|
FulfillmentAPI.list({ salesOrderId: id, status: "PENDING", index: 0, size: 1 }),
|
|
FulfillmentAPI.list({ salesOrderId: id, status: "WAITING_STOCK", index: 0, size: 1 })
|
|
]);
|
|
hasPendingFulfillment.value = !!pendingResult.list?.length || !!waitingStockResult.list?.length;
|
|
} catch {
|
|
// 取消接口仍会在后端校验待处理履约,查询失败时不阻断订单详情展示。
|
|
}
|
|
}
|
|
editRemark.value = order.value.remark || "";
|
|
photoItems.value = toPhotoItems(order.value.attachmentList);
|
|
} catch (error) {
|
|
Message.error(getErrorMessage(error, "订单加载失败"));
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
}
|
|
|
|
function toEdit(): void {
|
|
if (!order.value || !canUpdate.value) return;
|
|
editRemark.value = order.value.remark || "";
|
|
photoItems.value = toPhotoItems(order.value.attachmentList);
|
|
isEditing.value = true;
|
|
}
|
|
|
|
function cancelEdit(): void {
|
|
if (isSaving.value) return;
|
|
editRemark.value = order.value?.remark || "";
|
|
photoItems.value = toPhotoItems(order.value?.attachmentList);
|
|
isEditing.value = false;
|
|
}
|
|
|
|
async function saveEdit(): Promise<void> {
|
|
const orderId = order.value?.id;
|
|
if (!orderId || !canUpdate.value || !isEditing.value || isSaving.value) return;
|
|
isSaving.value = true;
|
|
try {
|
|
const result = await SalesOrderAPI.update({
|
|
id: orderId,
|
|
attachmentList: buildAttachmentList(),
|
|
remark: editRemark.value.trim() || null
|
|
});
|
|
order.value = result;
|
|
editRemark.value = result.remark || "";
|
|
photoItems.value = toPhotoItems(result.attachmentList);
|
|
isEditing.value = false;
|
|
Message.success("订单信息已保存");
|
|
} catch (error) {
|
|
Message.error(getErrorMessage(error, "订单信息保存失败"));
|
|
} finally {
|
|
isSaving.value = false;
|
|
}
|
|
}
|
|
|
|
async function toEventRecord(): Promise<void> {
|
|
if (order.value?.sourceEventRecord?.id) await router.push(`/management/registration-event-records/${order.value.sourceEventRecord.id}`);
|
|
}
|
|
|
|
async function toFundRecords(): Promise<void> {
|
|
if (order.value?.id) await router.push({
|
|
path: "/management/fund-records",
|
|
query: {
|
|
salesOrderId: order.value.id,
|
|
transitionDepth: String(getCurrentRouteDepth() + 1)
|
|
}
|
|
});
|
|
}
|
|
|
|
async function toFulfillmentList(): Promise<void> {
|
|
if (order.value?.id) await router.push({
|
|
path: "/management/order-fulfillments",
|
|
query: {
|
|
salesOrderId: order.value.id,
|
|
dateType: "all",
|
|
transitionDepth: String(getCurrentRouteDepth() + 1)
|
|
}
|
|
});
|
|
}
|
|
|
|
async function toCreateFulfillment(): Promise<void> {
|
|
if (order.value?.id) await router.push({
|
|
path: "/management/order-fulfillments/create",
|
|
query: {
|
|
salesOrderId: order.value.id,
|
|
transitionDepth: String(getCurrentRouteDepth() + 1)
|
|
}
|
|
});
|
|
}
|
|
|
|
async function toPayment(): Promise<void> {
|
|
if (order.value?.id) await router.push(`/management/sales-orders/${order.value.id}/payment`);
|
|
}
|
|
|
|
async function toRefund(): Promise<void> {
|
|
if (order.value?.id) await router.push(`/management/sales-orders/${order.value.id}/refund`);
|
|
}
|
|
|
|
async function toNoRefund(): Promise<void> {
|
|
if (order.value?.id) await router.push(`/management/sales-orders/${order.value.id}/no-refund`);
|
|
}
|
|
|
|
async function toCancel(): Promise<void> {
|
|
if (order.value?.id) await router.push(`/management/sales-orders/${order.value.id}/cancel`);
|
|
}
|
|
|
|
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(loadData);
|
|
watch(() => route.params.id, loadData);
|
|
onUnmounted(() => navBarStore.clearRight());
|
|
</script>
|
|
|
|
<style scoped lang="less">
|
|
.sales-order-detail {
|
|
gap: 1rem;
|
|
padding: calc(var(--app-nav-height) + 1rem) 0 2rem;
|
|
display: flex;
|
|
flex-direction: column;
|
|
|
|
.customer {
|
|
margin: 0 1rem;
|
|
}
|
|
|
|
.hero {
|
|
margin: 0 1rem;
|
|
padding: 1rem;
|
|
background: linear-gradient(135deg, #fff7ed, #eff6ff);
|
|
box-shadow: var(--td-shadow-1);
|
|
border-radius: 1.25rem;
|
|
|
|
.header {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
|
|
.tags {
|
|
gap: .5rem;
|
|
display: flex;
|
|
}
|
|
|
|
.eyebrow {
|
|
color: #c2410c;
|
|
display: block;
|
|
font-weight: 700;
|
|
letter-spacing: .08em;
|
|
}
|
|
}
|
|
|
|
.no {
|
|
margin: 1rem 0;
|
|
min-width: 0;
|
|
font-size: 1.2rem;
|
|
line-height: 1.35;
|
|
}
|
|
|
|
.meta {
|
|
gap: .75rem;
|
|
display: flex;
|
|
margin-top: .6rem;
|
|
align-items: end;
|
|
justify-content: space-between;
|
|
|
|
.time {
|
|
font-size: .875rem;
|
|
}
|
|
|
|
.amount {
|
|
|
|
.value {
|
|
font-size: 1.5rem;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.info {
|
|
margin: 0 1rem;
|
|
padding: .5rem 0;
|
|
overflow: hidden;
|
|
background: var(--td-bg-color-container, #fff);
|
|
box-shadow: var(--td-shadow-1);
|
|
border-radius: 1.25rem;
|
|
|
|
.form-title {
|
|
padding: .75rem 1rem .5rem;
|
|
font-size: .875rem;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.photo-wrap {
|
|
flex: 1;
|
|
}
|
|
|
|
.edit-actions {
|
|
gap: .5rem;
|
|
flex: 1;
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
}
|
|
|
|
.remark-editor {
|
|
width: 100%;
|
|
}
|
|
}
|
|
|
|
.product {
|
|
|
|
.form-item {
|
|
padding: 10px;
|
|
|
|
.item {
|
|
width: 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
|
|
.header {
|
|
width: 100%;
|
|
display: flex;
|
|
padding-bottom: .5rem;
|
|
border-bottom: 1px solid rgba(155, 155, 155, .2);
|
|
|
|
.left {
|
|
|
|
.avatar {
|
|
width: 3.5rem;
|
|
height: 3.5rem;
|
|
}
|
|
}
|
|
|
|
.main {
|
|
flex: 1;
|
|
display: flex;
|
|
padding-left: .5rem;
|
|
flex-direction: column;
|
|
justify-content: space-between;
|
|
|
|
.title {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
|
|
.name-gift {
|
|
gap: .5rem;
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
}
|
|
|
|
.description {
|
|
gap: .5rem;
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
}
|
|
}
|
|
}
|
|
|
|
.content {
|
|
display: flex;
|
|
padding-top: 5px;
|
|
justify-content: space-around;
|
|
|
|
.metric {
|
|
gap: .25rem;
|
|
flex: 1;
|
|
display: flex;
|
|
position: relative;
|
|
align-items: center;
|
|
flex-direction: column;
|
|
|
|
&:not(:last-child):after {
|
|
content: "";
|
|
top: 50%;
|
|
right: 0;
|
|
width: 1px;
|
|
height: 50%;
|
|
transform: translateY(calc(-50% + 2.5px));
|
|
position: absolute;
|
|
background: rgba(155, 155, 155, .2);
|
|
}
|
|
|
|
span {
|
|
color: var(--td-text-color-secondary);
|
|
font-size: .65rem;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.actions {
|
|
--td-button-border-radius: 1rem;
|
|
|
|
gap: .5rem;
|
|
padding: 0 1rem;
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.actions .t-button {
|
|
flex: 1 1 8rem;
|
|
}
|
|
}
|
|
|
|
:global(html.theme-dark) .sales-order-detail .hero {
|
|
background: linear-gradient(135deg, rgba(194, 65, 12, .2), rgba(37, 99, 235, .18));
|
|
|
|
.eyebrow, strong {
|
|
color: #fdba74;
|
|
}
|
|
}
|
|
</style>
|