add product-stock-preview for OrderFulfillmentDetail.vue
This commit is contained in:
@@ -57,6 +57,7 @@
|
|||||||
align="right"
|
align="right"
|
||||||
:disabled="!getFulfillmentUnit(item)"
|
:disabled="!getFulfillmentUnit(item)"
|
||||||
placeholder="请输入履约数量"
|
placeholder="请输入履约数量"
|
||||||
|
@change="refreshStockPreview"
|
||||||
>
|
>
|
||||||
<template #suffix-icon>
|
<template #suffix-icon>
|
||||||
<t-button
|
<t-button
|
||||||
@@ -92,9 +93,9 @@
|
|||||||
<t-form-item v-if="canDirectVerify" label="直接核销履约" content-align="right">
|
<t-form-item v-if="canDirectVerify" label="直接核销履约" content-align="right">
|
||||||
<t-switch v-model="directVerify" size="small" :disabled="isSaving || isStockChecking" />
|
<t-switch v-model="directVerify" size="small" :disabled="isSaving || isStockChecking" />
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item v-if="directVerify" label-width="0">
|
<t-form-item v-if="directVerify || stockPreviewList.length" label-width="0">
|
||||||
<div class="stock-tips">
|
<div class="stock-tips">
|
||||||
<span class="tips red">直接核销会立即扣减实物库存</span>
|
<span v-if="directVerify" class="tips red">直接核销会立即扣减实物库存</span>
|
||||||
<product-stock-preview v-if="stockPreviewList.length" :items="stockPreviewList" />
|
<product-stock-preview v-if="stockPreviewList.length" :items="stockPreviewList" />
|
||||||
</div>
|
</div>
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
@@ -302,9 +303,7 @@ async function fillAllQuantity(item: GaoSalesOrderItem): Promise<void> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
quantityMap[item.id] = quantity;
|
quantityMap[item.id] = quantity;
|
||||||
if (directVerify.value) {
|
await refreshStockPreview();
|
||||||
await previewDirectStock();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFulfillmentUnitList(item: GaoSalesOrderItem): ProductSkuUnit[] {
|
function getFulfillmentUnitList(item: GaoSalesOrderItem): ProductSkuUnit[] {
|
||||||
@@ -357,7 +356,7 @@ function openUnitPicker(item: GaoSalesOrderItem): void {
|
|||||||
unitPickerVisible.value = true;
|
unitPickerVisible.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function confirmUnitPicker(value: Array<string | number>): void {
|
async function confirmUnitPicker(value: Array<string | number>): Promise<void> {
|
||||||
const skuUnitId = String(value[0] || "");
|
const skuUnitId = String(value[0] || "");
|
||||||
const item = sourceItems.value.find(sourceItem => sourceItem.id === unitPickerSourceItemId.value);
|
const item = sourceItems.value.find(sourceItem => sourceItem.id === unitPickerSourceItemId.value);
|
||||||
const selectedUnit = item ? getFulfillmentUnitList(item).find(unit => unit.id === skuUnitId) : undefined;
|
const selectedUnit = item ? getFulfillmentUnitList(item).find(unit => unit.id === skuUnitId) : undefined;
|
||||||
@@ -366,6 +365,7 @@ function confirmUnitPicker(value: Array<string | number>): void {
|
|||||||
setFulfillmentUnit(item, fulfillmentUnit);
|
setFulfillmentUnit(item, fulfillmentUnit);
|
||||||
}
|
}
|
||||||
unitPickerVisible.value = false;
|
unitPickerVisible.value = false;
|
||||||
|
await refreshStockPreview();
|
||||||
}
|
}
|
||||||
|
|
||||||
function confirmTime(value: string | number): void {
|
function confirmTime(value: string | number): void {
|
||||||
@@ -386,7 +386,7 @@ function buildAttachments(): Partial<Attachment>[] {
|
|||||||
}] : []);
|
}] : []);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function validateDirectStock(): Promise<boolean> {
|
async function validateDirectStock(showShortageMessage = true): Promise<boolean> {
|
||||||
stockPreviewList.value = [];
|
stockPreviewList.value = [];
|
||||||
const selected = sourceItems.value.filter(item => !!item.id && quantityMap[item.id]);
|
const selected = sourceItems.value.filter(item => !!item.id && quantityMap[item.id]);
|
||||||
const skuMap = new Map<string, ProductSku>();
|
const skuMap = new Map<string, ProductSku>();
|
||||||
@@ -427,7 +427,9 @@ async function validateDirectStock(): Promise<boolean> {
|
|||||||
stockPreviewList.value = buildProductStockPreview(stockList, requirementList);
|
stockPreviewList.value = buildProductStockPreview(stockList, requirementList);
|
||||||
const shortageMessage = getProductStockShortageMessage(stockList, requirementList);
|
const shortageMessage = getProductStockShortageMessage(stockList, requirementList);
|
||||||
if (shortageMessage) {
|
if (shortageMessage) {
|
||||||
|
if (showShortageMessage) {
|
||||||
Message.warning(`${shortageMessage},不能直接核销履约`);
|
Message.warning(`${shortageMessage},不能直接核销履约`);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -438,15 +440,16 @@ async function validateDirectStock(): Promise<boolean> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function previewDirectStock(): Promise<void> {
|
async function previewDirectStock(force = false): Promise<void> {
|
||||||
if (!directVerify.value || isStockChecking.value) {
|
if ((!directVerify.value && !force) || isStockChecking.value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
stockCheckToken += 1;
|
stockCheckToken += 1;
|
||||||
const token = stockCheckToken;
|
const token = stockCheckToken;
|
||||||
|
const showShortageMessage = directVerify.value;
|
||||||
isStockChecking.value = true;
|
isStockChecking.value = true;
|
||||||
try {
|
try {
|
||||||
if (!await validateDirectStock() && token === stockCheckToken && directVerify.value) {
|
if (!await validateDirectStock(showShortageMessage) && token === stockCheckToken && directVerify.value) {
|
||||||
directVerify.value = false;
|
directVerify.value = false;
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
@@ -456,6 +459,13 @@ async function previewDirectStock(): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function refreshStockPreview(): Promise<void> {
|
||||||
|
if (!directVerify.value && !stockPreviewList.value.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await previewDirectStock(!directVerify.value);
|
||||||
|
}
|
||||||
|
|
||||||
function validate(): boolean {
|
function validate(): boolean {
|
||||||
if (!order.value?.id) {
|
if (!order.value?.id) {
|
||||||
Message.warning("请从订单详情进入履约创建");
|
Message.warning("请从订单详情进入履约创建");
|
||||||
@@ -523,7 +533,10 @@ async function save(): Promise<void> {
|
|||||||
attachmentList: buildAttachments(),
|
attachmentList: buildAttachments(),
|
||||||
remark: remark.value.trim() || null
|
remark: remark.value.trim() || null
|
||||||
};
|
};
|
||||||
if (directVerify.value && !await validateDirectStock()) return;
|
if (directVerify.value && !await validateDirectStock(true)) {
|
||||||
|
directVerify.value = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
const result = directVerify.value
|
const result = directVerify.value
|
||||||
? await FulfillmentAPI.createAndVerify(request)
|
? await FulfillmentAPI.createAndVerify(request)
|
||||||
: await FulfillmentAPI.create(request);
|
: await FulfillmentAPI.create(request);
|
||||||
|
|||||||
@@ -31,19 +31,8 @@
|
|||||||
v-for="item in fulfillment.itemList || []"
|
v-for="item in fulfillment.itemList || []"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
:title="item.productNameSnapshot || '未命名产品'"
|
:title="item.productNameSnapshot || '未命名产品'"
|
||||||
|
:note="getFulfillmentQuantityText(item)"
|
||||||
>
|
>
|
||||||
<template #description>
|
|
||||||
<div class="item-description">
|
|
||||||
<t-tag v-if="isGiftedFulfillmentItem(item)" size="small" theme="success" content="赠送" />
|
|
||||||
<product-sku-snapshot-tags
|
|
||||||
:product-name="item.productNameSnapshot"
|
|
||||||
:snapshot="item.skuSpecSnapshot"
|
|
||||||
:fallback="item.specSummarySnapshot || item.skuNameSnapshot || '默认 SKU'"
|
|
||||||
:unit="item.unitSnapshot"
|
|
||||||
/>
|
|
||||||
<span v-text="getFulfillmentQuantityText(item)" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template #leftIcon>
|
<template #leftIcon>
|
||||||
<t-image
|
<t-image
|
||||||
v-if="getProductPhotoURL(item)"
|
v-if="getProductPhotoURL(item)"
|
||||||
@@ -58,9 +47,29 @@
|
|||||||
v-text="(item.productNameSnapshot || '产').slice(0, 1)"
|
v-text="(item.productNameSnapshot || '产').slice(0, 1)"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
<template #description>
|
||||||
|
<div class="item-description">
|
||||||
|
<t-tag v-if="isGiftedFulfillmentItem(item)" size="small" theme="success" content="赠送" />
|
||||||
|
<product-sku-snapshot-tags
|
||||||
|
:product-name="item.productNameSnapshot"
|
||||||
|
:snapshot="item.skuSpecSnapshot"
|
||||||
|
:fallback="item.specSummarySnapshot || item.skuNameSnapshot || '默认 SKU'"
|
||||||
|
:unit="item.unitSnapshot"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</t-cell>
|
</t-cell>
|
||||||
<t-cell v-if="!fulfillment.itemList?.length" title="暂无履约明细" />
|
<t-cell v-if="!fulfillment.itemList?.length" title="暂无履约明细" />
|
||||||
</t-cell-group>
|
</t-cell-group>
|
||||||
|
<section
|
||||||
|
v-if="stockPreviewVisible && (stockPreviewLoading || stockPreviewList.length)"
|
||||||
|
class="stock-preview"
|
||||||
|
v-entrance-transition="105"
|
||||||
|
>
|
||||||
|
<div class="title">库存预览</div>
|
||||||
|
<t-loading v-if="stockPreviewLoading" size="1.5rem" text="加载中" />
|
||||||
|
<product-stock-preview v-else-if="stockPreviewList.length" :items="stockPreviewList" />
|
||||||
|
</section>
|
||||||
<section
|
<section
|
||||||
v-if="(!isOrderCancelled && isWaitingStock && (canVerify || canCreate)) || (!isOrderCancelled && isPending && (canVerify || canCreate)) || (isVerified && canRevoke)"
|
v-if="(!isOrderCancelled && isWaitingStock && (canVerify || canCreate)) || (!isOrderCancelled && isPending && (canVerify || canCreate)) || (isVerified && canRevoke)"
|
||||||
class="actions"
|
class="actions"
|
||||||
@@ -140,15 +149,17 @@ import { BottomPopup } from "timi-tdesign-mobile";
|
|||||||
import { PassTimeLabel } from "timi-web";
|
import { PassTimeLabel } 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 ProductAPI from "@/api/ProductAPI";
|
||||||
import SalesOrderAPI from "@/api/SalesOrderAPI";
|
import SalesOrderAPI from "@/api/SalesOrderAPI";
|
||||||
import MediaSelector from "@/components/mediaSelector/MediaSelector.vue";
|
import MediaSelector from "@/components/mediaSelector/MediaSelector.vue";
|
||||||
import ProductSkuSnapshotTags from "@/components/ProductSkuSnapshotTags.vue";
|
import ProductSkuSnapshotTags from "@/components/ProductSkuSnapshotTags.vue";
|
||||||
|
import ProductStockPreview from "@/components/ProductStockPreview.vue";
|
||||||
import { useNavBarStore } from "@/store/navBarStore";
|
import { useNavBarStore } from "@/store/navBarStore";
|
||||||
import type { OrderFulfillment, OrderFulfillmentItem } from "@/types/Fulfillment";
|
import type { OrderFulfillment, OrderFulfillmentItem } from "@/types/Fulfillment";
|
||||||
import type { GaoSalesOrderItemType, SalesOrder } from "@/types/SalesOrder";
|
import type { GaoSalesOrderItemType, SalesOrder } from "@/types/SalesOrder";
|
||||||
import type { PhotoItem } from "@/types/Photo";
|
import type { PhotoItem } from "@/types/Photo";
|
||||||
import type { Attachment } from "timi-web-api";
|
import type { Attachment } from "timi-web-api";
|
||||||
import { createPackageRequestNo, getPackageQuantityText } from "@/utils/package";
|
import { addPackageQuantity, createPackageRequestNo, getPackageQuantityText } from "@/utils/package";
|
||||||
import { getAttachmentReadURL } from "@/utils/attachment";
|
import { getAttachmentReadURL } from "@/utils/attachment";
|
||||||
import { getErrorMessage } from "@/utils/error";
|
import { getErrorMessage } from "@/utils/error";
|
||||||
import { getProductPhotoURL } from "@/utils/product";
|
import { getProductPhotoURL } from "@/utils/product";
|
||||||
@@ -156,6 +167,8 @@ import { getFulfillmentStatusText, getSalesOrderItemTypeText, getStatusTheme } f
|
|||||||
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 { uploadPhotoList } from "@/utils/photo";
|
||||||
|
import type { ProductStockPreviewItem } from "@/utils/productStock";
|
||||||
|
import { buildProductStockPreview, mergeProductStockRequirements } from "@/utils/productStock";
|
||||||
|
|
||||||
type ReasonAction = "reject" | "cancel";
|
type ReasonAction = "reject" | "cancel";
|
||||||
defineOptions({ name: "OrderFulfillmentDetail" });
|
defineOptions({ name: "OrderFulfillmentDetail" });
|
||||||
@@ -175,6 +188,8 @@ 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 stockPreviewList = ref<ProductStockPreviewItem[]>([]);
|
||||||
|
const stockPreviewLoading = ref(false);
|
||||||
const photoUrlList = computed(() => photoItems.value.map(item => item.url));
|
const photoUrlList = computed(() => photoItems.value.map(item => item.url));
|
||||||
const fulfillmentPhotoItems = computed<PhotoItem[]>(() => (fulfillment.value?.attachmentList || [])
|
const fulfillmentPhotoItems = computed<PhotoItem[]>(() => (fulfillment.value?.attachmentList || [])
|
||||||
.filter(item => !!item.id)
|
.filter(item => !!item.id)
|
||||||
@@ -192,19 +207,92 @@ const isOrderCancelled = computed(() => order.value?.status === "CANCELLED");
|
|||||||
const canVerify = computed(() => userStore.hasPermissions(permissionCodes.orderFulfillment.verify));
|
const canVerify = computed(() => userStore.hasPermissions(permissionCodes.orderFulfillment.verify));
|
||||||
const canCreate = computed(() => userStore.hasPermissions(permissionCodes.orderFulfillment.create));
|
const canCreate = computed(() => userStore.hasPermissions(permissionCodes.orderFulfillment.create));
|
||||||
const canRevoke = computed(() => userStore.hasPermissions(permissionCodes.orderFulfillment.revoke));
|
const canRevoke = computed(() => userStore.hasPermissions(permissionCodes.orderFulfillment.revoke));
|
||||||
|
const stockPreviewVisible = computed(() => (isWaitingStock.value || isPending.value)
|
||||||
|
&& userStore.hasPermissions(permissionCodes.product.read)
|
||||||
|
&& userStore.hasPermissions(permissionCodes.productStock.read));
|
||||||
const sourceItemType = computed<GaoSalesOrderItemType | undefined>(() => {
|
const sourceItemType = computed<GaoSalesOrderItemType | undefined>(() => {
|
||||||
const sourceOrderItemId = fulfillment.value?.itemList?.[0]?.sourceOrderItemId;
|
const sourceOrderItemId = fulfillment.value?.itemList?.[0]?.sourceOrderItemId;
|
||||||
return order.value?.itemList?.find(item => item.id === sourceOrderItemId)?.itemType;
|
return order.value?.itemList?.find(item => item.id === sourceOrderItemId)?.itemType;
|
||||||
});
|
});
|
||||||
|
|
||||||
function getFulfillmentQuantityText(item: OrderFulfillmentItem): string {
|
function getFulfillmentQuantityText(item: OrderFulfillmentItem): string {
|
||||||
return `履约数量 ${getPackageQuantityText(item.quantity)} ${item.unitSnapshot || "单位"}`;
|
return `${getPackageQuantityText(item.quantity)} ${item.unitSnapshot || "单位"}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isGiftedFulfillmentItem(item: OrderFulfillmentItem): boolean {
|
function isGiftedFulfillmentItem(item: OrderFulfillmentItem): boolean {
|
||||||
return Boolean(order.value?.itemList?.find(orderItem => orderItem.id === item.sourceOrderItemId)?.gifted);
|
return Boolean(order.value?.itemList?.find(orderItem => orderItem.id === item.sourceOrderItemId)?.gifted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const stockEnabledCache = new Map<string, Promise<boolean | null>>();
|
||||||
|
|
||||||
|
function loadStockEnabled(skuId: string): Promise<boolean | null> {
|
||||||
|
const cached = stockEnabledCache.get(skuId);
|
||||||
|
if (cached) {
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
const request = (async () => {
|
||||||
|
try {
|
||||||
|
const sku = await ProductAPI.skuDetail(skuId);
|
||||||
|
return sku.stockEnabled;
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
stockEnabledCache.set(skuId, request);
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadStockPreview(target: OrderFulfillment): Promise<void> {
|
||||||
|
stockPreviewList.value = [];
|
||||||
|
if (!stockPreviewVisible.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
stockPreviewLoading.value = true;
|
||||||
|
try {
|
||||||
|
const requirementList = mergeProductStockRequirements((await Promise.all((target.itemList || []).map(async item => {
|
||||||
|
if (!item.skuId || item.baseQuantity === undefined || item.baseQuantity === null) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const stockEnabled = await loadStockEnabled(item.skuId);
|
||||||
|
if (stockEnabled === null) {
|
||||||
|
throw new Error(`${item.productNameSnapshot || "产品"} 的库存信息加载失败`);
|
||||||
|
}
|
||||||
|
if (!stockEnabled) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
skuId: item.skuId,
|
||||||
|
baseQuantity: getPackageQuantityText(item.baseQuantity),
|
||||||
|
productName: item.productNameSnapshot || "未命名产品",
|
||||||
|
unitName: "基础单位",
|
||||||
|
stockEnabled
|
||||||
|
};
|
||||||
|
}))).filter((item): item is NonNullable<typeof item> => !!item));
|
||||||
|
if (!requirementList.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const stockList = await ProductAPI.stockBatchDetail({ skuIdList: requirementList.map(item => item.skuId) });
|
||||||
|
const ownReservationMap = new Map(requirementList.map(item => [item.skuId, item.baseQuantity]));
|
||||||
|
const previewStockList = target.status === "PENDING"
|
||||||
|
? stockList.map(stock => {
|
||||||
|
const ownReservation = ownReservationMap.get(stock.skuId || "");
|
||||||
|
if (!ownReservation) {
|
||||||
|
return stock;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...stock,
|
||||||
|
availableQuantity: addPackageQuantity(stock.availableQuantity ?? "0", ownReservation) || stock.availableQuantity
|
||||||
|
};
|
||||||
|
})
|
||||||
|
: stockList;
|
||||||
|
stockPreviewList.value = buildProductStockPreview(previewStockList, requirementList);
|
||||||
|
} catch (error) {
|
||||||
|
Message.error(getErrorMessage(error, "库存预览加载失败"));
|
||||||
|
} finally {
|
||||||
|
stockPreviewLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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;
|
||||||
@@ -215,6 +303,7 @@ async function loadData(): Promise<void> {
|
|||||||
order.value = result.salesOrderId && userStore.hasPermissions(permissionCodes.salesOrder.read)
|
order.value = result.salesOrderId && userStore.hasPermissions(permissionCodes.salesOrder.read)
|
||||||
? await SalesOrderAPI.detail(result.salesOrderId)
|
? await SalesOrderAPI.detail(result.salesOrderId)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
await loadStockPreview(result);
|
||||||
}
|
}
|
||||||
catch (error) { Message.error(getErrorMessage(error, "履约单加载失败")); }
|
catch (error) { Message.error(getErrorMessage(error, "履约单加载失败")); }
|
||||||
finally { loading.value = false; }
|
finally { loading.value = false; }
|
||||||
@@ -339,6 +428,25 @@ onUnmounted(() => navBarStore.clearRight());
|
|||||||
border-radius: 1.25rem;
|
border-radius: 1.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.stock-preview {
|
||||||
|
margin: 0 1rem;
|
||||||
|
padding: 1rem .5rem .75rem;
|
||||||
|
background: var(--td-bg-color-container, #fff);
|
||||||
|
box-shadow: var(--td-shadow-1);
|
||||||
|
border-radius: 1.25rem;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
padding: 0 .5rem .5rem;
|
||||||
|
font-size: .875rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.t-loading) {
|
||||||
|
justify-content: center;
|
||||||
|
padding: 1rem 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.items {
|
.items {
|
||||||
|
|
||||||
.item {
|
.item {
|
||||||
@@ -352,8 +460,8 @@ onUnmounted(() => navBarStore.clearRight());
|
|||||||
}
|
}
|
||||||
|
|
||||||
.avatar {
|
.avatar {
|
||||||
width: 3.5rem;
|
width: 3rem;
|
||||||
height: 3.5rem;
|
height: 3rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user