fix ProductStockOperationPopup.vue style
This commit is contained in:
@@ -147,10 +147,25 @@ async function edit(): Promise<void> {
|
|||||||
|
|
||||||
async function viewStock(): Promise<void> {
|
async function viewStock(): Promise<void> {
|
||||||
if (product.value?.id) {
|
if (product.value?.id) {
|
||||||
await router.push(`/management/product-stocks/${product.value.id}`);
|
await router.push({
|
||||||
|
name: "ProductStockDetail",
|
||||||
|
params: { productId: product.value.id },
|
||||||
|
query: { transitionDepth: String(getCurrentRouteDepth() + 1) }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
function disable(): void {
|
function disable(): void {
|
||||||
const id = product.value?.id;
|
const id = product.value?.id;
|
||||||
if (!id) {
|
if (!id) {
|
||||||
|
|||||||
@@ -2,12 +2,12 @@
|
|||||||
<div v-can="permissionCodes.productStock.read" class="product-stock-detail">
|
<div v-can="permissionCodes.productStock.read" class="product-stock-detail">
|
||||||
<t-loading v-if="isLoading" class="loading" size="32px" />
|
<t-loading v-if="isLoading" class="loading" size="32px" />
|
||||||
<template v-else-if="product && stock">
|
<template v-else-if="product && stock">
|
||||||
<product-profile :product="product">
|
<product-profile :product="product" v-entrance-transition>
|
||||||
<template #tags>
|
<template #tags>
|
||||||
<t-tag :content="getStockStatusText(stock)" variant="light-outline" :theme="getStockTheme(stock)" />
|
<t-tag :content="getStockStatusText(stock)" variant="light-outline" :theme="getStockTheme(stock)" />
|
||||||
</template>
|
</template>
|
||||||
</product-profile>
|
</product-profile>
|
||||||
<t-cell-group theme="card" title="库存">
|
<t-cell-group theme="card" title="库存" v-entrance-transition="70">
|
||||||
<t-cell class="empty-cell-title-margin">
|
<t-cell class="empty-cell-title-margin">
|
||||||
<template #title>
|
<template #title>
|
||||||
<span></span>
|
<span></span>
|
||||||
@@ -42,7 +42,8 @@
|
|||||||
v-if="canOperate && product.stockEnabled"
|
v-if="canOperate && product.stockEnabled"
|
||||||
class="actions-wrap"
|
class="actions-wrap"
|
||||||
title="库存操作"
|
title="库存操作"
|
||||||
description="所有操作必须选择业务单位、上传照片并填写原因或备注"
|
description="所有操作必须选择业务单位、上传照片并填写备注"
|
||||||
|
v-entrance-transition="140"
|
||||||
>
|
>
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<t-button
|
<t-button
|
||||||
@@ -144,10 +145,27 @@ async function loadAll(): Promise<void> {
|
|||||||
|
|
||||||
async function openRecordList(): Promise<void> {
|
async function openRecordList(): Promise<void> {
|
||||||
if (product.value?.id) {
|
if (product.value?.id) {
|
||||||
await router.push({ path: "/management/product-stock-records", query: { productId: product.value.id } });
|
await router.push({
|
||||||
|
name: "ProductStockRecordList",
|
||||||
|
query: {
|
||||||
|
productId: product.value.id,
|
||||||
|
transitionDepth: String(getCurrentRouteDepth() + 1)
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
function openOperation(type: ManualStockOperation): void {
|
function openOperation(type: ManualStockOperation): void {
|
||||||
operationType.value = type;
|
operationType.value = type;
|
||||||
operationVisible.value = true;
|
operationVisible.value = true;
|
||||||
|
|||||||
@@ -2,35 +2,56 @@
|
|||||||
<bottom-popup
|
<bottom-popup
|
||||||
v-model="visible"
|
v-model="visible"
|
||||||
:title="getStockOperationText(operation)"
|
:title="getStockOperationText(operation)"
|
||||||
height="min(88vh, 52rem)"
|
|
||||||
left-button-text="取消"
|
left-button-text="取消"
|
||||||
:right-button-text="isSaving ? '提交中' : '提交'"
|
:right-button-text="isSaving ? '提交中' : '提交'"
|
||||||
@left-button-click="visible = false"
|
@left-button-click="visible = false"
|
||||||
@right-button-click="submit"
|
@right-button-click="submit"
|
||||||
>
|
>
|
||||||
<div class="operation-popup">
|
<div class="operation-popup">
|
||||||
<div class="summary">
|
<t-form class="form" label-width="5rem">
|
||||||
<strong v-text="props.product.name || '未命名产品'" />
|
<t-form-item
|
||||||
<span class="gray" v-text="`当前可用库存:${getProductQuantityText(props.stock?.availableQuantity)} ${getProductBaseUnitName(props.product)}`" />
|
label="单位"
|
||||||
</div>
|
content-align="right"
|
||||||
<t-form label-align="left" label-width="5rem">
|
@click="openUnitPicker"
|
||||||
<t-form-item label="业务单位" required-mark @click="openUnitPicker">
|
arrow
|
||||||
|
required-mark
|
||||||
|
>
|
||||||
<span class="picker-text" v-text="selectedUnitName" />
|
<span class="picker-text" v-text="selectedUnitName" />
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item label="数量" required-mark>
|
<t-form-item label="数量" required-mark>
|
||||||
<t-input v-model.trim="quantity" type="number" align="right" borderless placeholder="必须大于 0" />
|
<t-input v-model="quantity" type="number" align="right" borderless />
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item label="原因">
|
<t-form-item label="备注" required-mark>
|
||||||
<t-input v-model.trim="reason" borderless maxlength="255" placeholder="原因或备注至少填写一项" />
|
<div class="remark-wrap">
|
||||||
|
<t-textarea
|
||||||
|
v-model="remark"
|
||||||
|
borderless
|
||||||
|
maxlength="500"
|
||||||
|
autosize
|
||||||
|
placeholder="请输入库存操作备注"
|
||||||
|
/>
|
||||||
|
<t-button
|
||||||
|
v-if="quickRemarkText"
|
||||||
|
type="button"
|
||||||
|
size="extra-small"
|
||||||
|
variant="outline"
|
||||||
|
:content="quickRemarkText"
|
||||||
|
@click="applyQuickRemark"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item label="备注">
|
<t-form-item label-width="0">
|
||||||
<t-textarea v-model.trim="remark" borderless maxlength="500" autosize placeholder="可补充说明" />
|
<package-photo-field v-model="photoItems" title="操作照片" :required="true" />
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<package-photo-field v-model="photoItems" title="操作照片" :required="true" />
|
|
||||||
</t-form>
|
</t-form>
|
||||||
</div>
|
</div>
|
||||||
</bottom-popup>
|
</bottom-popup>
|
||||||
<t-popup v-model="unitPickerVisible" placement="bottom">
|
<t-popup
|
||||||
|
v-model="unitPickerVisible"
|
||||||
|
placement="bottom"
|
||||||
|
:z-index="unitPickerZIndex"
|
||||||
|
:overlay-props="{ zIndex: unitPickerOverlayZIndex }"
|
||||||
|
>
|
||||||
<t-picker v-model="unitDraft" :columns="unitOptions" title="选择业务单位" @cancel="unitPickerVisible = false" @confirm="confirmUnit" />
|
<t-picker v-model="unitDraft" :columns="unitOptions" title="选择业务单位" @cancel="unitPickerVisible = false" @confirm="confirmUnit" />
|
||||||
</t-popup>
|
</t-popup>
|
||||||
</template>
|
</template>
|
||||||
@@ -39,9 +60,15 @@
|
|||||||
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 type { Product, ProductStock, ProductUnit, ProductStockChangeRequest, StockOperationType } from "@/types/Product";
|
import type {
|
||||||
|
Product,
|
||||||
|
ProductStock,
|
||||||
|
ProductStockChangeRequest,
|
||||||
|
ProductUnit,
|
||||||
|
StockOperationType
|
||||||
|
} from "@/types/Product";
|
||||||
import { getErrorMessage } from "@/utils/error";
|
import { getErrorMessage } from "@/utils/error";
|
||||||
import { createProductRequestNo, getProductBaseUnitName, getProductQuantityText, 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";
|
||||||
|
|
||||||
@@ -61,22 +88,23 @@ const emit = defineEmits<{
|
|||||||
const visible = defineModel<boolean>("visible", { default: false });
|
const visible = defineModel<boolean>("visible", { default: false });
|
||||||
const isSaving = ref(false);
|
const isSaving = ref(false);
|
||||||
const quantity = ref("");
|
const quantity = ref("");
|
||||||
const reason = ref("");
|
|
||||||
const remark = ref("");
|
const remark = ref("");
|
||||||
const requestNo = ref("");
|
const requestNo = ref("");
|
||||||
const photoItems = ref<PhotoItem[]>([]);
|
const photoItems = ref<PhotoItem[]>([]);
|
||||||
const selectedUnitId = ref("");
|
const selectedUnitId = ref("");
|
||||||
const unitPickerVisible = ref(false);
|
const unitPickerVisible = ref(false);
|
||||||
const unitDraft = ref<PickerValue[]>([]);
|
const unitDraft = ref<PickerValue[]>([]);
|
||||||
|
const unitPickerZIndex = 12001;
|
||||||
|
const unitPickerOverlayZIndex = unitPickerZIndex - 1;
|
||||||
|
|
||||||
const isInbound = computed(() => props.operation === "MANUAL_IN" || props.operation === "STOCKTAKE_GAIN");
|
const isInbound = computed(() => props.operation === "MANUAL_IN" || props.operation === "STOCKTAKE_GAIN");
|
||||||
const selectableUnits = computed<ProductUnit[]>(() => (props.product.unitList || []).filter(unit => isInbound.value ? unit.allowPurchase : unit.allowSale));
|
const selectableUnits = computed<ProductUnit[]>(() => (props.product.unitList || []).filter(unit => isInbound.value ? unit.allowPurchase : unit.allowSale));
|
||||||
const unitOptions = computed(() => [selectableUnits.value.map(unit => ({ label: unit.name || "未命名单位", value: unit.id || "" }))]);
|
const unitOptions = computed(() => [selectableUnits.value.map(unit => ({ label: unit.name || "未命名单位", value: unit.id || "" }))]);
|
||||||
const selectedUnitName = computed(() => selectableUnits.value.find(unit => unit.id === selectedUnitId.value)?.name || "请选择业务单位");
|
const selectedUnitName = computed(() => selectableUnits.value.find(unit => unit.id === selectedUnitId.value)?.name || "选择业务单位");
|
||||||
|
const quickRemarkText = computed(() => props.operation === "MANUAL_IN" ? "正常入库" : props.operation === "MANUAL_OUT" ? "正常出库" : "");
|
||||||
|
|
||||||
function resetForm(): void {
|
function resetForm(): void {
|
||||||
quantity.value = "";
|
quantity.value = "";
|
||||||
reason.value = "";
|
|
||||||
remark.value = "";
|
remark.value = "";
|
||||||
requestNo.value = "";
|
requestNo.value = "";
|
||||||
photoItems.value = [];
|
photoItems.value = [];
|
||||||
@@ -95,6 +123,12 @@ function confirmUnit(value: Array<string | number>): void {
|
|||||||
unitPickerVisible.value = false;
|
unitPickerVisible.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function applyQuickRemark(): void {
|
||||||
|
if (quickRemarkText.value) {
|
||||||
|
remark.value = quickRemarkText.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function validate(): boolean {
|
function validate(): boolean {
|
||||||
if (!selectedUnitId.value) {
|
if (!selectedUnitId.value) {
|
||||||
Message.warning("请选择业务单位");
|
Message.warning("请选择业务单位");
|
||||||
@@ -108,8 +142,8 @@ function validate(): boolean {
|
|||||||
Message.warning("该产品不允许输入小数数量");
|
Message.warning("该产品不允许输入小数数量");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!reason.value.trim() && !remark.value.trim()) {
|
if (!remark.value.trim()) {
|
||||||
Message.warning("原因或备注至少填写一项");
|
Message.warning("备注不能为空");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!photoItems.value.some(item => !!item.tempFileId)) {
|
if (!photoItems.value.some(item => !!item.tempFileId)) {
|
||||||
@@ -125,8 +159,7 @@ function buildRequest(): ProductStockChangeRequest {
|
|||||||
productId: props.product.id || "",
|
productId: props.product.id || "",
|
||||||
productUnitId: selectedUnitId.value,
|
productUnitId: selectedUnitId.value,
|
||||||
quantity: quantity.value,
|
quantity: quantity.value,
|
||||||
reason: reason.value.trim() || undefined,
|
remark: remark.value.trim(),
|
||||||
remark: remark.value.trim() || null,
|
|
||||||
attachmentList: photoItems.value.flatMap(item => item.tempFileId ? [{ tempFileId: item.tempFileId, attachType: item.attachType || "PHOTO" }] : [])
|
attachmentList: photoItems.value.flatMap(item => item.tempFileId ? [{ tempFileId: item.tempFileId, attachType: item.attachType || "PHOTO" }] : [])
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -171,14 +204,17 @@ watch(() => [visible.value, props.operation] as const, ([isVisible]) => {
|
|||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
.operation-popup {
|
.operation-popup {
|
||||||
.summary {
|
padding-bottom: calc(1rem + env(safe-area-inset-bottom, 0));
|
||||||
gap: .25rem;
|
|
||||||
display: grid;
|
.form {
|
||||||
padding: .5rem 0 1rem;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.t-form__item) {
|
.remark-wrap {
|
||||||
padding: .25rem 0;
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: start;
|
||||||
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,5 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-can="permissionCodes.productStock.read" class="product-stock-record-list">
|
<div v-can="permissionCodes.productStock.read" class="product-stock-record-list">
|
||||||
|
<div class="search bg-white">
|
||||||
|
<t-input
|
||||||
|
v-model.trim="keyword"
|
||||||
|
class="keyword"
|
||||||
|
type="search"
|
||||||
|
borderless
|
||||||
|
clearable
|
||||||
|
placeholder="搜索产品名称或编码"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<t-cell-group class="summary" theme="card">
|
<t-cell-group class="summary" theme="card">
|
||||||
<t-cell title="查询范围" :note="scopeText" />
|
<t-cell title="查询范围" :note="scopeText" />
|
||||||
<t-cell title="记录数量" :note="`共 ${total} 条`" />
|
<t-cell title="记录数量" :note="`共 ${total} 条`" />
|
||||||
@@ -7,7 +17,7 @@
|
|||||||
<t-pull-down-refresh
|
<t-pull-down-refresh
|
||||||
v-model="isRefreshing"
|
v-model="isRefreshing"
|
||||||
:refresh-timeout="30000"
|
:refresh-timeout="30000"
|
||||||
@refresh="reload"
|
@refresh="refresh"
|
||||||
@scrolltolower="loadMore"
|
@scrolltolower="loadMore"
|
||||||
>
|
>
|
||||||
<t-cell-group v-if="recordList.length" class="list" theme="card">
|
<t-cell-group v-if="recordList.length" class="list" theme="card">
|
||||||
@@ -35,12 +45,12 @@
|
|||||||
<div class="description">
|
<div class="description">
|
||||||
<span v-text="getQuantityText(record)" />
|
<span v-text="getQuantityText(record)" />
|
||||||
<span v-text="getBalanceText(record)" />
|
<span v-text="getBalanceText(record)" />
|
||||||
<span v-if="record.reason || record.remark" class="gray" v-text="record.reason || record.remark || ''" />
|
<span v-if="record.remark" class="gray" v-text="record.remark" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</t-cell>
|
</t-cell>
|
||||||
</t-cell-group>
|
</t-cell-group>
|
||||||
<div v-else class="empty" v-text="loading ? '正在加载库存流水' : '暂无库存流水'" />
|
<div v-else class="empty" v-text="emptyText" />
|
||||||
<div v-if="recordList.length" class="center-tips" v-text="loadStateText" />
|
<div v-if="recordList.length" class="center-tips" v-text="loadStateText" />
|
||||||
</t-pull-down-refresh>
|
</t-pull-down-refresh>
|
||||||
</div>
|
</div>
|
||||||
@@ -68,14 +78,28 @@ const loading = ref(false);
|
|||||||
const isRefreshing = ref(false);
|
const isRefreshing = ref(false);
|
||||||
const noMore = ref(false);
|
const noMore = ref(false);
|
||||||
const productId = computed(() => String(route.query.productId || ""));
|
const productId = computed(() => String(route.query.productId || ""));
|
||||||
|
const keyword = ref(getQueryText("keyword"));
|
||||||
|
let keywordTimer: number | undefined;
|
||||||
|
let requestToken = 0;
|
||||||
const scopeText = computed(() => {
|
const scopeText = computed(() => {
|
||||||
if (!productId.value) {
|
const scope = productId.value
|
||||||
return "全部产品";
|
? `指定产品 · ${recordList.value[0]?.productNameSnapshot || "当前产品"}`
|
||||||
|
: "全部产品";
|
||||||
|
return keyword.value.trim() ? `${scope} · 搜索:${keyword.value.trim()}` : scope;
|
||||||
|
});
|
||||||
|
const emptyText = computed(() => {
|
||||||
|
if (loading.value) {
|
||||||
|
return "正在加载库存流水";
|
||||||
}
|
}
|
||||||
return `指定产品 · ${recordList.value[0]?.productNameSnapshot || "当前产品"}`;
|
return keyword.value.trim() ? "未找到匹配的库存流水" : "暂无库存流水";
|
||||||
});
|
});
|
||||||
const loadStateText = computed(() => loading.value ? "正在加载" : noMore.value ? "已经到底了" : "上拉加载更多");
|
const loadStateText = computed(() => loading.value ? "正在加载" : noMore.value ? "已经到底了" : "上拉加载更多");
|
||||||
|
|
||||||
|
function getQueryText(key: string): string {
|
||||||
|
const value = route.query[key];
|
||||||
|
return Array.isArray(value) ? String(value[0] || "") : String(value || "");
|
||||||
|
}
|
||||||
|
|
||||||
function getOperationIcon(type?: StockOperationType): string {
|
function getOperationIcon(type?: StockOperationType): string {
|
||||||
return ({
|
return ({
|
||||||
INITIAL_IN: "download",
|
INITIAL_IN: "download",
|
||||||
@@ -110,36 +134,50 @@ function getBalanceText(record: StockRecord): string {
|
|||||||
return `库存 ${getProductQuantityText(record.onHandAfter)} · 预占 ${getProductQuantityText(record.reservedAfter)} · ${operator}`;
|
return `库存 ${getProductQuantityText(record.onHandAfter)} · 预占 ${getProductQuantityText(record.reservedAfter)} · ${operator}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadMore(): Promise<void> {
|
async function loadMore(force = false): Promise<void> {
|
||||||
if (loading.value || noMore.value) {
|
if (!force && (loading.value || noMore.value)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const token = ++requestToken;
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
const result = await ProductAPI.recordList({
|
const result = await ProductAPI.recordList({
|
||||||
productId: productId.value || undefined,
|
productId: productId.value || undefined,
|
||||||
|
keyword: keyword.value.trim() || undefined,
|
||||||
index: pageIndex.value,
|
index: pageIndex.value,
|
||||||
size: pageSize
|
size: pageSize
|
||||||
});
|
});
|
||||||
|
if (token !== requestToken) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const list = result.list || [];
|
const list = result.list || [];
|
||||||
recordList.value = recordList.value.concat(list);
|
recordList.value = recordList.value.concat(list);
|
||||||
total.value = result.total || 0;
|
total.value = result.total || 0;
|
||||||
pageIndex.value += 1;
|
pageIndex.value += 1;
|
||||||
noMore.value = list.length < pageSize || total.value < recordList.value.length || total.value === recordList.value.length;
|
noMore.value = list.length < pageSize || total.value < recordList.value.length || total.value === recordList.value.length;
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false;
|
if (token === requestToken) {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function reload(): Promise<void> {
|
async function reload(): Promise<void> {
|
||||||
if (loading.value) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
pageIndex.value = 0;
|
pageIndex.value = 0;
|
||||||
recordList.value = [];
|
recordList.value = [];
|
||||||
total.value = 0;
|
total.value = 0;
|
||||||
noMore.value = false;
|
noMore.value = false;
|
||||||
await loadMore();
|
await loadMore(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function refresh(): Promise<void> {
|
||||||
|
try {
|
||||||
|
await reload();
|
||||||
|
} catch (error) {
|
||||||
|
Message.error(getErrorMessage(error, "库存流水加载失败"));
|
||||||
|
} finally {
|
||||||
|
isRefreshing.value = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
@@ -151,7 +189,22 @@ onMounted(async () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => navBarStore.clearRight());
|
watch(keyword, () => {
|
||||||
|
window.clearTimeout(keywordTimer);
|
||||||
|
keywordTimer = window.setTimeout(async () => {
|
||||||
|
try {
|
||||||
|
await reload();
|
||||||
|
} catch (error) {
|
||||||
|
Message.error(getErrorMessage(error, "库存流水加载失败"));
|
||||||
|
}
|
||||||
|
}, 300);
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
navBarStore.clearRight();
|
||||||
|
window.clearTimeout(keywordTimer);
|
||||||
|
requestToken += 1;
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
@@ -161,6 +214,22 @@ onUnmounted(() => navBarStore.clearRight());
|
|||||||
padding: var(--app-nav-height) 0 1rem;
|
padding: var(--app-nav-height) 0 1rem;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
|
.search {
|
||||||
|
top: var(--app-nav-height);
|
||||||
|
z-index: 10;
|
||||||
|
padding: .75rem 1rem;
|
||||||
|
position: sticky;
|
||||||
|
background: var(--td-bg-color-page);
|
||||||
|
box-shadow: var(--td-shadow-1);
|
||||||
|
|
||||||
|
.keyword {
|
||||||
|
border: 1px solid var(--td-component-border);
|
||||||
|
padding: .5rem 1rem;
|
||||||
|
background: var(--td-bg-color-container);
|
||||||
|
border-radius: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.summary {
|
.summary {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="product-tab">
|
<div class="product-tab">
|
||||||
<t-notice-bar
|
|
||||||
theme="error"
|
|
||||||
visible
|
|
||||||
content="产品库设计方案及实现未完成,套餐功能仍在测试中,可以试用反馈可改进问题,但不要实际落到业务使用。产品库实现后套餐模块会联动更新,会清除测试的套餐数据"
|
|
||||||
/>
|
|
||||||
<t-cell-group
|
<t-cell-group
|
||||||
v-for="(group, index) in visibleMenuGroupList"
|
v-for="(group, index) in visibleMenuGroupList"
|
||||||
:key="group.title"
|
:key="group.title"
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ const productRoutes: RouteRecordRaw[] = [
|
|||||||
path: "/management/product-stocks/:productId",
|
path: "/management/product-stocks/:productId",
|
||||||
name: "ProductStockDetail",
|
name: "ProductStockDetail",
|
||||||
component: ProductStockDetail,
|
component: ProductStockDetail,
|
||||||
meta: pageMeta("库存详情")
|
meta: pageMeta("库存详情", 4)
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -111,7 +111,6 @@ export type StockRecord = {
|
|||||||
requestNo?: string;
|
requestNo?: string;
|
||||||
operatorUserId?: string;
|
operatorUserId?: string;
|
||||||
occurredAt?: number;
|
occurredAt?: number;
|
||||||
reason?: string | null;
|
|
||||||
remark?: string | null;
|
remark?: string | null;
|
||||||
operator?: Partial<User>;
|
operator?: Partial<User>;
|
||||||
attachmentList?: Partial<Attachment>[];
|
attachmentList?: Partial<Attachment>[];
|
||||||
@@ -120,6 +119,7 @@ export type StockRecord = {
|
|||||||
export type StockRecordPage = {
|
export type StockRecordPage = {
|
||||||
storeId?: string;
|
storeId?: string;
|
||||||
productId?: string;
|
productId?: string;
|
||||||
|
keyword?: string;
|
||||||
bizType?: StockBizType;
|
bizType?: StockBizType;
|
||||||
bizId?: string;
|
bizId?: string;
|
||||||
operationType?: StockOperationType;
|
operationType?: StockOperationType;
|
||||||
@@ -135,8 +135,7 @@ export type ProductStockChangeRequest = {
|
|||||||
productId: string;
|
productId: string;
|
||||||
productUnitId: string;
|
productUnitId: string;
|
||||||
quantity: number | string;
|
quantity: number | string;
|
||||||
reason?: string;
|
remark: string;
|
||||||
remark?: string | null;
|
|
||||||
attachmentList: Partial<Attachment>[];
|
attachmentList: Partial<Attachment>[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user