This commit is contained in:
yuhawu 2025-08-25 10:41:31 +08:00
parent 263a27c6a4
commit 188dbedbe6
16 changed files with 4542 additions and 4 deletions

86
src/api/modules/audit.js Normal file
View File

@ -0,0 +1,86 @@
import request from '@/utils/axios'
// 查询审核列表
export const getAuditList = (params) => {
return request({
url: '/zhishu/audit/list',
method: 'get',
params
})
}
// 导出审核列表
export const exportAudit = (data) => {
return request({
url: '/zhishu/audit/export',
method: 'post',
data,
responseType: 'blob'
})
}
// 获取审核详细信息
export const getAuditById = (id) => {
return request({
url: `/zhishu/audit/${id}`,
method: 'get'
})
}
// 新增审核
export const addAudit = (data) => {
return request({
url: '/zhishu/audit',
method: 'post',
data
})
}
// 修改审核
export const updateAudit = (data) => {
return request({
url: '/zhishu/audit',
method: 'put',
data
})
}
// 删除审核
export const deleteAudit = (ids) => {
return request({
url: `/zhishu/audit/${ids}`,
method: 'delete'
})
}
// 修改审核通过状态
export const updateAuditStatus = (data) => {
return request({
url: '/zhishu/audit/updateStatus',
method: 'put',
data
})
}
// 修改审核未通过状态
export const failSendAudit = (data) => {
return request({
url: '/zhishu/audit/failSend',
method: 'post',
data
})
}
// 审核API对象
export const auditApi = {
getAuditList,
exportAudit,
getAuditById,
addAudit,
updateAudit,
deleteAudit,
updateAuditStatus,
failSendAudit
}
export default auditApi

View File

@ -0,0 +1,36 @@
import instance from '../../utils/axios.js'
// 图书审核相关API
const bookAuditApi = {
// 获取图书审核列表
getBookAuditList: (params) => instance.get('/zhishu/bookAudit/list', { params }),
// 获取图书审核详细信息
getBookAuditById: (id) => instance.get(`/zhishu/bookAudit/${id}`),
// 新增图书审核
addBookAudit: (data) => instance.post('/zhishu/bookAudit', data),
// 修改图书审核
updateBookAudit: (data) => instance.put('/zhishu/bookAudit', data),
// 删除图书审核(支持批量删除)
deleteBookAudit: (ids) => {
const idStr = Array.isArray(ids) ? ids.join(',') : ids;
return instance.delete(`/zhishu/bookAudit/${idStr}`);
},
// 导出图书审核列表
exportBookAudit: (data) => instance.post('/zhishu/bookAudit/export', data, {
responseType: 'blob'
}),
// 修改审核状态(通过/未通过)
updateBookAuditStatus: (data) => instance.put('/zhishu/bookAudit/updateStatus', data),
// 审核未通过并记录日志
failSendBookAudit: (data) => instance.post('/zhishu/bookAudit/failSend', data)
};
// 导出模块
export { bookAuditApi };

View File

@ -0,0 +1,107 @@
import instance from '../../utils/axios.js'
// 查询任务列表
export const getExcelTaskList = (params = {}) => {
return instance.get('/zhishu/excelTask/list', { params })
}
// 获取任务详细信息
export const getExcelTaskDetail = (id) => {
return instance.get(`/zhishu/excelTask/${id}`)
}
// 新增任务
export const addExcelTask = (data) => {
return instance.post('/zhishu/excelTask', data)
}
// 修改任务
export const updateExcelTask = (data) => {
return instance.put('/zhishu/excelTask', data)
}
// 删除任务
export const deleteExcelTask = (ids) => {
return instance.delete(`/zhishu/excelTask/${ids}`)
}
// 导出任务列表
export const exportExcelTask = (params) => {
return instance.post('/zhishu/excelTask/export', params, {
responseType: 'blob'
})
}
// 获取导入模板
export const getImportTemplate = () => {
return instance.post('/zhishu/excelTask/importTemplate', {}, {
responseType: 'blob'
})
}
// 文件上传
export const uploadExcelFile = (file) => {
const formData = new FormData()
formData.append('file', file)
return instance.post('/zhishu/excelTask/upload', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
}
// 获取日志列表
export const getLogsList = (id) => {
return instance.get('/zhishu/excelTask/logsList', {
params: { id }
})
}
// 获取日志消息
export const getLogsMsg = (id) => {
return instance.get('/zhishu/excelTask/logsMsg', {
params: { id }
})
}
// 获取详细日志列表
export const getLogsDetailList = (taskId, shopId) => {
return instance.get(`/zhishu/excelTask/logsDetailList/${taskId}/${shopId}`)
}
// 下载日志
export const downloadLogs = (fileName) => {
return instance.get(`/zhishu/excelTask/downloadLogs/${fileName}`, {
responseType: 'blob'
})
}
// 暂停线程
export const pauseThread = (threadId, taskId) => {
return instance.get(`/zhishu/excelTask/pauseThread/${threadId}/${taskId}`)
}
// 继续执行线程
export const continueThread = (threadId, taskId) => {
return instance.get(`/zhishu/excelTask/continueThread/${threadId}/${taskId}`)
}
// 为了向后兼容,也导出整个对象
export const excelTaskApi = {
getExcelTaskList,
getExcelTaskDetail,
addExcelTask,
updateExcelTask,
deleteExcelTask,
exportExcelTask,
getImportTemplate,
uploadExcelFile,
getLogsList,
getLogsMsg,
getLogsDetailList,
downloadLogs,
pauseThread,
continueThread
}
export default excelTaskApi

View File

@ -0,0 +1,58 @@
import instance from '../../utils/axios.js'
// 拼多多商品相关API
const pddGoodsApi = {
// 获取授权回调代码
getCode: (params) => instance.get('/pddGoods', { params }),
// 获取店铺商品总数
getShopGoodsTotalNum: (shopId) => instance.get(`/pddGoods/getShopGoodsTotalNum?shopId=${shopId}`),
// 创建店铺商品列表同步任务
createShopGoodsList: (shopId, sycFlag) => instance.get(`/pddGoods/createShopGoodsList?shopId=${shopId}&sycFlag=${sycFlag}`),
// 创建店铺商品详细信息同步任务
createShopGoodsDetailList: (shopId) => instance.get(`/pddGoods/createShopGoodsDetailList?shopId=${shopId}`),
// 获取店铺商品列表
getShopGoodsList: (shopId, isOnsale, pageNum, pageSize) => instance.get(`/pddGoods/getShopGoodsList?shopId=${shopId}&isOnsale=${isOnsale}&pageNum=${pageNum}&pageSize=${pageSize}`),
// 导出店铺商品数据
emportShopGoods: (data) => instance.post('/pddGoods/emportShopGoods', data, { responseType: 'blob' }),
// 检查CSV文件是否存在
checkFile: (shopId) => instance.get(`/pddGoods/checkFile?shopId=${shopId}`),
// 批量修改所有商品上架状态
editShopGoodsAllByIsOnSale: (shopId, isOnSale) => instance.post('/pddGoods/editShopGoodsAllByIsOnSale', `shopId=${shopId}&isOnSale=${isOnSale}`, {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
}),
// 获取商品价格验证文件下载链接
getVerifyPriceUrl: (shopId) => instance.get(`/pddGoods/getVerifyPriceUrl?shopId=${shopId}`),
// 通过CSV修改商品价格数据
updateShopGoodsDataPriceCSV: (data) => instance.post('/pddGoods/updateShopGoodsDataPriceCSV', data),
// 批量设置商品为售罄状态
batchSetSoldOut: (shopId) => instance.post('/pddGoods/batchSetSoldOut', `shopId=${shopId}`, {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
}),
// 通过CSV批量翻新上架商品
batchRenovateOnSaleByCsv: (file, shopId) => {
const formData = new FormData()
formData.append('file', file)
formData.append('shopId', shopId)
return instance.post('/pddGoods/batchRenovateOnSaleByCsv', formData, {
headers: { 'Content-Type': 'multipart/form-data' }
})
},
// 翻新单个商品上架状态
renovateOnSale: (data) => instance.post('/pddGoods/renovateOnSale', data)
};
// 导出模块
export { pddGoodsApi };

View File

@ -0,0 +1,166 @@
import request from '@/utils/axios'
/**
* 店铺订单相关接口
*/
// 查询订单列表
export function getShopOrderList(params) {
return request({
url: '/zhishu/shopOrder/list',
method: 'get',
params
})
}
// 查询订单列表(分页)
export function getShopOrderPage(params) {
return request({
url: '/zhishu/shopOrder/page',
method: 'get',
params
})
}
// 导出订单列表
export function exportShopOrder(data) {
return request({
url: '/zhishu/shopOrder/export',
method: 'post',
data,
responseType: 'blob'
})
}
// 获取订单详细信息
export function getShopOrderInfo(id) {
return request({
url: `/zhishu/shopOrder/${id}`,
method: 'get'
})
}
// 新增订单
export function addShopOrder(data) {
return request({
url: '/zhishu/shopOrder',
method: 'post',
data
})
}
// 修改订单
export function updateShopOrder(data) {
return request({
url: '/zhishu/shopOrder',
method: 'put',
data
})
}
// 删除订单
export function deleteShopOrder(ids) {
return request({
url: `/zhishu/shopOrder/${ids}`,
method: 'delete'
})
}
// 订单列表查询接口(根据成交时间)
export function getInterShopOrder(data) {
return request({
url: '/zhishu/shopOrder/getInterShopOrder',
method: 'get',
data
})
}
// 根据订单编码查询订单信息
export function getOrderByOrderSn(orderSn) {
return request({
url: '/zhishu/shopOrder/listByOrderSn',
method: 'get',
params: { orderSn }
})
}
// 根据店铺id查询订单信息
export function getOrderByShopId(data) {
return request({
url: '/zhishu/shopOrder/listByShopId',
method: 'post',
data
})
}
// 批量插入/更新订单信息
export function insertOrUpdateOrderBatch(data) {
return request({
url: '/zhishu/shopOrder/insertOrUpdateOrderBatch',
method: 'post',
data
})
}
// 定时同步孔网订单
export function syncKfzOrderTask() {
return request({
url: '/zhishu/shopOrder/syncKfzOrderTask',
method: 'get'
})
}
// 获取配送方式列表
export function getDeliveryMethodList(params) {
return request({
url: '/zhishu/shopOrder/delivery/methodList',
method: 'get',
params
})
}
// 订单发货
export function orderDelivery(data) {
return request({
url: '/zhishu/shopOrder/delivery',
method: 'post',
data
})
}
// 检查订单信息
export function checkOrderInfo(orderId) {
return request({
url: '/zhishu/shopOrder/checkInfo',
method: 'post',
params: { orderId }
})
}
// 重新同步订单(已扣减库存订单不会再次扣减)
export function syncKfzHistoryOrder(params, data) {
return request({
url: '/zhishu/shopOrder/syncKfzHistoryOrder',
method: 'post',
params,
data
})
}
// 重新同步订单完成回调
export function syncKfzHistoryOrderCallBack(params) {
return request({
url: '/zhishu/shopOrder/syncKfzHistoryOrderCallBack',
method: 'get',
params
})
}
// 修改订单同步状态
export function updateIsSynOrder(data) {
return request({
url: '/zhishu/shopOrder/updateIsSynOrder',
method: 'post',
data
})
}

View File

@ -0,0 +1,66 @@
<template>
<div class="pagination-container">
<el-pagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[10, 20, 50, 100]"
:total="total"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</template>
<script setup>
import { computed } from 'vue'
defineOptions({
name: 'Pagination'
})
const props = defineProps({
total: {
type: Number,
default: 0
},
page: {
type: Number,
default: 1
},
limit: {
type: Number,
default: 10
}
})
const emit = defineEmits(['update:page', 'update:limit', 'pagination'])
const currentPage = computed({
get: () => props.page,
set: (val) => emit('update:page', val)
})
const pageSize = computed({
get: () => props.limit,
set: (val) => emit('update:limit', val)
})
const handleSizeChange = (size) => {
emit('update:limit', size)
emit('pagination', { page: currentPage.value, limit: size })
}
const handleCurrentChange = (page) => {
emit('update:page', page)
emit('pagination', { page, limit: pageSize.value })
}
</script>
<style scoped>
.pagination-container {
display: flex;
justify-content: flex-end;
margin-top: 20px;
}
</style>

View File

@ -0,0 +1,54 @@
<template>
<div class="right-toolbar">
<div class="toolbar-actions">
<el-button @click="toggleSearch" circle>
<el-icon>
<Search />
</el-icon>
</el-button>
<el-button @click="handleRefresh" circle>
<el-icon>
<Refresh />
</el-icon>
</el-button>
</div>
</div>
</template>
<script setup>
import { Search, Refresh } from '@element-plus/icons-vue'
defineOptions({
name: 'RightToolbar'
})
const props = defineProps({
showSearch: {
type: Boolean,
default: true
}
})
const emit = defineEmits(['update:showSearch', 'queryTable'])
const toggleSearch = () => {
emit('update:showSearch', !props.showSearch)
}
const handleRefresh = () => {
emit('queryTable')
}
</script>
<style scoped>
.right-toolbar {
display: flex;
justify-content: flex-end;
align-items: center;
}
.toolbar-actions {
display: flex;
gap: 10px;
}
</style>

View File

@ -2,7 +2,7 @@
<el-menu router :default-active="$route.path" :collapse="false" unique-opened background-color="#304156"
text-color="#bfcbd9" active-text-color="#409EFF">
<template v-for="item in filteredMenuTree" :key="item.id">
<!-- 有子菜单的情况 -->
<!-- 有子菜单的情况 -->
<el-sub-menu v-if="item.children && item.children.length > 0" :index="item.path || item.id.toString()">
<template #title>
<el-icon v-if="item.icon">

View File

@ -3,7 +3,7 @@
<!-- 顶部导航 -->
<el-header height="60px"><navbar /></el-header>
<el-container>
<!-- 侧边栏 -->
<!-- 侧边栏 -->
<el-aside width="220px"><Sidebar /></el-aside>
<!-- 标签页 -->
<el-main style="padding: 5px;">

View File

@ -52,7 +52,7 @@
const admin = JSON.parse(localStorage.getItem('userInfo'))
//
const userName = admin?.username || 'admin'
//
//
const router = useRouter()
//
const goTo = (path:string) => {

View File

@ -10,7 +10,7 @@
<span>{{ item.title }}</span>
</template>
<!-- 二级菜单 -->
<!-- 二级菜单 -->
<el-menu-item v-for="child in item.children" :key="child.path" :index="child.path">
{{ child.title }}
</el-menu-item>

635
src/views/Audit/index.vue Normal file
View File

@ -0,0 +1,635 @@
<template>
<div class="p-2">
<transition enter-active-class="animate__animated animate__fadeInDown"
leave-active-class="animate__animated animate__fadeOutUp">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true" class="search-form">
<div class="search-left">
<el-form-item label="企业名称" prop="companyName" label-width="100px">
<el-input v-model="queryParams.companyName" placeholder="请输入企业名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="联系方式" prop="contactPhone">
<el-input v-model="queryParams.contactPhone" placeholder="请输入联系方式" clearable @keyup.enter="handleQuery" />
</el-form-item>
</div>
<div class="search-right">
<el-form-item>
<el-button type="primary" @click="handleQuery">搜索</el-button>
<el-button @click="resetQuery">重置</el-button>
</el-form-item>
</div>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="never">
<template #header>
<el-row :gutter="10" class="mb8">
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="auditList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="ID" align="center" prop="id" :show-overflow-tooltip="true">
<template #default="{ row }">
<div class="truncate-cell">
{{ row.id }}
</div>
</template>
</el-table-column>
<el-table-column label="用户昵称" align="center" prop="userName" width="140" :show-overflow-tooltip="true">
<template #default="scope">
<div class="truncate-cell">
<el-button link @click="handleQueryInfo(scope.row)" class="apply-btn" title="点击显示用户详细信息">{{
scope.row.userName }}</el-button>
</div>
</template>
</el-table-column>
<el-table-column label="企业名称" align="center" prop="companyName" />
<el-table-column label="企业类型" align="center" prop="companyType">
<template #default="{ row }">
{{ row.companyType === '1' ? '有限责任公司' :
row.companyType === '2' ? '股份有限公司' :
row.companyType === '3' ? '个人独资企业' :
'合伙企业' }}
</template>
</el-table-column>
<el-table-column label="联系人名" align="center" prop="contactPerson" />
<el-table-column label="联系方式" align="center" prop="contactPhone" />
<el-table-column label="邮箱" align="center" prop="email" />
<el-table-column label="备注" align="center" prop="remark" width="120" :show-overflow-tooltip="true">
<template #default="{ row }">
<div class="truncate-cell">
{{ row.remark }}
</div>
</template>
</el-table-column>
<el-table-column label="审核状态" align="center">
<template #default="{ row }">
<el-tag :type="row.status === '0' ? 'success' : row.status === '1' ? 'danger' : 'info'"
:effect="row.status === '0' ? 'dark' : 'light'" size="large">
{{ row.status === '0' ? '通过' : row.status === '1' ? '未通过' : '待审核' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="120px">
<template #default="scope">
<el-tooltip content="通过" placement="top">
<el-button link type="success" @click="handleUpdateStatus(scope.row.id, '0')">
<el-icon><Check /></el-icon>
</el-button>
</el-tooltip>
<el-tooltip content="未通过" placement="top">
<el-button link type="danger" circle @click="handleOpenDialog(scope.row.id)">X</el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card>
<!-- 添加或修改审核对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
<el-form ref="auditFormRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="管理员ID" prop="adminId">
<el-input v-model="form.adminId" placeholder="请输入管理员ID" />
</el-form-item>
<el-form-item label="企业名称" prop="companyName">
<el-input v-model="form.companyName" placeholder="请输入企业名称" />
</el-form-item>
<el-form-item label="企业类型" prop="companyType">
<el-input v-model="form.companyType" placeholder="请输入企业类型" />
</el-form-item>
<el-form-item label="联系人名" prop="contactPerson">
<el-input v-model="form.contactPerson" placeholder="请输入联系人名" />
</el-form-item>
<el-form-item label="联系方式" prop="contactPhone">
<el-input v-model="form.contactPhone" placeholder="请输入联系方式" />
</el-form-item>
<el-form-item label="邮箱" prop="email">
<el-input v-model="form.email" placeholder="请输入邮箱" />
</el-form-item>
<el-form-item label="营业执照" prop="license">
<el-input v-model="form.license" placeholder="请输入营业执照" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" placeholder="请输入备注" />
</el-form-item>
<el-form-item label="审核状态" prop="status">
<el-radio-group v-model="form.status">
<el-radio :label="0" border size="large" value="0">通过</el-radio>
<el-radio :label="1" border size="large" value="1">未通过</el-radio>
<el-radio :label="2" border size="large" value="2">待审核</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
<!-- 未通过信息对话框 -->
<el-dialog title="填写未通过信息" center v-model="dialog1.visible" width="50%" :close-on-click-modal="false">
<el-form ref="formRef" :model="form" label-width="120px" :rules="rules">
<el-form-item label="驳回理由" prop="remark" :rules="[
{ required: true, message: '必须填写驳回理由', trigger: 'blur' },
{ min: 10, message: '至少输入10个字符', trigger: 'blur' }
]">
<el-input type="textarea" :rows="3" v-model="form.remark" placeholder="请输入具体驳回原因例如1.材料不完整2.信息有误..."
show-word-limit :maxlength="500" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialog1.visible = false">取消</el-button>
<el-button type="primary" @click="hitbackButton(form.remark)">
确认打回
</el-button>
</template>
</el-dialog>
<!-- 申请入驻对话框 -->
<el-dialog title="申请入驻" v-model="dialogVisible" width="50%" :before-close="handleClose">
<el-form ref="settleFormRef" :model="form" :rules="rules" label-width="120px" disabled>
<el-form-item label="身份证正反面" prop="cardIdentity">
<el-image v-if="cardFronUrl" :src="cardFronUrl" class="avatar" :preview-src-list="[cardFronUrl]"
style="width: 200px;height: 200px;margin-right: 30px">
</el-image>
<el-image v-if="cartSideUrl" :src="cartSideUrl" class="avatar" :preview-src-list="[cartSideUrl]"
style="width: 200px;height: 200px">
</el-image>
</el-form-item>
<el-form-item label="联系人" prop="contactPerson">
<el-input v-model="form.contactPerson" disabled="true" placeholder="联系人姓名"></el-input>
</el-form-item>
<el-form-item label="联系电话" prop="contactPhone">
<el-input v-model="form.contactPhone" placeholder="请输入联系电话"></el-input>
</el-form-item>
<el-form-item label="电子邮箱" prop="email">
<el-input v-model="form.email" placeholder="请输入电子邮箱"></el-input>
</el-form-item>
<el-form-item label="营业执照" prop="license">
<el-image v-if="imageUrl" :src="imageUrl" class="avatar" :preview-src-list="[imageUrl]"
style="width: 200px;height: 200px">
<template #tip>
<div class="upload-tip">请上传经营许可证</div>
</template>
</el-image>
</el-form-item>
<el-form-item style="display: none" label="营业执照名称:" prop="licenseName">
<el-input v-model="form.licenseName" placeholder="请输入营业执照名称"></el-input>
</el-form-item>
<el-form-item label="注册号:" prop="licenseNumber">
<el-input v-model="form.licenseNumber" disabled="true" placeholder="营业执照注册号"></el-input>
</el-form-item>
<el-form-item label="企业名称" prop="companyName">
<el-input v-model="form.companyName" disabled="true" placeholder="企业全称"></el-input>
</el-form-item>
<el-form-item label="店铺地址" prop="adress">
<el-input v-model="form.adress" disabled="true" placeholder="店铺地址"></el-input>
</el-form-item>
<el-form-item label="企业类型" prop="companyType">
<el-select v-model="form.companyType" placeholder="请选择企业类型">
<el-option label="有限责任公司" value="1"></el-option>
<el-option label="股份有限公司" value="2"></el-option>
<el-option label="个人独资企业" value="3"></el-option>
<el-option label="合伙企业" value="4"></el-option>
</el-select>
</el-form-item>
<el-form-item label="营业执照过期时间" prop="licenseTime">
<el-date-picker v-model="form.licenseTime" date-format="YYYY/MM/DD" value-format="YYYY/MM/DD" type="date"
placeholder="date" />
</el-form-item>
<el-form-item label="经营许可证" prop="businessLicense">
<el-image v-if="Imagelicense" :src="Imagelicense" class="avatar" :preview-src-list="[Imagelicense]"
style="width: 200px;height: 200px">
<template #tip>
<div class="upload-tip">请上传经营许可证</div>
</template>
</el-image>
</el-form-item>
<el-form-item label="经营许可证过期时间" prop="businessLicenseTime">
<el-date-picker v-model="form.businessLicenseTime" date-format="YYYY/MM/DD" value-format="YYYY/MM/DD"
type="date" placeholder="date" />
</el-form-item>
<el-form-item label="出版物许可证证照核实方式" prop="remark">
<el-input type="textarea" :rows="3" v-model="form.remark" placeholder="请输入其他需要说明的信息"></el-input>
</el-form-item>
</el-form>
</el-dialog>
</div>
</template>
<script setup>
defineOptions({
name: 'Audit'
})
import { ref, reactive, toRefs, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Check } from '@element-plus/icons-vue'
import { getAuditList, getAuditById, addAudit, updateAudit, deleteAudit, updateAuditStatus, failSendAudit, exportAudit } from '@/api/modules/audit'
import { ImageApi } from '@/api/modules/Image'
const auditList = ref([])
const buttonLoading = ref(false)
const loading = ref(true)
const showSearch = ref(true)
const ids = ref([])
const single = ref(true)
const multiple = ref(true)
const total = ref(0)
const queryFormRef = ref()
const auditFormRef = ref()
const formRef = ref()
const dialog = reactive({
visible: false,
title: ''
})
const dialogVisible = ref(false)
const dialog1 = reactive({
visible: false,
title: ''
})
//
const cardFronUrl = ref('')
//
const cartSideUrl = ref('')
//
const imageUrl = ref('')
const Imagelicense = ref('')
const initFormData = {
userId: undefined,
userName: undefined,
adminId: undefined,
status: undefined,
companyName: undefined,
companyType: undefined,
contactPerson: undefined,
contactPhone: undefined,
email: undefined,
license: undefined,
remark: undefined
}
const data = reactive({
form: { ...initFormData },
queryParams: {
pageNum: 1,
pageSize: 10,
userId: undefined,
userName: undefined,
adminId: undefined,
companyName: undefined,
companyType: undefined,
contactPerson: undefined,
contactPhone: undefined,
email: undefined,
license: undefined,
remark: undefined,
status: undefined
},
rules: {}
})
// API
const operationContext = reactive({
currentId: '',
currentData: {}
})
const { queryParams, form, rules } = toRefs(data)
//
const handleOpenDialog = (id) => {
operationContext.currentId = id
console.log(operationContext.currentId)
dialog1.visible = true
}
//
const handleClose = () => {
dialogVisible.value = false
}
/** 查询审核列表 */
const getList = async () => {
loading.value = true
try {
const res = await getAuditList(queryParams.value)
auditList.value = res.data?.list || res.data || []
total.value = res.data?.total || res.total || 0
} catch (error) {
console.error('获取审核列表失败:', error)
ElMessage.error('获取审核列表失败')
} finally {
loading.value = false
}
}
/** 取消按钮 */
const cancel = () => {
reset()
dialog.visible = false
}
/** 表单重置 */
const reset = () => {
form.value = { ...initFormData }
auditFormRef.value?.resetFields()
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields()
handleQuery()
}
/** 多选框选中数据 */
const handleSelectionChange = (selection) => {
ids.value = selection.map(item => item.id)
single.value = selection.length !== 1
multiple.value = !selection.length
}
/** 新增按钮操作 */
const handleAdd = () => {
reset()
dialog.visible = true
dialog.title = '添加审核'
}
const handleQueryInfo = async (row) => {
reset()
const _id = row?.id || ids.value[0]
try {
const res = await getAuditById(_id)
console.log(res)
console.log("res.data.license", res.data.license)
//
try {
const imageResponse = await ImageApi.getImage({ fileName: res.data.license })
imageUrl.value = imageResponse //
console.log("imageUrl.value", imageUrl.value)
} catch (imageError) {
console.error('获取营业执照图片失败:', imageError)
imageUrl.value = null
}
Imagelicense.value = res.data.businessLicense
console.log("Imagelicense.value", Imagelicense.value)
//
try {
const urlArray = JSON.parse(res.data.cardIdentity)
try {
const cardFrontResponse = await ImageApi.getImage({ fileName: urlArray[0] })
cardFronUrl.value = cardFrontResponse //
console.log("cardFronUrl.value", cardFronUrl.value)
} catch (imageError) {
console.error('获取身份证正面图片失败:', imageError)
cardFronUrl.value = null
}
try {
const cartSideResponse = await ImageApi.getImage({ fileName: urlArray[1] })
cartSideUrl.value = cartSideResponse //
console.log("cartSideUrl.value", cartSideUrl.value)
} catch (imageError) {
console.error('获取身份证反面图片失败:', imageError)
cartSideUrl.value = null
}
} catch (error) {
console.error('解析身份证图片失败:', error)
}
Object.assign(form.value, res.data)
dialogVisible.value = true
} catch (error) {
console.error('获取审核详情失败:', error)
ElMessage.error('获取审核详情失败')
}
}
/** 修改按钮操作 */
const handleUpdate = async (row) => {
reset()
const _id = row?.id || ids.value[0]
try {
const res = await getAuditById(_id)
Object.assign(form.value, res.data)
dialog.visible = true
dialog.title = '修改审核'
} catch (error) {
console.error('获取审核详情失败:', error)
ElMessage.error('获取审核详情失败')
}
}
/** 审核通过按钮操作 */
const handleUpdateStatus = async (id, status) => {
try {
await ElMessageBox.confirm(`是否审核编号为"${id}"的数据项?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
const params = {
id: id,
status: status
}
await updateAuditStatus(params)
ElMessage.success('审核通过')
await getList()
} catch (error) {
if (error !== 'cancel') {
console.error('审核状态更新失败:', error)
ElMessage.error('审核状态更新失败')
}
}
}
/**
* 打回按钮
*/
const hitbackButton = async (remark) => {
const payload = {
id: operationContext.currentId,
status: '1',
remark
}
try {
await failSendAudit(payload)
ElMessage.success('审核未通过操作成功')
dialog1.visible = false
await getList()
} catch (error) {
console.error('审核未通过操作失败:', error)
ElMessage.error('审核未通过操作失败')
}
}
/** 提交按钮 */
const submitForm = () => {
auditFormRef.value?.validate(async (valid) => {
if (valid) {
buttonLoading.value = true
try {
if (form.value.id) {
await updateAudit(form.value)
} else {
await addAudit(form.value)
}
ElMessage.success('操作成功')
dialog.visible = false
await getList()
} catch (error) {
console.error('提交失败:', error)
ElMessage.error('操作失败')
} finally {
buttonLoading.value = false
}
}
})
}
/** 删除按钮操作 */
const handleDelete = async (row) => {
const _ids = row?.id || ids.value
try {
await ElMessageBox.confirm(`是否确认删除审核编号为"${_ids}"的数据项?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
await deleteAudit(_ids)
ElMessage.success('删除成功')
await getList()
} catch (error) {
if (error !== 'cancel') {
console.error('删除失败:', error)
ElMessage.error('删除失败')
}
}
}
/** 导出按钮操作 */
const handleExport = async () => {
try {
const res = await exportAudit(queryParams.value)
//
const blob = new Blob([res], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' })
const url = window.URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = `audit_${new Date().getTime()}.xlsx`
link.click()
window.URL.revokeObjectURL(url)
ElMessage.success('导出成功')
} catch (error) {
console.error('导出失败:', error)
ElMessage.error('导出失败')
}
}
onMounted(() => {
getList()
})
</script>
<style>
/* 文本溢出处理 */
.truncate-cell {
width: 100%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.avatar-uploader {
border: 1px dashed var(--el-border-color);
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
transition: var(--el-transition-duration-fast);
}
.avatar-uploader:hover {
border-color: var(--el-color-primary);
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 140px;
height: 120px;
text-align: center;
}
.upload-tip {
margin-top: 8px;
width: 100%;
text-align: center;
}
.search-form {
display: flex;
justify-content: space-between;
align-items: flex-start;
}
.search-left {
display: flex;
flex-wrap: wrap;
align-items: center;
}
.search-right {
display: flex;
align-items: center;
}
</style>
<style scoped>
.avatar-uploader .avatar {
width: 178px;
height: 178px;
display: block;
}
</style>

View File

@ -0,0 +1,408 @@
<template>
<div class="p-2">
<div v-if="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="商品编号" prop="itemNumber">
<el-input v-model="queryParams.itemNumber" placeholder="请输入商品编号" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="品相" prop="conditionCode">
<el-input v-model="queryParams.conditionCode" placeholder="请输入品相" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="isbn" prop="isbn">
<el-input v-model="queryParams.isbn" placeholder="请输入isbn" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
<el-card shadow="never">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain @click="handleAdd">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain :disabled="single" @click="handleUpdate()">修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain :disabled="multiple" @click="handleDelete()">删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain @click="handleExport">导出</el-button>
</el-col>
<el-col :span="1.5">
<el-button @click="showSearch = !showSearch">{{ showSearch ? '隐藏搜索' : '显示搜索' }}</el-button>
</el-col>
</el-row>
</template>
<el-table v-loading="loading" :data="bookAuditList" @selection-change="handleSelectionChange">
<el-table-column type="expand">
<template #default="{ row }">
<el-form label-position="left" inline class="demo-table-expand">
<el-form-item>
<el-image style="width: 100px; height: 120px" :src="row.bookPic" fit="cover"
:preview-src-list="[row.bookPic]" />
</el-form-item>
</el-form>
</template>
</el-table-column>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="审核图书id" align="center" prop="id" />
<el-table-column label="产品编码" align="center" prop="productId" />
<el-table-column label="商品名称" align="center" prop="goodsName" :show-overflow-tooltip="true">
<template #default="{ row }">
<div class="truncate-cell">
{{ row.goodsName }}
</div>
</template>
</el-table-column>
<el-table-column label="isbn" align="center" prop="isbn" />
<el-table-column label="标准售价" align="center" prop="price">
<template #default="{ row }">
{{ (row.price / 100).toFixed(2) }}
</template>
</el-table-column>
<el-table-column label="审核状态" align="center" prop="status">
<template #default="{ row }">
<el-tag :type="row.status === '0' ? 'success' : row.status === '1' ? 'danger' : 'info'"
:effect="row.status === '0' ? 'dark' : 'light'" size="large">
{{ row.status === '0' ? '通过' : row.status === '1' ? '未通过' : '待审核' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="通过" placement="top">
<el-button link type="success" icon="Check" @click="handleUpdateStatus(scope.row.id, '0')">
<el-icon>
<Check />
</el-icon>
</el-button>
</el-tooltip>
<el-tooltip content="未通过" placement="top">
<el-button link type="danger" circle @click="handleUpdateStatus(scope.row.id, '1')">X</el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<el-pagination v-if="total > 0" :total="total" v-model:current-page="queryParams.pageNum"
v-model:page-size="queryParams.pageSize" @current-change="getList" @size-change="getList"
:page-sizes="[10, 20, 50, 100]" layout="total, sizes, prev, pager, next, jumper" />
</el-card>
<!-- 添加或修改图书审核管理对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
<el-form ref="bookAuditFormRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="用户id" prop="userId">
<el-input v-model="form.userId" placeholder="请输入用户id" />
</el-form-item>
<el-form-item label="产品编码" prop="productId">
<el-input v-model="form.productId" placeholder="请输入产品编码" />
</el-form-item>
<el-form-item label="商品名称" prop="goodsName">
<el-input v-model="form.goodsName" placeholder="请输入商品名称" />
</el-form-item>
<el-form-item label="isbn" prop="isbn">
<el-input v-model="form.isbn" placeholder="请输入isbn" />
</el-form-item>
<el-form-item label="货号" prop="artNo">
<el-input v-model="form.artNo" placeholder="请输入货号" />
</el-form-item>
<el-form-item label="品相" prop="conditionCode">
<el-input v-model="form.conditionCode" placeholder="请输入品相" />
</el-form-item>
<el-form-item label="商品编号" prop="itemNumber">
<el-input v-model="form.itemNumber" placeholder="请输入商品编号" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { bookAuditApi } from '@/api/modules/bookAudit'
import { Check } from '@element-plus/icons-vue'
//
const bookAuditList = ref([])
const buttonLoading = ref(false)
const loading = ref(true)
const showSearch = ref(true)
const ids = ref([])
const single = ref(true)
const multiple = ref(true)
const total = ref(0)
//
const queryFormRef = ref()
const bookAuditFormRef = ref()
//
const dialog = reactive({
visible: false,
title: ''
})
//
const initFormData = {
userId: '',
productId: '',
goodsName: '',
isbn: '',
artNo: '',
conditionCode: '',
itemNumber: '',
status: '',
inventory: '',
bookPic: ''
}
//
const queryParams = reactive({
pageNum: 1,
pageSize: 10,
userId: '',
productId: '',
goodsName: '',
isbn: '',
artNo: '',
conditionCode: '',
itemNumber: '',
status: '',
inventory: '',
bookPic: ''
})
//
const form = reactive({ ...initFormData })
//
const rules = reactive({
conditionCode: [
{ required: true, message: "品相不能为空", trigger: "blur" }
]
})
//
const handleUpdateStatus = async (id, status) => {
try {
await ElMessageBox.confirm(`是否审核编号为"${id}"的数据项?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
const params = {
id: id,
status: status
}
await bookAuditApi.updateBookAuditStatus(params)
ElMessage.success(status === '0' ? '审核通过' : '审核未通过')
await getList()
} catch (error) {
console.error('审核状态更新失败:', error)
}
}
//
const getList = async () => {
loading.value = true
try {
const filteredParams = Object.entries(queryParams)
.filter(([_, v]) => v !== '' && v !== null && v !== undefined)
.reduce((acc, [k, v]) => {
acc[k] = v
return acc
}, {})
const res = await bookAuditApi.getBookAuditList(filteredParams)
bookAuditList.value = res.data.list || res.data.rows || []
total.value = res.data.total || 0
} catch (error) {
console.error('获取列表失败:', error)
ElMessage.error('获取列表失败')
} finally {
loading.value = false
}
}
//
const cancel = () => {
reset()
dialog.visible = false
}
//
const reset = () => {
Object.assign(form, initFormData)
bookAuditFormRef.value?.resetFields()
}
//
const handleQuery = () => {
queryParams.pageNum = 1
getList()
}
//
const resetQuery = () => {
queryFormRef.value?.resetFields()
Object.assign(queryParams, {
pageNum: 1,
pageSize: 10,
userId: '',
productId: '',
goodsName: '',
isbn: '',
artNo: '',
conditionCode: '',
itemNumber: '',
status: '',
inventory: '',
bookPic: ''
})
handleQuery()
}
//
const handleSelectionChange = (selection) => {
ids.value = selection.map(item => item.id)
single.value = selection.length !== 1
multiple.value = !selection.length
}
//
const handleAdd = () => {
reset()
dialog.visible = true
dialog.title = "添加图书审核管理"
}
//
const handleUpdate = async (row) => {
reset()
const _id = row?.id || ids.value[0]
try {
const res = await bookAuditApi.getBookAuditById(_id)
Object.assign(form, res.data)
dialog.visible = true
dialog.title = "修改图书审核管理"
} catch (error) {
console.error('获取详情失败:', error)
ElMessage.error('获取详情失败')
}
}
//
const submitForm = () => {
bookAuditFormRef.value?.validate(async (valid) => {
if (valid) {
buttonLoading.value = true
try {
if (form.id) {
await bookAuditApi.updateBookAudit(form)
} else {
await bookAuditApi.addBookAudit(form)
}
ElMessage.success("操作成功")
dialog.visible = false
await getList()
} catch (error) {
console.error('提交失败:', error)
ElMessage.error('操作失败')
} finally {
buttonLoading.value = false
}
}
})
}
//
const handleDelete = async (row) => {
const _ids = row?.id || ids.value
try {
await ElMessageBox.confirm(`是否确认删除图书审核管理编号为"${_ids}"的数据项?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
await bookAuditApi.deleteBookAudit(_ids)
ElMessage.success("删除成功")
await getList()
} catch (error) {
console.error('删除失败:', error)
}
}
//
const handleExport = async () => {
try {
const res = await bookAuditApi.exportBookAudit(queryParams)
//
const url = window.URL.createObjectURL(new Blob([res.data]))
const link = document.createElement('a')
link.href = url
link.setAttribute('download', `bookAudit_${new Date().getTime()}.xlsx`)
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(url)
} catch (error) {
console.error('导出失败:', error)
ElMessage.error('导出失败')
}
}
//
onMounted(() => {
getList()
})
</script>
<style scoped>
/* 文本溢出处理 */
.truncate-cell {
width: 100%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* 增强悬浮提示样式 */
.el-tooltip__popper {
max-width: 400px;
word-break: break-all;
}
.demo-table-expand {
font-size: 0;
}
.demo-table-expand label {
width: 90px;
color: #99a9bf;
}
.demo-table-expand .el-form-item {
margin-right: 0;
margin-bottom: 0;
width: 50%;
}
</style>

View File

@ -0,0 +1,601 @@
<template>
<div class="p-2">
<transition :enter-active-class="animate.searchAnimate.enter" :leave-active-class="animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="任务类型" prop="taskType">
<el-select v-model="queryParams.taskType" placeholder="请选择任务类型" clearable>
<el-option label="发布任务" value="1" />
<el-option label="更新任务" value="2" />
<el-option label="下架任务" value="3" />
</el-select>
</el-form-item>
<el-form-item label="文件名称" prop="fileName">
<el-input v-model="queryParams.fileName" placeholder="请输入文件名称" clearable
@keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="任务状态" prop="taskStatus">
<el-select v-model="queryParams.taskStatus" placeholder="请选择任务状态" clearable>
<el-option label="执行中" value="1" />
<el-option label="已暂停" value="2" />
<el-option label="已完成" value="3" />
<el-option label="已失败" value="4" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="never">
<template #header>
<div class="card-header">
<span>书品导入任务列表</span>
<el-button type="primary" icon="Plus" @click="handleAdd">新增任务</el-button>
</div>
</template>
<el-table v-loading="loading" :data="taskList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="任务编码" align="center" prop="id" :show-overflow-tooltip="true">
<template #default="{ row }">
<div class="truncate-cell">
{{ row.id }}
</div>
</template>
</el-table-column>
<el-table-column label="任务类型" align="center" prop="taskType">
<template #default="scope">
<el-tag v-if="scope.row.taskType === '1'" type="success">发布任务</el-tag>
<el-tag v-else-if="scope.row.taskType === '2'" type="warning">更新任务</el-tag>
<el-tag v-else-if="scope.row.taskType === '3'" type="danger">下架任务</el-tag>
<el-tag v-else>未知</el-tag>
</template>
</el-table-column>
<el-table-column label="货区名称" align="center" prop="depotIds" />
<el-table-column label="文件名称" align="center" prop="fileName" width="250" />
<el-table-column label="执行数据条数" align="center" prop="dataNum" />
<el-table-column label="任务状态" align="center" prop="taskStatus">
<template #default="scope">
<el-tag v-if="scope.row.taskStatus === '1'" type="success">执行中</el-tag>
<el-tag v-else-if="scope.row.taskStatus === '2'" type="warning">已暂停</el-tag>
<el-tag v-else-if="scope.row.taskStatus === '3'" type="info">已完成</el-tag>
<el-tag v-else-if="scope.row.taskStatus === '4'" type="danger">已失败</el-tag>
<el-tag v-else>未知</el-tag>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="200">
<template #default="scope">
<span>{{ formatTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="查看日志" placement="top">
<el-button link type="primary" @click="showLogs(scope.row)">
日志
</el-button>
</el-tooltip>
<el-tooltip content="暂停任务" placement="top">
<el-button v-if="scope.row.taskStatus === '1'" link type="warning"
@click="pauseTask(scope.row)">
暂停
</el-button>
</el-tooltip>
<el-tooltip content="恢复任务" placement="top">
<el-button v-if="scope.row.taskStatus === '2'" link type="success"
@click="resumeTask(scope.row)">
恢复
</el-button>
</el-tooltip>
<el-tooltip content="删除任务" placement="top">
<el-button link type="danger" @click="handleDelete(scope.row)">
删除
</el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<el-pagination v-show="total > 0" :total="total" v-model:current-page="queryParams.pageNum"
v-model:page-size="queryParams.pageSize" :page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper" @size-change="getList" @current-change="getList" />
</el-card>
<!-- 添加任务对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="900px" append-to-body>
<el-form ref="taskFormRef" :model="form" :rules="rules" label-width="100px" style="height:600px">
<el-form-item label="任务类型" prop="taskType">
<el-select v-model="form.taskType" placeholder="请选择任务类型">
<el-option label="发布任务" value="1"></el-option>
<el-option label="更新任务" value="2"></el-option>
<el-option label="下架任务" value="3"></el-option>
</el-select>
</el-form-item>
<el-form-item label="仓库选择" prop="depotIds" style="width: 100%">
<el-checkbox-group v-model="form.depotIds">
<el-checkbox v-for="item in depotList" :key="item.value" :label="item.value">
{{ item.label }}
</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item label="文件选择" prop="fileUrl" style="width: 100%">
<el-row>
<el-col :span="14">
<el-upload ref="uploadRef" class="upload-demo" :action="uploadUrl" :limit="1"
:headers="uploadHeaders" :on-success="handleUploadSuccess" :on-exceed="handleExceed"
:before-upload="beforeUpload">
<template #trigger>
<el-button type="primary">上传文件</el-button>
</template>
</el-upload>
</el-col>
<el-col :span="6">
<el-button @click="downloadTemplate">下载模板</el-button>
</el-col>
</el-row>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
<!-- 日志对话框 -->
<el-dialog :title="dialog.logsTitle" v-model="dialog.logsVisible" width="800px" append-to-body>
<div class="logs-message">
{{ logsMessage }}
</div>
<el-table v-loading="loading" :data="taskLogsList" height="400px">
<el-table-column label="仓库名称" align="center" prop="depotName" />
<el-table-column label="进度" align="center">
<template #default="scope">
{{ scope.row.progress }}%
</template>
</el-table-column>
<el-table-column label="创建时间/修改时间" align="center" prop="createTime" width="300">
<template #default="scope">
<span>{{ scope.row.createTime }} / {{ scope.row.updateTime }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="查看" placement="top">
<el-button link type="primary" icon="Memo" @click="showLogsDetail(scope.row)">查看</el-button>
</el-tooltip>
<el-tooltip content="下载" placement="top">
<el-button link type="primary" icon="Download"
@click="downloadTaskLogs(scope.row.taskId + scope.row.shopId)">下载</el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<template #footer>
<div class="dialog-footer">
<el-button @click="closeLogsDialog"> </el-button>
</div>
</template>
</el-dialog>
<!-- 详细日志对话框 -->
<el-dialog :title="dialog.detailTitle" v-model="dialog.detailVisible" width="700px" append-to-body>
<el-table v-loading="loading" :data="taskLogsDetailList" height="700px">
<el-table-column label="日志名称" align="center" prop="logName" />
<el-table-column label="数量" align="center" prop="num" width="100px" />
<el-table-column label="占比" align="center" width="100px">
<template #default="scope">
{{ scope.row.progress }}%
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="100px">
<template #default="scope">
<el-tooltip content="下载日志" placement="top">
<el-button link type="primary" icon="Download"
@click="downloadTaskLogs(scope.row.mark)">下载日志</el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<template #footer>
<div class="dialog-footer">
<el-button @click="closeDetailDialog"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, reactive, onMounted, getCurrentInstance } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import {
getExcelTaskList,
addExcelTask,
deleteExcelTask,
getLogsList,
getLogsMsg,
getLogsDetailList,
downloadLogs,
pauseThread,
continueThread,
getImportTemplate,
uploadExcelFile
} from '@/api/modules/excelTask'
import { depotApi } from '@/api/modules/depot'
const { appContext } = getCurrentInstance()
const animate = appContext.config.globalProperties.$animate || {
searchAnimate: {
enter: 'animate__animated animate__fadeInDown',
leave: 'animate__animated animate__fadeOutUp'
}
}
//
const taskList = ref([])
const taskLogsList = ref([])
const taskLogsDetailList = ref([])
const depotList = ref([])
const buttonLoading = ref(false)
const loading = ref(true)
const showSearch = ref(true)
const ids = ref([])
const total = ref(0)
const logsMessage = ref('')
//
const queryFormRef = ref()
const taskFormRef = ref()
const uploadRef = ref()
//
const queryParams = ref({
pageNum: 1,
pageSize: 10,
taskType: undefined,
fileName: undefined,
taskStatus: undefined
})
//
const form = ref({
taskType: undefined,
depotIds: [],
fileUrl: undefined
})
//
const rules = {
taskType: [{ required: true, message: '任务类型不能为空', trigger: 'blur' }],
depotIds: [{ required: true, message: '仓库不能为空', trigger: 'blur' }],
fileUrl: [{ required: true, message: '文件不能为空', trigger: 'blur' }]
}
//
const dialog = reactive({
visible: false,
title: '',
logsVisible: false,
logsTitle: '',
detailVisible: false,
detailTitle: ''
})
//
const baseUrl = import.meta.env.VITE_APP_BASE_API
const uploadUrl = ref(baseUrl + '/zhishu/excelTask/upload')
const uploadHeaders = ref({
'Authorization': `Bearer ${localStorage.getItem('accessToken') || ''}`
})
//
const formatTime = (time) => {
if (!time) return ''
const date = new Date(time)
return date.toLocaleString('zh-CN')
}
//
const getList = async () => {
loading.value = true
try {
const res = await getExcelTaskList(queryParams.value)
taskList.value = res.data?.list || []
total.value = res.data?.total || 0
} catch (error) {
console.error('获取任务列表失败:', error)
ElMessage.error('获取任务列表失败')
} finally {
loading.value = false
}
}
//
const getDepotList = async () => {
try {
const res = await depotApi.getDepotNameList()
const resList = res.data?.rows || []
depotList.value = resList.map(item => ({
value: item.id,
label: item.name
}))
} catch (error) {
console.error('获取仓库列表失败:', error)
}
}
//
const handleQuery = () => {
queryParams.value.pageNum = 1
getList()
}
//
const resetQuery = () => {
queryFormRef.value?.resetFields()
handleQuery()
}
//
const handleSelectionChange = (selection) => {
ids.value = selection.map(item => item.id)
}
//
const handleAdd = () => {
resetForm()
dialog.visible = true
dialog.title = '添加任务'
}
//
const pauseTask = async (row) => {
try {
await pauseThread(row.threadId, row.id)
ElMessage.success('任务已暂停')
getList()
} catch (error) {
console.error('暂停任务失败:', error)
ElMessage.error('暂停任务失败')
}
}
//
const resumeTask = async (row) => {
try {
await continueThread(row.threadId, row.id)
ElMessage.success('任务已恢复')
getList()
} catch (error) {
console.error('恢复任务失败:', error)
ElMessage.error('恢复任务失败')
}
}
//
const handleDelete = async (row) => {
try {
await ElMessageBox.confirm(`是否确认删除任务编号为"${row.id}"的数据项?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
await deleteExcelTask(row.id)
ElMessage.success('删除成功')
getList()
} catch (error) {
if (error !== 'cancel') {
console.error('删除任务失败:', error)
ElMessage.error('删除任务失败')
}
}
}
//
const showLogs = async (row) => {
try {
const [logsRes, msgRes] = await Promise.all([
getLogsList(row.id),
getLogsMsg(row.id)
])
taskLogsList.value = logsRes.data || []
logsMessage.value = msgRes.data || ''
dialog.logsVisible = true
dialog.logsTitle = '任务日志'
} catch (error) {
console.error('获取日志失败:', error)
ElMessage.error('获取日志失败')
}
}
//
const showLogsDetail = async (row) => {
try {
const res = await getLogsDetailList(row.taskId, row.shopId)
taskLogsDetailList.value = res.data || []
dialog.detailVisible = true
dialog.detailTitle = `详细日志信息:${taskLogsDetailList.value[0]?.depotName || ''}`
} catch (error) {
console.error('获取详细日志失败:', error)
ElMessage.error('获取详细日志失败')
}
}
//
const downloadTaskLogs = async (fileName) => {
try {
const fullFileName = fileName + '.xlsx'
const url = `${baseUrl}/zhishu/excelTask/downloadLogs/${fullFileName}`
const link = document.createElement('a')
link.href = url
link.download = fullFileName
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
} catch (error) {
console.error('下载日志失败:', error)
ElMessage.error('下载日志失败')
}
}
//
const downloadTemplate = async () => {
try {
const res = await getImportTemplate()
const blob = new Blob([res.data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' })
const url = window.URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = `task_template_${new Date().getTime()}.xlsx`
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(url)
} catch (error) {
console.error('下载模板失败:', error)
ElMessage.error('下载模板失败')
}
}
//
const beforeUpload = (file) => {
const isExcel = file.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' ||
file.type === 'application/vnd.ms-excel'
if (!isExcel) {
ElMessage.error('只能上传Excel文件!')
return false
}
const isLt10M = file.size / 1024 / 1024 < 10
if (!isLt10M) {
ElMessage.error('上传文件大小不能超过 10MB!')
return false
}
return true
}
//
const handleUploadSuccess = (response) => {
if (response.code === 200) {
form.value.fileUrl = response.data?.url || ''
ElMessage.success('文件上传成功')
} else {
ElMessage.error(response.message || '文件上传失败')
}
}
//
const handleExceed = () => {
ElMessage.warning('只能上传一个文件')
}
//
const submitForm = async () => {
if (!taskFormRef.value) return
try {
await taskFormRef.value.validate()
buttonLoading.value = true
const submitData = {
...form.value,
depotIds: form.value.depotIds.join(',')
}
await addExcelTask(submitData)
ElMessage.success('操作成功')
dialog.visible = false
getList()
} catch (error) {
if (error !== false) { // errorfalse
console.error('提交失败:', error)
ElMessage.error('操作失败')
}
} finally {
buttonLoading.value = false
}
}
//
const resetForm = () => {
form.value = {
taskType: undefined,
depotIds: [],
fileUrl: undefined
}
taskFormRef.value?.resetFields()
}
//
const cancel = () => {
resetForm()
dialog.visible = false
}
//
const closeLogsDialog = () => {
dialog.logsVisible = false
taskLogsList.value = []
logsMessage.value = ''
}
//
const closeDetailDialog = () => {
dialog.detailVisible = false
taskLogsDetailList.value = []
}
//
onMounted(() => {
getList()
getDepotList()
})
</script>
<style scoped>
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.truncate-cell {
width: 100%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.logs-message {
width: 100%;
height: 162px;
background: #f2f2f2;
white-space: pre-wrap;
padding: 13px;
margin-bottom: 15px;
border-radius: 4px;
overflow-y: auto;
}
.el-tooltip__popper {
max-width: 400px;
word-break: break-all;
}
</style>

View File

@ -0,0 +1,879 @@
<template>
<div class="p-2">
<transition enter-active-class="animate__animated animate__fadeInDown"
leave-active-class="animate__animated animate__fadeOutUp">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form ref="queryFormRef" :model="queryParams" :inline="true" label-position="left" label-width="80px">
<el-form-item label="店铺名称" prop="shopId">
<el-select v-model="shopId" placeholder="请选择店铺" clearable @change="handleQuery">
<el-option v-for="dict in shopList" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
<el-form-item label="状态" prop="is_onsale">
<el-select v-model="queryParams.is_onsale" placeholder="请选择状态" clearable @change="handleQuery">
<el-option key="0" label="下架" value="0" />
<el-option key="1" label="上架" value="1" />
</el-select>
</el-form-item>
</el-form>
<el-form-item>
<el-button type="primary" @click="handleQuery">搜索</el-button>
<el-button type="primary" @click="exportData">导出</el-button>
<el-button type="primary" @click="confirmTongBu">删除后拉取</el-button>
<el-button type="primary" @click="tongBu1">拉取</el-button>
<el-button type="danger" @click="handleAdd" v-hasPermi="['zhishu:pddGoods:tongbu']">
拼多多专属详情同步 </el-button>
<el-button type="primary" @click="editIsOnSale('1')">整店上架</el-button>
<el-button type="primary" @click="editIsOnSale('0')">整店下架</el-button>
<el-button type="danger" @click="repeatData">下架重复</el-button>
<el-button type="success" @click="showRenovateDialog">翻新上架</el-button>
<el-button type="primary" @click="createVerifyPriceUrl">生成核价链接</el-button>
</el-form-item>
</el-form>
</el-card>
<el-dialog v-model="verifyPriceDialog" title="核价链接" width="700px" @close="closeData">
<div class="verify-price-dialog">
<el-form :model="encrypForm" :rules="encrypRules" ref="encrypFormRef">
<el-form-item label="店铺优惠" prop="shopPrice" v-if="!pricingLink">
<el-input v-model="encrypForm.shopPrice" placeholder="请输入店铺优惠、在0-20之间整数" />
</el-form-item>
<el-form-item label="利润区间" prop="profitRange" v-if="!pricingLink">
<el-row :gutter="10">
<el-col :span="10">
<el-form-item prop="minPrice">
<el-input v-model="encrypForm.minPrice" type="number" placeholder="利润最小值(整数)">
<template #append></template>
</el-input>
</el-form-item>
</el-col>
<el-col :span="4" style="text-align: center; line-height: 32px"> </el-col>
<el-col :span="10">
<el-form-item prop="maxPrice">
<el-input v-model="encrypForm.maxPrice" type="number" placeholder="利润最大值(整数)">
<template #append></template>
</el-input>
</el-form-item>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="价格模版名称" prop="shopPrice" v-if="pricingLink">
<el-input v-model="priceName" disabled />
</el-form-item>
</el-form>
<div style="height: 110px; padding: 10px">
<span>{{ pricingLink }}</span>
</div>
</div>
<template #footer>
<el-button v-if="pricingLink" @click="copyLink" type="primary" style="margin-left: 10px"> 一键复制 </el-button>
<el-button type="primary" @click="encrypMethod" v-if="!pricingLink">确定</el-button>
</template>
</el-dialog>
</div>
</transition>
<!-- 翻新上架弹窗 -->
<el-dialog v-model="renovateDialogVisible" title="批量翻新上架" width="500px">
<el-upload ref="upload" class="upload-demo" :auto-upload="false" :on-change="handleFileChange"
:before-upload="beforeUpload" :limit="1" :file-list="fileList" accept=".csv">
<template #trigger>
<el-button type="primary">选择CSV文件</el-button>
</template>
<template #tip>
<div class="el-upload__tip">请上传包含商品信息的CSV文件</div>
</template>
</el-upload>
<template #footer>
<el-button @click="renovateDialogVisible = false">取消</el-button>
<el-button type="primary" :loading="uploading" @click="handleUpload">开始上传</el-button>
</template>
</el-dialog>
<el-card shadow="never">
<template #header>
<el-row :gutter="10" class="mb8">
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="pddGoodsList" :element-loading-text="loadingText"
element-loading-svg-view-box="-10, -10, 50, 50" element-loading-background="rgba(122, 122, 122, 0.8)"
@selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="缩略图" align="left" prop="thumb_url" width="70" :show-overflow-tooltip="true">
<template #default="{ row }">
<el-image style="width: 30px; height: 30px" :src="row.Img" fit="scale-down"
:preview-src-list="[currentPreviewImg]" preview-teleported @click="handlePreview(row.Img)">
<template #error>
<div class="image-slot">暂无</div>
</template>
</el-image>
</template>
</el-table-column>
<el-table-column label="商品id" align="center" prop="TrilateralId" width="150" />
<el-table-column label="商品编码" align="center" prop="ISBN" width="150" />
<el-table-column label="商品名称" align="center" prop="Title" />
<el-table-column label="库存数量" align="center" prop="Stock" width="100" />
<el-table-column label="是否在架上" align="center" prop="IsOnSale" width="100">
<template #default="{ row }">
{{ row.IsOnSale == '1' ? '是' : '否' }}
</template>
</el-table-column>
<el-table-column label="价格" align="center" prop="TotalPrice" width="100">
<template #default="{ row }">
{{ row.TotalPrice ? row.TotalPrice / 100 : '' }}
</template>
</el-table-column>
<el-table-column label="规格编码/货号(孔网)" align="center" prop="SkuCode" />
<el-table-column label="创建时间" align="center" prop="FinishTime" width="200" />
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card>
</div>
<!-- 添加这个新的弹窗用于展示重复商品数据 -->
<el-dialog v-model="repeatDataDialog" title="重复商品列表" width="80%">
<el-table :data="repeatDataList" v-loading="repeatLoading" border style="width: 100%">
<el-table-column prop="trilateralId" label="商品ID" width="180" />
<el-table-column label="商品图片" width="120">
<template #default="{ row }">
<el-image style="width: 50px; height: 50px" :src="row.img" fit="cover" />
</template>
</el-table-column>
<el-table-column prop="title" label="商品名称" show-overflow-tooltip />
<el-table-column prop="isbn" label="商品编码" width="150" />
<el-table-column prop="stock" label="库存" width="80" />
<el-table-column prop="isOnSale" label="是否在售" width="100">
<template #default="{ row }">
{{ row.isOnSale === '1' ? '是' : '否' }}
</template>
</el-table-column>
<el-table-column prop="finishTime" label="创建时间" width="180" />
</el-table>
<template #footer>
<el-pagination v-model:current-page="repeatQuery.pageNum" v-model:page-size="repeatQuery.pageSize"
:total="repeatTotal" layout="total, sizes, prev, pager, next, jumper" @size-change="handleRepeatSizeChange"
@current-change="handleRepeatCurrentChange" />
<el-button type="danger" :loading="buttonLoading" @click="batchOffShelf">批量下架</el-button>
<el-button @click="repeatDataDialog = false">关闭</el-button>
</template>
</el-dialog>
<!-- 添加或修改充值对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
<el-form ref="userRechargeFormRef" :rules="rules" label-width="80px">
<el-form-item label="支付方式" prop="rechargType">
<el-select v-model="rechargType" placeholder="请选择支付方式">
<el-option v-for="dict in t_recharge_way" :key="dict.value" :label="dict.label"
:value="dict.value"></el-option>
</el-select>
</el-form-item>
<span style="color: red; font-size: 14px; margin-left: 12px">{{ rechargText }}</span>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="tongBuDetail"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
<el-dialog :title="dialog.titlePrcode" v-model="dialog.visiblePrcode" width="360px" append-to-body>
<div>
<img :src="qrCodeText" alt="Base64 图片" />
</div>
</el-dialog>
</template>
<script setup name="Notice">
import { ref, reactive, onMounted, toRefs } from 'vue';
import { pddGoodsApi } from '@/api/modules/pddGoods';
import { taskApi } from '@/api/modules/task';
const { getShopGoodsTask, getRepeatData, selectTaskStatusByTaskType, getShopTaskAllNum } = taskApi;
import { userRechargeApi } from '@/api/modules/userRecharge';
const {
listUserRecharge,
getUserRecharge,
delUserRecharge,
addUserRecharge,
updateUserRecharge,
userRecharge,
checkTask,
editRechargeToError,
getConfigKey
} = userRechargeApi;
import { shopApi } from '@/api/modules/shop';
const { encrypUrlMenthod, getListShop, getVerifyPriceUrl } = shopApi;
import { ElMessage, ElMessageBox } from 'element-plus';
//
const verifyPriceDialog = ref(false);
const verifyPriceUrl = ref('');
const shopTotalCount = ref('');
const loadingVerifyUrl = ref(false);
const rechargType = ref('');
const qrCodeText = ref('');
const rechargText = ref('');
// proxy使
const t_notice_status = ref([]);
const t_recharge_way = ref([]);
const pddGoodsList = ref([]);
const buttonLoading = ref(false);
const loading = ref(false);
const showSearch = ref(true);
const ids = ref([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const shopId = ref('');
const loadingText = ref('正在加载数据');
const queryFormRef = ref();
const noticeFormRef = ref();
const userRechargeFormRef = ref();
const dialog = reactive({
visible: false,
title: ''
});
const repeatDataDialog = ref(false);
const repeatLoading = ref(false);
const repeatDataList = ref([]);
const repeatTotal = ref(0);
const repeatQuery = reactive({
pageNum: 1,
pageSize: 10
});
const selectedRepeatItems = ref([]);
const initFormData = {
created_at: undefined,
goods_id: undefined,
goods_name: undefined,
goods_quantity: undefined,
goods_reserve_quantity: undefined,
image_url: undefined,
is_more_sku: undefined,
is_onsale: undefined,
sku_list: undefined,
thumb_url: undefined,
price: undefined,
skuCode: undefined
};
const data = reactive({
form: { ...initFormData },
queryParams: {
pageNum: 1,
pageSize: 10,
created_at: undefined,
goods_id: undefined,
goods_name: undefined,
goods_quantity: undefined,
goods_reserve_quantity: undefined,
image_url: undefined,
is_more_sku: undefined,
is_onsale: undefined,
sku_list: undefined,
thumb_url: undefined,
price: undefined,
skuCode: undefined,
params: {}
}
});
//
const encrypForm = reactive({
dataUrl: undefined,
shopPrice: undefined,
maxPrice: undefined,
minPrice: undefined,
shopId: undefined,
shopType: undefined,
totalCount: undefined
});
const pricingLink = ref('');
const priceName = ref('');
const { queryParams, form } = toRefs(data);
//
const encrypFormRef = ref(); //
const encrypRules = {
shopPrice: [
{ required: true, message: '请输入店铺优惠', trigger: 'blur' },
{
validator: (rule, value, callback) => {
const num = Number(value);
if (!Number.isInteger(num)) {
callback(new Error('必须输入整数'));
} else if (num < 0 || num > 20) {
callback(new Error('请输入0-20之间的整数'));
} else {
callback();
}
},
trigger: 'blur'
}
],
minPrice: [
{ required: true, message: '请输入利润最小值', trigger: 'blur' },
{
validator: (rule, value, callback) => {
if (!Number.isInteger(Number(value))) {
callback(new Error('必须输入整数'));
} else {
callback();
}
},
trigger: 'blur'
}
],
maxPrice: [
{ required: true, message: '请输入利润最大值', trigger: 'blur' },
{
validator: function (rule, value, callback) {
const min = Number(encrypForm.minPrice);
const max = Number(value);
if (!Number.isInteger(max)) {
callback(new Error('必须输入整数'));
} else if (max <= min) {
callback(new Error('最大值必须大于最小值'));
} else {
callback();
}
}.bind(this),
trigger: 'blur'
}
]
};
/** 查询消息通知列表 */
const getList = async () => {
loading.value = true;
const isOnsale = queryParams.value.is_onsale ? queryParams.value.is_onsale : '';
const res = await pddGoodsApi.getShopGoodsList(shopId.value, isOnsale, queryParams.value.pageNum, queryParams.value.pageSize);
// API
console.log('API返回的原始数据:', res);
//
if (res.data) {
pddGoodsList.value = res.data.records || [];
total.value = res.data.total || 0;
console.log('使用res.data结构records数量:', pddGoodsList.value.length, '总数:', total.value);
} else {
//
pddGoodsList.value = res.records || [];
total.value = res.total || 0;
console.log('使用直接结构records数量:', pddGoodsList.value.length, '总数:', total.value);
}
loading.value = false;
};
/** 取消按钮 */
const cancel = () => {
reset();
dialog.visible = false;
};
/** 表单重置 */
const reset = () => {
form.value = { ...initFormData };
noticeFormRef.value?.resetFields();
};
/** 搜索按钮操作 */
const handleQuery = async () => {
if (shopId.value == null || shopId.value == undefined || shopId.value == '') {
ElMessage.error('请选择店铺');
return;
}
queryParams.value.pageNum = 1;
getList();
};
/**
* 导出
*/
const exportData = async () => {
const isOnsale = queryParams.value.is_onsale ? queryParams.value.is_onsale : '';
proxy?.download(
'huidiao/pdd/emportShopGoods',
{
'shopId': shopId.value,
'isOnsale': isOnsale
},
`baseInfo_${new Date().getTime()}.xlsx`
);
};
const createVerifyPriceUrl = async () => {
if (!shopId.value) {
ElMessage.error('请先选择店铺');
return;
}
if (pddGoodsList.value.length === 0) {
proxy?.$modal.msgError('当前店铺没有商品数据');
return;
}
try {
loadingVerifyUrl.value = true;
const allNum = await getShopTaskAllNum(shopId.value, '1');
console.log("res1",allNum)
if (allNum.data > 0) {
verifyPriceUrl.value = shopId.value;
shopTotalCount.value = allNum.data;
verifyPriceDialog.value = true;
} else {
ElMessage.error('请先同步店铺数据');
}
} catch (error) {
console.error('生成核价链接出错:', error);
ElMessage.error('生成核价链接出错');
} finally {
loadingVerifyUrl.value = false;
}
};
// APIAPI
const createVerifyPriceUrlApi = async (shopId) => {
// API
return await getVerifyPriceUrl(shopId);
};
const copyVerifyUrl = () => {
if (!verifyPriceUrl.value) return;
navigator.clipboard
.writeText(verifyPriceUrl.value)
.then(() => {
ElMessage.success('链接已复制到剪贴板');
})
.catch((err) => {
console.error('复制失败:', err);
ElMessage.error('复制链接失败');
});
};
/**
* 详细商品支付选择
*/
const handleAdd = async () => {
const checkRes = await getShopGoodsTask(shopId.value);
if (checkRes.code == 200) {
//
const totalData = await pddGoodsApi.getShopGoodsTotalNum(shopId.value);
reset();
dialog.visible = true;
dialog.title = '充值';
rechargText.value = '查询店铺总数据条数:' + totalData.data + '; 充值金额为:' + (Math.ceil((totalData.data / 10000) * 100) / 100).toFixed(2);
buttonLoading.value = false;
} else {
ElMessage.error(checkRes.msg);
}
};
const confirmTongBu = () => {
if (shopId.value == null || shopId.value == undefined || shopId.value == '') {
ElMessage.error('请选择店铺');
return;
}
ElMessageBox.confirm('本操作将删除选择店铺的所有商品再从孔网拉取,请谨慎操作,点击确定将执行该操作。', '提示', { type: 'warning' }).then(() => {
tongBu();
}).catch(() => { });
};
const tongBu = async () => {
if (shopId.value == null || shopId.value == undefined || shopId.value == '') {
ElMessage.error('请选择店铺');
return;
}
let sycFlag = 1;
const checkRes = await getShopGoodsTask(shopId.value);
if (checkRes.code == 200) {
const res = await pddGoodsApi.createShopGoodsList(shopId.value, sycFlag);
ElMessage.success(res.msg);
} else {
ElMessage.error(checkRes.msg);
}
};
const tongBu1 = async () => {
if (shopId.value == null || shopId.value == undefined || shopId.value == '') {
ElMessage.error('请选择店铺');
return;
}
let sycFlag = 2;
const checkRes = await getShopGoodsTask(shopId.value);
if (checkRes.code == 200) {
const res = await pddGoodsApi.createShopGoodsList(shopId.value, sycFlag);
ElMessage.success(res.msg);
} else {
ElMessage.error(checkRes.msg);
}
};
const tongBuDetail = async () => {
if (shopId.value == null || shopId.value == undefined || shopId.value == '') {
ElMessage.error('请选择店铺');
return;
}
if (rechargType.value == null || rechargType.value == undefined || rechargType.value == '') {
ElMessage.error('请选择支付方式');
return;
}
buttonLoading.value = true;
//
const totalData = await pddGoodsApi.getShopGoodsTotalNum(shopId.value);
if (totalData.code == 200 && totalData.data > 0) {
//10000
const price = (Math.ceil((totalData.data / 10000) * 100) / 100).toFixed(2);
const data = {
'rechargType': '1',
'rechargPrice': price
};
const res = await userRecharge(data);
if (res.code == 200) {
qrCodeText.value = 'data:image/png;base64,' + res.data.img;
dialog.visiblePrcode = true;
dialog.titlePrcode = '支付';
// 2
let count = 0;
const maxCount = 40; // 40
let intervalId = setInterval(async () => {
if (count < maxCount) {
const status = await checkTask(res.data.id);
if (status == '1') {
count = 80;
const res = await pddGoodsApi.createShopGoodsDetailList(shopId.value);
ElMessage.success(res.msg);
}
count++;
} else {
if (count == 40) {
editRechargeToError(res.data.id);
}
dialog.visible = false;
dialog.visiblePrcode = false;
qrCodeText.value = undefined;
buttonLoading.value = false;
getList();
clearInterval(intervalId); //
}
}, 3000);
}
} else {
ElMessage.error('未获取到店铺商品数量信息');
}
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
handleQuery();
};
const currentPreviewImg = ref(''); //
//
const handlePreview = (imgUrl) => {
currentPreviewImg.value = imgUrl;
};
/** 多选框选中数据 */
const handleSelectionChange = (selection) => {
ids.value = selection.map((item) => item.id);
single.value = selection.length != 1;
multiple.value = !selection.length;
};
const shopList = ref([]);
const getShopList = async () => {
const res = await getListShop();
console.log("res",res)
if (res.code === 200 && Array.isArray(res.data)) {
shopList.value = res.data.map(item => ({
value: item.id,
label: item.shopName
}));
}
};
const formatTimestamp = (row, column, timestamp) => {
if (!timestamp) return '';
// 10JSDate
const date = new Date(timestamp * 1000);
// YYYY-MM-DD hh:mm:ss
const padZero = (num) => num.toString().padStart(2, '0');
const year = date.getFullYear();
const month = padZero(date.getMonth() + 1);
const day = padZero(date.getDate());
const hours = padZero(date.getHours());
const minutes = padZero(date.getMinutes());
const seconds = padZero(date.getSeconds());
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
};
//
const encrypMethod = async () => {
try {
// 1.
await encrypFormRef.value?.validate();
// 2.
loadingVerifyUrl.value = true;
// 3. -
const requestData = {
...encrypForm,
shopPrice: Math.round(Number(encrypForm.shopPrice) * 100), //
minPrice: Math.round(Number(encrypForm.minPrice) * 100), //
maxPrice: Math.round(Number(encrypForm.maxPrice) * 100), //
dataUrl: verifyPriceUrl.value,
shopId: shopId.value,
totalCount: shopTotalCount.value,
shopType: '1' //
};
// 4.
const res = await encrypUrlMenthod(requestData);
// 5.
if (res) {
pricingLink.value = res.url;
if (res.url != null) {
priceName.value = res.name;
}
ElMessage.success('核价链接生成成功');
} else {
ElMessage.warning('拼多多以外店铺正在开发中,请重新选择');
}
} catch (error) {
// catch
console.error('表单验证失败:', error);
} finally {
loadingVerifyUrl.value = false;
}
};
//
const copyLink = async () => {
try {
await navigator.clipboard.writeText(pricingLink.value);
ElMessage.success('复制成功');
} catch (err) {
ElMessage.error('复制失败,请手动复制');
}
};
const closeData = async () => {
pricingLink.value = null;
encrypForm.dataUrl = null;
encrypForm.shopId = null;
encrypForm.shopPrice = null;
encrypForm.maxPrice = null;
encrypForm.minPrice = null;
};
/**
* 去重
*/
const repeatData = async () => {
if (shopId.value == null || shopId.value == undefined || shopId.value == '') {
ElMessage.error('请选择店铺');
return;
}
repeatQuery.pageNum = 1;
repeatQuery.pageSize = 10;
repeatTotal.value = 0;
repeatDataList.value = [];
buttonLoading.value = false;
repeatDataDialog.value = true;
try {
getRepeatDataList();
} catch (error) {
console.error('获取重复数据失败:', error);
ElMessage.error('获取重复数据失败');
repeatLoading.value = false;
}
};
const getRepeatDataList = async () => {
repeatLoading.value = true;
const res = await getRepeatData(shopId.value, repeatQuery.pageNum, repeatQuery.pageSize);
//
if (res.data) {
repeatDataList.value = res.data.records || [];
repeatTotal.value = res.data.total || 0;
} else {
//
repeatDataList.value = res.records || [];
repeatTotal.value = res.total || 0;
}
repeatLoading.value = false;
};
//
const handleRepeatSizeChange = (val) => {
repeatQuery.pageSize = val;
getRepeatDataList();
};
const handleRepeatCurrentChange = (val) => {
repeatQuery.pageNum = val;
getRepeatDataList();
};
/**
* 整店上/下架
*/
const editIsOnSale = async (isOnSale) => {
if (shopId.value == null || shopId.value == undefined || shopId.value == '') {
ElMessage.error('请选择店铺');
return;
}
const res = await pddGoodsApi.editShopGoodsAllByIsOnSale(shopId.value, isOnSale);
if (res.code == 200) {
ElMessage.success(res.msg);
} else {
ElMessage.error(res.msg);
}
};
/**
* 批量下架任务
*/
const batchOffShelf = async () => {
buttonLoading.value = true;
const check = await selectTaskStatusByTaskType(shopId.value, 'BATCH_SET_SOLD_OUT');
if (check.code == 200) {
const res = await pddGoodsApi.batchSetSoldOut(shopId.value);
repeatDataDialog.value = false;
buttonLoading.value = false;
ElMessage.success(res.msg);
} else {
ElMessage.error(check.msg);
buttonLoading.value = false;
}
};
onMounted(() => {
getShopList();
});
//
const renovateDialogVisible = ref(false); //
const uploadFile = ref(); //
const fileList = ref([]); //
const uploading = ref(false); //
//
const showRenovateDialog = () => {
if (!shopId.value) {
proxy?.$modal.msgError('请先选择店铺');
return;
}
renovateDialogVisible.value = true;
};
//
const handleUpload = async () => {
if (fileList.value.length === 0) {
ElMessage.error('请先选择CSV文件');
return;
}
try {
uploading.value = true;
const file = fileList.value[0].raw;
const res = await batchRenovateOnSaleByCsv(file, shopId.value);
if (res.code === 200) {
ElMessage.success(res.msg || '已受理翻新上架请求');
renovateDialogVisible.value = false;
fileList.value = []; //
} else {
ElMessage.error(res.msg || '翻新上架失败');
}
} catch (error) {
console.error('翻新上架出错:', error);
ElMessage.error('翻新上架出错');
} finally {
uploading.value = false;
}
};
//
const beforeUpload = (file) => {
const isCSV = file.type === 'text/csv' || file.name.endsWith('.csv');
if (!isCSV) {
ElMessage.error('只能上传CSV格式文件');
return false;
}
return true;
};
//
const handleFileChange = (file, fileList) => {
uploadFile.value = file;
fileList.value = fileList.slice(-1); //
};
</script>
<style>
/* 文本溢出处理 */
.truncate-cell {
width: 100%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.verify-price-dialog {
padding: 0 20px;
}
.url-display {
margin-bottom: 20px;
}
.tips p {
margin: 5px 0;
line-height: 1.5;
}
.upload-demo {
text-align: center;
padding: 20px;
}
</style>

File diff suppressed because it is too large Load Diff