|
|
@@ -21,7 +21,7 @@ import {
|
|
|
ElSelect,
|
|
|
ElOption,
|
|
|
ElInputNumber,
|
|
|
- ElMessageBox
|
|
|
+ ElDivider
|
|
|
} from "element-plus";
|
|
|
import {
|
|
|
type Ref,
|
|
|
@@ -32,6 +32,7 @@ import {
|
|
|
defineComponent,
|
|
|
onMounted
|
|
|
} from "vue";
|
|
|
+import { http } from "@/utils/http";
|
|
|
|
|
|
export function useNewProductApply(tableRef: Ref) {
|
|
|
const form = reactive({
|
|
|
@@ -65,7 +66,7 @@ export function useNewProductApply(tableRef: Ref) {
|
|
|
},
|
|
|
{
|
|
|
label: "商品名称",
|
|
|
- prop: "productName",
|
|
|
+ prop: "name",
|
|
|
minWidth: 150
|
|
|
},
|
|
|
{
|
|
|
@@ -174,19 +175,38 @@ export function useNewProductApply(tableRef: Ref) {
|
|
|
props: {
|
|
|
formInline: {
|
|
|
id: row?.id,
|
|
|
- productName: row?.productName ?? "",
|
|
|
+ productName: row?.productName ?? row?.name ?? "",
|
|
|
barcode: row?.barcode ?? "",
|
|
|
category: row?.category ?? "",
|
|
|
brand: row?.brand ?? "",
|
|
|
specification: row?.specification ?? "",
|
|
|
unit: row?.unit ?? "",
|
|
|
price: row?.price ?? 0,
|
|
|
- costPrice: row?.costPrice ?? 0,
|
|
|
+ costPrice: row?.costPrice ?? row?.cost ?? 0,
|
|
|
+ description: row?.description ?? "",
|
|
|
+ productType: row?.productType ?? 0,
|
|
|
+ standard: row?.standard ?? 1,
|
|
|
+ stickerNum: row?.stickerNum ?? "",
|
|
|
+ length: row?.length ?? null,
|
|
|
+ width: row?.width ?? null,
|
|
|
+ height: row?.height ?? null,
|
|
|
+ exhibitHeight: row?.exhibitHeight ?? null,
|
|
|
+ columns: row?.columns ?? null,
|
|
|
+ numbers: row?.numbers ?? null,
|
|
|
+ reason: row?.reason ?? "",
|
|
|
images: row?.images ?? "",
|
|
|
- description: row?.description ?? ""
|
|
|
+ imageUrl: row?.imageUrl ?? "",
|
|
|
+ imageUrl2: row?.imageUrl2 ?? "",
|
|
|
+ imageUrl3: row?.imageUrl3 ?? "",
|
|
|
+ imageUrl4: row?.imageUrl4 ?? "",
|
|
|
+ imageUrl5: row?.imageUrl5 ?? "",
|
|
|
+ originalImageUrl1: row?.originalImageUrl1 ?? "",
|
|
|
+ originalImageUrl2: row?.originalImageUrl2 ?? "",
|
|
|
+ originalImageUrl3: row?.originalImageUrl3 ?? "",
|
|
|
+ originalImageUrl4: row?.originalImageUrl4 ?? ""
|
|
|
}
|
|
|
},
|
|
|
- width: "600px",
|
|
|
+ width: "900px",
|
|
|
draggable: true,
|
|
|
closeOnClickModal: false,
|
|
|
contentRenderer: () => h(EditForm, { ref: formRef }),
|
|
|
@@ -279,6 +299,87 @@ export function useNewProductApply(tableRef: Ref) {
|
|
|
};
|
|
|
}
|
|
|
|
|
|
+// 上传图片辅助函数
|
|
|
+async function uploadFile(options: any): Promise<string> {
|
|
|
+ const formData = new FormData();
|
|
|
+ formData.append("file", options.file);
|
|
|
+ // 用 beforeRequestCallback 清除默认的 Content-Type: application/json,
|
|
|
+ // 让浏览器自动设置 multipart/form-data 及正确的 boundary
|
|
|
+ const res: any = await http.request("post", "/upload", { data: formData }, {
|
|
|
+ beforeRequestCallback: (config: any) => {
|
|
|
+ delete config.headers["Content-Type"];
|
|
|
+ }
|
|
|
+ } as any);
|
|
|
+ if (res.code === 200 && res.data) {
|
|
|
+ return res.data.url || res.data;
|
|
|
+ }
|
|
|
+ throw new Error(res.message || "上传失败");
|
|
|
+}
|
|
|
+
|
|
|
+// 图片上传组件(纯自定义,避免 ElUpload 的 fileList 与 httpRequest 状态冲突)
|
|
|
+const ImageUploadItem = defineComponent({
|
|
|
+ props: {
|
|
|
+ label: { type: String, required: true },
|
|
|
+ modelValue: { type: String, default: "" }
|
|
|
+ },
|
|
|
+ emits: ["update:modelValue"],
|
|
|
+ setup(props, { emit }) {
|
|
|
+ const uploading = ref(false);
|
|
|
+ const inputRef = ref<HTMLInputElement>();
|
|
|
+
|
|
|
+ const handleClick = () => {
|
|
|
+ if (props.modelValue) return; // 已有图片,不触发上传
|
|
|
+ inputRef.value?.click();
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleFileChange = async (e: Event) => {
|
|
|
+ const file = (e.target as HTMLInputElement).files?.[0];
|
|
|
+ if (!file) return;
|
|
|
+ uploading.value = true;
|
|
|
+ try {
|
|
|
+ const url = await uploadFile({ file });
|
|
|
+ emit("update:modelValue", url);
|
|
|
+ } catch {
|
|
|
+ // 上传失败,静默处理
|
|
|
+ } finally {
|
|
|
+ uploading.value = false;
|
|
|
+ // 重置 input,允许重复选择同一文件
|
|
|
+ if (inputRef.value) inputRef.value.value = "";
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleRemove = (e: Event) => {
|
|
|
+ e.stopPropagation();
|
|
|
+ emit("update:modelValue", "");
|
|
|
+ };
|
|
|
+
|
|
|
+ return () => (
|
|
|
+ <div class="image-upload-item">
|
|
|
+ <div class="upload-card" onClick={handleClick}>
|
|
|
+ {props.modelValue ? (
|
|
|
+ <>
|
|
|
+ <img class="upload-card-img" src={props.modelValue} />
|
|
|
+ <span class="upload-card-remove" onClick={handleRemove}>×</span>
|
|
|
+ </>
|
|
|
+ ) : uploading.value ? (
|
|
|
+ <span class="upload-card-loading">...</span>
|
|
|
+ ) : (
|
|
|
+ <el-icon><i class="ri-add-line" /></el-icon>
|
|
|
+ )}
|
|
|
+ <input
|
|
|
+ ref={inputRef}
|
|
|
+ type="file"
|
|
|
+ accept="image/*"
|
|
|
+ style="display:none"
|
|
|
+ onChange={handleFileChange}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <span class="upload-label">{props.label}</span>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
const EditForm = defineComponent({
|
|
|
props: {
|
|
|
formInline: {
|
|
|
@@ -288,60 +389,172 @@ const EditForm = defineComponent({
|
|
|
},
|
|
|
setup(props) {
|
|
|
const ruleFormRef = ref();
|
|
|
+
|
|
|
+ // 从已有数据中解析图片URL
|
|
|
+ const p = props.formInline as any;
|
|
|
+ const parseImg = (idx: number) => {
|
|
|
+ // 优先从独立的 imageUrl 字段获取
|
|
|
+ const directFields = [p.imageUrl, p.imageUrl2, p.imageUrl3, p.imageUrl4, p.imageUrl5,
|
|
|
+ p.originalImageUrl1, p.originalImageUrl2, p.originalImageUrl3, p.originalImageUrl4];
|
|
|
+ if (directFields[idx]) return directFields[idx];
|
|
|
+ // 从逗号分隔的 images 字符串获取
|
|
|
+ if (p.images) {
|
|
|
+ const parts = p.images.split(",");
|
|
|
+ return (parts[idx] || "").trim();
|
|
|
+ }
|
|
|
+ return "";
|
|
|
+ };
|
|
|
+
|
|
|
const model = reactive({
|
|
|
- id: props.formInline.id,
|
|
|
- productName: props.formInline.productName,
|
|
|
- barcode: props.formInline.barcode,
|
|
|
- category: props.formInline.category,
|
|
|
- brand: props.formInline.brand,
|
|
|
- specification: props.formInline.specification,
|
|
|
- unit: props.formInline.unit,
|
|
|
- price: props.formInline.price,
|
|
|
- costPrice: props.formInline.costPrice,
|
|
|
- images: props.formInline.images,
|
|
|
- description: props.formInline.description
|
|
|
+ id: p.id,
|
|
|
+ productName: p.productName || p.name || "",
|
|
|
+ barcode: p.barcode || "",
|
|
|
+ category: p.category || "",
|
|
|
+ brand: p.brand || "",
|
|
|
+ specification: p.specification || "",
|
|
|
+ unit: p.unit || "",
|
|
|
+ price: p.price ?? 0,
|
|
|
+ costPrice: p.costPrice ?? p.cost ?? 0,
|
|
|
+ description: p.description || "",
|
|
|
+ productType: p.productType ?? 0,
|
|
|
+ standard: p.standard ?? 1,
|
|
|
+ stickerNum: p.stickerNum || "",
|
|
|
+ length: p.length ?? null,
|
|
|
+ width: p.width ?? null,
|
|
|
+ height: p.height ?? null,
|
|
|
+ exhibitHeight: p.exhibitHeight ?? null,
|
|
|
+ columns: p.columns ?? null,
|
|
|
+ numbers: p.numbers ?? null,
|
|
|
+ reason: p.reason || "",
|
|
|
+ pic1: parseImg(0), pic2: parseImg(1), pic3: parseImg(2), pic4: parseImg(3), pic5: parseImg(4),
|
|
|
+ oriPic1: parseImg(5), oriPic2: parseImg(6), oriPic3: parseImg(7), oriPic4: parseImg(8)
|
|
|
});
|
|
|
+
|
|
|
const rules = {
|
|
|
productName: [{ required: true, message: "请输入商品名称", trigger: "blur" }],
|
|
|
barcode: [{ required: true, message: "请输入条形码", trigger: "blur" }]
|
|
|
};
|
|
|
|
|
|
const getRef = () => ruleFormRef.value;
|
|
|
- const getForm = () => model;
|
|
|
+ const getForm = () => {
|
|
|
+ const images = [
|
|
|
+ model.pic1, model.pic2, model.pic3, model.pic4, model.pic5,
|
|
|
+ model.oriPic1, model.oriPic2, model.oriPic3, model.oriPic4
|
|
|
+ ].map(s => s || "").join(",");
|
|
|
+ const { pic1, pic2, pic3, pic4, pic5, oriPic1, oriPic2, oriPic3, oriPic4, ...rest } = model;
|
|
|
+ return { ...rest, images };
|
|
|
+ };
|
|
|
|
|
|
return () => (
|
|
|
- <ElForm ref={ruleFormRef} model={model} rules={rules} label-width="100px">
|
|
|
+ <ElForm ref={ruleFormRef} model={model} rules={rules} label-width="110px" class="new-apply-form">
|
|
|
+ {/* 基本信息 */}
|
|
|
+ <div class="form-section-title">基本信息</div>
|
|
|
<ElFormItem label="商品名称" prop="productName">
|
|
|
- <ElInput v-model={model.productName} placeholder="请输入商品名称" />
|
|
|
+ <ElInput v-model={model.productName} placeholder="请输入商品名称" maxlength={50} />
|
|
|
</ElFormItem>
|
|
|
<ElFormItem label="条形码" prop="barcode">
|
|
|
- <ElInput v-model={model.barcode} placeholder="请输入条形码" />
|
|
|
+ <ElInput v-model={model.barcode} placeholder="请输入条形码" maxlength={30} />
|
|
|
</ElFormItem>
|
|
|
<ElFormItem label="分类" prop="category">
|
|
|
- <ElInput v-model={model.category} placeholder="请输入分类" />
|
|
|
+ <ElInput v-model={model.category} placeholder="请输入分类" maxlength={20} />
|
|
|
</ElFormItem>
|
|
|
<ElFormItem label="品牌" prop="brand">
|
|
|
- <ElInput v-model={model.brand} placeholder="请输入品牌" />
|
|
|
+ <ElInput v-model={model.brand} placeholder="请输入品牌" maxlength={30} />
|
|
|
</ElFormItem>
|
|
|
<ElFormItem label="规格" prop="specification">
|
|
|
- <ElInput v-model={model.specification} placeholder="请输入规格" />
|
|
|
+ <ElInput v-model={model.specification} placeholder="请输入规格" maxlength={30} />
|
|
|
</ElFormItem>
|
|
|
<ElFormItem label="单位" prop="unit">
|
|
|
- <ElInput v-model={model.unit} placeholder="请输入单位" />
|
|
|
+ <ElInput v-model={model.unit} placeholder="请输入单位" maxlength={10} />
|
|
|
+ </ElFormItem>
|
|
|
+
|
|
|
+ <ElDivider />
|
|
|
+
|
|
|
+ {/* 商品类型 */}
|
|
|
+ <div class="form-section-title">商品类型</div>
|
|
|
+ <ElFormItem label="商品类型" prop="productType">
|
|
|
+ <ElSelect v-model={model.productType} placeholder="请选择商品类型">
|
|
|
+ <ElOption label="静态" value={0} />
|
|
|
+ <ElOption label="动态" value={1} />
|
|
|
+ </ElSelect>
|
|
|
</ElFormItem>
|
|
|
+ <ElFormItem label="标品/非标品" prop="standard">
|
|
|
+ <ElSelect v-model={model.standard} placeholder="请选择">
|
|
|
+ <ElOption label="标品" value={1} />
|
|
|
+ <ElOption label="非标品" value={2} />
|
|
|
+ </ElSelect>
|
|
|
+ </ElFormItem>
|
|
|
+ <ElFormItem label="学习机号" prop="stickerNum">
|
|
|
+ <ElInput v-model={model.stickerNum} placeholder="请输入学习机号" maxlength={20} />
|
|
|
+ </ElFormItem>
|
|
|
+
|
|
|
+ <ElDivider />
|
|
|
+
|
|
|
+ {/* 价格信息 */}
|
|
|
+ <div class="form-section-title">价格信息</div>
|
|
|
<ElFormItem label="售价(元)" prop="price">
|
|
|
- <ElInputNumber v-model={model.price} precision={2} min={0} />
|
|
|
+ <ElInputNumber v-model={model.price} precision={2} min={0} style="width:100%" />
|
|
|
</ElFormItem>
|
|
|
<ElFormItem label="成本价(元)" prop="costPrice">
|
|
|
- <ElInputNumber v-model={model.costPrice} precision={2} min={0} />
|
|
|
+ <ElInputNumber v-model={model.costPrice} precision={2} min={0} style="width:100%" />
|
|
|
+ </ElFormItem>
|
|
|
+
|
|
|
+ <ElDivider />
|
|
|
+
|
|
|
+ {/* 商品图片 */}
|
|
|
+ <div class="form-section-title">加工图(5张)</div>
|
|
|
+ <ElFormItem label=" " label-width="0">
|
|
|
+ <div class="image-upload-grid">
|
|
|
+ <ImageUploadItem label="正面图" v-model={model.pic1} />
|
|
|
+ <ImageUploadItem label="背面图" v-model={model.pic2} />
|
|
|
+ <ImageUploadItem label="侧面图" v-model={model.pic3} />
|
|
|
+ <ImageUploadItem label="顶部图" v-model={model.pic4} />
|
|
|
+ <ImageUploadItem label="称重图" v-model={model.pic5} />
|
|
|
+ </div>
|
|
|
+ </ElFormItem>
|
|
|
+
|
|
|
+ <div class="form-section-title">原图(4张)</div>
|
|
|
+ <ElFormItem label=" " label-width="0">
|
|
|
+ <div class="image-upload-grid">
|
|
|
+ <ImageUploadItem label="正面原图" v-model={model.oriPic1} />
|
|
|
+ <ImageUploadItem label="背面原图" v-model={model.oriPic2} />
|
|
|
+ <ImageUploadItem label="侧面原图" v-model={model.oriPic3} />
|
|
|
+ <ImageUploadItem label="顶部原图" v-model={model.oriPic4} />
|
|
|
+ </div>
|
|
|
+ </ElFormItem>
|
|
|
+
|
|
|
+ <ElDivider />
|
|
|
+
|
|
|
+ {/* 货道参数 */}
|
|
|
+ <div class="form-section-title">货道参数</div>
|
|
|
+ <ElFormItem label="长度(mm)" prop="length">
|
|
|
+ <ElInputNumber v-model={model.length} min={0} style="width:100%" />
|
|
|
+ </ElFormItem>
|
|
|
+ <ElFormItem label="宽度(mm)" prop="width">
|
|
|
+ <ElInputNumber v-model={model.width} min={0} style="width:100%" />
|
|
|
+ </ElFormItem>
|
|
|
+ <ElFormItem label="高度(mm)" prop="height">
|
|
|
+ <ElInputNumber v-model={model.height} min={0} style="width:100%" />
|
|
|
+ </ElFormItem>
|
|
|
+ <ElFormItem label="陈列高度(mm)" prop="exhibitHeight">
|
|
|
+ <ElInputNumber v-model={model.exhibitHeight} min={0} style="width:100%" />
|
|
|
+ </ElFormItem>
|
|
|
+ <ElFormItem label="商品占列" prop="columns">
|
|
|
+ <ElInputNumber v-model={model.columns} min={0} style="width:100%" />
|
|
|
+ </ElFormItem>
|
|
|
+ <ElFormItem label="每货道数量" prop="numbers">
|
|
|
+ <ElInputNumber v-model={model.numbers} min={0} style="width:100%" />
|
|
|
+ </ElFormItem>
|
|
|
+
|
|
|
+ <ElDivider />
|
|
|
+
|
|
|
+ {/* 备注与描述 */}
|
|
|
+ <div class="form-section-title">其他信息</div>
|
|
|
+ <ElFormItem label="申请原因" prop="reason">
|
|
|
+ <ElInput v-model={model.reason} type="textarea" rows={2} placeholder="请输入申请原因" maxlength={500} show-word-limit />
|
|
|
</ElFormItem>
|
|
|
<ElFormItem label="商品描述" prop="description">
|
|
|
- <ElInput
|
|
|
- v-model={model.description}
|
|
|
- type="textarea"
|
|
|
- rows={3}
|
|
|
- placeholder="请输入商品描述"
|
|
|
- />
|
|
|
+ <ElInput v-model={model.description} type="textarea" rows={3} placeholder="请输入商品描述" maxlength={500} show-word-limit />
|
|
|
</ElFormItem>
|
|
|
</ElForm>
|
|
|
);
|