Files
ruoyi-vue/src/views/system/erpProduct/index.vue
2026-04-05 20:47:03 +08:00

729 lines
25 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="100px">
<el-form-item label="ERP账号" prop="appid" required>
<el-select
v-model="queryParams.appid"
placeholder="请选择ERP账号必选"
clearable
style="width: 200px"
@change="handleAccountChange"
>
<el-option
v-for="item in erpAccountList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
<span style="color: #f56c6c; margin-left: 10px; font-size: 12px;">* 不同账号的商品列表和权限不同</span>
</el-form-item>
<el-form-item label="商品标题" prop="title">
<el-input
v-model="queryParams.title"
placeholder="请输入商品标题"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="商品状态" prop="productStatus">
<el-select v-model="queryParams.productStatus" placeholder="请选择" clearable style="width: 150px">
<el-option label="全部" :value="null" />
<el-option label="删除" :value="-1" />
<el-option label="待发布" :value="21" />
<el-option label="销售中" :value="22" />
<el-option label="已售罄" :value="23" />
<el-option label="手动下架" :value="31" />
<el-option label="售出下架" :value="33" />
<el-option label="自动下架" :value="36" />
</el-select>
</el-form-item>
<el-form-item label="闲鱼会员名" prop="userName">
<el-input
v-model="queryParams.userName"
placeholder="请输入闲鱼会员名"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-refresh"
size="mini"
@click="handleSyncAll"
v-hasPermi="['jarvis:erpProduct:pull']"
>全量同步</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="el-icon-top"
size="mini"
:disabled="multiple"
@click="handleBatchPublish"
v-hasPermi="['jarvis:erpProduct:publish']"
>批量上架</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-bottom"
size="mini"
:disabled="multiple"
@click="handleBatchDownShelf"
v-hasPermi="['jarvis:erpProduct:downShelf']"
>批量下架</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-delete"
size="mini"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['jarvis:erpProduct:remove']"
>删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="info"
plain
icon="el-icon-download"
size="mini"
@click="handleExport"
v-hasPermi="['jarvis:erpProduct:export']"
>导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="erpProductList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="商品图片" align="center" prop="mainImage" width="100">
<template slot-scope="scope">
<el-image
v-if="scope.row.mainImage"
:src="scope.row.mainImage"
:preview-src-list="[scope.row.mainImage]"
style="width: 60px; height: 60px; border-radius: 4px;"
fit="cover"
/>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="商品信息" align="left" min-width="250" :show-overflow-tooltip="true">
<template slot-scope="scope">
<div>
<div style="font-weight: bold; margin-bottom: 5px;">
{{ scope.row.title }}
</div>
<div style="color: #666; font-size: 12px;">
商品ID: {{ scope.row.productId }}
</div>
</div>
</template>
</el-table-column>
<el-table-column label="价格/库存" align="center" width="120">
<template slot-scope="scope">
<div>
<div style="color: #f56c6c; font-weight: bold;">
¥{{ formatPrice(scope.row.price) }}
</div>
<div style="color: #666; font-size: 12px;">
库存: {{ scope.row.stock || 0 }}
</div>
</div>
</template>
</el-table-column>
<el-table-column label="商品状态" align="center" prop="productStatus" width="100">
<template slot-scope="scope">
<el-tag
:type="getStatusType(scope.row.productStatus)"
size="mini"
>
{{ getStatusText(scope.row.productStatus) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="闲鱼会员名" align="center" prop="userName" width="120" />
<el-table-column label="上架时间" align="center" width="180">
<template slot-scope="scope">
<div v-if="scope.row.onlineTime">
{{ formatTime(scope.row.onlineTime) }}
</div>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="更新时间" align="center" width="180">
<template slot-scope="scope">
<div v-if="scope.row.updateTimeXy">
{{ formatTime(scope.row.updateTimeXy) }}
</div>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="ERP应用" align="center" prop="appid" width="120" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="150">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-view"
@click="handleView(scope.row)"
>查看</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-top"
@click="handleSinglePublish(scope.row)"
v-if="scope.row.productStatus === 2"
v-hasPermi="['jarvis:erpProduct:publish']"
>上架</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-bottom"
@click="handleSingleDownShelf(scope.row)"
v-if="scope.row.productStatus === 1"
v-hasPermi="['jarvis:erpProduct:downShelf']"
>下架</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<!-- 全量同步对话框 -->
<el-dialog title="全量同步闲鱼商品" :visible.sync="syncDialogVisible" width="500px" append-to-body>
<el-form ref="syncForm" :model="syncForm" label-width="100px">
<el-form-item label="ERP应用">
<el-select v-model="syncForm.appid" placeholder="请选择ERP应用" style="width: 100%">
<el-option
v-for="item in erpAccountList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="商品状态">
<el-select v-model="syncForm.productStatus" placeholder="请选择(留空为全部)" clearable style="width: 100%">
<el-option label="全部" :value="null" />
<el-option label="删除" :value="-1" />
<el-option label="待发布" :value="21" />
<el-option label="销售中" :value="22" />
<el-option label="已售罄" :value="23" />
<el-option label="手动下架" :value="31" />
<el-option label="售出下架" :value="33" />
<el-option label="自动下架" :value="36" />
</el-select>
<div style="color: #909399; font-size: 12px; margin-top: 5px;">
<div> 留空表示同步全部状态的商品</div>
<div> 系统将自动遍历所有页码同步所有商品</div>
<div> 会自动更新本地已有商品删除远程已不存在的商品</div>
</div>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitSyncAll" :loading="syncing">开始同步</el-button>
<el-button @click="syncDialogVisible = false"> </el-button>
</div>
</el-dialog>
<!-- 查看商品详情对话框 -->
<el-dialog title="商品详情" :visible.sync="viewDialogVisible" width="800px" append-to-body>
<el-descriptions :column="2" border v-if="viewForm">
<el-descriptions-item label="商品ID">{{ viewForm.productId }}</el-descriptions-item>
<el-descriptions-item label="商品标题">{{ viewForm.title }}</el-descriptions-item>
<el-descriptions-item label="商品价格">
¥{{ formatPrice(viewForm.price) }}
</el-descriptions-item>
<el-descriptions-item label="商品库存">{{ viewForm.stock || 0 }}</el-descriptions-item>
<el-descriptions-item label="商品状态">
<el-tag :type="getStatusType(viewForm.productStatus)" size="mini">
{{ getStatusText(viewForm.productStatus) }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="销售状态">{{ viewForm.saleStatus || '-' }}</el-descriptions-item>
<el-descriptions-item label="闲鱼会员名">{{ viewForm.userName || '-' }}</el-descriptions-item>
<el-descriptions-item label="ERP应用">{{ viewForm.appid || '-' }}</el-descriptions-item>
<el-descriptions-item label="上架时间">
{{ viewForm.onlineTime ? formatTime(viewForm.onlineTime) : '-' }}
</el-descriptions-item>
<el-descriptions-item label="下架时间">
{{ viewForm.offlineTime ? formatTime(viewForm.offlineTime) : '-' }}
</el-descriptions-item>
<el-descriptions-item label="售出时间">
{{ viewForm.soldTime ? formatTime(viewForm.soldTime) : '-' }}
</el-descriptions-item>
<el-descriptions-item label="商品链接" :span="2">
<el-link v-if="viewForm.productUrl" :href="viewForm.productUrl" target="_blank" type="primary">
{{ viewForm.productUrl }}
</el-link>
<span v-else>-</span>
</el-descriptions-item>
<el-descriptions-item label="商品图片" :span="2">
<el-image
v-if="viewForm.mainImage"
:src="viewForm.mainImage"
style="width: 200px; height: 200px;"
fit="cover"
/>
<span v-else>-</span>
</el-descriptions-item>
<el-descriptions-item label="备注" :span="2">{{ viewForm.remark || '-' }}</el-descriptions-item>
</el-descriptions>
<div slot="footer" class="dialog-footer">
<el-button @click="viewDialogVisible = false"> </el-button>
</div>
</el-dialog>
<!-- 批量上架对话框 -->
<el-dialog title="批量上架商品" :visible.sync="publishDialogVisible" width="500px" append-to-body>
<el-form ref="publishForm" :model="publishForm" label-width="120px">
<el-form-item label="选择账号">
<el-select v-model="publishForm.appid" placeholder="请选择ERP应用" style="width: 100%">
<el-option
v-for="item in erpAccountList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="闲鱼会员名" required>
<el-select
v-model="publishForm.userName"
placeholder="请选择闲鱼会员名"
filterable
style="width: 100%"
@focus="loadUsernames"
>
<el-option
v-for="item in usernameList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="商品数量">
<el-input :value="selectedProductIds.length" readonly />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitBatchPublish" :loading="publishing"> </el-button>
<el-button @click="publishDialogVisible = false"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { listErpProduct, getErpProduct, delErpProduct, pullProductList, syncAllProducts, batchPublish, batchDownShelf, getERPAccounts, getUsernames } from "@/api/system/erpProduct";
export default {
name: "ErpProduct",
data() {
return {
// 遮罩层
loading: true,
// 选中数组
ids: [],
// 选中的商品ID数组
selectedProductIds: [],
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 闲鱼商品表格数据
erpProductList: [],
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
title: null,
productStatus: null,
userName: null,
appid: null
},
// ERP账号列表
erpAccountList: [],
// 全量同步对话框
syncDialogVisible: false,
syncForm: {
appid: null,
productStatus: null
},
syncing: false,
// 查看对话框
viewDialogVisible: false,
viewForm: null,
// 账号切换提示
accountWarningShown: false,
// 批量上架对话框
publishDialogVisible: false,
publishForm: {
appid: null,
userName: null
},
publishing: false,
// 会员名列表
usernameList: []
};
},
created() {
this.loadERPAccounts();
},
methods: {
/** 查询闲鱼商品列表 */
getList() {
// 如果没有选择账号,提示用户
if (!this.queryParams.appid) {
this.$modal.msgWarning("请先选择ERP账号");
this.loading = false;
return;
}
this.loading = true;
listErpProduct(this.queryParams).then(response => {
this.erpProductList = response.rows;
this.total = response.total;
this.loading = false;
}).catch(() => {
this.loading = false;
});
},
/** 加载ERP账号列表 */
loadERPAccounts() {
getERPAccounts().then(response => {
this.erpAccountList = response.data || [];
// 自动将第一个ERP账号作为参数并调用列表数据
if (this.erpAccountList.length > 0 && !this.queryParams.appid) {
this.queryParams.appid = this.erpAccountList[0].value;
this.getList();
} else if (!this.queryParams.appid) {
this.loading = false;
}
}).catch(() => {
this.loading = false;
});
},
/** 加载会员名列表 */
loadUsernames() {
getUsernames({ pageSize: 100 }).then(response => {
this.usernameList = response.data || [];
});
},
/** 账号变更处理 */
handleAccountChange() {
// 账号切换时清空选中项
this.ids = [];
this.selectedProductIds = [];
this.multiple = true;
this.single = true;
// 重新加载列表
this.queryParams.pageNum = 1;
this.getList();
},
/** 搜索按钮操作 */
handleQuery() {
if (!this.queryParams.appid) {
this.$modal.msgWarning("请先选择ERP账号");
return;
}
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
// 多选框选中数据
handleSelectionChange(selection) {
this.ids = selection.map(item => item.id);
this.selectedProductIds = selection.map(item => item.productId);
this.single = selection.length !== 1;
this.multiple = !selection.length;
},
/** 全量同步按钮操作 */
handleSyncAll() {
if (!this.queryParams.appid) {
this.$modal.msgWarning("请先选择ERP账号");
return;
}
this.syncDialogVisible = true;
this.syncForm = {
appid: this.queryParams.appid,
productStatus: null
};
},
/** 提交全量同步 */
submitSyncAll() {
if (!this.syncForm.appid) {
this.$modal.msgWarning("请选择ERP账号");
return;
}
this.$confirm(
'全量同步将自动遍历所有页码,同步所有商品数据,并删除远程已不存在的本地商品。是否继续?',
'确认全量同步',
{
confirmButtonText: '确定同步',
cancelButtonText: '取消',
type: 'warning'
}
).then(() => {
this.syncing = true;
syncAllProducts(this.syncForm).then(response => {
if (response.code === 200) {
this.$modal.msgSuccess(response.msg || "同步成功");
this.syncDialogVisible = false;
// 刷新列表
this.getList();
} else {
this.$modal.msgError(response.msg || "同步失败");
}
this.syncing = false;
}).catch((error) => {
this.$modal.msgError(error.message || "同步失败");
this.syncing = false;
});
}).catch(() => {
// 用户取消
});
},
/** 查看按钮操作 */
handleView(row) {
const id = row.id || this.ids[0];
getErpProduct(id).then(response => {
this.viewForm = response.data;
this.viewDialogVisible = true;
});
},
/** 单个上架 */
handleSinglePublish(row) {
if (!row.appid) {
this.$modal.msgWarning("该商品缺少ERP账号信息无法上架");
return;
}
this.$prompt('请输入闲鱼会员名', '上架商品', {
confirmButtonText: '确定',
cancelButtonText: '取消',
inputPlaceholder: '请输入闲鱼会员名',
inputValidator: (value) => {
if (!value) {
return '闲鱼会员名不能为空';
}
return true;
}
}).then(({ value }) => {
const data = {
productIds: [row.productId],
userName: value,
appid: row.appid
};
this.publishing = true;
batchPublish(data).then(response => {
this.$modal.msgSuccess("上架成功");
this.publishing = false;
this.getList();
}).catch(() => {
this.publishing = false;
});
}).catch(() => {});
},
/** 单个下架 */
handleSingleDownShelf(row) {
if (!row.appid) {
this.$modal.msgWarning("该商品缺少ERP账号信息无法下架");
return;
}
this.$confirm('确定要下架该商品吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
const data = {
productIds: [row.productId],
appid: row.appid
};
batchDownShelf(data).then(response => {
this.$modal.msgSuccess("下架成功");
this.getList();
});
}).catch(() => {});
},
/** 批量上架按钮操作 */
handleBatchPublish() {
if (this.selectedProductIds.length === 0) {
this.$modal.msgWarning("请先选择要上架的商品");
return;
}
// 检查选中的商品是否属于同一个账号
const selectedProducts = this.erpProductList.filter(item => this.selectedProductIds.includes(item.productId));
const appids = [...new Set(selectedProducts.map(p => p.appid))];
if (appids.length > 1) {
this.$modal.msgWarning("选中的商品属于不同的ERP账号请分别操作");
return;
}
const accountAppid = appids[0] || this.queryParams.appid;
if (!accountAppid) {
this.$modal.msgWarning("请先选择ERP账号或确保选中的商品有关联的账号");
return;
}
this.publishForm = {
appid: accountAppid,
userName: null
};
this.publishDialogVisible = true;
},
/** 提交批量上架 */
submitBatchPublish() {
if (!this.publishForm.userName) {
this.$modal.msgWarning("请选择闲鱼会员名");
return;
}
const data = {
productIds: this.selectedProductIds,
userName: this.publishForm.userName,
appid: this.publishForm.appid
};
this.publishing = true;
batchPublish(data).then(response => {
this.$modal.msgSuccess(response.msg || "批量上架成功");
this.publishDialogVisible = false;
this.publishing = false;
this.getList();
}).catch(() => {
this.publishing = false;
});
},
/** 批量下架按钮操作 */
handleBatchDownShelf() {
if (this.selectedProductIds.length === 0) {
this.$modal.msgWarning("请先选择要下架的商品");
return;
}
// 检查选中的商品是否属于同一个账号
const selectedProducts = this.erpProductList.filter(item => this.selectedProductIds.includes(item.productId));
const appids = [...new Set(selectedProducts.map(p => p.appid))];
if (appids.length > 1) {
this.$modal.msgWarning("选中的商品属于不同的ERP账号请分别操作");
return;
}
const accountAppid = appids[0] || this.queryParams.appid;
if (!accountAppid) {
this.$modal.msgWarning("请先选择ERP账号或确保选中的商品有关联的账号");
return;
}
this.$confirm('确定要批量下架选中的 ' + this.selectedProductIds.length + ' 个商品吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
const data = {
productIds: this.selectedProductIds,
appid: accountAppid
};
batchDownShelf(data).then(response => {
this.$modal.msgSuccess(response.msg || "批量下架成功");
this.getList();
});
}).catch(() => {});
},
/** 删除按钮操作 */
handleDelete(row) {
const ids = row.id || this.ids;
this.$modal.confirm('是否确认删除闲鱼商品编号为"' + ids + '"的数据项?').then(() => {
return delErpProduct(ids);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
},
/** 导出按钮操作 */
handleExport() {
if (!this.queryParams.appid) {
this.$modal.msgWarning("请先选择ERP账号");
return;
}
this.download('jarvis/erpProduct/export', {
...this.queryParams
}, `erpProduct_${new Date().getTime()}.xlsx`)
},
/** 格式化价格(分转元) */
formatPrice(price) {
if (price == null) return '0.00';
return (price / 100).toFixed(2);
},
/** 格式化时间(时间戳转日期) */
formatTime(timestamp) {
if (!timestamp) return '-';
const date = new Date(timestamp * 1000);
return this.parseTime(date, '{y}-{m}-{d} {h}:{i}:{s}');
},
/** 获取状态文本 */
getStatusText(status) {
if (status == null) return '-';
const statusMap = {
'-1': '删除',
'21': '待发布',
'22': '销售中',
'23': '已售罄',
'31': '手动下架',
'33': '售出下架',
'36': '自动下架'
};
return statusMap[String(status)] || '未知(' + status + ')';
},
/** 获取状态类型 */
getStatusType(status) {
if (status == null) return '';
const typeMap = {
'-1': 'danger', // 删除
'21': 'info', // 待发布
'22': 'success', // 销售中
'23': 'warning', // 已售罄
'31': 'warning', // 手动下架
'33': 'info', // 售出下架
'36': 'warning' // 自动下架
};
return typeMap[String(status)] || '';
}
}
};
</script>
<style scoped>
</style>