583 lines
19 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-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.goods_code" class="wid100" clearable></el-input></div>
</div>
<div class="row">
<span class="span">商品品牌:</span>
<div class="right">
<el-select v-model="filter.brand_id" placeholder="请选择" clearable class="wid100" filterable>
<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-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-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="warning" @click="handleExport"><span class="iconfont icon-daochu"></span>&nbsp;导出</el-button> -->
</div>
<el-table :data="goodsList" style="width: 100%" border v-loading="loading">
<el-table-column prop="id" label="ID" width="80" align="center" />
<el-table-column label="商品名称" min-width="150">
<template #default="scope">
<div class="imgBox">
<div v-if="scope.row.images && scope.row.images[0]">
<el-image :z-index="9999" :src="scope.row.images[0]" :hide-on-click-modal="true" :preview-src-list="[scope.row.images[0]]" fit="cover" :preview-teleported="true" />
</div>
<div>{{ scope.row.title }}</div>
</div>
</template>
</el-table-column>
<el-table-column prop="brand.name" label="商品品牌" align="center" />
<el-table-column prop="goods_code" label="商品编码" align="center" />
<el-table-column prop="warehouse.name" label="仓库" align="center" />
<el-table-column prop="introduce" label="说明" align="center" />
<el-table-column prop="admin_user.username" 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="sort" label="排序" align="center" />
<el-table-column prop="created_at" label="添加时间" align="center" />
<el-table-column label="操作" align="center" width="150">
<template #default="scope">
<el-button size="small" type="primary" circle @click="handleEdit(scope.row)"><el-icon><Edit /></el-icon></el-button>
<el-button size="small" circle @click="handleView(scope.row)"><el-icon><ZoomIn /></el-icon></el-button>
<el-button size="small" type="danger" circle @click="handleRemove(scope.row)"><el-icon><Delete /></el-icon></el-button>
</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="showDialog" width="900px" :title="opaType == 'view' ? '查看详情' : opaType == 'add' ? '新增' : '编辑' " @close="resetForm">
<el-form label-position="right" label-width="110px" :disabled="opaType == 'view'">
<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.goods_code" clearable></el-input>
</el-form-item>
<el-form-item label="商品品牌:">
<el-select v-model="itemInfo.brand_id" placeholder="请选择" clearable>
<el-option v-for="it in brandList" :key="it.id" :label="it.name" :value="it.id" />
</el-select>
</el-form-item>
<el-form-item label="商品图片:">
<el-upload
ref="uploadRef"
action="/api/upload/img"
:headers="headers"
:on-remove="handleRemoveImg"
:on-error="handleUploadError"
:on-preview="handlePreview"
:on-success="handleSuccess"
:file-list="fileList"
:show-file-list="true"
list-type="picture-card"
accept=".png,.jpg,.gif">
<el-icon><Plus /></el-icon>
</el-upload>
</el-form-item>
<el-form-item label="仓库:">
<el-select v-model="itemInfo.warehouse_id" placeholder="请选择" clearable>
<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.introduce" type="textarea" :rows="4"></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-input-number v-model="itemInfo.sort" :min="0" />
</el-form-item>
</el-form>
<div v-for="(item, i) in skusList" :key="i" class="skuBox">
<div class="tit">规格{{ i + 1 }}</div>
<el-form label-width="130px" :inline="true" :disabled="opaType == 'view'">
<el-form-item label="规格名称:">
<el-input placeholder="规格名称" v-model="item.title" clearable style="width: 250px;"></el-input>
</el-form-item>
<el-form-item label="规格编码:">
<el-input placeholder="规格编码" v-model="item.sku_code" clearable style="width: 180px;" :disabled="item.mx_goods_skus && item.mx_goods_skus.length"></el-input>&nbsp;&nbsp;
<el-button type="text" @click="openCode(item, i)" v-if="item.mx_goods_skus && item.mx_goods_skus.length" size="mini">修改编码</el-button>
</el-form-item>
<el-form-item label="仓库:">
<el-select v-model="item.warehouse_id" placeholder="请选择" clearable style="width: 250px;">
<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-radio-group v-model="item.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="item.freight_type" style="width: 250px;">
<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="item.cost_factor" placeholder="请选择" clearable style="width: 250px;">
<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="item.goods_cost" clearable style="width: 250px;"></el-input>
</el-form-item>
<el-form-item label="供应商结算成本:">
<el-input placeholder="供应商结算成本" v-model="item.settle_cost" clearable style="width: 250px;"></el-input>
</el-form-item>
<el-form-item label="其他成本:">
<el-input placeholder="其他成本" v-model="item.other_cost" clearable style="width: 250px;"></el-input>
</el-form-item>
<el-form-item label="运费成本:">
<el-input placeholder="运费成本" v-model="item.freight_cost" clearable style="width: 250px;"></el-input>
</el-form-item>
<el-form-item label="总成本:">
<el-input placeholder="总成本" v-model="item.total_cost" clearable style="width: 250px;"></el-input>
</el-form-item>
<el-form-item label="赠品:">
<el-radio-group v-model="item.gift" style="width: 250px;">
<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="item.sort" :min="0" />&nbsp;&nbsp;&nbsp;&nbsp;
<el-button type="danger" v-if="opaType != 'view'" @click="handleSkuDelete(i)" size="small"><el-icon><Delete /></el-icon>&nbsp;删除</el-button>
</el-form-item>
</el-form>
</div>
<el-form label-position="right" label-width="110px" v-if="opaType != 'view'">
<el-form-item label="">
<el-button type="success" @click="toAddSku()">增加规格</el-button>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer" v-if="opaType != 'view'">
<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="showCode" width="400px" title="修改编码">
<el-form label-width="70px">
<el-form-item label="编码:">
<el-input placeholder="" v-model="new_code" clearable></el-input>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showCode = false">取消</el-button>
<el-button type="primary" @click="commitCode()">确定</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script>
import { onMounted, reactive, toRefs, ref, nextTick } from "vue"
import { get, post } from "@/api/request"
import { Search, Plus, Edit, ZoomIn, Delete } from '@element-plus/icons'
import { ElMessage, ElMessageBox } from 'element-plus'
import { parseErrors } from 'components/common'
export default {
components: {
Search, Plus, Edit, ZoomIn, Delete
},
setup() {
const data = reactive({
filter: {},
goodsList: [],
page: 1,
pageSize: 10,
total: 0,
loading: false,
opaType: '',
showDialog: false,
opa_loading: false,
headers: {
Authorization: localStorage.getItem('token'),
'Shop-Id': localStorage.getItem('shopId') || ''
},
picVisible: false,
dialogImageUrl: '',
fileList: [],
brandList: [],
warehouseList: [],
itemInfo: {},
skusList: [],
tempIndex: 0,
tempId: 0,
showCode: false,
new_code: ''
})
function handleSearch() {
data.page = 1
fetchData()
}
const fetchData = () => {
data.loading = true
let params = {
page: data.page,
pageSize: data.pageSize,
...data.filter
}
get(`/api/goods`, params).then((res) => {
data.goodsList = res.data
data.total = res.meta.total
data.loading = false
}).catch(() => {
data.loading = false
})
}
function handleCurrentChange(e) {
data.page = e
fetchData()
}
function handleSizeChange(e) {
data.page = 1
data.pageSize = e
fetchData()
}
function handleAdd() {
data.opaType = 'add'
data.fileList = []
data.skusList = []
data.itemInfo = {
status: 1,
sort: 0
}
data.showDialog = true
}
function handleEdit(item) {
data.opaType = 'edit'
get(`/api/goods/${item.id}`).then((res) => {
data.itemInfo = res.data
data.fileList = []
if(res.data.images && res.data.images.length) {
res.data.images.forEach((it) => {
data.fileList.push({url: it})
})
}
data.skusList = res.data.skus || []
data.showDialog = true
})
}
function handleView(item) {
data.opaType = 'view'
get(`/api/goods/${item.id}`).then((res) => {
data.itemInfo = res.data
data.fileList = []
if(res.data.images && res.data.images.length) {
res.data.images.forEach((it) => {
data.fileList.push({url: it})
})
}
data.skusList = res.data.skus || []
data.showDialog = true
})
}
function commitOpa() {
for (let index = 0; index < data.skusList.length; index++) {
let item = data.skusList[index]
item.freight_cost = item.freight_cost || 0
if(!item.goods_cost || item.goods_cost * 1 == 0) {
// return ElMessage({ type: 'info', message: '商品成本必填' })
}
}
data.opa_loading = true
let imgs = []
data.fileList.forEach((it) => {
imgs.push(it.url)
})
let params = {
...data.itemInfo
}
params.skus = data.skusList || []
params.images = imgs
if(data.opaType == 'add') {
post(`/api/goods`, 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/${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 handleRemoveImg(res, ress) {
data.fileList = []
}
function handleUploadError(err) {
if(JSON.parse(err.message) && JSON.parse(err.message).message) {
ElMessage({ type: 'error', message: JSON.parse(err.message).message })
}
}
function handlePreview(File) {
data.dialogImageUrl = File.url
data.picVisible = true
}
function handleSuccess(res) {
data.fileList = [{url: res.data.link}]
}
function handleSkuDelete(index) {
data.skusList.splice(index, 1)
}
const uploadRef = ref(null)
const resetForm = () => {
data.fileList = []
nextTick(() => {
uploadRef.value.clearFiles()
})
}
function toAddSku() {
let sku = {
title: '',
sku_code: '',
warehouse_id: data.itemInfo.warehouse_id || '',
status: 1,
sort: 0,
gift: 0,
settle_cost: 0,
total_cost: 0,
goods_cost: 0,
other_cost: 0,
freight_cost: 0,
freight_type: 1,
cost_factor: 1
}
data.skusList.push(sku)
}
function handleRemove(item) {
ElMessageBox.confirm('确定要删除当前数据吗?', '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
post(`/api/goods/${item.id}`, {}, 'DELETE').then(() => {
getGroupList()
ElMessage({ type: 'success', message: '删除成功' })
})
})
}
function getBrandList() {
get(`/api/all/brands`).then((res) => {
data.brandList = res.data
})
}
function getWarehouseList() {
get(`/api/all/warehouses`).then((res) => {
data.warehouseList = res.data
})
}
function openCode(row, i) {
data.tempIndex = i
data.tempId = row.id
data.new_code = ''
data.showCode = true
}
function commitCode() {
if(!data.new_code) {
return ElMessage.info('请输入编码')
}
let params = {
skuId: data.tempId,
skuCode: data.new_code
}
post(`/api/goods/sku/changeCode`, params).then((res) => {
data.skusList[data.tempIndex].sku_code = data.new_code
data.showCode = false
ElMessage.success('修改成功')
})
}
onMounted(() => {
fetchData()
getBrandList()
getWarehouseList()
})
return {
uploadRef,
...toRefs(data),
handleSearch,
handleCurrentChange,
handleSizeChange,
fetchData,
handleAdd,
handleEdit,
commitOpa,
handleRemove,
handleUploadError,
handlePreview,
handleSuccess,
handleView,
handleSkuDelete,
resetForm,
toAddSku,
handleRemoveImg,
getBrandList,
getWarehouseList,
openCode,
commitCode
}
}
}
</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);
}
.wid100{
width: 100%;
}
}
}
.opaBox{
margin-bottom: 15px;
}
.imgBox{
display: flex;
align-items: center;
.el-image{
width: 70px;
height: 70px;
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>