升级Vue3,iView替换ElementPlus

- 删除babel配置、更新依赖与入口初始化
- 全量替换UI组件、样式适配,新增迁移文档与标签/过滤器自动化替换脚本
This commit is contained in:
lifenlong
2026-06-05 17:49:43 +08:00
parent 615ee91511
commit 832fda813b
322 changed files with 25693 additions and 24453 deletions

View File

@@ -85,7 +85,7 @@
flex-wrap: wrap !important;
}
::v-deep .sku-item-content-val {
:deep(.sku-item-content-val) {
margin-right: 20px;
}
}
@@ -215,7 +215,7 @@ div.base-info-item {
}
/*teatarea*/
::v-deep .el-textarea {
:deep(.el-textarea) {
width: 150%;
}
@@ -226,7 +226,7 @@ div.base-info-item {
/*折叠面板*/
.el-collapse-item {
::v-deep .el-collapse-item__header {
:deep(.el-collapse-item__header) {
text-align: left;
background-color: #f8f8f8;
padding: 0 10px;
@@ -240,7 +240,7 @@ div.base-info-item {
width: 25%;
}
::v-deep .el-form-item__content {
:deep(.el-form-item__content) {
margin-left: 120px;
text-align: left;
}
@@ -251,7 +251,7 @@ div.base-info-item {
color: #999;
}
::v-deep .el-collapse-item__content {
:deep(.el-collapse-item__content) {
padding: 10px 0;
text-align: left;
}
@@ -279,6 +279,11 @@ div.base-info-item {
}
/** 底部步骤 */
.footer-btns {
display: flex;
gap: 10px;
}
.footer {
width: 100%;
margin-top: 20px;
@@ -296,11 +301,11 @@ div.base-info-item {
/*图片上传组件第一张图设置封面*/
.goods-images {
::v-deep li.el-upload-list__item:first-child {
:deep(li.el-upload-list__item:first-child) {
position: relative;
}
::v-deep li.el-upload-list__item:first-child:after {
:deep(li.el-upload-list__item:first-child:after) {
content: "";
color: #fff;
font-weight: bold;
@@ -394,7 +399,7 @@ div.base-info-item {
}
.required {
::v-deep .ivu-form-item-label::before {
:deep(.el-form-item__label::before) {
content: "*";
display: inline-block;
margin-right: 4px;
@@ -483,7 +488,7 @@ div.base-info-item {
justify-content: center;
align-items: center;
::v-deep img {
:deep(img) {
margin-right: 20px;
width: 100px;
margin-left: 10px;
@@ -491,7 +496,7 @@ div.base-info-item {
::v-deep p {
:deep(p) {
color: #999;
font-size: 14px;
margin-top: 10px;

View File

@@ -1,185 +1,187 @@
<template>
<div class="search">
<Card>
<Row @keydown.enter.native="handleSearch">
<Form
ref="searchForm"
:model="searchForm"
inline
:label-width="70"
class="search-form"
>
<Form-item label="商品名称" prop="goodsName">
<Input
type="text"
v-model="searchForm.goodsName"
placeholder="请输入商品名称"
clearable
style="width: 200px"
/>
</Form-item>
<Form-item label="商品编号" prop="goodsId">
<Input
type="text"
v-model="searchForm.goodsId"
placeholder="请输入商品编号"
clearable
style="width: 200px"
/>
</Form-item>
<Form-item label="状态" prop="status">
<Select
v-model="searchForm.marketEnable"
placeholder="请选择"
clearable
style="width: 200px"
>
<Option value="DOWN">下架</Option>
<Option value="UPPER">上架</Option>
</Select>
</Form-item>
<Form-item label="商品分类" prop="category">
<Cascader
<el-card>
<div @keyup.enter="handleSearch">
<el-form ref="searchForm" :model="searchForm" inline label-width="70px" class="search-form">
<el-form-item label="商品名称" prop="goodsName">
<el-input v-model="searchForm.goodsName" placeholder="请输入商品名称" clearable style="width: 200px" />
</el-form-item>
<el-form-item label="商品编号" prop="goodsId">
<el-input v-model="searchForm.goodsId" placeholder="请输入商品编号" clearable style="width: 200px" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="searchForm.marketEnable" placeholder="请选择" clearable style="width: 200px">
<el-option label="下架" value="DOWN" />
<el-option label="上架" value="UPPER" />
</el-select>
</el-form-item>
<el-form-item label="商品分类" prop="category">
<el-cascader
v-model="category"
:options="categoryList"
placeholder="请选择商品分类"
style="width: 200px"
:data="categoryList"
></Cascader>
</Form-item>
<Form-item label="货号" prop="id">
<Input
type="text"
v-model="searchForm.skuSn"
placeholder="请输入货号"
clearable
style="width: 200px"
/>
</Form-item>
<Button @click="handleSearch" type="primary" class="search-btn">搜索</Button>
<Button @click="handleReset" class="search-btn">重置</Button>
</Form>
</Row>
<Tabs @on-click="switchTabs" value="updateStock" v-model="stockType">
<TabPane label="商品库存管理" name="stockManage">
<Row class="operation padding-row">
<Button @click="exportStock" type="primary" class="export">批量导出</Button>
<Button @click="openImportStock" class="export">批量导入</Button>
</Row>
<Table
class="mt_10"
border
:loading="loading"
:columns="stockManageColumns"
:data="stockAllData"
ref="table"
>
<template slot="goodsSlot" slot-scope="{ row }">
<div style="margin-top: 5px; height: 90px; display: flex">
<div style="">
<img :src="row.thumbnail" style="height: 80px; margin-top: 3px; width: 70px"/>
</div>
<div style="margin-left: 13px;margin-top: 5px;">
<div class="div-zoom" style="color: black;">
{{ row.goodsName }}
</div>
<div class="div-zoom" style="margin-top: 5px;">
ID: {{ row.goodsId }}
</div>
</div>
</div>
</template>
<template slot="skuSlot" slot-scope="{ row }">
<div style="margin-top: 5px; height: 90px; display: flex">
<div style="margin-left: 13px;margin-top: 5px;">
<div class="div-zoom" style="color: black;">
{{ row.simpleSpecs }}
</div>
<div class="div-zoom" style="margin-top: 5px;">
ID: {{ row.id }}
</div>
</div>
</div>
</template>
</Table>
</TabPane>
<TabPane label="预警商品" name="warnList">
<Table
class="mt_10"
border
:loading="loading"
:columns="columns"
:data="warnData"
ref="table"
>
</Table>
</TabPane>
<TabPane label="设置预警" name="warnSetting">
<Table
class="mt_10"
border
:loading="loading"
:columns="settingColumns"
:data="skuAllData"
ref="table"
>
<template slot="alertQuantitySlot" slot-scope="{ row }">
<Input type="number" v-model="row.alertQuantity" clearable placeholder="请输入预警库存"
@on-blur="updateWarnStock(row)" @on-change="checkVal(row)"/>
</template>
</Table>
</TabPane>
</Tabs>
<Row type="flex" justify="end" class="mt_10">
<Page
:current="searchForm.pageNumber"
:total="total"
:page-size="searchForm.pageSize"
@on-change="changePage"
@on-page-size-change="changePageSize"
:page-size-opts="[20, 50]"
size="small"
show-total
show-elevator
show-sizer
></Page>
</Row>
</Card>
<Modal
title="更新库存"
v-model="updateStockModalVisible"
:mask-closable="false"
:width="610"
>
<Table
class="mt_10"
:columns="updateStockColumns"
:data="stockList"
border
></Table>
<div slot="footer">
<Button type="text" @click="updateStockModalVisible = false">取消</Button>
<Button type="primary" @click="updateStock">更新</Button>
</el-form-item>
<el-form-item label="货号" prop="id">
<el-input v-model="searchForm.skuSn" placeholder="请输入货号" clearable style="width: 200px" />
</el-form-item>
<el-form-item>
<el-button type="primary" class="search-btn" @click="handleSearch">搜索</el-button>
<el-button class="search-btn" @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
</div>
</Modal>
<Modal title="导入商品信息" v-model="importModal" :mask-closable="false">
<div style="text-align: center">
<Upload :before-upload="handleUpload" name="files"
accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel"
multiple type="drag" :action="action" :headers="accessToken">
<el-tabs v-model="stockType" @tab-click="switchTabs">
<el-tab-pane label="商品库存管理" name="stockManage">
<div class="operation padding-row">
<el-button type="primary" class="export" @click="exportStock">批量导出</el-button>
<el-button class="export" @click="openImportStock">批量导入</el-button>
</div>
<el-table ref="table" v-loading="loading" border :data="stockAllData" class="mt_10" style="width: 100%">
<el-table-column label="商品信息" min-width="400">
<template #default="{ row }">
<div style="margin-top: 5px; height: 90px; display: flex">
<img :src="row.thumbnail" style="height: 80px; margin-top: 3px; width: 70px" alt="" />
<div style="margin-left: 13px; margin-top: 5px">
<div class="div-zoom" style="color: black">{{ row.goodsName }}</div>
<div class="div-zoom" style="margin-top: 5px">ID: {{ row.goodsId }}</div>
</div>
</div>
</template>
</el-table-column>
<el-table-column label="SKU信息" min-width="400">
<template #default="{ row }">
<div style="margin-top: 5px">
<div class="div-zoom" style="color: black">{{ row.simpleSpecs }}</div>
<div class="div-zoom" style="margin-top: 5px">ID: {{ row.id }}</div>
</div>
</template>
</el-table-column>
<el-table-column label="上架状态" width="130">
<template #default="{ row }">
<el-tag :type="row.marketEnable === 'DOWN' ? 'danger' : 'success'">
{{ row.marketEnable === "DOWN" ? "下架" : "上架" }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="审核状态" width="120">
<template #default="{ row }">
<el-tag
:type="
row.authFlag === 'PASS' ? 'success' : row.authFlag === 'TOBEAUDITED' ? 'warning' : 'danger'
"
>
{{
row.authFlag === "PASS" ? "通过" : row.authFlag === "TOBEAUDITED" ? "待审核" : "审核拒绝"
}}
</el-tag>
</template>
</el-table-column>
<el-table-column label="库存" min-width="120">
<template #default="{ row }">{{ row.quantity || 0 }}</template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane label="预警商品" name="warnList">
<el-table v-loading="loading" border :data="warnData" class="mt_10" style="width: 100%">
<el-table-column prop="goodsName" label="商品名称" min-width="400" show-overflow-tooltip />
<el-table-column label="库存" min-width="120">
<template #default="{ row }">{{ row.quantity || 0 }}</template>
</el-table-column>
<el-table-column label="预警值" min-width="120">
<template #default="{ row }">{{ row.alertQuantity || 0 }}</template>
</el-table-column>
<el-table-column label="操作" width="120" align="center" fixed="right">
<template #default="{ row }">
<a class="link-text" @click="openUpdataStockModal(row)">库存</a>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane label="设置预警" name="warnSetting">
<el-table v-loading="loading" border :data="skuAllData" class="mt_10" style="width: 100%">
<el-table-column prop="goodsName" label="商品名称" />
<el-table-column label="库存" width="200">
<template #default="{ row }">{{ row.quantity || 0 }}</template>
</el-table-column>
<el-table-column label="预警值" width="200">
<template #default="{ row }">
<el-input
v-model="row.alertQuantity"
type="number"
clearable
placeholder="请输入预警库存"
@blur="updateWarnStock(row)"
@change="checkVal(row)"
/>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
</el-tabs>
<div class="mt_10" style="display: flex; justify-content: flex-end">
<el-pagination
v-model:current-page="searchForm.pageNumber"
v-model:page-size="searchForm.pageSize"
:page-sizes="[20, 50]"
:total="total"
layout="total, sizes, prev, pager, next, jumper"
size="small"
@current-change="changePage"
@size-change="changePageSize"
/>
</div>
</el-card>
<el-dialog v-model="updateStockModalVisible" title="更新库存" width="610px" :close-on-click-modal="false">
<el-table :data="stockList" border class="mt_10" style="width: 100%">
<el-table-column label="sku规格" min-width="120">
<template #default="{ row }">{{ row.simpleSpecs }}</template>
</el-table-column>
<el-table-column label="审核状态" width="130">
<template #default="{ row }">
<el-tag
:type="
row.authFlag === 'PASS' ? 'success' : row.authFlag === 'TOBEAUDITED' ? 'primary' : 'danger'
"
>
{{
row.authFlag === "TOBEAUDITED" ? "待审核" : row.authFlag === "PASS" ? "通过" : "审核拒绝"
}}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="200" align="center">
<template #default="{ row }">
<el-input-number v-model="row.quantity" :min="0" />
</template>
</el-table-column>
</el-table>
<template #footer>
<el-button @click="updateStockModalVisible = false">取消</el-button>
<el-button type="primary" @click="updateStock">更新</el-button>
</template>
</el-dialog>
<el-dialog v-model="importModal" title="导入商品信息" :close-on-click-modal="false">
<div v-loading="spinShow" style="text-align: center">
<el-upload drag :before-upload="handleUpload" :show-file-list="false" accept=".xlsx,.xls">
<div style="padding: 50px 0">
<Icon type="ios-cloud-upload" size="102" style="color: #3399ff"></Icon>
<div style="font-size: 48px; color: #3399ff"></div>
<h2>选择或拖拽文件上传</h2>
</div>
<Spin fix v-if="spinShow"></Spin>
</Upload>
</el-upload>
</div>
<div slot="footer">
<Button type="text" @click="importModal = false">确定</Button>
</div>
</Modal>
<template #footer>
<el-button type="primary" @click="importModal = false">确定</el-button>
</template>
</el-dialog>
</div>
</template>
@@ -191,7 +193,7 @@ import {
importStockExcel,
queryExportStock,
updateGoodsAlertStocks,
updateGoodsSkuStocks
updateGoodsSkuStocks,
} from "@/api/goods";
export default {
@@ -199,227 +201,44 @@ export default {
data() {
return {
importModal: false,
id: "", //要操作的id
loading: true, // 表单加载状态
updateStockModalVisible: false, // 更新库存模态框显隐
stockAllUpdate: undefined, // 更新库存数量
stockType: 'stockManage',
spinShow: false,
loading: true,
updateStockModalVisible: false,
stockType: "stockManage",
searchForm: {
// 搜索框初始化对象
pageNumber: 1, // 当前页数
pageSize: 20, // 页面大小
sort: "create_time", // 默认排序字段
order: "desc", // 默认排序方式
pageNumber: 1,
pageSize: 20,
sort: "create_time",
order: "desc",
},
checkFlag: false, //检测成功标志
// 表单验证规则
formValidate: {},
//修改库存的数据
checkFlag: false,
stockList: [],
stockManageColumns: [
{
title: "商品信息",
key: "goodsName",
midwidth: 400,
slot: "goodsSlot",
}, {
title: "SKU信息",
key: "simpleSpecs",
midwidth: 400,
tooltip: true,
slot: "skuSlot",
}, {
title: "上架状态",
key: "marketEnable",
width: 130,
sortable: false,
render: (h, params) => {
if (params.row.marketEnable == "DOWN") {
return h("Tag", {props: {color: "red"}}, "下架");
} else if (params.row.marketEnable == "UPPER") {
return h("Tag", {props: {color: "green"}}, "上架");
}
},
}, {
title: "审核状态",
key: "authFlag",
width: 120,
render: (h, params) => {
if (params.row.authFlag == "PASS") {
return h("Tag", {props: {color: "green"}}, "通过");
} else if (params.row.authFlag == "TOBEAUDITED") {
return h("Tag", {props: {color: "volcano"}}, "待审核");
} else if (params.row.authFlag == "REFUSE") {
return h("Tag", {props: {color: "red"}}, "审核拒绝");
}
},
}, {
title: "库存",
key: "quantity",
midwidth: 280,
render: (h, params) => {
if (params.row.quantity) {
return h("div", params.row.quantity);
} else {
return h("div", 0);
}
},
},],
//列表名称
columns: [
{
title: "商品名称",
key: "goodsName",
midwidth: 400,
tooltip: true,
},
{
title: "库存",
key: "quantity",
midwidth: 280,
render: (h, params) => {
if (params.row.quantity) {
return h("div", params.row.quantity);
} else {
return h("div", 0);
}
},
},
{
title: "预警值",
key: "alertQuantity",
midwidth: 280,
render: (h, params) => {
if (params.row.alertQuantity) {
return h("div", params.row.alertQuantity);
} else {
return h("div", 0);
}
},
},
{
title: "操作",
key: "action",
align: "center",
fixed: "right",
width: 200,
render: (h, params) => {
return h("div", [
h(
"a",
{
style: {
color: "#2d8cf0",
cursor: "pointer",
textDecoration: "none",
marginRight: "5px",
},
on: {
click: () => {
this.openUpdataStockModal(params.row);
},
},
},
"库存"
),
]);
},
},
],
settingColumns: [
{
title: "商品名称",
key: "goodsName",
},
{
title: "库存",
key: "quantity",
width: 400,
render: (h, params) => {
if (params.row.quantity) {
return h("div", params.row.quantity);
} else {
return h("div", 0);
}
},
},
{
title: "预警值",
key: "alertQuantity",
width: 400,
slot: 'alertQuantitySlot',
},
],
updateStockColumns: [
{
title: "sku规格",
key: "sn",
minWidth: 120,
render: (h, params) => {
return h("div", {}, params.row.simpleSpecs);
},
},
{
title: "审核状态",
key: "authFlag",
width: 130,
render: (h, params) => {
if (params.row.authFlag == "TOBEAUDITED") {
return h("Tag", {props: {color: "blue"}}, "待审核");
} else if (params.row.authFlag == "PASS") {
return h("Tag", {props: {color: "green"}}, "通过");
} else if (params.row.authFlag == "REFUSE") {
return h("Tag", {props: {color: "red"}}, "审核拒绝");
}
},
},
{
title: "操作",
key: "action",
align: "center",
width: 200,
render: (h, params) => {
let vm = this;
return h("InputNumber", {
props: {
value: params.row.quantity,
},
on: {
"on-change": (event) => {
vm.stockList[params.index].quantity = event;
},
},
});
},
},
],
warnData: [], // 表单数据
skuAllData: [], //SKU数据
stockAllData: [],//SKU库存数据
total: 0, //sku数据总数
categoryList: [], //分类列表
category: '', //选中分类
warnData: [],
skuAllData: [],
stockAllData: [],
total: 0,
categoryList: [],
category: [],
selectedSku: {},
file: null,
};
},
methods: {
init() {
// 初始化数据
this.getDataList();
this.deepGroup();
},
openUpdataStockModal(row) {
this.stockList = []
this.stockList = [];
this.selectedSku = JSON.parse(JSON.stringify(row));
this.stockList.push(this.selectedSku);
this.updateStockModalVisible = true;
},
// 更新库存
updateStock() {
let updateStockList = this.stockList.map((i) => {
let j = {skuId: i.id, quantity: i.quantity};
return j;
});
const updateStockList = this.stockList.map((i) => ({
skuId: i.id,
quantity: i.quantity,
}));
updateGoodsSkuStocks(updateStockList).then((res) => {
if (res.success) {
this.updateStockModalVisible = false;
@@ -428,192 +247,144 @@ export default {
}
});
},
//修改预警值
updateWarnStock(row) {
if (this.checkFlag) {
let submit = {skuId: row.id, alertQuantity: row.alertQuantity}
updateGoodsAlertStocks(submit).then(res => {
updateGoodsAlertStocks({ skuId: row.id, alertQuantity: row.alertQuantity }).then((res) => {
if (res.success) {
this.$Message.success('更新成功')
this.$Message.success("更新成功");
}
})
});
}
},
//检测输入值是否正确
checkVal(row) {
if (
!/^[+]{0,1}(\d+)$|^[+]{0,1}(\d+\.\d+)$/.test(row.alertQuantity) ||
parseInt(row.alertQuantity) < 0 ||
parseInt(row.alertQuantity) > 99999999
) {
// 校验未通过 进行提示
this.$Message.error("请输入0~99999999之间的数字值")
row.alertQuantity = 0
this.$Message.error("请输入0~99999999之间的数字值");
row.alertQuantity = 0;
this.checkFlag = false;
return;
}
this.checkFlag = true;
},
//切换分页
switchTabs() {
this.handleReset();
this.getDataList();
},
// 改变页数
changePageSize(v) {
this.searchForm.pageSize = v;
this.getDataList();
},
//改变页码
changePage(v) {
this.searchForm.pageNumber = v;
this.getDataList();
this.clearSelectAll();
},
// 搜索
handleSearch() {
this.searchForm.pageNumber = 1;
this.searchForm.pageSize = 10;
this.searchForm.categoryPath = this.category ? this.category.join(",") : null;
this.searchForm.categoryPath = this.category?.length ? this.category.join(",") : null;
this.getDataList();
},
// 重置搜索条件
handleReset() {
this.searchForm = {};
this.searchForm = {
pageNumber: 1,
pageSize: 20,
sort: "create_time",
order: "desc",
};
this.category = [];
this.searchForm.pageNumber = 1;
this.searchForm.pageSize = 10;
// 重新加载数据
this.getDataList();
},
// 获取商品列表数据
getDataList() {
this.loading = true;
// 带多条件搜索参数获取表单数据
if (this.stockType == 'warnList') {
//调用预警库存
getGoodsListDataByStockSeller(this.searchForm).then(res => {
if (this.stockType === "warnList") {
getGoodsListDataByStockSeller(this.searchForm).then((res) => {
if (res.success) {
this.warnData = res.result.records
this.total = res.result.total
this.loading = false;
this.warnData = res.result.records;
this.total = res.result.total;
}
})
} else if (this.stockType == 'warnSetting') {
//调用获取全部sku
getGoodsSkuListDataSeller(this.searchForm).then(res => {
this.loading = false;
});
} else if (this.stockType === "warnSetting") {
getGoodsSkuListDataSeller(this.searchForm).then((res) => {
if (res.success) {
this.skuAllData = res.result.records
this.total = res.result.total
this.loading = false;
this.skuAllData = res.result.records;
this.total = res.result.total;
}
})
} else if (this.stockType == 'stockManage') {
//调用获取全部sku
getGoodsSkuListDataSeller(this.searchForm).then(res => {
this.loading = false;
});
} else {
getGoodsSkuListDataSeller(this.searchForm).then((res) => {
if (res.success) {
this.stockAllData = res.result.records
this.total = res.result.total
this.loading = false;
this.stockAllData = res.result.records;
this.total = res.result.total;
}
})
this.loading = false;
});
}
},
//组织分类树
deepGroup() {
getGoodsCategoryAll().then(res => {
getGoodsCategoryAll().then((res) => {
if (res.success) {
res.result.forEach((item) => {
let childWay = []; //第二级
// 第二层
if (item.children) {
item.children.forEach((child) => {
// // 第三层
if (child.children) {
child.children.forEach((grandson, index, arr) => {
arr[index] = {
value: grandson.id,
label: grandson.name,
children: "",
};
});
}
let children = {
value: child.id,
label: child.name,
children: child.children,
};
childWay.push(children);
});
}
// 第一层
let way = {
value: item.id,
label: item.name,
children: childWay,
};
this.categoryList.push(way);
});
this.categoryList = res.result.map((item) => ({
value: item.id,
label: item.name,
children: (item.children || []).map((child) => ({
value: child.id,
label: child.name,
children: (child.children || []).map((grandson) => ({
value: grandson.id,
label: grandson.name,
})),
})),
}));
}
})
});
},
// 导出订单
async exportStock() {
let randomNumber = '';
let randomNumber = "";
for (let i = 0; i < 10; i++) {
randomNumber += Math.floor(Math.random() * 10);
}
;
queryExportStock(this.searchForm)
.then((res) => {
const blob = new Blob([res], {
type: "application/vnd.ms-excel;charset=utf-8",
});
//对于<a>标签,只有 Firefox 和 Chrome内核 支持 download 属性
//IE10以上支持blob但是依然不支持download
const blob = new Blob([res], { type: "application/vnd.ms-excel;charset=utf-8" });
if ("download" in document.createElement("a")) {
//支持a标签download的浏览器
const link = document.createElement("a"); //创建a标签
link.download = randomNumber + ".xlsx"; //a标签添加属性
const link = document.createElement("a");
link.download = randomNumber + ".xlsx";
link.style.display = "none";
link.href = URL.createObjectURL(blob);
document.body.appendChild(link);
link.click(); //执行下载
URL.revokeObjectURL(link.href); //释放url
document.body.removeChild(link); //释放标签
link.click();
URL.revokeObjectURL(link.href);
document.body.removeChild(link);
} else {
navigator.msSaveBlob(blob, fileName);
navigator.msSaveBlob(blob, randomNumber + ".xlsx");
}
})
.catch((err) => {
console.log(err);
});
.catch((err) => console.log(err));
},
openImportStock() {
this.importModal = true;
},
// 上传数据
handleUpload(file) {
this.file = file;
this.upload();
return false;
},
/**
* 上传文件
*/
upload() {
let fd = new FormData();
const fd = new FormData();
fd.append("files", this.file);
this.spinShow = true;
importStockExcel(fd).then(res => {
importStockExcel(fd).then((res) => {
this.spinShow = false;
if (res.success) {
this.spinShow = false;
this.$Message.success("导入成功");
this.importModal = false;
this.getDataList();
}
})
});
},
},
mounted() {
@@ -623,4 +394,9 @@ export default {
</script>
<style lang="scss" scoped>
@import "@/styles/table-common.scss";
.link-text {
color: #2d8cf0;
cursor: pointer;
text-decoration: none;
}
</style>

View File

@@ -1,174 +1,104 @@
<template>
<div class="search">
<Card>
<Form ref="searchForm" :model="searchForm" inline :label-width="70" class="search-form mb_10"
@keydown.enter.native="handleSearch">
<Form-item label="商品名称" prop="goodsName">
<Input type="text" v-model="searchForm.goodsName" placeholder="请输入商品名称" clearable style="width: 240px" />
</Form-item>
<Form-item label="商品编号" prop="id">
<Input
type="text"
v-model="searchForm.id"
placeholder="商品编号"
clearable
style="width: 200px"
/>
</Form-item>
<Form-item style="margin-left: -35px" class="br">
<Button @click="handleSearch" type="primary" icon="ios-search"
>搜索</Button
>
<Button @click="handleReset">重置</Button>
</Form-item>
</Form>
</Card>
<Card>
<Table :loading="loading" border :columns="columns" :data="data" ref="table" class="mt_10"></Table>
<Row type="flex" justify="end" class="mt_10">
<Page :current="searchForm.pageNumber" :total="total" :page-size="searchForm.pageSize" @on-change="changePage"
@on-page-size-change="changePageSize" :page-size-opts="[10, 20, 50]" size="small" show-total show-elevator
show-sizer></Page>
</Row>
</Card>
<el-card>
<el-form
ref="searchForm"
:model="searchForm"
inline
label-width="70px"
class="search-form mb_10"
@keyup.enter="handleSearch"
>
<el-form-item label="商品名称" prop="goodsName">
<el-input v-model="searchForm.goodsName" placeholder="请输入商品名称" clearable style="width: 240px" />
</el-form-item>
<el-form-item label="商品编号" prop="id">
<el-input v-model="searchForm.id" placeholder="商品编号" clearable style="width: 200px" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">搜索</el-button>
<el-button @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card>
<el-table v-loading="loading" border :data="data" ref="table" class="mt_10" style="width: 100%">
<el-table-column prop="id" label="编号" min-width="120" />
<el-table-column label="商品原图" width="120" align="center">
<template #default="{ row }">
<img
:src="row.original"
alt="加载图片失败"
style="cursor: pointer; width: 80px; height: 60px; margin: 10px 0; object-fit: contain"
/>
</template>
</el-table-column>
<el-table-column prop="goodsName" label="商品名称" min-width="120" show-overflow-tooltip />
<el-table-column label="商品价格" width="120">
<template #default="{ row }">
<priceColorScheme :value="row.price || 0" :color="$mainColor" />
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" min-width="120" />
<el-table-column label="操作" align="center" width="150">
<template #default="{ row }">
<a class="link-text" @click="editGoods(row)">编辑</a>
<span class="op-split">|</span>
<a class="link-text" @click="removeDraft(row.id)">删除</a>
</template>
</el-table-column>
</el-table>
<div class="mt_10" style="display: flex; justify-content: flex-end">
<el-pagination
v-model:current-page="searchForm.pageNumber"
v-model:page-size="searchForm.pageSize"
:page-sizes="[10, 20, 50]"
:total="total"
layout="total, sizes, prev, pager, next, jumper"
size="small"
@current-change="changePage"
@size-change="changePageSize"
/>
</div>
</el-card>
</div>
</template>
<script>
import { getDraftGoodsListData, deleteDraftGoods } from "@/api/goods";
export default {
name: "goods",
components: {},
data() {
return {
loading: true, // 表单加载状态
loading: true,
searchForm: {
// 搜索框初始化对象
pageNumber: 1, // 当前页数
pageSize: 10, // 页面大小
sort: "create_time", // 默认排序字段
order: "desc", // 默认排序方式
pageNumber: 1,
pageSize: 10,
sort: "create_time",
order: "desc",
saveType: "TEMPLATE",
},
columns: [
// 表头
{
title: "编号",
key: "id",
minWidth: 120,
},
{
title: "商品原图",
key: "original",
width: 120,
align: "center",
render: (h, params) => {
return h("img", {
attrs: {
src: params.row.original,
alt: "加载图片失败",
},
style: {
cursor: "pointer",
width: "80px",
height: "60px",
margin: "10px 0",
"object-fit": "contain",
},
});
},
},
{
title: "商品名称",
key: "goodsName",
minWidth: 120,
},
{
title: "商品价格",
key: "price",
width: 120,
render: (h, params) => {
return h("priceColorScheme", {props:{value:params.row.price || 0,color:this.$mainColor}} );
},
},
{
title: "创建时间",
key: "createTime",
minWidth: 120,
},
{
title: "操作",
key: "action",
align: "center",
width: 150,
render: (h, params) => {
return h("div", [
h(
"a",
{
style: {
color: "#2d8cf0",
cursor: "pointer",
textDecoration: "none",
marginRight: "5px",
},
on: {
click: () => {
this.editGoods(params.row);
},
},
},
"编辑"
),
h(
"span",
{
style: { margin: "0 8px", color: "#dcdee2" },
},
"|"
),
h(
"a",
{
style: {
color: "#2d8cf0",
cursor: "pointer",
textDecoration: "none",
},
on: {
click: () => {
this.removeDraft(params.row.id);
},
},
},
"删除"
),
]);
},
},
],
data: [], // 表单数据
total: 0, // 表单数据总数
data: [],
total: 0,
};
},
methods: {
init() {
// 初始化数据
this.getDataList();
},
// 编辑模板
editGoods(v) {
this.$router.push({
name: "goods-template-operation-edit",
query: { draftId: v.id },
});
},
// 删除模板
removeDraft(id) {
let showType = "模版";
this.$Modal.confirm({
title: "确认审核",
content: "您确认要删除id为 " + id + " 的" + showType + "吗?",
content: "您确认要删除id为 " + id + " 的模版吗?",
loading: true,
onOk: () => {
deleteDraftGoods(id).then((res) => {
@@ -181,34 +111,25 @@ export default {
},
});
},
// 改变页数
changePage(v) {
this.searchForm.pageNumber = v;
changePage() {
this.getDataList();
},
// 改变页码
changePageSize(v) {
this.searchForm.pageSize = v;
changePageSize() {
this.getDataList();
},
// 搜索
handleSearch() {
this.searchForm.pageNumber = 1;
this.searchForm.pageSize = 10;
this.getDataList();
},
// 重置
handleReset() {
this.$refs.searchForm.resetFields();
this.searchForm.pageNumber = 1;
this.searchForm.pageSize = 10;
// 重新加载数据
this.getDataList();
},
// 获取列表数据
getDataList() {
this.loading = true;
// 带多条件搜索参数获取表单数据
getDraftGoodsListData(this.searchForm).then((res) => {
this.loading = false;
if (res.success) {
@@ -222,11 +143,21 @@ export default {
this.init();
},
watch: {
$route(to, from) {
$route() {
this.init();
},
},
};
</script>
<style lang="scss" scoped>
.link-text {
color: #409eff;
cursor: pointer;
text-decoration: none;
}
.op-split {
margin: 0 8px;
color: #dcdee2;
}
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,11 @@
<template>
<div class="goods-operation">
<div class="step-list">
<steps :current="activestep" style="height:60px;margin-top: 10px">
<step title="选择商品品类"/>
<step title="填写商品详情"/>
<step title="商品发布成功"/>
</steps>
<el-steps :active="activestep" align-center style="height: 60px; margin-top: 10px">
<el-step title="选择商品品类" />
<el-step title="填写商品详情" />
<el-step title="商品发布成功" />
</el-steps>
</div>
<!-- 第一步 选择分类 -->
<first-step ref='first' v-show="activestep === 0" @change="getFirstData"></first-step>

View File

@@ -1,7 +1,7 @@
<template>
<div>
<!-- 选择商品类型 -->
<Modal v-model="selectGoodsType" width="550" :closable="false">
<el-dialog v-model="selectGoodsType" width="550px" :show-close="false">
<div class="goods-type-list">
<div
class="goods-type-item"
@@ -17,7 +17,7 @@
</div>
</div>
</div>
</Modal>
</el-dialog>
<!-- 商品分类 -->
<div class="content-goods-publish">
<div class="goods-category">
@@ -63,10 +63,10 @@
</div>
<!-- 底部按钮 -->
<div class="footer">
<ButtonGroup>
<Button type="primary" @click="selectGoodsType = true">商品类型</Button>
<Button type="primary" @click="next">下一步</Button>
</ButtonGroup>
<div class="footer-btns">
<el-button type="primary" @click="selectGoodsType = true">商品类型</el-button>
<el-button type="primary" @click="next">下一步</el-button>
</div>
</div>
</div>
</template>

View File

@@ -1,115 +1,124 @@
<template>
<div>
<Modal v-model="visible" title="预览图片">
<el-dialog v-model="visible" title="预览图片">
<img v-if="visible" :src="previewPicture" style="width: 100%">
</Modal>
</el-dialog>
<div class="content-goods-publish">
<Form ref="baseInfoForm" :label-width="120" :model="baseInfoForm" :rules="baseInfoFormRule">
<el-form ref="baseInfoForm" label-width="120px" :model="baseInfoForm" :rules="baseInfoFormRule">
<div class="base-info-item">
<h4>基本信息</h4>
<div class="form-item-view">
<FormItem label="商品分类">
<el-form-item label="商品分类">
<span class="goods-category-name">{{
this.baseInfoForm.categoryName[0]
}}</span>
<span> &gt; {{ this.baseInfoForm.categoryName[1] }}</span>
<span> &gt; {{ this.baseInfoForm.categoryName[2] }}</span>
</FormItem>
<FormItem label="商品名称" prop="goodsName">
<Input v-model="baseInfoForm.goodsName" clearable placeholder="商品名称" style="width: 260px" type="text" />
</FormItem>
</el-form-item>
<el-form-item label="商品名称" prop="goodsName">
<el-input v-model="baseInfoForm.goodsName" clearable placeholder="商品名称" style="width: 260px" type="text" />
</el-form-item>
<FormItem label="商品价格" prop="price">
<Input v-model="baseInfoForm.price" clearable placeholder="商品价格" style="width: 260px" type="text" />
</FormItem>
<FormItem label="商品卖点" prop="sellingPoint">
<Input v-model="baseInfoForm.sellingPoint" :rows="4" style="width: 260px" type="textarea" />
</FormItem>
<FormItem label="商品品牌" prop="brandId">
<Select v-model="baseInfoForm.brandId" filterable style="width: 200px">
<Option v-for="item in brandList" :key="item.id" :label="item.name" :value="item.id"></Option>
</Select>
<Button class="refresh-icon" icon="md-refresh" shape="circle" type="text"
@click="refresh('brand')"></Button>
</FormItem>
<el-form-item label="商品价格" prop="price">
<el-input v-model="baseInfoForm.price" clearable placeholder="商品价格" style="width: 260px" type="text" />
</el-form-item>
<el-form-item label="商品卖点" prop="sellingPoint">
<el-input v-model="baseInfoForm.sellingPoint" :rows="4" style="width: 260px" type="textarea" />
</el-form-item>
<el-form-item label="商品品牌" prop="brandId">
<el-select v-model="baseInfoForm.brandId" filterable style="width: 200px">
<el-option v-for="item in brandList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
<el-button class="refresh-icon" circle link @click="refresh('brand')">
<el-icon><Refresh /></el-icon>
</el-button>
</el-form-item>
<FormItem class="form-item-view-el" label="计量单位" prop="goodsUnit">
<Select v-model="baseInfoForm.goodsUnit" style="width: 100px">
<Option v-for="(item, index) in goodsUnitList" :key="index" :value="item">{{ item }}
</Option>
</Select>
<Button class="refresh-icon" icon="md-refresh" shape="circle" type="text"
@click="refresh('goodsUnit')"></Button>
</FormItem>
<FormItem class="form-item-view-el" label="销售模式" prop="salesModel">
<RadioGroup v-if="baseInfoForm.goodsType != 'VIRTUAL_GOODS'" v-model="baseInfoForm.salesModel"
button-style="solid" type="button" @on-change="handleSalesModeChange">
<Radio label="RETAIL" title="零售型">零售型</Radio>
<Radio label="WHOLESALE" title="批发型">批发型</Radio>
</RadioGroup>
<RadioGroup v-else v-model="baseInfoForm.salesModel" button-style="solid" type="button">
<Radio label="RETAIL" title="零售型">
<span>虚拟型</span>
</Radio>
</RadioGroup>
</FormItem>
<FormItem v-if="baseInfoForm.salesModel == 'WHOLESALE'" class="form-item-view-el" label="销售规则"
<el-form-item class="form-item-view-el" label="计量单位" prop="goodsUnit">
<el-select v-model="baseInfoForm.goodsUnit" style="width: 100px">
<el-option v-for="(item, index) in goodsUnitList" :key="index" :label="item" :value="item" />
</el-select>
<el-button class="refresh-icon" circle link @click="refresh('goodsUnit')">
<el-icon><Refresh /></el-icon>
</el-button>
</el-form-item>
<el-form-item class="form-item-view-el" label="销售模式" prop="salesModel">
<el-radio-group
v-if="baseInfoForm.goodsType != 'VIRTUAL_GOODS'"
v-model="baseInfoForm.salesModel"
@change="handleSalesModeChange"
>
<el-radio-button value="RETAIL">零售型</el-radio-button>
<el-radio-button value="WHOLESALE">批发型</el-radio-button>
</el-radio-group>
<el-radio-group v-else v-model="baseInfoForm.salesModel">
<el-radio-button value="RETAIL">虚拟型</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item v-if="baseInfoForm.salesModel == 'WHOLESALE'" class="form-item-view-el" label="销售规则"
prop="wholesaleRule">
<div class="form-item-view-wholesale">
<div>
<Table :columns="wholesaleColumns" :data="wholesaleData" border>
<template slot="wholesaleNum" slot-scope="{ row, index }">
<div>
<Input v-model="wholesaleData[index].num" min="1" number type="number"
@on-blur="checkWholesaleNum(index)">
<span slot="append">{{
baseInfoForm.goodsUnit || ""
}}</span>
</Input>
</div>
</template>
<template slot="wholesalePrice" slot-scope="{ row, index }">
<div style="
display: flex;
justify-content: space-between;
align-items: center;
">
<Input v-model="wholesaleData[index].price" min="1" number style="width: 190px" type="number"
@on-blur="checkWholesalePrice(index)">
<span slot="append"></span>
</Input>
<Button v-if="index > 0" size="small" style="margin-left: 5px" type="error"
@click="handleDeleteWholesaleData(index)">删除
</Button>
</div>
</template>
</Table>
<el-table :data="wholesaleData" border style="width: 100%">
<el-table-column label="购买数量" align="center">
<template #default="{ $index }">
<el-input v-model="wholesaleData[$index].num" type="number" @blur="checkWholesaleNum($index)">
<template #append>{{ baseInfoForm.goodsUnit || "" }}</template>
</el-input>
</template>
</el-table-column>
<el-table-column label="商品单价" align="center" width="280">
<template #default="{ $index }">
<div style="display: flex; justify-content: space-between; align-items: center">
<el-input
v-model="wholesaleData[$index].price"
type="number"
style="width: 190px"
@blur="checkWholesalePrice($index)"
>
<template #append></template>
</el-input>
<el-button
v-if="$index > 0"
size="small"
style="margin-left: 5px"
type="danger"
@click="handleDeleteWholesaleData($index)"
>
删除
</el-button>
</div>
</template>
</el-table-column>
</el-table>
<Button v-if="wholesaleData.length < 3" icon="md-add" style="margin-top: 10px"
@click="handleAddWholesaleData()">
<el-button v-if="wholesaleData.length < 3" style="margin-top: 10px" @click="handleAddWholesaleData()">
<el-icon><Plus /></el-icon>
添加价格区间
</Button>
</el-button>
</div>
<div class="form-item-view-wholesale-preview">
<Table :columns="wholesalePreviewColumns" :data="wholesaleData" border></Table>
<el-table :data="wholesaleData" border style="width: 100%">
<el-table-column label="销售规则" width="300">
<template #default="{ row }">
当商品购买数量 {{ row.num }} 售价为 {{ row.price }}/{{ baseInfoForm.goodsUnit }}
</template>
</el-table-column>
</el-table>
</div>
</div>
</FormItem>
<FormItem class="form-item-view-el" label="商品发布" prop="release">
<RadioGroup v-model="baseInfoForm.release" button-style="solid" type="button">
<Radio :label="1" title="上架">
<span></span>
</Radio>
<Radio :label="0" title="下架">
<span>下架</span>
</Radio>
</RadioGroup>
</FormItem>
</el-form-item>
<el-form-item class="form-item-view-el" label="商品发布" prop="release">
<el-radio-group v-model="baseInfoForm.release">
<el-radio-button :value="1">上架</el-radio-button>
<el-radio-button :value="0"></el-radio-button>
</el-radio-group>
</el-form-item>
</div>
<h4>商品规格及图片</h4>
<div class="form-item-view">
<FormItem class="form-item-view-el required" label="主图" prop="goodsGalleryFiles">
<el-form-item class="form-item-view-el required" label="主图" prop="goodsGalleryFiles">
<div style="display: flex; flex-wrap: wrap;">
<vuedraggable :animation="200" :list="baseInfoForm.goodsGalleryFiles">
<div v-for="(item, __index) in baseInfoForm.goodsGalleryFiles" :key="__index"
@@ -118,8 +127,8 @@
<img :src="item" />
<div class="demo-upload-list-cover">
<div>
<Icon size="30" type="md-search" @click.native="handleViewGoodsPicture(item)"></Icon>
<Icon size="30" type="md-trash" @click.native="handleRemoveGoodsPicture(item)"></Icon>
<el-button link type="primary" @click="handleViewGoodsPicture(item)">预览</el-button>
<el-button link type="danger" @click="handleRemoveGoodsPicture(item)">删除</el-button>
</div>
</div>
</template>
@@ -140,85 +149,96 @@
<!--</Upload>-->
</div>
<div style="width: 100%;display: flex;justify-content: start;margin-top: 10px;">
<Button @click="handleCLickImg('goodsGalleryFiles')" type="primary">上传图片</Button>
<el-button @click="handleCLickImg('goodsGalleryFiles')" type="primary">上传图片</el-button>
</div>
<Modal v-model="goodsPictureVisible" title="View Image">
<el-dialog v-model="goodsPictureVisible" title="View Image">
<img v-if="goodsPictureVisible" :src="previewGoodsPicture" style="width: 100%" />
</Modal>
</FormItem>
<FormItem>
</el-dialog>
</el-form-item>
<el-form-item>
<div style="color: grey">主图仅支持pngjpgjpeg格式宽高至少600*600px大小2M内可拖拽调整主图顺序</div>
</FormItem>
<FormItem class="form-item-view-el" label="主图视频" prop="goodsVideo">
</el-form-item>
<el-form-item class="form-item-view-el" label="主图视频" prop="goodsVideo">
<div class="goods-video">
<div v-if="baseInfoForm.goodsVideo">
<div>
<video :src="baseInfoForm.goodsVideo" class="video" controls style="max-width: 300px;" />
</div>
</div>
<Upload ref="upload" :action="uploadFileUrl" :format="['avi', 'wmv', 'mpeg', 'mp4', 'mov']"
:headers="{ ...accessToken }" :max-size="10240" :on-error="() => { loadingVideo = false }"
:on-exceeded-size="handleVideoMaxSize" :on-format-error="handleFormatError"
:on-progress="() => { loadingVideo = true }" :on-success="handleSuccessGoodsVideo"
:show-upload-list="false" multiple style="margin-left: 10px" type="drag">
<Button :loading="loadingVideo" icon="ios-cloud-upload-outline" type="text">
<span v-if="!loadingVideo">
{{ baseInfoForm.goodsVideo ? "" : "" }}上传视频
</span>
<span v-else>
正在上传...
</span>
</Button>
</Upload>
<el-upload
:action="uploadFileUrl"
:headers="{ ...accessToken }"
accept=".avi,.wmv,.mpeg,.mp4,.mov"
:show-file-list="false"
:before-upload="beforeVideoUpload"
:on-success="handleSuccessGoodsVideo"
:on-error="() => { loadingVideo = false }"
style="margin-left: 10px; display: inline-block"
>
<el-button :loading="loadingVideo" type="primary" link>
{{ loadingVideo ? "正在上传..." : `${baseInfoForm.goodsVideo ? "" : ""}上传视频` }}
</el-button>
</el-upload>
</div>
</FormItem>
</el-form-item>
<div class="layout" style="width: 100%">
<Collapse v-model="open_panel">
<Panel name="1">
自定义规格项
<div slot="content">
<Form>
<el-collapse v-model="open_panel">
<el-collapse-item title="自定义规格项" name="1">
<div>
<div v-for="(item, $index) in skuInfo" :key="$index" class="sku-item-content">
<Card :bordered="true" class="ivu-card-body">
<a slot="extra" style="margin-left: 6px">
<Icon size="20" type="md-trash" @click="handleCloseSkuItem($index, item)"></Icon>
</a>
<el-card class="ivu-card-body">
<template #header>
<el-button link type="danger" @click="handleCloseSkuItem($index, item)">删除规格项</el-button>
</template>
<div>
<div style="display: flex;margin-bottom: 10px;font-weight: bold">规格项</div>
<FormItem class="sku-item-content-val flex" label="">
<el-form-item class="sku-item-content-val flex" label="">
<div>
<AutoComplete v-model="item.name" :filter-method="filterMethod" :maxlength="30"
placeholder="请输入规格项名称" style="width: 150px" @on-focus="changeSkuItem(item.name)"
@on-change="editSkuItem(item.name, $index, item)">
</AutoComplete>
<el-autocomplete
v-model="item.name"
:fetch-suggestions="(q, cb) => cb([])"
maxlength="30"
placeholder="请输入规格项名称"
style="width: 150px"
@focus="changeSkuItem(item.name)"
@change="editSkuItem(item.name, $index, item)"
/>
<iSwitch v-if="$index === 0" style="margin-left: 10px" size="small"
@on-change="changeSkuOpenImage" v-model="openImage" /><span v-if="$index === 0"
style="margin-left: 5px">添加规格图片</span>
<el-switch
v-if="$index === 0"
v-model="openImage"
style="margin-left: 10px"
@change="changeSkuOpenImage"
/>
<span v-if="$index === 0" style="margin-left: 5px">添加规格图片</span>
</div>
</FormItem>
</el-form-item>
</div>
<div class="sku-val">
<div style="margin-bottom: 10px;font-weight: bold;display: flex">规格值 (输入完成后,鼠标点击其他地方后生效)</div>
<Form :model="item" class="flex">
<el-form :model="item" class="flex">
<!--规格值文本列表-->
<FormItem v-for="(val, index) in item.spec_values" :key="index"
<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: center; align-items: center;">
<AutoComplete ref="input" v-model="val.value" :disabled="containsSameSkuItem"
:filter-method="filterMethod" :maxlength="30" placeholder="请输入规格值"
style="width: 180px" @on-focus="changeSkuVals(val, item.name)"
@on-blur="checkSkuVal(val, $index, item)"
@on-change="skuValueChange(val, index, item)">
</AutoComplete>
<a style="margin-left: 6px" >
<Icon size="15" type="md-trash" @click="handleCloseSkuValue(val, index, item)">
</Icon>
</a>
<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>
<!-- 内联错误提示 -->
@@ -231,205 +251,251 @@
<img :src="img" style="width: 180px;height: 140px" />
<div class="sku-upload-list-cover">
<div style="margin-top: 50px">
<Icon size="25" type="md-search" @click="handleView(img)"></Icon>
<Icon size="25" type="md-trash" @click="handleRemove(val.images, __index)">
</Icon>
<el-icon :size="25" style="cursor:pointer;margin-right:8px" @click="handleView(img)"><ZoomIn /></el-icon>
<el-icon :size="25" style="cursor:pointer" @click="handleRemove(val.images, __index)"><Delete /></el-icon>
</div>
</div>
</template>
</div>
</vuedraggable>
<Upload ref="uploadSku" :action="uploadFileUrl" v-if="val.images < 1"
:before-upload="handleBeforeUpload" :format="['jpg', 'jpeg', 'png', 'webp']"
:headers="{ ...accessToken }" :max-size="2048" :on-error="() => { $Spin.hide(); }"
:on-exceeded-size="handleMaxSize" :on-format-error="handleFormatError"
:on-progress="() => { $Spin.show(); }" :on-success="(res, file) => {
handleSuccess(res, file, val.images)
}" :show-upload-list="false" style="width: 180px;height: 140px;margin-right: 10px"
type="drag">
<div>
<Icon size="136" type="ios-camera"></Icon>
</div>
</Upload>
<el-upload
v-if="val.images.length < 1"
:action="uploadFileUrl"
:before-upload="handleBeforeUpload"
:headers="{ ...accessToken }"
:on-error="() => {}"
:on-exceeded-size="handleMaxSize"
:on-success="(res, file) => handleSuccess(res, file, val.images)"
:show-file-list="false"
accept=".jpg,.jpeg,.png,.webp"
drag
style="width: 180px;height: 140px;margin-right: 10px"
>
<el-icon :size="40"><Camera /></el-icon>
</el-upload>
</div>
</FormItem>
</el-form-item>
<FormItem
<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;">
<AutoComplete ref="input" v-model="newSkuValues[$index]" :disabled="containsSameSkuItem"
:filter-method="filterMethod" :maxlength="30" placeholder="自定义规格值"
style="width: 180px" @on-blur="addSpec($index, item)"
v-on:keyup.native.enter="addSpec($index, item)">
</AutoComplete>
</FormItem>
</Form>
<el-autocomplete
v-model="newSkuValues[$index]"
:disabled="containsSameSkuItem"
:fetch-suggestions="(q, cb) => cb([])"
maxlength="30"
placeholder="自定义规格值"
style="width: 180px"
@blur="addSpec($index, item)"
@keyup.enter="addSpec($index, item)"
/>
</el-form-item>
</el-form>
</div>
</Card>
</el-card>
</div>
</Form>
<div style="display: flex">
<Button class="add-sku-btn" type="primary" @click="addSkuItem">添加规格项
</Button>
<el-button class="add-sku-btn" type="primary" @click="addSkuItem">添加规格项
</el-button>
</div>
&nbsp;
<!-- <Button class="add-sku-btn" size="small" type="warning" @click="handleClearSku">清空规格项-->
<!-- </Button>-->
</div>
</Panel>
<Panel name="2">
规格详细
<div slot="content">
</div>
</el-collapse-item>
<el-collapse-item title="规格详细" name="2">
<div v-if="needToloadSku" class="topinfo" @click="handleLoadingSkuData">点击加载sku数据</div>
<div slot="content" :class="needToloadSku ? 'mask' : ''">
<Table :columns="skuTableColumn" :data="skuTableData" class="mt_10" style="
width: 100%;
.ivu-table-overflowX {
overflow-x: hidden;
}
">
<template slot="sn" slot-scope="{ row }">
<Input v-model="row.sn" clearable placeholder="请输入货号"
@on-change="updateSkuTable(row, 'sn')" />
</template>
<div v-if="baseInfoForm.goodsType !== 'VIRTUAL_GOODS'" slot="weight" slot-scope="{ row }">
<Input v-model="row.weight" clearable placeholder="请输入重量"
@on-change="updateSkuTable(row, 'weight')">
<span slot="append">kg</span>
</Input>
</div>
<template slot="quantity" slot-scope="{ row }">
<Input v-model="row.quantity" clearable placeholder="请输入库存"
@on-change="updateSkuTable(row, 'quantity')">
<span slot="append">{{
baseInfoForm.goodsUnit || ""
}}</span>
</Input>
</template>
<template slot="cost" slot-scope="{ row }">
<Input v-model="row.cost" clearable placeholder="请输入成本价"
@on-change="updateSkuTable(row, 'cost')">
<span slot="append">元</span>
</Input>
</template>
<template slot="price" slot-scope="{ row }">
<Input v-model="row.price" clearable placeholder="请输入价格"
@on-change="updateSkuTable(row, 'price')">
<span slot="append">元</span>
</Input>
</template>
<template slot="wholePrice0" slot-scope="{ row }">
<Input v-if="wholesaleData[0]" v-model="wholesaleData[0].price" clearable disabled>
<span slot="append">元</span>
</Input>
</template>
<template slot="wholePrice1" slot-scope="{ row }">
<Input v-if="wholesaleData[1]" v-model="wholesaleData[1].price" clearable disabled>
<span slot="append">元</span>
</Input>
</template>
<template slot="wholePrice2" slot-scope="{ row }">
<Input v-if="wholesaleData[2]" v-model="wholesaleData[2].price" clearable disabled>
<span slot="append">元</span>
</Input>
</template>
</Table>
<div :class="needToloadSku ? 'mask' : ''">
<el-table :data="skuTableData" border class="mt_10" style="width: 100%">
<el-table-column
v-for="(col, colIndex) in skuTableColumn"
:key="colIndex"
:label="col.title"
:prop="col.key"
min-width="120"
>
<template #default="{ row }">
<span v-if="col.key && !col.slot">{{ row[col.key] }}</span>
<el-input
v-else-if="col.slot === 'sn'"
v-model="row.sn"
clearable
placeholder="请输入货号"
@change="updateSkuTable(row, 'sn')"
/>
<el-input
v-else-if="col.slot === 'weight' && baseInfoForm.goodsType !== 'VIRTUAL_GOODS'"
v-model="row.weight"
clearable
placeholder="请输入重量"
@change="updateSkuTable(row, 'weight')"
>
<template #append>kg</template>
</el-input>
<el-input
v-else-if="col.slot === 'quantity'"
v-model="row.quantity"
clearable
placeholder="请输入库存"
@change="updateSkuTable(row, 'quantity')"
>
<template #append>{{ baseInfoForm.goodsUnit || "" }}</template>
</el-input>
<el-input
v-else-if="col.slot === 'cost'"
v-model="row.cost"
clearable
placeholder="请输入成本价"
@change="updateSkuTable(row, 'cost')"
>
<template #append>元</template>
</el-input>
<el-input
v-else-if="col.slot === 'price'"
v-model="row.price"
clearable
placeholder="请输入价格"
@change="updateSkuTable(row, 'price')"
>
<template #append>元</template>
</el-input>
<el-input
v-else-if="col.slot === 'wholePrice0' && wholesaleData[0]"
v-model="wholesaleData[0].price"
disabled
>
<template #append>元</template>
</el-input>
<el-input
v-else-if="col.slot === 'wholePrice1' && wholesaleData[1]"
v-model="wholesaleData[1].price"
disabled
>
<template #append>元</template>
</el-input>
<el-input
v-else-if="col.slot === 'wholePrice2' && wholesaleData[2]"
v-model="wholesaleData[2].price"
disabled
>
<template #append>元</template>
</el-input>
</template>
</el-table-column>
</el-table>
</div>
</div>
</Panel>
</Collapse>
</el-collapse-item>
</el-collapse>
</div>
</div>
<h4 v-if="showContent">规格描述内容</h4>
<div v-if="showContent" class="form-item-view">
<div>
<FormItem :label="contentImage" class="form-item-view-el">
<el-form-item :label="contentImage" class="form-item-view-el">
<!-- {{item.url}} -->
<div v-for="(item, index) in listImages.images" :key="index" style="width:100%;display:flex;">
<img :src="item.url" style="width:100px;flex:1;margin-top:10px;cursor:pointer;"
@click="handleView(item.url)" />
</div>
</FormItem>
</el-form-item>
</div>
</div>
<h4>商品详情描述</h4>
<div class="form-item-view">
<div class="tree-bar">
<FormItem class="form-item-view-el" label="店内分类" prop="shopCategory">
<Tree ref="tree" :check-strictly="false" :data="shopCategory" show-checkbox style="text-align: left"
@on-select-change="selectTree" @on-check-change="changeSelect"></Tree>
</FormItem>
<el-form-item class="form-item-view-el" label="店内分类" prop="shopCategory">
<el-tree
ref="tree"
:data="shopCategory"
show-checkbox
node-key="id"
:props="{ label: 'title', children: 'children' }"
style="text-align: left"
@node-click="(data) => selectTree([data])"
@check="(_, ctx) => changeSelect(ctx.checkedNodes)"
/>
</el-form-item>
</div>
<FormItem class="form-item-view-el" label="PC商品描述" prop="intro" style="width: 100%">
<el-form-item class="form-item-view-el" label="PC商品描述" prop="intro" style="width: 100%">
<editor ref="editor" v-model="baseInfoForm.intro" height="800px" openXss></editor>
<div class="promise-intro-btn">
<Button type="primary" @click="promiseIntroEditor">将PC商品描述同步到移动端描述
</Button>
<el-button type="primary" @click="promiseIntroEditor">将PC商品描述同步到移动端描述
</el-button>
</div>
</FormItem>
</el-form-item>
<FormItem class="form-item-view-el" label="移动端描述" prop="skuList" style="width: 100%">
<el-form-item class="form-item-view-el" label="移动端描述" prop="skuList" style="width: 100%">
<editor ref="introEditor" v-model="baseInfoForm.mobileIntro" height="800px" openXss></editor>
</FormItem>
</el-form-item>
</div>
<div v-if="baseInfoForm.goodsType != 'VIRTUAL_GOODS'">
<h4>商品物流信息</h4>
<div class="form-item-view">
<FormItem class="form-item-view-el" label="物流模板" prop="templateId">
<Select v-model="baseInfoForm.templateId" style="width: 200px">
<Option v-for="item in logisticsTemplate" :key="item.id" :value="item.id">{{ item.name }}
</Option>
</Select>
<Button class="refresh-icon" icon="md-refresh" shape="circle" type="text"
@click="refresh('template')"></Button>
</FormItem>
<FormItem v-if="baseInfoForm.salesModel == 'WHOLESALE'" class="form-item-view-el" label="商品重量"
<el-form-item class="form-item-view-el" label="物流模板" prop="templateId">
<el-select v-model="baseInfoForm.templateId" style="width: 200px">
<el-option v-for="item in logisticsTemplate" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
<el-button class="refresh-icon" circle link @click="refresh('template')">
<el-icon><Refresh /></el-icon>
</el-button>
</el-form-item>
<el-form-item v-if="baseInfoForm.salesModel == 'WHOLESALE'" class="form-item-view-el" label="商品重量"
prop="weight">
<Input v-model="baseInfoForm.weight" placeholder="请输入商品重量">
<span slot="append">kg</span></Input>
</FormItem>
<el-input v-model="baseInfoForm.weight" placeholder="请输入商品重量">
<template #append>kg</template>
</el-input>
</el-form-item>
</div>
</div>
<h4>参数信息</h4>
<div class="form-item-view">
<FormItem v-for="(paramsItem, paramsIndex) in goodsParams" :key="paramsItem.id || paramsIndex"
<el-form-item v-for="(paramsItem, paramsIndex) in goodsParams" :key="paramsItem.id || paramsIndex"
:label="`${paramsItem.paramName}`"
:prop="`goodsParams.${paramsIndex}.paramValue`"
:rules="paramsItem.required ? { required: true, message: `${paramsItem.paramName}不能为空`, trigger: 'change' } : {}">
<Select v-model="baseInfoForm.goodsParams[paramsIndex].paramValue" clearable placeholder="请选择" style="width: 200px"
@on-change="(val) => selectParams(paramsItem, val, paramsIndex)">
<Option v-for="option in getParamOptions(paramsItem.options)" :key="option" :label="option"
:value="option">
</Option>
</Select>
</FormItem>
<el-select v-model="baseInfoForm.goodsParams[paramsIndex].paramValue" clearable placeholder="请选择" style="width: 200px"
@change="(val) => selectParams(paramsItem, val, paramsIndex)">
<el-option
v-for="option in getParamOptions(paramsItem.options)"
:key="option"
:label="option"
:value="option"
/>
</el-select>
</el-form-item>
</div>
</div>
</Form>
</el-form>
</div>
<!-- 底部按钮 -->
<div class="footer">
<ButtonGroup>
<Button type="primary" @click="pre">上一步</Button>
<Button :loading="submitLoading" type="primary" @click="save">
<div class="footer-btns">
<el-button type="primary" @click="pre">上一步</el-button>
<el-button :loading="submitLoading" type="primary" @click="save">
{{ this.$route.query.id ? "保存" : "保存商品" }}
</Button>
</ButtonGroup>
</el-button>
</div>
</div>
<Modal v-model="showGoodsVideo" title="查看视频">
<el-dialog v-model="showGoodsVideo" title="查看视频">
<div id="dplayer">
</div>
</Modal>
</el-dialog>
<!--<Modal width="1200px" v-model="picModelFlag">-->
<!--<el-dialog width="1200px" v-model="picModelFlag">-->
<!--<ossManage @callback="callbackSelected" ref="ossManage" />-->
<!--</Modal>-->
<Modal v-model="picModelFlag" width="1200px" @on-ok="confirmUrls">
<ossManage ref="ossManage" :isComponent="true" :initialize="picModelFlag" @callback="callbackSelected"
@selected="(list) => { selectedImage = list }" />
</Modal>
<!--</el-dialog>-->
<el-dialog v-model="picModelFlag" width="1200px" title="选择图片" append-to-body destroy-on-close>
<ossManage
ref="ossManage"
:isComponent="true"
:initialize="picModelFlag"
@callback="callbackSelected"
@selected="(list) => { selectedImage = list }"
/>
<template #footer>
<el-button @click="picModelFlag = false">取消</el-button>
<el-button type="primary" @click="confirmUrls">确定</el-button>
</template>
</el-dialog>
</div>
</template>
@@ -437,6 +503,7 @@
import * as API_GOODS from "@/api/goods";
import * as API_Shop from "@/api/shops";
import cloneObj from "@/utils/index";
import { Camera, Delete, Plus, Refresh, ZoomIn } from "@element-plus/icons-vue";
import vuedraggable from "vuedraggable";
import tinymec from "@/views/lili-components/editor/index.vue";
@@ -450,9 +517,14 @@ import ossManage from "@/views/shop/ossManages";
export default {
name: "goodsOperationSec",
components: {
Camera,
Delete,
Plus,
Refresh,
ZoomIn,
editor: tinymec,
vuedraggable,
ossManage
ossManage,
},
props: {
firstData: {
@@ -745,21 +817,21 @@ export default {
},
selectParams(params, value, paramsIndex) {
if (!Array.isArray(this.baseInfoForm.goodsParamsDTOList)) {
this.$set(this.baseInfoForm, "goodsParamsDTOList", []);
this.baseInfoForm.goodsParamsDTOList = [];
}
// 确保baseInfoForm.goodsParams存在
if (!Array.isArray(this.baseInfoForm.goodsParams)) {
this.$set(this.baseInfoForm, "goodsParams", []);
this.baseInfoForm.goodsParams = [];
}
// 确保对应索引的参数项存在
if (!this.baseInfoForm.goodsParams[paramsIndex]) {
this.$set(this.baseInfoForm.goodsParams, paramsIndex, {});
this.baseInfoForm.goodsParams[paramsIndex] = {};
}
// 更新baseInfoForm中的值用于验证
this.$set(this.baseInfoForm.goodsParams[paramsIndex], 'paramValue', value || '');
this.baseInfoForm.goodsParams[paramsIndex].paramValue = value || '';
const list = this.baseInfoForm.goodsParamsDTOList;
const paramId = params && params.id ? String(params.id) : "";
@@ -770,7 +842,7 @@ export default {
list.splice(index, 1);
}
// 清空表单项的值
this.$set(params, 'paramValue', '');
params.paramValue = '';
return;
}
const newItem = {
@@ -783,13 +855,13 @@ export default {
};
if (index >= 0) {
this.$set(list, index, newItem);
list[index] = newItem;
} else {
list.push(newItem);
}
// 同步更新表单项的值
this.$set(params, 'paramValue', value);
params.paramValue = value;
},
// 编辑sku图片
editSkuPicture(row) {
@@ -844,7 +916,6 @@ export default {
},
// sku图片上传成功
handleSuccess(res, file, images) {
this.$Spin.hide();
if (file.response) {
file.url = file.response.result;
if (images) {
@@ -942,7 +1013,6 @@ export default {
},
// 商品图片上传成功
handleSuccessGoodsPicture(res, file) {
this.$Spin.hide();
if (file.response) {
file.url = file.response.result;
this.baseInfoForm.goodsGalleryFiles.push(file.url);
@@ -1263,11 +1333,11 @@ export default {
.sort((a, b) => Number(a.sort || 0) - Number(b.sort || 0));
// 初始化baseInfoForm.goodsParams用于表单验证
this.$set(this.baseInfoForm, 'goodsParams', []);
this.baseInfoForm.goodsParams = [];
this.goodsParams.forEach((param, index) => {
this.$set(this.baseInfoForm.goodsParams, index, {
this.baseInfoForm.goodsParams[index] = {
paramValue: param.paramValue || ''
});
};
});
// 确保表单验证能正确初始化
@@ -1297,8 +1367,7 @@ export default {
this.$Message.error("规格项不能为空");
return;
}
// 写入对象,下标,具体对象
this.$set(this.skuInfo, this.skuInfo.length, {
this.skuInfo.push({
spec_values: [{ name: "", value: "", images: [] }],
name: "",
});
@@ -1420,7 +1489,7 @@ export default {
checkSkuVal(val, groupIndex, spec) {
if (val.value === "") {
// 内联错误提示,不使用弹窗
this.$set(val, '_error', '规格值不能为空!');
val._error = '规格值不能为空!';
// 如果规格项和规格名称存在,从表格数据中移除包含该空规格值的行
if (spec && spec.name && this.skuInfo[groupIndex]) {
@@ -1568,7 +1637,7 @@ export default {
} else {
itemValue.images = this.baseInfoForm.goodsGalleryFiles
}
this.$set(item.spec_values, item.spec_values.length, itemValue);
item.spec_values.push(itemValue);
// 生成新的规格组合
const newCombinations = this.generateSkuCombinations(this.skuInfo);
@@ -1952,7 +2021,8 @@ export default {
this.$nextTick(() => {
this.skuTableData[index][item] = row[item];
});
// this.$set(this.skuTableData,[index][item],row[item])
// // migrated
(this.skuTableData,[index][item],row[item])
},
// 店内分类选择
selectTree(v) {