583 lines
19 KiB
Vue
583 lines
19 KiB
Vue
<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> 筛选</el-button></div>
|
||
</div>
|
||
</div>
|
||
|
||
<el-card shadow="never">
|
||
<div class="opaBox">
|
||
<el-button type="primary" @click="handleAdd"><el-icon><Plus /></el-icon> 新增</el-button>
|
||
<!-- <el-button type="warning" @click="handleExport"><span class="iconfont icon-daochu"></span> 导出</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>
|
||
<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" />
|
||
<el-button type="danger" v-if="opaType != 'view'" @click="handleSkuDelete(i)" size="small"><el-icon><Delete /></el-icon> 删除</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>
|