fix ProductDetail.vue style
This commit is contained in:
+164
-112
@@ -1,66 +1,111 @@
|
||||
<template>
|
||||
<div v-can="permissionCodes.product.read" class="product-detail">
|
||||
<t-loading v-if="isLoading" class="loading" size="32px" />
|
||||
<template v-else-if="product">
|
||||
<section class="hero" v-entrance-transition>
|
||||
<span class="eyebrow" v-text="'产品库'" />
|
||||
<div class="hero-title">
|
||||
<h1 v-text="product.name || '未命名产品'" />
|
||||
<t-tag :content="getProductStatusText(product.status)" variant="light-outline" :theme="getProductStatusTheme(product.status)" />
|
||||
<t-loading v-if="isLoading" size="32px" />
|
||||
<template v-if="product">
|
||||
<card-container class="info">
|
||||
<div class="content">
|
||||
<div class="left">
|
||||
<t-image
|
||||
v-if="getProductPhotoURL(product)"
|
||||
:src="getProductPhotoURL(product)"
|
||||
:alt="product.name || '产品图片'"
|
||||
fit="cover"
|
||||
class="avatar"
|
||||
/>
|
||||
<div
|
||||
v-else
|
||||
class="avatar placeholder"
|
||||
v-text="(product.name || product.code || '产').slice(0, 1)"
|
||||
/>
|
||||
</div>
|
||||
<div class="main">
|
||||
<div class="header">
|
||||
<h3 class="name" v-text="product.name || '未命名产品'" />
|
||||
<div class="code gray" v-text="product.code || '-'" />
|
||||
</div>
|
||||
<div class="tags">
|
||||
<t-tag
|
||||
variant="light-outline"
|
||||
theme="primary"
|
||||
:content="getProductTypeText(product.productType)"
|
||||
/>
|
||||
<t-tag
|
||||
variant="light-outline"
|
||||
:theme="getProductStatusTheme(product.status)"
|
||||
:content="getProductStatusText(product.status)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hero-meta">
|
||||
<span v-text="`${product.code || '-'} · ${getProductTypeText(product.productType)}`" />
|
||||
<span v-text="`${product.unitList?.length || 0} 个业务单位`" />
|
||||
</div>
|
||||
</section>
|
||||
<card-container v-if="product.attachmentList?.length" title="产品附件">
|
||||
<photo-scroller
|
||||
:model-value="attachmentUrlList"
|
||||
:photo-items="attachmentItemList"
|
||||
title="附件列表"
|
||||
:show-add="false"
|
||||
:show-remove="false"
|
||||
:show-viewer-actions="false"
|
||||
/>
|
||||
</card-container>
|
||||
<card-container title="基本信息">
|
||||
<t-cell-group theme="card" class="info">
|
||||
<t-cell title="产品名称" :note="product.name || '未命名产品'" />
|
||||
<t-cell title="产品编码" :note="product.code || '-'" />
|
||||
<t-cell title="产品类型" :note="getProductTypeText(product.productType)" />
|
||||
<t-cell title="产品分类" :note="product.category?.name || '未分类'" />
|
||||
<t-cell title="基础单位" :note="product.baseUnit || '-'" />
|
||||
<t-cell title="参考成本" :note="formatProductAmount(product.referenceCost)" />
|
||||
<t-cell title="库存管理" :note="product.stockEnabled ? '已开启' : '未开启'" />
|
||||
<t-cell title="状态" :note="getProductStatusText(product.status)" />
|
||||
</t-cell-group>
|
||||
</card-container>
|
||||
<card-container title="业务单位" :description="product.unitList?.length ? '名称、基础单位数量、售价和可用范围由产品库统一维护' : '暂无业务单位'">
|
||||
<t-cell-group theme="card" class="units">
|
||||
<t-cell v-for="unit in product.unitList || []" :key="unit.id || unit.name">
|
||||
<template #title>
|
||||
<div class="unit-title">
|
||||
<strong v-text="unit.name || '未命名单位'" />
|
||||
<t-tag v-if="unit.isBase" size="small" theme="primary" variant="light-outline" content="基础单位" />
|
||||
</div>
|
||||
</template>
|
||||
<template #note>
|
||||
<span v-text="`1 ${unit.name || '单位'} = ${unit.baseQuantityPerUnit} ${product.baseUnit || '基础单位'}`" />
|
||||
</template>
|
||||
<template #description>
|
||||
<div class="unit-description">
|
||||
<span v-text="`建议售价:${formatProductAmount(unit.salePrice)}`" />
|
||||
<span v-text="getUnitUsageText(unit)" />
|
||||
</div>
|
||||
</template>
|
||||
</t-cell>
|
||||
</t-cell-group>
|
||||
</card-container>
|
||||
<card-container v-if="product.remark" title="备注">
|
||||
<div class="remark" v-text="product.remark" />
|
||||
</card-container>
|
||||
<t-cell-group title="基本信息" theme="card">
|
||||
<t-cell title="产品分类" :note="product.category?.name || '未分类'" />
|
||||
<t-cell title="基础单位" :note="product.baseUnit || '-'" />
|
||||
<t-cell title="参考成本" :note="formatProductAmount(product.referenceCost)" />
|
||||
<t-cell title="库存管理" :note="product.stockEnabled ? '启用' : '关闭'" />
|
||||
<t-cell title="状态" :note="getProductStatusText(product.status)" />
|
||||
<t-cell title="备注" :note="Text.display(product.remark)"></t-cell>
|
||||
<t-cell>
|
||||
<photo-scroller
|
||||
:model-value="attachmentUrlList"
|
||||
:photo-items="attachmentItemList"
|
||||
title="附件列表"
|
||||
:show-add="false"
|
||||
:show-remove="false"
|
||||
:show-viewer-actions="false"
|
||||
/>
|
||||
</t-cell>
|
||||
</t-cell-group>
|
||||
<t-cell-group title="业务单位" theme="card" class="units">
|
||||
<t-cell
|
||||
v-for="unit in product.unitList || []"
|
||||
:key="unit.id || unit.name"
|
||||
>
|
||||
<template #title>
|
||||
<div class="title">
|
||||
<h3 class="name" v-text="unit.name || '未命名单位'" />
|
||||
<t-tag
|
||||
v-if="!unit.isBase"
|
||||
theme="default"
|
||||
size="small"
|
||||
:content="`${unit.baseQuantityPerUnit} ${product.baseUnit}`"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template #note>
|
||||
<span class="price">
|
||||
<span class="qute red text-bold">¥</span>
|
||||
<span
|
||||
class="value text-bold red"
|
||||
v-text="formatProductAmount(unit.salePrice)"
|
||||
/>
|
||||
</span>
|
||||
</template>
|
||||
<template #description>
|
||||
<div class="tags">
|
||||
<t-tag
|
||||
v-if="unit.isBase"
|
||||
theme="primary"
|
||||
variant="light-outline"
|
||||
content="基础单位"
|
||||
/>
|
||||
<t-tag
|
||||
v-if="unit.allowSale"
|
||||
theme="primary"
|
||||
variant="light-outline"
|
||||
content="可销售"
|
||||
/>
|
||||
<t-tag
|
||||
v-if="unit.allowPurchase"
|
||||
theme="primary"
|
||||
variant="light-outline"
|
||||
content="可采购"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</t-cell>
|
||||
</t-cell-group>
|
||||
</template>
|
||||
<t-empty v-else description="产品不存在或已被删除" />
|
||||
</div>
|
||||
<nav-bar-action-menu v-if="actionList.length" :actions="actionList" />
|
||||
</template>
|
||||
@@ -77,9 +122,16 @@ import type { PhotoItem } from "@/types/Photo";
|
||||
import type { Product, ProductUnit } from "@/types/Product";
|
||||
import { getAttachmentReadURL } from "@/utils/attachment";
|
||||
import { getErrorMessage } from "@/utils/error";
|
||||
import { formatProductAmount, getProductStatusText, getProductStatusTheme, getProductTypeText } from "@/utils/product";
|
||||
import {
|
||||
formatProductAmount,
|
||||
getProductPhotoURL,
|
||||
getProductStatusText,
|
||||
getProductStatusTheme,
|
||||
getProductTypeText
|
||||
} from "@/utils/product";
|
||||
import { Message } from "@/utils/message";
|
||||
import { permissionCodes } from "@/utils/permission";
|
||||
import { Text } from "timi-web";
|
||||
|
||||
defineOptions({ name: "ProductDetail" });
|
||||
|
||||
@@ -188,73 +240,73 @@ onUnmounted(() => navBarStore.clearRight());
|
||||
<style scoped lang="less">
|
||||
.product-detail {
|
||||
gap: 1rem;
|
||||
display: grid;
|
||||
padding: calc(var(--app-nav-height) + 1rem) 0 1rem;
|
||||
display: flex;
|
||||
padding: calc(var(--app-nav-height) + 1rem) 0 1rem 0;
|
||||
flex-direction: column;
|
||||
|
||||
.hero {
|
||||
.info {
|
||||
margin: 0 1rem;
|
||||
padding: 1rem;
|
||||
background: linear-gradient(135deg, #e8fffa, #eff6ff);
|
||||
box-shadow: var(--td-shadow-1);
|
||||
border-radius: 1.25rem;
|
||||
|
||||
.eyebrow {
|
||||
color: #0f766e;
|
||||
display: block;
|
||||
font-size: .6875rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: .08em;
|
||||
.content {
|
||||
gap: .5rem;
|
||||
padding: .5rem;
|
||||
display: flex;
|
||||
|
||||
.avatar {
|
||||
width: 7rem;
|
||||
height: 7rem;
|
||||
}
|
||||
|
||||
.main {
|
||||
flex: 1 1 auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
|
||||
.header {
|
||||
|
||||
.name {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.code {
|
||||
font-size: .875rem;
|
||||
margin-top: .25rem;
|
||||
}
|
||||
}
|
||||
|
||||
.tags {
|
||||
gap: .5rem;
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.hero-title {
|
||||
.units {
|
||||
|
||||
.title {
|
||||
gap: .5rem;
|
||||
display: flex;
|
||||
margin-top: .35rem;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.name {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 0;
|
||||
min-width: 0;
|
||||
font-size: 1.25rem;
|
||||
line-height: 1.35;
|
||||
.price {
|
||||
|
||||
.value {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.hero-meta {
|
||||
gap: .75rem;
|
||||
.tags {
|
||||
gap: .5rem;
|
||||
display: flex;
|
||||
margin-top: .6rem;
|
||||
color: var(--td-text-color-secondary);
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.loading {
|
||||
margin: 3rem auto;
|
||||
}
|
||||
|
||||
.info,
|
||||
.units {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.unit-title {
|
||||
gap: .5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.unit-description {
|
||||
gap: .125rem;
|
||||
display: grid;
|
||||
}
|
||||
|
||||
.remark {
|
||||
padding: 1rem;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,11 @@
|
||||
fit="cover"
|
||||
class="avatar"
|
||||
/>
|
||||
<div v-else class="avatar placeholder" v-text="(item.name || item.code || '产').slice(0, 1)" />
|
||||
<div
|
||||
v-else
|
||||
class="avatar placeholder"
|
||||
v-text="(item.name || item.code || '产').slice(0, 1)"
|
||||
/>
|
||||
</template>
|
||||
<template #title>
|
||||
<div class="text-bold" v-text="item.name || '未命名产品'" />
|
||||
@@ -79,7 +83,7 @@ import type { NavBarAction } from "@/types/NavBarAction";
|
||||
import type { Product, ProductCategory, ProductStatus, ProductType } from "@/types/Product";
|
||||
import { getAttachmentReadURL } from "@/utils/attachment";
|
||||
import { getErrorMessage } from "@/utils/error";
|
||||
import { getProductStatusText, getProductStatusTheme, getProductTypeText } from "@/utils/product";
|
||||
import { getProductPhotoURL, getProductStatusText, getProductStatusTheme, getProductTypeText } from "@/utils/product";
|
||||
import { Message } from "@/utils/message";
|
||||
import { permissionCodes } from "@/utils/permission";
|
||||
|
||||
@@ -128,11 +132,6 @@ const categoryOptions = computed(() => [{ label: "全部分类", value: "ALL" },
|
||||
}))]);
|
||||
const emptyText = computed(() => isLoading.value ? "正在加载产品" : keyword.value ? "未找到匹配产品" : "暂无产品");
|
||||
|
||||
function getProductPhotoURL(item: Product): string {
|
||||
const attachment = item.attachmentList?.find(item => item.attachType === "PHOTO" && !!item.id);
|
||||
return attachment?.id ? getAttachmentReadURL(attachment.id, 128) : "";
|
||||
}
|
||||
|
||||
async function loadCategories(): Promise<void> {
|
||||
const result = await ProductAPI.categoryList({ index: 0, size: 1000, status: "ACTIVE" });
|
||||
categoryList.value = result.list || [];
|
||||
|
||||
Reference in New Issue
Block a user