Commit 41ad7911f5fdf544106a3ca6b5ac7edf37856359

Authored by wuxw
1 parent cc7e0d1a

开发 admin 营销功能

src/api/market/marketGoodsItemManageApi.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +
  3 +// 获取营销商品列表
  4 +export function listMarketGoodsItem(params) {
  5 + return new Promise((resolve, reject) => {
  6 + request({
  7 + url: '/marketGoods.listMarketGoodsItem',
  8 + method: 'get',
  9 + params
  10 + }).then(response => {
  11 + const res = response.data
  12 + if (res.code == 0) {
  13 + resolve(res)
  14 + } else {
  15 + reject(new Error(res.msg || '获取营销商品列表失败'))
  16 + }
  17 + }).catch(error => {
  18 + reject(error)
  19 + })
  20 + })
  21 +}
  22 +
  23 +// 添加营销商品
  24 +export function saveMarketGoodsItem(data) {
  25 + return new Promise((resolve, reject) => {
  26 + request({
  27 + url: '/marketGoods.saveMarketGoodsItem',
  28 + method: 'post',
  29 + data
  30 + }).then(response => {
  31 + const res = response.data
  32 + if (res.code == 0) {
  33 + resolve(res)
  34 + } else {
  35 + reject(new Error(res.msg || '添加营销商品失败'))
  36 + }
  37 + }).catch(error => {
  38 + reject(error)
  39 + })
  40 + })
  41 +}
  42 +
  43 +// 更新营销商品
  44 +export function updateMarketGoodsItem(data) {
  45 + return new Promise((resolve, reject) => {
  46 + request({
  47 + url: '/marketGoods.updateMarketGoodsItem',
  48 + method: 'post',
  49 + data
  50 + }).then(response => {
  51 + const res = response.data
  52 + if (res.code == 0) {
  53 + resolve(res)
  54 + } else {
  55 + reject(new Error(res.msg || '更新营销商品失败'))
  56 + }
  57 + }).catch(error => {
  58 + reject(error)
  59 + })
  60 + })
  61 +}
  62 +
  63 +// 删除营销商品
  64 +export function deleteMarketGoodsItem(data) {
  65 + return new Promise((resolve, reject) => {
  66 + request({
  67 + url: '/marketGoods.deleteMarketGoodsItem',
  68 + method: 'post',
  69 + data
  70 + }).then(response => {
  71 + const res = response.data
  72 + if (res.code == 0) {
  73 + resolve(res)
  74 + } else {
  75 + reject(new Error(res.msg || '删除营销商品失败'))
  76 + }
  77 + }).catch(error => {
  78 + reject(error)
  79 + })
  80 + })
  81 +}
0 \ No newline at end of file 82 \ No newline at end of file
src/components/market/AddMarketGoodsItem.vue 0 → 100644
  1 +<template>
  2 + <el-dialog :title="$t('common.add')" :visible.sync="visible" width="50%" @close="handleClose">
  3 + <el-form ref="form" :model="form" :rules="rules" label-width="120px">
  4 + <el-form-item :label="$t('marketGoodsItem.table.prodName')" prop="prodName">
  5 + <el-input v-model="form.prodName" :placeholder="$t('marketGoodsItem.table.prodName')" />
  6 + </el-form-item>
  7 + <el-form-item :label="$t('marketGoodsItem.table.prodDesc')" prop="prodDesc">
  8 + <el-input v-model="form.prodDesc" type="textarea" :placeholder="$t('marketGoodsItem.table.prodDesc')" />
  9 + </el-form-item>
  10 + <el-form-item :label="$t('marketGoodsItem.table.picUrl')" prop="picUrl">
  11 + <upload-image-url ref="uploadImage" :image-count="1" @notifyUploadCoverImage="handleImageChange" />
  12 + </el-form-item>
  13 + <el-form-item :label="$t('marketGoodsItem.table.picLink')" prop="picLink">
  14 + <el-input v-model="form.picLink" :placeholder="$t('marketGoodsItem.table.picLink')" />
  15 + </el-form-item>
  16 + <el-form-item :label="$t('marketGoodsItem.table.price')" prop="price">
  17 + <el-input v-model="form.price" :placeholder="$t('marketGoodsItem.table.price')" />
  18 + </el-form-item>
  19 + <el-form-item :label="$t('marketGoodsItem.table.shopName')" prop="shopName">
  20 + <el-input v-model="form.shopName" :placeholder="$t('marketGoodsItem.table.shopName')" />
  21 + </el-form-item>
  22 + <el-form-item :label="$t('marketGoodsItem.table.sort')" prop="sort">
  23 + <el-input-number v-model="form.sort" :min="1" :placeholder="$t('marketGoodsItem.table.sort')" />
  24 + </el-form-item>
  25 + </el-form>
  26 + <div slot="footer" class="dialog-footer">
  27 + <el-button @click="visible = false">{{ $t('common.cancel') }}</el-button>
  28 + <el-button type="primary" @click="handleSubmit">{{ $t('common.save') }}</el-button>
  29 + </div>
  30 + </el-dialog>
  31 +</template>
  32 +
  33 +<script>
  34 +import { saveMarketGoodsItem } from '@/api/market/marketGoodsItemManageApi'
  35 +import UploadImageUrl from '@/components/upload/UploadImageUrl'
  36 +
  37 +export default {
  38 + name: 'AddMarketGoodsItem',
  39 + components: {
  40 + UploadImageUrl
  41 + },
  42 + data() {
  43 + return {
  44 + visible: false,
  45 + form: {
  46 + goodsId: '',
  47 + prodName: '',
  48 + prodDesc: '',
  49 + picUrl: '',
  50 + picLink: '',
  51 + price: '',
  52 + shopName: '',
  53 + sort: 1
  54 + },
  55 + rules: {
  56 + prodName: [
  57 + { required: true, message: this.$t('marketGoodsItem.validate.prodNameRequired'), trigger: 'blur' },
  58 + { max: 128, message: this.$t('marketGoodsItem.validate.prodNameMaxLength'), trigger: 'blur' }
  59 + ],
  60 + prodDesc: [
  61 + { required: true, message: this.$t('marketGoodsItem.validate.prodDescRequired'), trigger: 'blur' },
  62 + { max: 256, message: this.$t('marketGoodsItem.validate.prodDescMaxLength'), trigger: 'blur' }
  63 + ],
  64 + picUrl: [
  65 + { required: true, message: this.$t('marketGoodsItem.validate.picUrlRequired'), trigger: 'blur' }
  66 + ],
  67 + picLink: [
  68 + { required: true, message: this.$t('marketGoodsItem.validate.picLinkRequired'), trigger: 'blur' },
  69 + { max: 512, message: this.$t('marketGoodsItem.validate.picLinkMaxLength'), trigger: 'blur' }
  70 + ],
  71 + price: [
  72 + { required: true, message: this.$t('marketGoodsItem.validate.priceRequired'), trigger: 'blur' },
  73 + { max: 10, message: this.$t('marketGoodsItem.validate.priceMaxLength'), trigger: 'blur' }
  74 + ],
  75 + shopName: [
  76 + { required: true, message: this.$t('marketGoodsItem.validate.shopNameRequired'), trigger: 'blur' },
  77 + { max: 128, message: this.$t('marketGoodsItem.validate.shopNameMaxLength'), trigger: 'blur' }
  78 + ],
  79 + sort: [
  80 + { required: true, message: this.$t('marketGoodsItem.validate.sortRequired'), trigger: 'blur' }
  81 + ]
  82 + }
  83 + }
  84 + },
  85 + methods: {
  86 + open(data) {
  87 + this.form = {
  88 + ...this.form,
  89 + ...data
  90 + }
  91 + this.visible = true
  92 + this.$nextTick(() => {
  93 + this.$refs.form && this.$refs.form.clearValidate()
  94 + })
  95 + },
  96 + handleClose() {
  97 + this.$refs.form.resetFields()
  98 + this.$refs.uploadImage.clear()
  99 + },
  100 + handleImageChange(images) {
  101 + if (images && images.length > 0) {
  102 + this.form.picUrl = images[0]
  103 + } else {
  104 + this.form.picUrl = ''
  105 + }
  106 + },
  107 + handleSubmit() {
  108 + this.$refs.form.validate(async valid => {
  109 + if (valid) {
  110 + try {
  111 + await saveMarketGoodsItem(this.form)
  112 + this.$message.success(this.$t('marketGoodsItem.saveSuccess'))
  113 + this.visible = false
  114 + this.$emit('success')
  115 + } catch (error) {
  116 + this.$message.error(error.message)
  117 + }
  118 + }
  119 + })
  120 + }
  121 + }
  122 +}
  123 +</script>
0 \ No newline at end of file 124 \ No newline at end of file
src/components/market/DeleteMarketGoodsItem.vue 0 → 100644
  1 +<template>
  2 + <el-dialog
  3 + :title="$t('common.confirm')"
  4 + :visible.sync="visible"
  5 + width="30%"
  6 + @close="handleClose">
  7 + <div class="confirm-content">
  8 + <i class="el-icon-warning" style="color: #E6A23C; font-size: 24px; margin-right: 10px;"></i>
  9 + <span>{{ $t('common.deleteConfirm') }}</span>
  10 + </div>
  11 + <div slot="footer" class="dialog-footer">
  12 + <el-button @click="visible = false">{{ $t('common.cancel') }}</el-button>
  13 + <el-button type="primary" @click="handleConfirm">{{ $t('common.confirm') }}</el-button>
  14 + </div>
  15 + </el-dialog>
  16 +</template>
  17 +
  18 +<script>
  19 +import { deleteMarketGoodsItem } from '@/api/market/marketGoodsItemManageApi'
  20 +
  21 +export default {
  22 + name: 'DeleteMarketGoodsItem',
  23 + data() {
  24 + return {
  25 + visible: false,
  26 + form: {
  27 + itemId: ''
  28 + }
  29 + }
  30 + },
  31 + methods: {
  32 + open(data) {
  33 + this.form.itemId = data.itemId
  34 + this.visible = true
  35 + },
  36 + handleClose() {
  37 + this.form.itemId = ''
  38 + },
  39 + async handleConfirm() {
  40 + try {
  41 + await deleteMarketGoodsItem(this.form)
  42 + this.$message.success(this.$t('marketGoodsItem.deleteSuccess'))
  43 + this.visible = false
  44 + this.$emit('success')
  45 + } catch (error) {
  46 + this.$message.error(error.message)
  47 + }
  48 + }
  49 + }
  50 +}
  51 +</script>
  52 +
  53 +<style scoped>
  54 +.confirm-content {
  55 + display: flex;
  56 + align-items: center;
  57 + justify-content: center;
  58 + font-size: 16px;
  59 +}
  60 +</style>
0 \ No newline at end of file 61 \ No newline at end of file
src/components/market/EditMarketGoodsItem.vue 0 → 100644
  1 +<template>
  2 + <el-dialog
  3 + :title="$t('common.edit')"
  4 + :visible.sync="visible"
  5 + width="50%"
  6 + @close="handleClose">
  7 + <el-form ref="form" :model="form" :rules="rules" label-width="120px">
  8 + <el-form-item :label="$t('marketGoodsItem.table.prodName')" prop="prodName">
  9 + <el-input
  10 + v-model="form.prodName"
  11 + :placeholder="$t('marketGoodsItem.table.prodName')" />
  12 + </el-form-item>
  13 + <el-form-item :label="$t('marketGoodsItem.table.prodDesc')" prop="prodDesc">
  14 + <el-input
  15 + v-model="form.prodDesc"
  16 + type="textarea"
  17 + :placeholder="$t('marketGoodsItem.table.prodDesc')" />
  18 + </el-form-item>
  19 + <el-form-item :label="$t('marketGoodsItem.table.picUrl')" prop="picUrl">
  20 + <upload-image-url ref="uploadImage" :image-count="1" @notifyUploadCoverImage="handleImageChange" />
  21 + </el-form-item>
  22 + <el-form-item :label="$t('marketGoodsItem.table.picLink')" prop="picLink">
  23 + <el-input
  24 + v-model="form.picLink"
  25 + :placeholder="$t('marketGoodsItem.table.picLink')" />
  26 + </el-form-item>
  27 + <el-form-item :label="$t('marketGoodsItem.table.price')" prop="price">
  28 + <el-input
  29 + v-model="form.price"
  30 + :placeholder="$t('marketGoodsItem.table.price')" />
  31 + </el-form-item>
  32 +
  33 + <el-form-item :label="$t('marketGoodsItem.table.shopName')" prop="shopName">
  34 + <el-input
  35 + v-model="form.shopName"
  36 + :placeholder="$t('marketGoodsItem.table.shopName')" />
  37 + </el-form-item>
  38 + <el-form-item :label="$t('marketGoodsItem.table.sort')" prop="sort">
  39 + <el-input-number
  40 + v-model="form.sort"
  41 + :min="1"
  42 + :placeholder="$t('marketGoodsItem.table.sort')" />
  43 + </el-form-item>
  44 + </el-form>
  45 + <div slot="footer" class="dialog-footer">
  46 + <el-button @click="visible = false">{{ $t('common.cancel') }}</el-button>
  47 + <el-button type="primary" @click="handleSubmit">{{ $t('common.save') }}</el-button>
  48 + </div>
  49 + </el-dialog>
  50 +</template>
  51 +
  52 +<script>
  53 +import { updateMarketGoodsItem } from '@/api/market/marketGoodsItemManageApi'
  54 +import UploadImageUrl from '@/components/upload/UploadImageUrl'
  55 +
  56 +export default {
  57 + name: 'EditMarketGoodsItem',
  58 + components: {
  59 + UploadImageUrl
  60 + },
  61 + data() {
  62 + return {
  63 + visible: false,
  64 + form: {
  65 + itemId: '',
  66 + goodsId: '',
  67 + prodName: '',
  68 + prodDesc: '',
  69 + picUrl: '',
  70 + picLink: '',
  71 + price: '',
  72 + shopName: '',
  73 + sort: 1
  74 + },
  75 + rules: {
  76 + prodName: [
  77 + { required: true, message: this.$t('marketGoodsItem.validate.prodNameRequired'), trigger: 'blur' },
  78 + { max: 128, message: this.$t('marketGoodsItem.validate.prodNameMaxLength'), trigger: 'blur' }
  79 + ],
  80 + prodDesc: [
  81 + { required: true, message: this.$t('marketGoodsItem.validate.prodDescRequired'), trigger: 'blur' },
  82 + { max: 256, message: this.$t('marketGoodsItem.validate.prodDescMaxLength'), trigger: 'blur' }
  83 + ],
  84 + picUrl: [
  85 + { required: true, message: this.$t('marketGoodsItem.validate.picUrlRequired'), trigger: 'blur' }
  86 + ],
  87 + picLink: [
  88 + { required: true, message: this.$t('marketGoodsItem.validate.picLinkRequired'), trigger: 'blur' },
  89 + { max: 512, message: this.$t('marketGoodsItem.validate.picLinkMaxLength'), trigger: 'blur' }
  90 + ],
  91 + price: [
  92 + { required: true, message: this.$t('marketGoodsItem.validate.priceRequired'), trigger: 'blur' },
  93 + { max: 10, message: this.$t('marketGoodsItem.validate.priceMaxLength'), trigger: 'blur' }
  94 + ],
  95 + shopName: [
  96 + { required: true, message: this.$t('marketGoodsItem.validate.shopNameRequired'), trigger: 'blur' },
  97 + { max: 128, message: this.$t('marketGoodsItem.validate.shopNameMaxLength'), trigger: 'blur' }
  98 + ],
  99 + sort: [
  100 + { required: true, message: this.$t('marketGoodsItem.validate.sortRequired'), trigger: 'blur' }
  101 + ],
  102 + itemId: [
  103 + { required: true, message: this.$t('marketGoodsItem.validate.itemIdRequired'), trigger: 'blur' }
  104 + ]
  105 + }
  106 + }
  107 + },
  108 + methods: {
  109 + open(data) {
  110 + this.form = {
  111 + ...this.form,
  112 + ...data
  113 + }
  114 + this.visible = true
  115 + this.$nextTick(() => {
  116 + this.$refs.form && this.$refs.form.clearValidate()
  117 + })
  118 +
  119 + setTimeout(() => {
  120 + this.$refs.uploadImage.setImages([data.picUrl])
  121 + },500)
  122 + },
  123 + handleClose() {
  124 + this.$refs.form.resetFields()
  125 + },
  126 + handleImageChange(images) {
  127 + if (images && images.length > 0) {
  128 + this.form.picUrl = images[0]
  129 + } else {
  130 + this.form.picUrl = ''
  131 + }
  132 + },
  133 + async handleSubmit() {
  134 + this.$refs.form.validate(async valid => {
  135 + if (valid) {
  136 + try {
  137 + await updateMarketGoodsItem(this.form)
  138 + this.$message.success(this.$t('marketGoodsItem.updateSuccess'))
  139 + this.visible = false
  140 + this.$emit('success')
  141 + } catch (error) {
  142 + this.$message.error(error.message)
  143 + }
  144 + }
  145 + })
  146 + }
  147 + }
  148 +}
  149 +</script>
0 \ No newline at end of file 150 \ No newline at end of file
src/components/market/MarketWayGoods.vue
@@ -4,12 +4,8 @@ @@ -4,12 +4,8 @@
4 <div class="search-wrapper"> 4 <div class="search-wrapper">
5 <el-row :gutter="20"> 5 <el-row :gutter="20">
6 <el-col :span="6"> 6 <el-col :span="6">
7 - <el-input  
8 - v-model="searchForm.name"  
9 - :placeholder="$t('marketGoods.name')"  
10 - clearable  
11 - @keyup.enter.native="handleSearch"  
12 - /> 7 + <el-input v-model="searchForm.name" :placeholder="$t('marketGoods.name')" clearable
  8 + @keyup.enter.native="handleSearch" />
13 </el-col> 9 </el-col>
14 <el-col :span="6"> 10 <el-col :span="6">
15 <el-button type="primary" @click="handleSearch"> 11 <el-button type="primary" @click="handleSearch">
@@ -22,74 +18,33 @@ @@ -22,74 +18,33 @@
22 </el-row> 18 </el-row>
23 </div> 19 </div>
24 20
25 - <el-table  
26 - v-loading="loading"  
27 - :data="tableData"  
28 - border  
29 - style="width: 100%"  
30 - >  
31 - <el-table-column  
32 - prop="name"  
33 - :label="$t('marketGoods.name')"  
34 - align="center"  
35 - />  
36 - <el-table-column  
37 - :label="$t('marketGoods.goodsCount')"  
38 - align="center"  
39 - > 21 + <el-table v-loading="loading" :data="tableData" border style="width: 100%">
  22 + <el-table-column prop="name" :label="$t('marketGoods.name')" align="center" />
  23 + <el-table-column :label="$t('marketGoods.goodsCount')" align="center">
40 <template slot-scope="scope"> 24 <template slot-scope="scope">
41 {{ scope.row.goodsCount }} 25 {{ scope.row.goodsCount }}
42 - <el-button  
43 - type="text"  
44 - @click="handleViewGoods(scope.row)"  
45 - > 26 + <el-button type="text" @click="handleViewGoods(scope.row)">
46 ({{ $t('marketGoods.viewGoods') }}) 27 ({{ $t('marketGoods.viewGoods') }})
47 </el-button> 28 </el-button>
48 </template> 29 </template>
49 </el-table-column> 30 </el-table-column>
50 - <el-table-column  
51 - prop="createTime"  
52 - :label="$t('marketGoods.createTime')"  
53 - align="center"  
54 - />  
55 - <el-table-column  
56 - prop="remark"  
57 - :label="$t('marketGoods.remark')"  
58 - align="center"  
59 - />  
60 - <el-table-column  
61 - :label="$t('marketGoods.operation')"  
62 - align="center"  
63 - width="200"  
64 - > 31 + <el-table-column prop="createTime" :label="$t('marketGoods.createTime')" align="center" />
  32 + <el-table-column prop="remark" :label="$t('marketGoods.remark')" align="center" />
  33 + <el-table-column :label="$t('marketGoods.operation')" align="center" width="200">
65 <template slot-scope="scope"> 34 <template slot-scope="scope">
66 - <el-button  
67 - size="mini"  
68 - type="primary"  
69 - @click="handleEdit(scope.row)"  
70 - > 35 + <el-button size="mini" type="primary" @click="handleEdit(scope.row)">
71 {{ $t('marketGoods.edit') }} 36 {{ $t('marketGoods.edit') }}
72 </el-button> 37 </el-button>
73 - <el-button  
74 - size="mini"  
75 - type="danger"  
76 - @click="handleDelete(scope.row)"  
77 - > 38 + <el-button size="mini" type="danger" @click="handleDelete(scope.row)">
78 {{ $t('marketGoods.delete') }} 39 {{ $t('marketGoods.delete') }}
79 </el-button> 40 </el-button>
80 </template> 41 </template>
81 </el-table-column> 42 </el-table-column>
82 </el-table> 43 </el-table>
83 44
84 - <el-pagination  
85 - :current-page.sync="pagination.current"  
86 - :page-sizes="[10, 20, 30, 50]"  
87 - :page-size="pagination.size"  
88 - :total="pagination.total"  
89 - layout="total, sizes, prev, pager, next, jumper"  
90 - @size-change="handleSizeChange"  
91 - @current-change="handleCurrentChange"  
92 - /> 45 + <el-pagination :current-page.sync="pagination.current" :page-sizes="[10, 20, 30, 50]" :page-size="pagination.size"
  46 + :total="pagination.total" layout="total, sizes, prev, pager, next, jumper" @size-change="handleSizeChange"
  47 + @current-change="handleCurrentChange" />
93 </el-card> 48 </el-card>
94 49
95 <add-market-goods ref="addMarketGoods" @success="handleSuccess" /> 50 <add-market-goods ref="addMarketGoods" @success="handleSuccess" />
@@ -160,7 +115,7 @@ export default { @@ -160,7 +115,7 @@ export default {
160 this.$refs.deleteMarketGoods.open(row) 115 this.$refs.deleteMarketGoods.open(row)
161 }, 116 },
162 handleViewGoods(row) { 117 handleViewGoods(row) {
163 - this.$router.push(`/market/goods-item?goodsId=${row.goodsId}`) 118 + this.$router.push(`/views/market/marketGoodsItemManage?goodsId=${row.goodsId}`)
164 }, 119 },
165 handleSuccess() { 120 handleSuccess() {
166 this.getList() 121 this.getList()
@@ -186,7 +141,7 @@ export default { @@ -186,7 +141,7 @@ export default {
186 .search-wrapper { 141 .search-wrapper {
187 margin-bottom: 20px; 142 margin-bottom: 20px;
188 } 143 }
189 - 144 +
190 .el-pagination { 145 .el-pagination {
191 margin-top: 20px; 146 margin-top: 20px;
192 text-align: right; 147 text-align: right;
src/components/market/UploadImage.vue 0 → 100644
  1 +<template>
  2 + <div class="upload-image-wrapper">
  3 + <div v-for="(image, index) in images" :key="index" class="image-item">
  4 + <el-image
  5 + style="width: 100px; height: 100px"
  6 + :src="image"
  7 + fit="cover"
  8 + :preview-src-list="images">
  9 + </el-image>
  10 + <i class="el-icon-delete" @click="removeImage(index)"></i>
  11 + </div>
  12 + <div
  13 + v-if="images.length < imageCount"
  14 + class="upload-button"
  15 + @click="triggerUpload">
  16 + <i class="el-icon-plus"></i>
  17 + </div>
  18 + <input
  19 + type="file"
  20 + ref="fileInput"
  21 + accept="image/*"
  22 + style="display: none"
  23 + @change="handleFileChange">
  24 + </div>
  25 +</template>
  26 +
  27 +<script>
  28 +export default {
  29 + name: 'UploadImage',
  30 + props: {
  31 + imageCount: {
  32 + type: Number,
  33 + default: 1
  34 + },
  35 + defaultImages: {
  36 + type: Array,
  37 + default: () => []
  38 + }
  39 + },
  40 + data() {
  41 + return {
  42 + images: []
  43 + }
  44 + },
  45 + watch: {
  46 + defaultImages: {
  47 + immediate: true,
  48 + handler(val) {
  49 + if (val && val.length > 0) {
  50 + this.images = [...val]
  51 + }
  52 + }
  53 + }
  54 + },
  55 + methods: {
  56 + triggerUpload() {
  57 + this.$refs.fileInput.click()
  58 + },
  59 + handleFileChange(event) {
  60 + const files = event.target.files
  61 + if (!files || files.length === 0) return
  62 +
  63 + const file = files[0]
  64 + if (file.size > 2 * 1024 * 1024) {
  65 + this.$message.error(this.$t('uploadImage.sizeLimit'))
  66 + return
  67 + }
  68 +
  69 + const reader = new FileReader()
  70 + reader.onload = (e) => {
  71 + this.images.push(e.target.result)
  72 + this.$emit('change', [...this.images])
  73 + }
  74 + reader.readAsDataURL(file)
  75 + event.target.value = null
  76 + },
  77 + removeImage(index) {
  78 + this.images.splice(index, 1)
  79 + this.$emit('change', [...this.images])
  80 + },
  81 + setImages(images) {
  82 + this.images = [...images]
  83 + },
  84 + clear() {
  85 + this.images = []
  86 + }
  87 + }
  88 +}
  89 +</script>
  90 +
  91 +<style scoped>
  92 +.upload-image-wrapper {
  93 + display: flex;
  94 + flex-wrap: wrap;
  95 +}
  96 +
  97 +.image-item {
  98 + position: relative;
  99 + margin-right: 10px;
  100 + margin-bottom: 10px;
  101 +}
  102 +
  103 +.image-item .el-icon-delete {
  104 + position: absolute;
  105 + top: -10px;
  106 + right: -10px;
  107 + font-size: 18px;
  108 + color: #F56C6C;
  109 + cursor: pointer;
  110 + background: #fff;
  111 + border-radius: 50%;
  112 +}
  113 +
  114 +.upload-button {
  115 + width: 100px;
  116 + height: 100px;
  117 + line-height: 100px;
  118 + text-align: center;
  119 + border: 1px dashed #d9d9d9;
  120 + border-radius: 6px;
  121 + cursor: pointer;
  122 + color: #8c939d;
  123 + font-size: 28px;
  124 +}
  125 +
  126 +.upload-button:hover {
  127 + border-color: #409EFF;
  128 +}
  129 +</style>
0 \ No newline at end of file 130 \ No newline at end of file
src/i18n/index.js
@@ -98,6 +98,7 @@ import { messages as userIntegralMessages } from &#39;../views/mall/userIntegralLang @@ -98,6 +98,7 @@ import { messages as userIntegralMessages } from &#39;../views/mall/userIntegralLang
98 import { messages as integralDetailMessages } from '../views/mall/integralDetailLang' 98 import { messages as integralDetailMessages } from '../views/mall/integralDetailLang'
99 import { messages as marketSmsManageMessages } from '../views/market/marketSmsManageLang' 99 import { messages as marketSmsManageMessages } from '../views/market/marketSmsManageLang'
100 import { messages as marketWayMessages } from '../views/market/marketWayLang' 100 import { messages as marketWayMessages } from '../views/market/marketWayLang'
  101 +import { messages as marketGoodsItemManageMessages } from '../views/market/marketGoodsItemManageLang'
101 102
102 Vue.use(VueI18n) 103 Vue.use(VueI18n)
103 104
@@ -200,6 +201,7 @@ const messages = { @@ -200,6 +201,7 @@ const messages = {
200 ...integralDetailMessages.en, 201 ...integralDetailMessages.en,
201 ...marketSmsManageMessages.en, 202 ...marketSmsManageMessages.en,
202 ...marketWayMessages.en, 203 ...marketWayMessages.en,
  204 + ...marketGoodsItemManageMessages.en,
203 }, 205 },
204 zh: { 206 zh: {
205 ...loginMessages.zh, 207 ...loginMessages.zh,
@@ -298,6 +300,7 @@ const messages = { @@ -298,6 +300,7 @@ const messages = {
298 ...integralDetailMessages.zh, 300 ...integralDetailMessages.zh,
299 ...marketSmsManageMessages.zh, 301 ...marketSmsManageMessages.zh,
300 ...marketWayMessages.zh, 302 ...marketWayMessages.zh,
  303 + ...marketGoodsItemManageMessages.zh,
301 } 304 }
302 } 305 }
303 306
src/router/index.js
@@ -476,6 +476,11 @@ const routes = [ @@ -476,6 +476,11 @@ const routes = [
476 name:'/pages/admin/marketWay', 476 name:'/pages/admin/marketWay',
477 component: () => import('@/views/market/marketWayList.vue') 477 component: () => import('@/views/market/marketWayList.vue')
478 }, 478 },
  479 + {
  480 + path:'/views/market/marketGoodsItemManage',
  481 + name:'/views/market/marketGoodsItemManage',
  482 + component: () => import('@/views/market/marketGoodsItemManageList.vue')
  483 + },
479 // 其他子路由可以在这里添加 484 // 其他子路由可以在这里添加
480 ] 485 ]
481 }, 486 },
src/views/market/marketGoodsItemManageLang.js 0 → 100644
  1 +export const messages = {
  2 + en: {
  3 + marketGoodsItem: {
  4 + search: {
  5 + title: 'Search Conditions',
  6 + prodName: 'Please enter product name'
  7 + },
  8 + list: {
  9 + title: 'Marketing Products'
  10 + },
  11 + table: {
  12 + picUrl: 'Image',
  13 + prodName: 'Product Name',
  14 + prodDesc: 'Product Description',
  15 + picLink: 'Access URL',
  16 + price: 'Price',
  17 + shopName: 'Shop Name',
  18 + sort: 'Sort'
  19 + },
  20 + fetchError: 'Failed to get marketing products'
  21 + }
  22 + },
  23 + zh: {
  24 + marketGoodsItem: {
  25 + search: {
  26 + title: '查询条件',
  27 + prodName: '请输入商品名称'
  28 + },
  29 + list: {
  30 + title: '营销商品'
  31 + },
  32 + table: {
  33 + picUrl: '图片',
  34 + prodName: '商品名称',
  35 + prodDesc: '商品简介',
  36 + picLink: '访问地址',
  37 + price: '金额',
  38 + shopName: '商铺名称',
  39 + sort: '排序'
  40 + },
  41 + fetchError: '获取营销商品失败'
  42 + }
  43 + }
  44 +}
0 \ No newline at end of file 45 \ No newline at end of file
src/views/market/marketGoodsItemManageList.vue 0 → 100644
  1 +<template>
  2 + <div class="market-goods-item-container">
  3 + <!-- 查询条件 -->
  4 + <el-card class="search-wrapper">
  5 + <div slot="header" class="clearfix text-left">
  6 + <span>{{ $t('marketGoodsItem.search.title') }}</span>
  7 + </div>
  8 + <el-row :gutter="20">
  9 + <el-col :span="6">
  10 + <el-input v-model="searchForm.prodName" :placeholder="$t('marketGoodsItem.search.prodName')" clearable />
  11 + </el-col>
  12 + <el-col :span="2">
  13 + <el-button type="primary" @click="handleSearch">
  14 + <i class="el-icon-search"></i>
  15 + {{ $t('common.search') }}
  16 + </el-button>
  17 + </el-col>
  18 + </el-row>
  19 + </el-card>
  20 +
  21 + <!-- 列表 -->
  22 + <el-card class="list-wrapper">
  23 + <div slot="header" class="clearfix flex justify-between">
  24 + <span>{{ $t('marketGoodsItem.list.title') }}</span>
  25 + <div style="float: right;">
  26 + <el-button type="primary" size="small" @click="handleAdd">
  27 + {{ $t('common.add') }}
  28 + </el-button>
  29 + <el-button type="default" size="small" @click="goBack">
  30 + {{ $t('common.back') }}
  31 + </el-button>
  32 + </div>
  33 + </div>
  34 +
  35 + <el-table v-loading="loading" :data="tableData" border style="width: 100%">
  36 + <el-table-column prop="picUrl" :label="$t('marketGoodsItem.table.picUrl')" align="center">
  37 + <template slot-scope="scope">
  38 + <el-image style="width: 60px; height: 60px; cursor: pointer; border-radius: 4px;"
  39 + :src="scope.row.picUrl || '/img/noPhoto.jpg'" :preview-src-list="[scope.row.picUrl || '/img/noPhoto.jpg']"
  40 + fit="cover" />
  41 + </template>
  42 + </el-table-column>
  43 + <el-table-column prop="prodName" :label="$t('marketGoodsItem.table.prodName')" align="center" />
  44 + <el-table-column prop="prodDesc" :label="$t('marketGoodsItem.table.prodDesc')" align="center" />
  45 + <el-table-column prop="picLink" :label="$t('marketGoodsItem.table.picLink')" align="center" />
  46 + <el-table-column prop="price" :label="$t('marketGoodsItem.table.price')" align="center" />
  47 + <el-table-column prop="shopName" :label="$t('marketGoodsItem.table.shopName')" align="center" />
  48 + <el-table-column prop="sort" :label="$t('marketGoodsItem.table.sort')" align="center" />
  49 + <el-table-column :label="$t('common.operation')" align="center" width="200">
  50 + <template slot-scope="scope">
  51 + <el-button size="mini" type="primary" @click="handleEdit(scope.row)">
  52 + {{ $t('common.edit') }}
  53 + </el-button>
  54 + <el-button size="mini" type="danger" @click="handleDelete(scope.row)">
  55 + {{ $t('common.delete') }}
  56 + </el-button>
  57 + </template>
  58 + </el-table-column>
  59 + </el-table>
  60 +
  61 + <el-pagination :current-page.sync="page.current" :page-sizes="[10, 20, 30, 50]" :page-size="page.size"
  62 + :total="page.total" layout="total, sizes, prev, pager, next, jumper" @size-change="handleSizeChange"
  63 + @current-change="handleCurrentChange" />
  64 + </el-card>
  65 +
  66 + <!-- 子组件 -->
  67 + <add-market-goods-item ref="addDialog" @success="handleSuccess" />
  68 + <edit-market-goods-item ref="editDialog" @success="handleSuccess" />
  69 + <delete-market-goods-item ref="deleteDialog" @success="handleSuccess" />
  70 + </div>
  71 +</template>
  72 +
  73 +<script>
  74 +import { listMarketGoodsItem } from '@/api/market/marketGoodsItemManageApi'
  75 +import AddMarketGoodsItem from '@/components/market/AddMarketGoodsItem'
  76 +import EditMarketGoodsItem from '@/components/market/EditMarketGoodsItem'
  77 +import DeleteMarketGoodsItem from '@/components/market/DeleteMarketGoodsItem'
  78 +
  79 +export default {
  80 + name: 'MarketGoodsItemManageList',
  81 + components: {
  82 + AddMarketGoodsItem,
  83 + EditMarketGoodsItem,
  84 + DeleteMarketGoodsItem
  85 + },
  86 + data() {
  87 + return {
  88 + loading: false,
  89 + searchForm: {
  90 + goodsId: this.$route.query.goodsId || '',
  91 + prodName: '',
  92 + shopName: ''
  93 + },
  94 + tableData: [],
  95 + page: {
  96 + current: 1,
  97 + size: 10,
  98 + total: 0
  99 + }
  100 + }
  101 + },
  102 + created() {
  103 + this.getList()
  104 + },
  105 + methods: {
  106 + async getList() {
  107 + try {
  108 + this.loading = true
  109 + const params = {
  110 + page: this.page.current,
  111 + row: this.page.size,
  112 + ...this.searchForm
  113 + }
  114 + const { data, total } = await listMarketGoodsItem(params)
  115 + this.tableData = data
  116 + this.page.total = total
  117 + } catch (error) {
  118 + this.$message.error(this.$t('marketGoodsItem.fetchError'))
  119 + } finally {
  120 + this.loading = false
  121 + }
  122 + },
  123 + handleSearch() {
  124 + this.page.current = 1
  125 + this.getList()
  126 + },
  127 + handleAdd() {
  128 + this.$refs.addDialog.open({
  129 + goodsId: this.searchForm.goodsId
  130 + })
  131 + },
  132 + handleEdit(row) {
  133 + this.$refs.editDialog.open(row)
  134 + },
  135 + handleDelete(row) {
  136 + this.$refs.deleteDialog.open(row)
  137 + },
  138 + handleSuccess() {
  139 + this.getList()
  140 + },
  141 + handleSizeChange(val) {
  142 + this.page.size = val
  143 + this.getList()
  144 + },
  145 + handleCurrentChange(val) {
  146 + this.page.current = val
  147 + this.getList()
  148 + },
  149 + goBack() {
  150 + this.$router.go(-1)
  151 + }
  152 + }
  153 +}
  154 +</script>
  155 +
  156 +<style lang="scss" scoped>
  157 +.market-goods-item-container {
  158 + padding: 20px;
  159 +
  160 + .search-wrapper {
  161 + margin-bottom: 20px;
  162 +
  163 + .search-title {
  164 + font-size: 16px;
  165 + font-weight: bold;
  166 + }
  167 + }
  168 +
  169 + .list-wrapper {
  170 + .list-header {
  171 + display: flex;
  172 + justify-content: space-between;
  173 + align-items: center;
  174 + }
  175 + }
  176 +
  177 + .el-pagination {
  178 + margin-top: 20px;
  179 + text-align: right;
  180 + }
  181 +}
  182 +</style>
0 \ No newline at end of file 183 \ No newline at end of file
src/更新 views/market/marketGoodsItemManageLang.js 0 → 100644
  1 +export const messages = {
  2 + en: {
  3 + marketGoodsItem: {
  4 + search: {
  5 + title: 'Search Conditions',
  6 + prodName: 'Please enter product name'
  7 + },
  8 + list: {
  9 + title: 'Marketing Products'
  10 + },
  11 + form: {
  12 + prodName: 'Product Name',
  13 + prodNamePlaceholder: 'Please enter product name',
  14 + prodDesc: 'Product Description',
  15 + prodDescPlaceholder: 'Please enter product description',
  16 + picUrl: 'Image URL',
  17 + picLink: 'Access URL',
  18 + picLinkPlaceholder: 'Please enter access URL',
  19 + price: 'Price',
  20 + pricePlaceholder: 'Please enter price',
  21 + shopName: 'Shop Name',
  22 + shopNamePlaceholder: 'Please enter shop name',
  23 + sort: 'Sort',
  24 + sortPlaceholder: 'Please enter sort number'
  25 + },
  26 + table: {
  27 + picUrl: 'Image',
  28 + prodName: 'Product Name',
  29 + prodDesc: 'Product Description',
  30 + picLink: 'Access URL',
  31 + price: 'Price',
  32 + shopName: 'Shop Name',
  33 + sort: 'Sort'
  34 + },
  35 + validate: {
  36 + prodNameRequired: 'Product name is required',
  37 + prodNameMaxLength: 'Product name cannot exceed 128 characters',
  38 + prodDescRequired: 'Product description is required',
  39 + prodDescMaxLength: 'Product description cannot exceed 256 characters',
  40 + picUrlRequired: 'Image URL is required',
  41 + picLinkRequired: 'Access URL is required',
  42 + picLinkMaxLength: 'Access URL cannot exceed 512 characters',
  43 + priceRequired: 'Price is required',
  44 + priceMaxLength: 'Price cannot exceed 10 characters',
  45 + shopNameRequired: 'Shop name is required',
  46 + shopNameMaxLength: 'Shop name cannot exceed 128 characters',
  47 + sortRequired: 'Sort is required',
  48 + itemIdRequired: 'Item ID is required'
  49 + },
  50 + deleteConfirm: 'Are you sure to delete this marketing product?',
  51 + saveSuccess: 'Add marketing product successfully',
  52 + updateSuccess: 'Update marketing product successfully',
  53 + deleteSuccess: 'Delete marketing product successfully',
  54 + fetchError: 'Failed to get marketing products'
  55 + },
  56 + uploadImage: {
  57 + sizeLimit: 'Image size cannot exceed 2MB'
  58 + }
  59 + },
  60 + zh: {
  61 + marketGoodsItem: {
  62 + search: {
  63 + title: '查询条件',
  64 + prodName: '请输入商品名称'
  65 + },
  66 + list: {
  67 + title: '营销商品'
  68 + },
  69 + form: {
  70 + prodName: '商品名称',
  71 + prodNamePlaceholder: '请输入商品名称',
  72 + prodDesc: '商品简介',
  73 + prodDescPlaceholder: '请输入商品简介',
  74 + picUrl: '图片地址',
  75 + picLink: '访问地址',
  76 + picLinkPlaceholder: '请输入图片访问地址',
  77 + price: '金额',
  78 + pricePlaceholder: '请输入金额',
  79 + shopName: '商铺名称',
  80 + shopNamePlaceholder: '请输入商铺名称',
  81 + sort: '排序',
  82 + sortPlaceholder: '请输入排序号'
  83 + },
  84 + table: {
  85 + picUrl: '图片',
  86 + prodName: '商品名称',
  87 + prodDesc: '商品简介',
  88 + picLink: '访问地址',
  89 + price: '金额',
  90 + shopName: '商铺名称',
  91 + sort: '排序'
  92 + },
  93 + validate: {
  94 + prodNameRequired: '商品名称不能为空',
  95 + prodNameMaxLength: '商品名称不能超过128个字符',
  96 + prodDescRequired: '商品简介不能为空',
  97 + prodDescMaxLength: '商品简介不能超过256个字符',
  98 + picUrlRequired: '图片地址不能为空',
  99 + picLinkRequired: '访问地址不能为空',
  100 + picLinkMaxLength: '访问地址不能超过512个字符',
  101 + priceRequired: '金额不能为空',
  102 + priceMaxLength: '金额不能超过10个字符',
  103 + shopNameRequired: '商铺名称不能为空',
  104 + shopNameMaxLength: '商铺名称不能超过128个字符',
  105 + sortRequired: '排序不能为空',
  106 + itemIdRequired: '编号不能为空'
  107 + },
  108 + deleteConfirm: '确定删除该营销商品吗?',
  109 + saveSuccess: '添加营销商品成功',
  110 + updateSuccess: '更新营销商品成功',
  111 + deleteSuccess: '删除营销商品成功',
  112 + fetchError: '获取营销商品失败'
  113 + },
  114 + uploadImage: {
  115 + sizeLimit: '图片大小不能超过2MB'
  116 + }
  117 + }
  118 +}
0 \ No newline at end of file 119 \ No newline at end of file