feat: 更新 API 请求参数与组件样式优化

- 在 account.js 和 member.js 中添加 loading 和 skipMessage 参数,提升 API 调用的用户体验。
- 在 ShowGoods.vue 中优化商品详情展示样式,增强可读性和交互性。
- 在 Cart.vue 中重构购物车商品列表结构,提升布局一致性。
- 在多个页面中修复了提示信息的内容,确保用户操作的清晰反馈。
This commit is contained in:
田香琪
2026-07-06 16:33:21 +08:00
parent e13ca6281d
commit 297bdd95dd
28 changed files with 689 additions and 259 deletions

View File

@@ -106,8 +106,24 @@
.el-form-item__content {
margin-left: 0 !important;
align-items: flex-start;
}
}
.sku-val-item {
display: flex;
flex-direction: column;
align-items: flex-start;
}
.sku-val-input-row {
display: flex;
align-items: center;
}
.sku-val-image {
margin-top: 10px;
}
}
div.base-info-item {

View File

@@ -128,7 +128,7 @@
>
<template #item="{ element }">
<div class="demo-upload-list">
<img :src="element" />
<img :src="element" @error="handleGoodsPictureError($event, element)" />
<div class="demo-upload-list-cover">
<div>
<el-button link type="primary" @click="handleViewGoodsPicture(element)">预览</el-button>
@@ -220,29 +220,27 @@
<el-form :model="item" class="sku-val-form flex">
<!--规格值文本列表-->
<el-form-item v-for="(val, index) in item.spec_values" :key="index"
class="sku-item-content-val flex" label="" style="line-height: 32px;">
<div style="display: flex; justify-content: flex-start; align-items: center;">
<el-autocomplete
v-model="val.value"
:disabled="containsSameSkuItem"
:fetch-suggestions="(q, cb) => cb([])"
maxlength="30"
placeholder="请输入规格值"
style="width: 180px"
@focus="changeSkuVals(val, item.name)"
@blur="checkSkuVal(val, $index, item)"
@change="skuValueChange(val, index, item)"
/>
<el-button link type="danger" style="margin-left: 6px" @click="handleCloseSkuValue(val, index, item)">
<el-icon><Delete /></el-icon>
</el-button>
</div>
class="sku-item-content-val" label="">
<div class="sku-val-item">
<div class="sku-val-input-row">
<el-autocomplete
v-model="val.value"
:disabled="containsSameSkuItem"
:fetch-suggestions="(q, cb) => cb([])"
maxlength="30"
placeholder="请输入规格值"
style="width: 180px"
@focus="changeSkuVals(val, item.name)"
@blur="checkSkuVal(val, $index, item)"
@change="skuValueChange(val, index, item)"
/>
<el-button link type="danger" style="margin-left: 6px" @click="handleCloseSkuValue(val, index, item)">
<el-icon><Delete /></el-icon>
</el-button>
</div>
<!-- 内联错误提示 -->
<div v-if="val._error" class="sku-inline-error">{{ val._error }}</div>
<div v-if="$index === 0 && openImage" style="margin-top: 10px">
<div v-if="$index === 0 && openImage" class="sku-val-image">
<vuedraggable
v-model="val.images"
:animation="200"
@@ -279,12 +277,13 @@
>
<el-icon :size="40"><Camera /></el-icon>
</el-upload>
</div>
</div>
</el-form-item>
<el-form-item
v-if="item.spec_values.length < 10 && item.spec_values.length >= 1 && item.spec_values[0].value !== ''"
class="sku-item-content-val flex" label="" style="line-height: 32px;">
class="sku-item-content-val" label="">
<el-autocomplete
v-model="newSkuValues[$index]"
:disabled="containsSameSkuItem"
@@ -498,6 +497,7 @@
ref="ossManage"
:isComponent="true"
:initialize="picModelFlag"
:max-select="selectedFormBtnName === 'goodsGalleryFiles' ? 1 : 0"
@callback="callbackSelected"
@selected="(list) => { selectedImage = list }"
/>
@@ -664,6 +664,9 @@ export default {
/** 商品参数用于验证 */
goodsParams: [],
},
invalidGoodsGalleryFiles: [],
goodsPictureFallback:
"data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%22148%22%20height%3D%22148%22%20viewBox%3D%220%200%20148%20148%22%3E%3Crect%20width%3D%22148%22%20height%3D%22148%22%20fill%3D%22%23f5f7fa%22/%3E%3Ctext%20x%3D%2274%22%20y%3D%2274%22%20text-anchor%3D%22middle%22%20dominant-baseline%3D%22middle%22%20font-size%3D%2212%22%20fill%3D%22%23999%22%3EImage%20unavailable%3C/text%3E%3C/svg%3E",
/** 表格头 */
skuTableColumn: [],
/** 表格数据 */
@@ -916,13 +919,28 @@ export default {
},
// 查看商品大图
handleViewGoodsPicture(url) {
if (this.invalidGoodsGalleryFiles.includes(url)) {
this.$Message.warning("图片链接已失效,请重新上传主图");
return;
}
this.previewGoodsPicture = url;
this.goodsPictureVisible = true;
},
handleGoodsPictureError(event, url) {
if (!this.invalidGoodsGalleryFiles.includes(url)) {
this.invalidGoodsGalleryFiles.push(url);
}
const img = event && event.target;
if (!img || img.dataset.fallbackApplied) return;
img.dataset.fallbackApplied = "true";
img.src = this.goodsPictureFallback;
},
// 移除商品图片
handleRemoveGoodsPicture(file) {
this.baseInfoForm.goodsGalleryFiles =
this.baseInfoForm.goodsGalleryFiles.filter((i) => i !== file);
this.invalidGoodsGalleryFiles =
this.invalidGoodsGalleryFiles.filter((i) => i !== file);
},
assignSkuTableIndex() {
this.skuTableData.forEach((row, index) => {
@@ -1151,6 +1169,7 @@ export default {
response.result.recommend
? (response.result.recommend = 1)
: (response.result.recommend = 0);
this.invalidGoodsGalleryFiles = [];
this.baseInfoForm = { ...this.baseInfoForm, ...response.result };
this.baseInfoForm.release = 1; //即使是被放入仓库,修改的时候也会显示会立即发布
this.categoryId = response.result.categoryPath.split(",")[2];
@@ -2117,6 +2136,14 @@ export default {
this.$Message.error("请上传商品图片");
return;
}
const invalidGalleryFiles = submit.goodsGalleryFiles.filter((url) =>
this.invalidGoodsGalleryFiles.includes(url)
);
if (invalidGalleryFiles.length > 0) {
this.submitLoading = false;
this.$Message.error("存在已失效的商品主图,请删除后重新上传");
return;
}
if (submit.templateId === "") submit.templateId = 0;
let flag = false;
let paramValue = "";

View File

@@ -3,12 +3,12 @@
<el-button @click="handleClickUploadImage">上传图片</el-button>
<el-dialog v-model="show" width="850px" title="上传图片" append-to-body :z-index="3500">
<div class="import-oss" @click="importOSS">从资源库中导入</div>
<div style="display: flex; flex-wrap: wrap">
<div class="upload-images-wrap">
<vuedraggable
v-model="images"
:animation="200"
:item-key="draggableItemKey"
style="display: flex; flex-wrap: wrap"
class="upload-images-draggable"
>
<template #item="{ element, index }">
<div class="upload-list">
@@ -212,27 +212,57 @@ export default {
.wrapper {
margin: 10px 0;
}
$upload-item-size: 150px;
.upload-images-wrap {
display: flex;
flex-wrap: wrap;
align-items: flex-start;
}
.upload-images-draggable {
display: flex;
flex-wrap: wrap;
}
.upload-list {
width: 150px;
height: 150px;
width: $upload-item-size;
height: $upload-item-size;
text-align: center;
border: 1px solid transparent;
border: 1px solid #dcdfe6;
border-radius: 4px;
display: inline-block;
background: #fff;
position: relative;
margin-right: 4px;
vertical-align: bottom;
margin: 0 4px 4px 0;
overflow: hidden;
flex-shrink: 0;
}
.upload-box {
margin: 10px 0;
width: $upload-item-size;
height: $upload-item-size;
margin: 0 4px 4px 0;
display: inline-block;
vertical-align: bottom;
flex-shrink: 0;
:deep(.el-upload) {
width: $upload-item-size;
height: $upload-item-size;
display: block;
}
:deep(.el-upload-dragger) {
width: $upload-item-size;
height: $upload-item-size;
min-height: $upload-item-size;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
border-radius: 4px;
box-sizing: border-box;
}
}
.upload-trigger {
width: 148px;
height: 148px;
line-height: 148px;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;

View File

@@ -277,7 +277,7 @@
<dd>
<el-button
v-if="canConfirmReceipt"
type="danger"
type="primary"
:loading="confirmLoading"
@click="sellerConfirmSubmit('PASS')"
style="margin-left: 5px"
@@ -286,7 +286,7 @@
</el-button>
<el-button
v-if="canConfirmReceipt"
type="primary"
type="danger"
:loading="confirmLoading"
@click="sellerConfirmSubmit('REFUSE')"
style="margin-left: 5px"

View File

@@ -185,25 +185,31 @@
height: auto;
margin: 0;
:deep(.el-checkbox__label) {
.el-checkbox__label {
display: none;
}
}
&.oss-card-selectable {
cursor: pointer;
}
.preview {
width: 100%;
height: 26px;
background-color: #ffffff;
text-align: center;
line-height: 30px;
color: #666666;
position: absolute;
left: 0;
bottom: 0;
display: flex;
flex-wrap: nowrap;
align-items: center;
justify-content: center;
> div {
width: 100%;
flex: 1;
display: flex;
align-items: center;
justify-content: center;
height: 100%;
}
}
}

View File

@@ -59,6 +59,10 @@
padding: 0 4px;
}
.oss-manage-date-popper {
z-index: 5000 !important;
}
</style>
<template>
<div class="search">
@@ -88,6 +92,7 @@
end-placeholder="结束日期"
style="width: 240px"
type="daterange"
popper-class="oss-manage-date-popper"
@change="selectDateRange"
/>
</el-form-item>
@@ -162,37 +167,43 @@
<el-checkbox-group v-model="selectedOss" @change="selectOssChange">
<div class="img-box">
<div v-for="(item, index) in data" :key="index" class="img-item">
<el-checkbox :value="item.id + ',' + item.url" class="check-box">
<div
class="card"
@mouseenter="onMouseOver(item, index)"
@mouseleave="onMouseOut(item, index)"
>
<img :src="item.url" alt="" />
<div v-if="item.isShowPreview" class="preview">
<div @click.prevent="download(item)">
<el-tooltip content="下载" placement="top">
<el-icon :size="18"><Download /></el-icon>
</el-tooltip>
</div>
<div @click.prevent="remove(item)">
<el-tooltip content="删除" placement="top">
<el-icon :size="18"><Delete /></el-icon>
</el-tooltip>
</div>
<div @click.prevent="showPic(item)">
<el-tooltip content="预览" placement="top">
<el-icon :size="22"><View /></el-icon>
</el-tooltip>
</div>
<div
class="card"
:class="{
'custom-checkbox-card-checked': selectedOss.includes(item.id + ',' + item.url),
'oss-card-selectable': isComponent && selectImage,
}"
@mouseenter="onMouseOver(item, index)"
@mouseleave="onMouseOut(item, index)"
@click="handleCardClick(item, $event)"
>
<el-checkbox
:value="item.id + ',' + item.url"
class="card-checkbox"
@click.stop
/>
<img :src="item.url" alt="" />
<div v-if="item.isShowPreview" class="preview">
<div @click.prevent="download(item)">
<el-tooltip content="下载" placement="top">
<el-icon :size="18"><Download /></el-icon>
</el-tooltip>
</div>
<div @click.prevent="remove(item)">
<el-tooltip content="删除" placement="top">
<el-icon :size="18"><Delete /></el-icon>
</el-tooltip>
</div>
<div @click.prevent="showPic(item)">
<el-tooltip content="预览" placement="top">
<el-icon :size="22"><View /></el-icon>
</el-tooltip>
</div>
</div>
</el-checkbox>
<div>
<el-tooltip :content="item.name" placement="bottom">
<div class="text">{{ item.name }}</div>
</el-tooltip>
</div>
<el-tooltip :content="item.name" placement="bottom">
<div class="text">{{ item.name }}</div>
</el-tooltip>
</div>
</div>
</el-checkbox-group>
@@ -1070,6 +1081,18 @@ export default {
this.$emit("selected", e);
}
},
handleCardClick(item, event) {
if (!(this.isComponent && this.selectImage)) return;
if (event?.target?.closest?.(".preview") || event?.target?.closest?.(".card-checkbox")) {
return;
}
const key = `${item.id},${item.url}`;
const next = this.selectedOss.includes(key)
? this.selectedOss.filter((k) => k !== key)
: [...this.selectedOss, key];
this.selectedOss = next;
this.selectOssChange(next);
},
// 页码改变时回调
pageChange(value) {
this.selectedOss = [];

View File

@@ -165,9 +165,13 @@
<div v-for="(item, index) in data" :key="index" class="img-item">
<div
class="card"
:class="{ 'custom-checkbox-card-checked': selectedOss.includes(item.id + ',' + item.url) }"
:class="{
'custom-checkbox-card-checked': selectedOss.includes(item.id + ',' + item.url),
'oss-card-selectable': isComponent && selectImage
}"
@mouseenter="onMouseOver(item, index)"
@mouseleave="onMouseOut(item, index)"
@click="toggleSelectedOss(item)"
>
<el-checkbox
:value="item.id + ',' + item.url"
@@ -176,17 +180,17 @@
/>
<img :src="item.url" alt="" />
<div v-if="item.isShowPreview" class="preview">
<div @click.prevent="download(item)">
<div @click.stop.prevent="download(item)">
<el-tooltip content="下载" placement="top">
<el-icon :size="18"><Download /></el-icon>
</el-tooltip>
</div>
<div @click.prevent="remove(item)">
<div @click.stop.prevent="remove(item)">
<el-tooltip content="删除" placement="top">
<el-icon :size="18"><Delete /></el-icon>
</el-tooltip>
</div>
<div @click.prevent="showPic(item)">
<div @click.stop.prevent="showPic(item)">
<el-tooltip content="预览" placement="top">
<el-icon :size="22"><View /></el-icon>
</el-tooltip>
@@ -488,6 +492,10 @@ export default {
type: Boolean,
default: false,
},
maxSelect: {
type: Number,
default: 0,
},
},
data() {
return {
@@ -1059,6 +1067,10 @@ export default {
// 复选框值改变时触发
selectOssChange(e) {
if (e) {
if (this.maxSelect > 0 && e.length > this.maxSelect) {
e = e.slice(-this.maxSelect);
this.selectedOss = e;
}
this.selectList = e.map(item => {
return {id: item.split(',')[0]}
});
@@ -1069,6 +1081,22 @@ export default {
this.$emit("selected", e);
}
},
toggleSelectedOss(item) {
if (!item) return;
const value = `${item.id},${item.url}`;
const index = this.selectedOss.indexOf(value);
if (index > -1) {
this.selectedOss.splice(index, 1);
} else if (this.maxSelect === 1) {
this.selectedOss = [value];
} else {
if (this.maxSelect > 0 && this.selectedOss.length >= this.maxSelect) {
this.selectedOss = this.selectedOss.slice(1);
}
this.selectedOss.push(value);
}
this.selectOssChange(this.selectedOss);
},
// 页码改变时回调
pageChange(value) {
this.selectedOss = [];