新增需求及bug修复

feature/0423
fanfan 10 months ago
parent b45d5bd5bb
commit 9c0da9844a
  1. 12
      src/api/goods/index.js
  2. 35
      src/components/Table/GoodsItem/GoodsItem.vue
  3. 6
      src/config/router.config.js
  4. 98
      src/views/goods/Index.vue
  5. 170
      src/views/goods/modify.vue

@ -11,6 +11,7 @@ const api = {
delete: '/goods/delete', delete: '/goods/delete',
state: '/goods/state', state: '/goods/state',
dataList:'store/platformList', dataList:'store/platformList',
exportData:'/goods/import',
} }
// 列表记录 // 列表记录
@ -107,3 +108,14 @@ export function getDataList (data) {
data: data data: data
}) })
} }
/**
* 导出
* @param {*} data
*/
export function exportData (data) {
return axios({
url: api.exportData,
method: 'post',
data: data
})
}

@ -6,17 +6,19 @@
</div> </div>
<div class="in-right"> <div class="in-right">
<!-- 商品名称 --> <!-- 商品名称 -->
<p class="title twoline-hide" :style="{ width: `${dataObj.titleWidth}px` }">{{ dataObj.title }}</p> <a-tooltip placement="topLeft" :title="dataObj.title">
<p class="title twoline-hide" :style="{ width: `${dataObj.titleWidth}px` }">{{ dataObj.title }}</p></a-tooltip
>
<!-- 副标题 --> <!-- 副标题 -->
<p <p v-if="isEmpty(dataObj.goodsProps)" class="subtitle" :class="{ 'c-p': subTitleColor }">
v-if="isEmpty(dataObj.goodsProps)" <a-tooltip placement="topLeft" :title="dataObj.subtitle"> {{ dataObj.subtitle }}</a-tooltip>
class="subtitle" </p>
:class="{ 'c-p': subTitleColor }"
>{{ dataObj.subtitle }}</p>
<!-- 商品规格 --> <!-- 商品规格 -->
<div v-else class="goods-props clearfix"> <div v-else class="goods-props clearfix">
<div class="goods-props-item" v-for="(props, idx) in dataObj.goodsProps" :key="idx"> <div class="goods-props-item" v-for="(props, idx) in dataObj.goodsProps" :key="idx">
<span>{{ props.value.name }}</span> <a-tooltip placement="topLeft" :title="props.value.name">
<span>{{ props.value.name }}</span></a-tooltip
>
</div> </div>
</div> </div>
</div> </div>
@ -34,28 +36,29 @@ export default {
// //
data: PropTypes.object.def({}), data: PropTypes.object.def({}),
// //
subTitleColor: PropTypes.bool.def(false) subTitleColor: PropTypes.bool.def(false),
}, },
computed: { computed: {
dataObj() { dataObj() {
return Object.assign({ return Object.assign(
{
image: '', image: '',
imageAlt: '', imageAlt: '',
title: '', title: '',
subtitle: '', subtitle: '',
goodsProps: [], goodsProps: [],
titleWidth: 200 titleWidth: 200,
}, this.$props.data) },
} this.$props.data
)
},
}, },
data() { data() {
return { return {
isEmpty isEmpty,
} }
}, },
methods: { methods: {},
}
} }
</script> </script>

@ -252,6 +252,12 @@ export const asyncRouterMap = [
meta: { title: '编辑商品', keepAlive: false, permission: ['/goods/update'] }, meta: { title: '编辑商品', keepAlive: false, permission: ['/goods/update'] },
hidden: true, hidden: true,
}, },
{
path: '/goods/modify',
component: () => import(/* webpackChunkName: "goods" */ '@/views/goods/modify'),
meta: { title: '批量修改', keepAlive: false, permission: ['/goods/modify'] },
hidden: true,
},
{ {
path: '/goods/copy', path: '/goods/copy',
component: () => import(/* webpackChunkName: "goods" */ '@/views/goods/Copy'), component: () => import(/* webpackChunkName: "goods" */ '@/views/goods/Copy'),

@ -34,6 +34,22 @@
</a-select-option> </a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
<a-form-item label="商品价格">
<div style="display: flex" class="goodsType">
<a-input v-decorator="['goods_price_min']" placeholder="请输入" /><span style="padding: 0 3px 0 5px"
>-</span
>
<a-input style="margin-left: 5px" v-decorator="['goods_price_max']" placeholder="请输入" />
</div>
</a-form-item>
<a-form-item label="利润率">
<div style="display: flex" class="goodsType">
<a-input v-decorator="['profit_rate_min']" placeholder="请输入" /><span style="padding: 0 3px 0 5px"
>-</span
>
<a-input style="margin-left: 5px" v-decorator="['profit_rate_max']" placeholder="请输入" />
</div>
</a-form-item>
<a-form-item class="search-btn"> <a-form-item class="search-btn">
<a-button type="primary" icon="search" html-type="submit">搜索</a-button> <a-button type="primary" icon="search" html-type="submit">搜索</a-button>
</a-form-item> </a-form-item>
@ -59,6 +75,12 @@
@click="handleImport()" @click="handleImport()"
>批量导入</a-button >批量导入</a-button
> >
<a-button style="background-color: #501212; color: #fff; border: none" @click="handleExport(selectedRowKeys)"
>导出</a-button
>
<a-button class="fl-l" style="background-color: #f0baae; border: none" type="primary" @click="handleModify()"
>批量修改</a-button
>
<div v-if="selectedRowKeys.length" class="button-group"> <div v-if="selectedRowKeys.length" class="button-group">
<a-button-group class="ml-10"> <a-button-group class="ml-10">
<a-button v-action:status icon="arrow-up" @click="handleUpdateStatus(selectedRowKeys, true)">上架</a-button> <a-button v-action:status icon="arrow-up" @click="handleUpdateStatus(selectedRowKeys, true)">上架</a-button>
@ -87,8 +109,11 @@
</a> </a>
</span> </span>
<!-- 商品名称 --> <!-- 商品名称 -->
<span slot="goods_name" slot-scope="text"> <span slot="goods_name" slot-scope="text, item">
<p class="twoline-hide" style="width: 270px">{{ text }}</p> <a v-if="item.link" :href="item.link" style="white-space: pre-wrap; color: #1890ff" target="_blank">{{
text
}}</a>
<p style="white-space: pre-wrap" v-else>{{ text }}</p>
</span> </span>
<!-- 商品状态 --> <!-- 商品状态 -->
<span slot="status" slot-scope="text, item"> <span slot="status" slot-scope="text, item">
@ -119,70 +144,91 @@
import * as GoodsApi from '@/api/goods' import * as GoodsApi from '@/api/goods'
import { ContentHeader, STable } from '@/components' import { ContentHeader, STable } from '@/components'
import CategoryModel from '@/common/model/Category' import CategoryModel from '@/common/model/Category'
import store from '../../store'
// //
const columns = [ const columns = [
{ {
title: '商品ID', title: '商品ID',
dataIndex: 'goods_id', dataIndex: 'goods_id',
width: '100px', width: '80px',
sorter: true, sorter: true,
}, },
{ {
title: '渠道', title: '渠道',
width: '60px', width: '50px',
dataIndex: 'channel_name', dataIndex: 'channel_name',
}, },
{ {
title: '商品编号', title: '商品编号',
dataIndex: 'goods_no', dataIndex: 'goods_no',
width: '120px', width: '100px',
}, },
{ {
title: '商品图片', title: '商品图片',
width: '100px',
dataIndex: 'goods_image', dataIndex: 'goods_image',
scopedSlots: { customRender: 'goods_image' }, scopedSlots: { customRender: 'goods_image' },
}, },
{ {
title: '商品名称', title: '商品名称',
dataIndex: 'goods_name', dataIndex: 'goods_name',
width: '302px', width: '260px',
scopedSlots: { customRender: 'goods_name' }, scopedSlots: { customRender: 'goods_name' },
}, },
{ {
title: '商品价格', title: '商品价格',
width: '90px',
dataIndex: 'goods_price_min', dataIndex: 'goods_price_min',
scopedSlots: { customRender: 'goods_price_min' }, scopedSlots: { customRender: 'goods_price_min' },
sorter: true, sorter: true,
}, },
{ {
title: '成本价', title: '成本价',
dataIndex: 'cost_price', width: '80px',
scopedSlots: { customRender: 'cost_price' }, dataIndex: 'cost_price_min',
scopedSlots: { customRender: 'cost_price_min' },
sorter: true,
},
{
title: '利润',
width: '80px',
dataIndex: 'profit',
sorter: true, sorter: true,
}, },
{
title: '利润率',
width: '80px',
dataIndex: 'profit_rate',
sorter: true,
scopedSlots: { customRender: 'profit_rate' },
},
{ {
title: '总销量', title: '总销量',
width: '80px',
dataIndex: 'sales_actual', dataIndex: 'sales_actual',
sorter: true, sorter: true,
}, },
{ {
title: '库存总量', title: '库存总量',
width: '90px',
dataIndex: 'stock_total', dataIndex: 'stock_total',
sorter: true, sorter: true,
}, },
{ {
title: '状态', title: '状态',
width: '60px',
dataIndex: 'status', dataIndex: 'status',
scopedSlots: { customRender: 'status' }, scopedSlots: { customRender: 'status' },
}, },
{ {
title: '排序', title: '排序',
width: '80px',
dataIndex: 'sort', dataIndex: 'sort',
sorter: true, sorter: true,
}, },
{ {
title: '添加时间', title: '添加时间',
width: '180px', width: '130px',
dataIndex: 'create_time', dataIndex: 'create_time',
sorter: true, sorter: true,
}, },
@ -265,6 +311,40 @@ export default {
}, },
}, },
methods: { methods: {
//
handleModify() {
this.$router.push('/goods/modify')
},
//
handleExport(goodsIds) {
const formData = this.searchForm.getFieldsValue()
this.isLoading = true
var index = window.publicConfig.BASE_API.indexOf('?')
var resolve = window.publicConfig.BASE_API.substring(0, index)
let params =
'&goodsName=' +
(formData.goodsName || '') +
'&goodsNo=' +
(formData.goodsNo || '') +
'&goods_price_max=' +
(formData.goods_price_max || '') +
'&goods_price_min=' +
(formData.goods_price_min || '') +
'&profit_rate_max=' +
(formData.profit_rate_max || '') +
'&profit_rate_min=' +
(formData.profit_rate_min || '') +
'&categoryId=' +
(this.queryParam.categoryId || '') +
'&channel=' +
this.queryParam.channel +
'&goodsIds=' +
goodsIds +
'&Access-Token=' +
store.getters.token
console.log(params)
window.open(resolve + '?s=/admin/goods/export' + params)
},
getChannel(val) { getChannel(val) {
this.queryParam.channel = val this.queryParam.channel = val
}, },

@ -0,0 +1,170 @@
<template>
<a-card :bordered="false">
<div class="card-title">
<span>{{ $route.meta.pageTitle }}</span>
</div>
<a-spin :spinning="isLoading">
<div class="container">
<div class="upload-dragger">
<a-upload-dragger
accept=".xls, .xlsx"
:multiple="false"
:fileList="fileList"
:showUploadList="false"
:beforeUpload="beforeUpload"
:remove="handleRemove"
>
<p class="ant-upload-drag-icon">
<a-icon type="cloud-upload" />
</p>
<div v-if="!fileList.length">
<p class="ant-upload-text">点击选择文件或者将文件拖拽至此区域</p>
<p class="ant-upload-hint">仅支持.xls, .xlsx 格式的excel文件限2M以内</p>
</div>
<div v-else>
<p class="ant-upload-text">
<span>{{ fileList[0].name }}</span>
<a class="ml-10" href="javascript:void(0);" @click.stop="handleRemove(fileList[0])">删除</a>
</p>
<a-button class="mt-20" type="primary" @click.stop="onFormSubmit">立即导入</a-button>
</div>
</a-upload-dragger>
</div>
<div class="import-explain">
<h2 class="title">导入说明</h2>
<a-timeline class="timeline">
<a-timeline-item class="timeline-item">
<p class="name">使用模板</p>
<ul class="content">
<li class="content-li">
<span>模板中最多不能超过500个商品如超过500个商品请分批导入</span>
</li>
<li class="content-li">
<span>模板中的字段含义以及填写规则可查看模板文件中标题栏的标注</span>
</li>
</ul>
</a-timeline-item>
</a-timeline>
</div>
<!-- 文件选择器 -->
<FilesModal ref="FilesModal" :multiple="true" :maxNum="10" :actions="['delete', 'move', 'copyIds']" />
</div>
</a-spin>
</a-card>
</template>
<script>
import * as GoodsApi from '@/api/goods'
import { FilesModal } from '@/components/Modal'
export default {
components: {
FilesModal,
},
data() {
return {
//
isLoading: false,
//
fileList: [],
// 2m
uploadSizeLimit: '2',
}
},
//
created() {},
methods: {
//
handleSelectImage() {
this.$refs.FilesModal.show()
},
//
beforeUpload(file) {
const fileSizeMb = file.size / 1024 / 1024
if (fileSizeMb > this.uploadSizeLimit) {
this.$message.error(`上传的文件大小不能超出${this.uploadSizeLimit}MB`)
return false
}
this.fileList = [file]
return false
},
//
handleRemove(file) {
const { fileList } = this
const index = fileList.indexOf(file)
if (index > -1) {
fileList.splice(index, 1)
}
},
// api
onFormSubmit() {
const { fileList } = this
const formData = new FormData()
formData.append('file', fileList[0])
this.isLoading = true
GoodsApi.exportData(formData)
.then((result) => {
this.fileList = []
this.$message.success(result.message, 1.8)
setTimeout(() => this.$router.push('/goods/import/list'), 1500)
})
.finally(() => (this.isLoading = false))
},
},
}
</script>
<style lang="less" scoped>
/deep/.ant-upload.ant-upload-drag .ant-upload {
padding: 82px 0;
}
/deep/span.ant-radio + * {
color: #7a7a7a;
}
.card-title {
display: flex;
justify-content: space-between;
}
.container {
width: 1000px;
margin: 0 auto;
}
.timeline {
color: #111;
}
.import-explain {
margin-top: 40px;
.title {
margin-bottom: 25px;
font-weight: bold;
font-size: 15px;
}
.name {
font-size: 14px;
margin-bottom: 8px;
}
.timeline-item {
padding-bottom: 20px;
}
.content {
color: #7a7a7a;
padding-left: 20px;
.content-li {
list-style-type: disc;
margin-bottom: 5px;
}
}
}
</style>
Loading…
Cancel
Save