fix ProductStockOperationPopup.vue style

This commit is contained in:
Timi
2026-09-03 20:45:31 +08:00
parent 2b060a2536
commit 7a47e3a9f8
7 changed files with 189 additions and 57 deletions
@@ -2,35 +2,56 @@
<bottom-popup
v-model="visible"
:title="getStockOperationText(operation)"
height="min(88vh, 52rem)"
left-button-text="取消"
:right-button-text="isSaving ? '提交中' : '提交'"
@left-button-click="visible = false"
@right-button-click="submit"
>
<div class="operation-popup">
<div class="summary">
<strong v-text="props.product.name || '未命名产品'" />
<span class="gray" v-text="`当前可用库存:${getProductQuantityText(props.stock?.availableQuantity)} ${getProductBaseUnitName(props.product)}`" />
</div>
<t-form label-align="left" label-width="5rem">
<t-form-item label="业务单位" required-mark @click="openUnitPicker">
<t-form class="form" label-width="5rem">
<t-form-item
label="单位"
content-align="right"
@click="openUnitPicker"
arrow
required-mark
>
<span class="picker-text" v-text="selectedUnitName" />
</t-form-item>
<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 label="原因">
<t-input v-model.trim="reason" borderless maxlength="255" placeholder="原因或备注至少填写一项" />
<t-form-item label="备注" required-mark>
<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 label="备注">
<t-textarea v-model.trim="remark" borderless maxlength="500" autosize placeholder="可补充说明" />
<t-form-item label-width="0">
<package-photo-field v-model="photoItems" title="操作照片" :required="true" />
</t-form-item>
<package-photo-field v-model="photoItems" title="操作照片" :required="true" />
</t-form>
</div>
</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-popup>
</template>
@@ -39,9 +60,15 @@
import { watch } from "vue";
import { BottomPopup } from "timi-tdesign-mobile";
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 { createProductRequestNo, getProductBaseUnitName, getProductQuantityText, getStockOperationText } from "@/utils/product";
import { createProductRequestNo, getStockOperationText } from "@/utils/product";
import { Message } from "@/utils/message";
import type { PhotoItem } from "@/types/Photo";
@@ -61,22 +88,23 @@ const emit = defineEmits<{
const visible = defineModel<boolean>("visible", { default: false });
const isSaving = ref(false);
const quantity = ref("");
const reason = ref("");
const remark = ref("");
const requestNo = ref("");
const photoItems = ref<PhotoItem[]>([]);
const selectedUnitId = ref("");
const unitPickerVisible = ref(false);
const unitDraft = ref<PickerValue[]>([]);
const unitPickerZIndex = 12001;
const unitPickerOverlayZIndex = unitPickerZIndex - 1;
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 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 {
quantity.value = "";
reason.value = "";
remark.value = "";
requestNo.value = "";
photoItems.value = [];
@@ -95,6 +123,12 @@ function confirmUnit(value: Array<string | number>): void {
unitPickerVisible.value = false;
}
function applyQuickRemark(): void {
if (quickRemarkText.value) {
remark.value = quickRemarkText.value;
}
}
function validate(): boolean {
if (!selectedUnitId.value) {
Message.warning("请选择业务单位");
@@ -108,8 +142,8 @@ function validate(): boolean {
Message.warning("该产品不允许输入小数数量");
return false;
}
if (!reason.value.trim() && !remark.value.trim()) {
Message.warning("原因或备注至少填写一项");
if (!remark.value.trim()) {
Message.warning("备注不能为空");
return false;
}
if (!photoItems.value.some(item => !!item.tempFileId)) {
@@ -125,8 +159,7 @@ function buildRequest(): ProductStockChangeRequest {
productId: props.product.id || "",
productUnitId: selectedUnitId.value,
quantity: quantity.value,
reason: reason.value.trim() || undefined,
remark: remark.value.trim() || null,
remark: remark.value.trim(),
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">
.operation-popup {
.summary {
gap: .25rem;
display: grid;
padding: .5rem 0 1rem;
padding-bottom: calc(1rem + env(safe-area-inset-bottom, 0));
.form {
margin: 0;
}
:deep(.t-form__item) {
padding: .25rem 0;
.remark-wrap {
width: 100%;
display: flex;
align-items: start;
justify-content: space-between;
}
}
</style>