fix ConsumptionRecordList.vue style
This commit is contained in:
@@ -1,10 +1,26 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-can="permissionCodes.product.read" class="product-list">
|
<div v-can="permissionCodes.product.read" class="product-list">
|
||||||
<div class="filters bg-white">
|
<div class="filters bg-white">
|
||||||
<t-input v-model.trim="keyword" class="keyword" type="search" borderless clearable placeholder="搜索产品名称或编码" />
|
<t-input
|
||||||
|
v-model="keyword"
|
||||||
|
class="keyword"
|
||||||
|
type="search"
|
||||||
|
borderless
|
||||||
|
placeholder="搜索产品名称或编码"
|
||||||
|
/>
|
||||||
<t-dropdown-menu class="menus">
|
<t-dropdown-menu class="menus">
|
||||||
<t-dropdown-item v-model="status" label="状态" :options="statusOptions" @change="selectStatus" />
|
<t-dropdown-item
|
||||||
<t-dropdown-item v-model="productType" label="类型" :options="productTypeOptions" @change="selectProductType" />
|
v-model="status"
|
||||||
|
label="状态"
|
||||||
|
:options="statusOptions"
|
||||||
|
@change="selectStatus"
|
||||||
|
/>
|
||||||
|
<t-dropdown-item
|
||||||
|
v-model="productType"
|
||||||
|
label="类型"
|
||||||
|
:options="productTypeOptions"
|
||||||
|
@change="selectProductType"
|
||||||
|
/>
|
||||||
<t-dropdown-item
|
<t-dropdown-item
|
||||||
:model-value="categoryIdList"
|
:model-value="categoryIdList"
|
||||||
label="分类"
|
label="分类"
|
||||||
|
|||||||
@@ -2,29 +2,37 @@
|
|||||||
<div v-can="permissionCodes.salesOrder.read" class="consumption-record-list">
|
<div v-can="permissionCodes.salesOrder.read" class="consumption-record-list">
|
||||||
<div class="filters">
|
<div class="filters">
|
||||||
<t-input
|
<t-input
|
||||||
|
class="keyword"
|
||||||
:value="customerDisplayText"
|
:value="customerDisplayText"
|
||||||
class="customer-input"
|
|
||||||
readonly
|
readonly
|
||||||
borderless
|
borderless
|
||||||
placeholder="选择或扫码客户"
|
placeholder="选择或扫码客户"
|
||||||
@click="customerSelectorVisible = true"
|
@click="customerSelectorVisible = true"
|
||||||
>
|
>
|
||||||
<template #suffix-icon>
|
<template #suffix-icon>
|
||||||
<t-button size="small" variant="text" :content="scannerVisible ? '关闭扫码' : '扫码'" @click.stop="toggleScanner" />
|
<t-button
|
||||||
|
size="extra-small"
|
||||||
|
theme="primary"
|
||||||
|
variant="outline"
|
||||||
|
:content="scannerVisible ? '关闭扫码' : '扫码'"
|
||||||
|
@click.stop="toggleScanner"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</t-input>
|
</t-input>
|
||||||
<t-dropdown-menu>
|
<t-dropdown-menu class="menus">
|
||||||
<t-dropdown-item v-model="sourceType" label="消费来源" :options="sourceTypeOptions" @change="reloadRecords" />
|
<t-dropdown-item
|
||||||
<t-dropdown-item v-model="status" label="消费状态" :options="statusOptions" @change="reloadRecords" />
|
v-model="sourceType"
|
||||||
</t-dropdown-menu>
|
label="消费来源"
|
||||||
<code-scanner
|
:options="sourceTypeOptions"
|
||||||
v-if="scannerVisible"
|
@change="reloadRecords"
|
||||||
visible
|
|
||||||
:status-text="scanStatus"
|
|
||||||
active-text="正在识别客户编码,请将条码或二维码移入取景框。"
|
|
||||||
@decode="handleDecodedText"
|
|
||||||
@status-change="handleScanStatusChange"
|
|
||||||
/>
|
/>
|
||||||
|
<t-dropdown-item
|
||||||
|
v-model="status"
|
||||||
|
label="消费状态"
|
||||||
|
:options="statusOptions"
|
||||||
|
@change="reloadRecords"
|
||||||
|
/>
|
||||||
|
</t-dropdown-menu>
|
||||||
</div>
|
</div>
|
||||||
<t-pull-down-refresh
|
<t-pull-down-refresh
|
||||||
v-model="isRefreshing"
|
v-model="isRefreshing"
|
||||||
@@ -33,33 +41,64 @@
|
|||||||
@scrolltolower="loadRecords"
|
@scrolltolower="loadRecords"
|
||||||
>
|
>
|
||||||
<div class="list-wrap">
|
<div class="list-wrap">
|
||||||
|
<div v-if="scannerVisible" class="scanner-wrap">
|
||||||
|
<code-scanner
|
||||||
|
visible
|
||||||
|
:status-text="scanStatus"
|
||||||
|
active-text="正在识别客户编码,请将条码或二维码移入取景框。"
|
||||||
|
@decode="handleDecodedText"
|
||||||
|
@status-change="handleScanStatusChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<t-cell-group class="summary" theme="card">
|
<t-cell-group class="summary" theme="card">
|
||||||
<t-cell title="记录数" :note="`共 ${total} 条`" />
|
<t-cell title="记录数" :note="`共 ${total} 条`" />
|
||||||
<t-cell title="消费金额合计" :note="`¥${formatPackageAmount(amountTotal)}`" />
|
<t-cell title="消费金额合计" :note="`¥${formatPackageAmount(amountTotal)}`" />
|
||||||
</t-cell-group>
|
</t-cell-group>
|
||||||
<t-cell-group v-if="recordList.length" theme="card">
|
<t-cell-group v-if="recordList.length" theme="card">
|
||||||
<t-cell
|
<t-cell
|
||||||
|
class="item"
|
||||||
v-for="(item, index) in recordList"
|
v-for="(item, index) in recordList"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
v-entrance-transition="index * 30"
|
|
||||||
:title="getRecordTitle(item)"
|
|
||||||
:note="getRecordNote(item)"
|
|
||||||
:arrow="canOpenSource(item)"
|
:arrow="canOpenSource(item)"
|
||||||
|
v-entrance-transition="index * 30"
|
||||||
@click="openSource(item)"
|
@click="openSource(item)"
|
||||||
>
|
>
|
||||||
<template #description>
|
<template #leftIcon>
|
||||||
<div class="description">
|
<t-image
|
||||||
<span v-text="getCustomerText(item)" />
|
v-if="getCustomerPhotoURL(item.customer, 64)"
|
||||||
<span v-text="getEffectiveAtText(item)" />
|
:src="getCustomerPhotoURL(item.customer, 64)"
|
||||||
|
:alt="getCustomerText(item)"
|
||||||
|
fit="cover"
|
||||||
|
class="avatar"
|
||||||
|
/>
|
||||||
|
<div v-else class="avatar fallback" v-text="getCustomerText(item).slice(0, 1)" />
|
||||||
|
</template>
|
||||||
|
<template #title>
|
||||||
|
<div class="title">
|
||||||
|
<span class="customer" v-text="getCustomerText(item)" />
|
||||||
|
<t-tag
|
||||||
|
class="value"
|
||||||
|
size="small"
|
||||||
|
:theme="item.sourceType === 'LEGACY_EVENT' ? 'default' : 'primary'"
|
||||||
|
variant="light"
|
||||||
|
:content="getRecordTitle(item)"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #rightIcon>
|
<template #description>
|
||||||
<div class="right">
|
<div class="description">
|
||||||
<t-tag :content="getSourceText(item.sourceType)" size="small" theme="primary" variant="light-outline" />
|
<t-tag
|
||||||
<t-tag :content="getStatusText(item.status)" size="small" :theme="item.status === 'ACTIVE' ? 'success' : 'danger'" variant="light-outline" />
|
size="small"
|
||||||
<strong v-text="`¥${formatPackageAmount(item.amount)}`" />
|
:theme="item.status === 'ACTIVE' ? 'success' : 'danger'"
|
||||||
|
variant="light-outline"
|
||||||
|
:content="getStatusText(item.status)"
|
||||||
|
/>
|
||||||
|
<pass-time-label class="time" :timestamp="item.effectiveAt" underline />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
<template #note>
|
||||||
|
<strong class="red" v-text="`¥${formatPackageAmount(item.amount)}`" />
|
||||||
|
</template>
|
||||||
</t-cell>
|
</t-cell>
|
||||||
</t-cell-group>
|
</t-cell-group>
|
||||||
<div v-else class="center-tips" v-text="isLoading ? '正在加载消费记录' : '暂无消费记录'" />
|
<div v-else class="center-tips" v-text="isLoading ? '正在加载消费记录' : '暂无消费记录'" />
|
||||||
@@ -76,7 +115,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { DropdownValue } from "tdesign-mobile-vue";
|
import type { DropdownValue } from "tdesign-mobile-vue";
|
||||||
import { Time } from "timi-web";
|
import { PassTimeLabel } from "timi-web";
|
||||||
import { useUserStore } from "timi-web-api";
|
import { useUserStore } from "timi-web-api";
|
||||||
import ConsumptionRecordAPI from "@/api/ConsumptionRecordAPI";
|
import ConsumptionRecordAPI from "@/api/ConsumptionRecordAPI";
|
||||||
import CustomerAPI from "@/api/CustomerAPI";
|
import CustomerAPI from "@/api/CustomerAPI";
|
||||||
@@ -84,8 +123,14 @@ import CodeScanner from "@/components/CodeScanner.vue";
|
|||||||
import CustomerSelector from "@/components/CustomerSelector.vue";
|
import CustomerSelector from "@/components/CustomerSelector.vue";
|
||||||
import { useNavBarStore } from "@/store/navBarStore";
|
import { useNavBarStore } from "@/store/navBarStore";
|
||||||
import type { Customer } from "@/types/Customer";
|
import type { Customer } from "@/types/Customer";
|
||||||
import type { ConsumptionRecord, ConsumptionRecordPage, ConsumptionRecordSourceType, ConsumptionRecordStatus } from "@/types/ConsumptionRecord";
|
import type {
|
||||||
|
ConsumptionRecord,
|
||||||
|
ConsumptionRecordPage,
|
||||||
|
ConsumptionRecordSourceType,
|
||||||
|
ConsumptionRecordStatus
|
||||||
|
} from "@/types/ConsumptionRecord";
|
||||||
import { getErrorMessage } from "@/utils/error";
|
import { getErrorMessage } from "@/utils/error";
|
||||||
|
import { getCustomerPhotoURL } from "@/utils/customer";
|
||||||
import { Message } from "@/utils/message";
|
import { Message } from "@/utils/message";
|
||||||
import { formatPackageAmount } from "@/utils/package";
|
import { formatPackageAmount } from "@/utils/package";
|
||||||
import { permissionCodes } from "@/utils/permission";
|
import { permissionCodes } from "@/utils/permission";
|
||||||
@@ -220,30 +265,15 @@ async function loadRecords(): Promise<void> {
|
|||||||
|
|
||||||
function getRecordTitle(record: ConsumptionRecord): string {
|
function getRecordTitle(record: ConsumptionRecord): string {
|
||||||
if (record.sourceType === "LEGACY_EVENT") {
|
if (record.sourceType === "LEGACY_EVENT") {
|
||||||
return record.sourcePayload?.eventName || "历史消费事件";
|
return record.sourcePayload?.eventName || "消费事件";
|
||||||
}
|
}
|
||||||
return record.orderType === "PACKAGE" ? "套餐订单消费" : "产品订单消费";
|
return record.orderType === "PACKAGE" ? "套餐订单" : "产品订单";
|
||||||
}
|
|
||||||
|
|
||||||
function getRecordNote(record: ConsumptionRecord): string {
|
|
||||||
if (record.status === "REVOKED") {
|
|
||||||
return record.revokeReason || "消费事实已失效";
|
|
||||||
}
|
|
||||||
return record.sourceType === "LEGACY_EVENT" ? "查看原始登记记录" : "查看销售订单";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCustomerText(record: ConsumptionRecord): string {
|
function getCustomerText(record: ConsumptionRecord): string {
|
||||||
return record.customer?.name || record.customer?.code || record.customerId || "未知客户";
|
return record.customer?.name || record.customer?.code || record.customerId || "未知客户";
|
||||||
}
|
}
|
||||||
|
|
||||||
function getEffectiveAtText(record: ConsumptionRecord): string {
|
|
||||||
return record.effectiveAt ? Time.toDateTime(record.effectiveAt) : "未知生效时间";
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSourceText(value?: ConsumptionRecordSourceType): string {
|
|
||||||
return value === "LEGACY_EVENT" ? "历史消费" : "销售订单";
|
|
||||||
}
|
|
||||||
|
|
||||||
function getStatusText(value?: ConsumptionRecordStatus): string {
|
function getStatusText(value?: ConsumptionRecordStatus): string {
|
||||||
return value === "REVOKED" ? "已失效" : "有效";
|
return value === "REVOKED" ? "已失效" : "有效";
|
||||||
}
|
}
|
||||||
@@ -280,44 +310,103 @@ onMounted(async () => {
|
|||||||
|
|
||||||
.filters {
|
.filters {
|
||||||
top: var(--app-nav-height);
|
top: var(--app-nav-height);
|
||||||
z-index: 1;
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 10;
|
||||||
|
display: flex;
|
||||||
|
padding: .75rem 1rem;
|
||||||
position: sticky;
|
position: sticky;
|
||||||
background: var(--td-bg-color-page);
|
background: #F5F5F8;
|
||||||
box-shadow: var(--td-shadow-1);
|
box-shadow: var(--td-shadow-1);
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
.customer-input {
|
.keyword {
|
||||||
--td-input-vertical-padding: .5rem 1rem;
|
--td-input-vertical-padding: 8px 14px;
|
||||||
|
|
||||||
margin: .5rem 1rem;
|
|
||||||
background: var(--td-bg-color-container);
|
|
||||||
border: 1px solid var(--td-component-border);
|
border: 1px solid var(--td-component-border);
|
||||||
|
z-index: 1;
|
||||||
|
background: var(--td-bg-color-container);
|
||||||
border-radius: 1rem;
|
border-radius: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.menus {
|
||||||
|
--td-spacer-1: 6px;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
margin-top: -1rem;
|
||||||
|
padding-top: .8rem;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid rgba(148, 163, 184, .18);
|
||||||
|
border-radius: 0 0 1rem 1rem;
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.t-dropdown-menu__item) {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.t-dropdown-menu__title) {
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-wrap {
|
.list-wrap {
|
||||||
padding: 1rem 0;
|
gap: 1rem;
|
||||||
|
display: flex;
|
||||||
|
padding-top: 1rem;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.scanner-wrap {
|
||||||
|
margin: 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item {
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
width: 3.5rem;
|
||||||
|
height: 3.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
gap: .5rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.customer {
|
||||||
|
font-size: .875rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.description {
|
.description {
|
||||||
gap: .5rem;
|
gap: .5rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
color: var(--td-text-color-secondary);
|
align-items: center;
|
||||||
|
|
||||||
|
.time {
|
||||||
|
font-size: .75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.summary {
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.right {
|
|
||||||
gap: .35rem;
|
|
||||||
display: flex;
|
|
||||||
align-items: flex-end;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
strong {
|
|
||||||
color: var(--td-error-color);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:deep(.consumption-record-list .avatar .t-image__img) {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
border-radius: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(html.theme-dark) .consumption-record-list {
|
||||||
|
.fallback {
|
||||||
|
background: #115e59;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user