598 lines
21 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="pageBox">
<div class="searchBox">
<div class="row">
<span class="span">商品</span>
<div class="right">
<el-select v-model="goodsIds" placeholder="请选择" clearable filterable class="wid100" multiple collapse-tags>
<el-option v-for="it in goodsList" :key="it.id" :label="it.title" :value="it.id" />
</el-select>
</div>
</div>
<div class="row">
<span class="span">商品品牌:</span>
<div class="right">
<el-select v-model="brandIds" placeholder="请选择" clearable class="wid100" filterable multiple collapse-tags>
<el-option v-for="it in brandList" :key="it.id" :label="it.name" :value="it.id" />
</el-select>
</div>
</div>
<div class="row">
<span class="span">规格名称:</span>
<div class="right"><el-input v-model="filter.title" class="wid100" clearable></el-input></div>
</div>
<div class="row">
<span class="span">规格编码:</span>
<div class="right"><el-input v-model="filter.sku_code" class="wid100" clearable></el-input></div>
</div>
<div class="row">
<span class="span">仓库:</span>
<div class="right">
<el-select v-model="filter.warehouse_id" placeholder="请选择" clearable class="wid100" filterable>
<el-option v-for="it in warehouseList" :key="it.id" :label="it.name" :value="it.id" />
</el-select>
</div>
</div>
<div class="row">
<span class="span">可售库存:</span>
<div class="right">
<el-input v-model="filter.min_available_inventory" class="wid100" clearable></el-input>&nbsp;~&nbsp;
<el-input v-model="filter.max_available_inventory" class="wid100" clearable></el-input>
</div>
</div>
<div class="row">
<span class="span">状态:</span>
<div class="right">
<el-select v-model="filter.status" placeholder="请选择" clearable class="wid100">
<el-option label="启用" :value="1" />
<el-option label="不启用" :value="0" />
</el-select>
</div>
</div>
<div class="row">
<span class="span"></span>
<div class="right"><el-button type="primary" @click="handleSearch"><el-icon><Search /></el-icon>&nbsp;筛选</el-button></div>
</div>
</div>
<el-card shadow="never">
<div class="opaBox">
<el-button type="primary" @click="handleAdd"><el-icon><Plus /></el-icon>&nbsp;新增</el-button>
<el-button type="primary" v-if="role" :disabled="!chooseList.length" @click="lte_stock = 0, showStock = true">设置预警库存</el-button>
<el-button type="warning" @click="handleExport" :loading="exportLoading"><span class="iconfont icon-daochu"></span>&nbsp;导出</el-button>
</div>
<el-table :data="tableList" style="width: 100%" border v-loading="loading" @select="select"
ref="multipleTable" @select-all="selectAll">
<el-table-column type="selection" width="55" />
<el-table-column prop="id" label="ID" width="80" align="center" />
<el-table-column prop="goods.title" label="商品名称" align="center" />
<el-table-column prop="goods.goods_code" label="商品货号" align="center" />
<el-table-column prop="title" label="规格名称" align="center" />
<el-table-column prop="sku_code" label="规格编码" align="center" />
<el-table-column label="发货模式" align="center">
<template #default="scope">
<div v-if="scope.row.warehouse">
<span v-if="scope.row.warehouse.ship_mode == 'express'">一件代发</span>
<span v-else>自营仓发</span>
</div>
</template>
</el-table-column>
<el-table-column prop="actual_inventory" label="实际库存" align="center" />
<el-table-column prop="total_lock_num" label="总锁定库存" align="center" />
<el-table-column prop="first_stage_stock" label="一阶段库存" align="center" />
<el-table-column prop="second_stage_stock" label="二阶段库存" align="center" />
<el-table-column prop="third_stage_stock" label="三阶段库存" align="center" />
<el-table-column prop="lock_in_stock" label="运营锁定库存" align="center" />
<el-table-column prop="after_sale_stock" label="售后锁定库存" align="center" />
<el-table-column prop="available_inventory" label="可售库存" align="center" />
<el-table-column prop="lte_stock" label="预警库存" align="center" />
<el-table-column prop="goods_cost" label="商品成本" align="center" />
<el-table-column prop="freight_cost" label="运费成本" align="center" />
<el-table-column prop="warehouse.name" label="仓库" align="center" />
<el-table-column label="状态" align="center">
<template #default="scope">
<span>{{ scope.row.status ? '启用' : '不启用' }}</span>
</template>
</el-table-column>
<el-table-column prop="created_at" label="添加时间" align="center" />
<el-table-column label="操作" align="center" width="150" fixed="right">
<template #default="scope">
<el-button type="primary" circle @click="handleEdit(scope.row)"><el-icon><Edit /></el-icon></el-button>
<el-tooltip effect="dark" content="上传商品到聚水潭" placement="top">
<el-button type="primary" circle @click="handleUpload(scope.row)"><el-icon><Upload /></el-icon></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<div class="page-pagination">
<el-pagination
:current-page="page"
background
layout="prev, pager, next, sizes, total"
:total="total"
:page-sizes="[10, 50, 100]"
:page-size="pageSize"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"></el-pagination>
</div>
</el-card>
<el-dialog v-model="showUpdate" width="600px" title="新增">
<el-form label-position="right" label-width="110px">
<el-form-item label="所属商品:">
<el-select v-model="itemInfo.goods_id" placeholder="请选择" clearable filterable disabled>
<el-option v-for="it in goodsList" :key="it.id" :label="it.title" :value="it.id" />
</el-select>
</el-form-item>
<el-form-item label="规格名称:">
<el-input v-model="itemInfo.title" clearable disabled></el-input>
</el-form-item>
<el-form-item label="规格编码:">
<el-input v-model="itemInfo.sku_code" clearable disabled></el-input>
</el-form-item>
<el-form-item label="赠品:">
<el-radio-group v-model="itemInfo.gift" disabled>
<el-radio :label="0">不是</el-radio>
<el-radio :label="1">是</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="总锁定库存:">
<el-input v-model="itemInfo.total_lock_num" disabled></el-input>
</el-form-item>
<el-form-item label="预警库存:">
<el-input v-model="itemInfo.lte_stock" clearable></el-input>
</el-form-item>
<el-form-item label="一阶段库存:">
<el-input v-model="itemInfo.first_stage_stock" clearable></el-input>
</el-form-item>
<el-form-item label="二阶段库存:">
<el-input v-model="itemInfo.second_stage_stock" clearable></el-input>
</el-form-item>
<el-form-item label="三阶段库存:">
<el-input v-model="itemInfo.third_stage_stock" clearable></el-input>
</el-form-item>
<el-form-item label="运营锁定库存:">
<el-input v-model="itemInfo.lock_in_stock" clearable></el-input>
</el-form-item>
<el-form-item label="售后锁定库存:">
<el-input v-model="itemInfo.after_sale_stock" clearable disabled></el-input>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showUpdate = false">取消</el-button>
<el-button type="primary" @click="commitUpdate()" :loading="opa_loading">确定</el-button>
</span>
</template>
</el-dialog>
<el-dialog v-model="showDialog" width="800px" :title="opaType == 'add' ? '新增' : '编辑' ">
<el-form label-position="right" label-width="130px">
<el-form-item label="所属商品:">
<el-select v-model="itemInfo.goods_id" placeholder="请选择" clearable filterable>
<el-option v-for="it in goodsList" :key="it.id" :label="it.title" :value="it.id" />
</el-select>
</el-form-item>
<el-form-item label="规格名称:">
<el-input v-model="itemInfo.title" clearable></el-input>
</el-form-item>
<el-form-item label="规格编码:">
<el-input v-model="itemInfo.sku_code" clearable></el-input>
</el-form-item>
<el-form-item label="仓库:">
<el-select v-model="itemInfo.warehouse_id" placeholder="请选择" clearable filterable>
<el-option v-for="it in warehouseList" :key="it.id" :label="it.name" :value="it.id" />
</el-select>
</el-form-item>
<el-form-item label="售后锁定库存:">
<el-input v-model="itemInfo.after_sale_stock" clearable></el-input>
</el-form-item>
<el-form-item label="运费类型:">
<el-radio-group v-model="itemInfo.freight_type">
<el-radio :label="1">有运费</el-radio>
<el-radio :label="3">无运费</el-radio>
<el-radio :label="2">后补</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="成本系数:">
<el-select v-model="itemInfo.cost_factor" placeholder="请选择" clearable style="width: 200px;">
<el-option label="1" :value="1" />
<el-option label="1.02" :value="1.02" />
<el-option label="1.05" :value="1.05" />
</el-select>
</el-form-item>
<el-form-item label="采购成本:">
<el-input placeholder="采购成本" v-model="itemInfo.goods_cost" clearable></el-input>
</el-form-item>
<el-form-item label="供应商结算成本:">
<el-input placeholder="供应商结算成本" v-model="itemInfo.settle_cost" clearable></el-input>
</el-form-item>
<el-form-item label="其他成本:">
<el-input placeholder="其他成本" v-model="itemInfo.other_cost" clearable></el-input>
</el-form-item>
<el-form-item label="运费成本:">
<el-input v-model="itemInfo.freight_cost" clearable></el-input>
</el-form-item>
<el-form-item label="总成本:">
<el-input placeholder="总成本" v-model="itemInfo.total_cost" clearable></el-input>
</el-form-item>
<el-form-item label="状态:">
<el-radio-group v-model="itemInfo.status">
<el-radio :label="1">启用</el-radio>
<el-radio :label="0">不启用</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="赠品:">
<el-radio-group v-model="itemInfo.gift">
<el-radio :label="0">不是</el-radio>
<el-radio :label="1">是</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="排序:">
<el-input-number v-model="itemInfo.sort" :min="0" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">取消</el-button>
<el-button type="primary" @click="commitOpa()" :loading="opa_loading">确定</el-button>
</span>
</template>
</el-dialog>
<!-- 预览图片 -->
<el-dialog v-model="picVisible" center width="800px">
<img :src="dialogImageUrl" style="max-width: 700px; margin: 0 auto;display: block;" />
</el-dialog>
<el-dialog v-model="showStock" width="600px" title="设置预警库存">
<el-form label-position="right" label-width="110px">
<el-form-item label="预警库存:">
<el-input-number v-model="lte_stock" clearable></el-input-number>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showStock = false">取消</el-button>
<el-button type="primary" @click="commitStock()">确定</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script>
import { onMounted, reactive, toRefs } from "vue"
import { get, post } from "@/api/request"
import { Search, Plus, Edit, ZoomIn, Delete, Upload } from '@element-plus/icons'
import { ElMessage, ElMessageBox } from 'element-plus'
import { parseErrors } from 'components/common'
export default {
components: {
Search, Plus, Edit, ZoomIn, Delete, Upload
},
setup() {
const data = reactive({
filter: {},
tableList: [],
page: 1,
pageSize: 10,
total: 0,
loading: false,
opaType: '',
showDialog: false,
opa_loading: false,
warehouseList: [],
goodsList: [],
itemInfo: {},
role: localStorage.getItem('roleName') == '爆品运营',
goodsIds: [],
brandIds: [],
brandList: [],
chooseList: [],
lte_stock: 0,
showStock: false,
showUpdate: false,
exportLoading: false
})
function handleSearch() {
data.page = 1
fetchData()
}
const fetchData = () => {
data.loading = true
let params = {
page: data.page,
pageSize: data.pageSize,
service_id: data.service_id,
...data.filter,
brand_ids: data.brandIds.join(','),
goods_ids: data.goodsIds.join(',')
}
get(`/api/goods-skus`, params).then((res) => {
data.tableList = res.data
data.total = res.meta.total
data.loading = false
}).catch(() => {
data.loading = false
})
}
const handleExport = () => {
data.exportLoading = true
let params = {
page: data.page,
pageSize: data.pageSize,
service_id: data.service_id,
...data.filter,
brand_ids: data.brandIds.join(','),
goods_ids: data.goodsIds.join(','),
export: 1
}
get(`/api/goods-skus`, params, 'blob').then((res) => {
downLoadXls(res)
ElMessage({ type: "success", message: "导出成功!" })
data.exportLoading = false
}).catch(() => {
data.exportLoading = false
})
}
function downLoadXls(response) {
const content = response
const blob = new Blob([content])
const fileName = `商品规格.xlsx`
if ('download' in document.createElement('a')) {
const elink = document.createElement('a')
elink.download = fileName
elink.style.display = 'none'
elink.href = URL.createObjectURL(blob)
document.body.appendChild(elink)
elink.click()
URL.revokeObjectURL(elink.href)
document.body.removeChild(elink)
} else {
navigator.msSaveBlob(blob, fileName)
}
}
function handleCurrentChange(e) {
data.page = e
fetchData()
}
function handleSizeChange(e) {
data.page = 1
data.pageSize = e
fetchData()
}
function handleAdd() {
data.opaType = 'add'
data.itemInfo = {
status: 1,
sort: 0,
gift: 0,
freight_type: 1,
cost_factor: ''
}
data.showDialog = true
}
function handleEdit(item) {
data.opaType = 'edit'
data.itemInfo = JSON.parse(JSON.stringify(item))
if(data.role) {
data.showUpdate = true
} else {
data.showDialog = true
}
}
function handleUpload(row) {
ElMessageBox.confirm('确定操作上传吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
post(`/api/goods/sku/jstUpload/${row.id}`, {}, 'PUT').then(() => {
ElMessage({ type: 'success', message: '操作成功' })
})
})
}
function commitOpa() {
if(!data.itemInfo.goods_cost || data.itemInfo.goods_cost * 1 == 0) {
// return ElMessage({ type: 'info', message: '商品成本必填' })
}
data.opa_loading = true
let params = {
...data.itemInfo
}
if(data.opaType == 'add') {
post(`/api/goods-skus`, params).then(() => {
data.page = 1
fetchData()
ElMessage({ type: 'success', message: '新增成功' })
data.showDialog = false
data.opa_loading = false
}).catch((err) => {
data.opa_loading = false
parseErrors(err.response.data.errors)
})
} else {
post(`/api/goods-skus/${data.itemInfo.id}`, params, 'PUT').then(() => {
fetchData()
ElMessage({ type: 'success', message: '编辑成功' })
data.showDialog = false
data.opa_loading = false
}).catch((err) => {
data.opa_loading = false
parseErrors(err.response.data.errors)
})
}
}
function commitUpdate() {
data.opa_loading = true
let params = {
lock_in_stock: data.itemInfo.lock_in_stock,
first_stage_stock: data.itemInfo.first_stage_stock,
second_stage_stock: data.itemInfo.second_stage_stock,
third_stage_stock: data.itemInfo.third_stage_stock,
lte_stock: data.itemInfo.lte_stock,
goods_cost: data.itemInfo.goods_cost,
freight_cost: data.itemInfo.freight_cost
}
post(`/api/goods/sku/lockInStock/${data.itemInfo.id}`, params, 'PUT').then(() => {
fetchData()
ElMessage({ type: 'success', message: '编辑成功' })
data.showUpdate = false
data.opa_loading = false
}).catch((err) => {
data.opa_loading = false
parseErrors(err.response.data.errors)
})
}
function getWarehouseList() {
get(`/api/all/warehouses`).then((res) => {
data.warehouseList = res.data
})
}
function getGoodsList() {
get(`/api/all/goods`).then((res) => {
data.goodsList = res.data
})
}
function getBrandList() {
get(`/api/all/brands`).then((res) => {
data.brandList = res.data
})
}
function select(e) {
data.chooseList = e
}
function selectAll(e) {
data.chooseList = e
}
function commitStock() {
let ids = []
data.chooseList.forEach((it) => {
ids.push(it.id)
})
let params = {
ids: ids,
lte_stock: data.lte_stock
}
post('/api/goods/sku/batchUpdateLetStock', params, 'PUT').then((res) => {
ElMessage({ type: 'success', message: '操作成功' })
data.chooseList = []
data.showStock = false
fetchData()
})
}
onMounted(() => {
fetchData()
getWarehouseList()
getGoodsList()
getBrandList()
})
return {
...toRefs(data),
handleSearch,
handleCurrentChange,
handleSizeChange,
handleExport,
fetchData,
handleAdd,
handleEdit,
handleUpload,
commitOpa,
commitUpdate,
getWarehouseList,
getGoodsList,
getBrandList,
select,
selectAll,
commitStock
}
}
}
</script>
<style lang="scss" scoped>
.searchBox{
display: flex;
flex-wrap: wrap;
background-color: #fff;
padding: 15px 0 0 0;
border-radius: 4px;
margin-bottom: 15px;
.row{
display: flex;
align-items: center;
width: 20%;
box-sizing: border-box;
margin-bottom: 15px;
&.row1{
width: 25%;
}
.span{
display: block;
width: 80px;
font-size: 14px;
text-align: right;
box-sizing: border-box;
}
.right{
width: calc(100% - 100px);
display: flex;
align-items: center;
}
.wid100{
width: 100%;
}
}
}
.opaBox{
margin-bottom: 15px;
}
.imgBox{
display: flex;
flex-wrap: wrap;
.el-image{
width: 60px;
height: 60px;
border-radius: 4px;
margin-right: 10px;
}
}
.skuBox{
border: 1px solid #e5e5e5;
border-radius: 5px;
padding: 15px 0;
margin-bottom: 15px;
background-color: #f3f3f3;
.tit{
padding-left: 40px;
font-weight: 600;
font-size: 15px;
margin-bottom: 15px;
}
}
</style>