mirror of
https://gitee.com/gz-yami/mall4v.git
synced 2026-03-22 08:07:17 +08:00
增加产品管理模块
This commit is contained in:
99
src/components/mul-pic-upload/index.vue
Normal file
99
src/components/mul-pic-upload/index.vue
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-upload
|
||||||
|
:action="uploadAction"
|
||||||
|
:headers="uploadHeaders"
|
||||||
|
list-type="picture-card"
|
||||||
|
:on-preview="handlePictureCardPreview"
|
||||||
|
:on-remove="handleRemove"
|
||||||
|
:on-success="handleUploadSuccess"
|
||||||
|
:file-list="imageList"
|
||||||
|
:before-upload="beforeAvatarUpload"
|
||||||
|
>
|
||||||
|
<el-icon><Plus /></el-icon>
|
||||||
|
</el-upload>
|
||||||
|
<el-dialog v-model="dialogVisible">
|
||||||
|
<img
|
||||||
|
width="100%"
|
||||||
|
:src="dialogImageUrl"
|
||||||
|
alt=""
|
||||||
|
>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import $cookie from 'vue-cookies'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
|
const uploadHeaders = { Authorization: $cookie.get('Authorization') }
|
||||||
|
const uploadAction = http.adornUrl('/admin/file/upload/element')
|
||||||
|
const props = defineProps({
|
||||||
|
modelValue: {
|
||||||
|
default: '',
|
||||||
|
type: String
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
const dialogImageUrl = ref('')
|
||||||
|
const dialogVisible = ref(false)
|
||||||
|
const resourcesUrl = import.meta.env.VITE_APP_RESOURCES_URL
|
||||||
|
|
||||||
|
const imageList = computed(() => {
|
||||||
|
const res = []
|
||||||
|
if (props.modelValue) {
|
||||||
|
const imageArray = props.modelValue?.split(',')
|
||||||
|
for (let i = 0; i < imageArray.length; i++) {
|
||||||
|
res.push({ url: resourcesUrl + imageArray[i], response: imageArray[i] })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
emit('update:modelValue', props.modelValue)
|
||||||
|
return res
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图片上传
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
const handleUploadSuccess = (response, file, fileList) => {
|
||||||
|
const pics = fileList.map(file => {
|
||||||
|
if (typeof file.response === 'string') {
|
||||||
|
return file.response
|
||||||
|
}
|
||||||
|
return file.response.data
|
||||||
|
}).join(',')
|
||||||
|
emit('update:modelValue', pics)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 限制图片上传大小
|
||||||
|
*/
|
||||||
|
const beforeAvatarUpload = (file) => {
|
||||||
|
const isJPG = file.type === 'image/jpeg' || file.type === 'image/png' || file.type === 'image/gif' || file.type === 'image/jpg'
|
||||||
|
if (!isJPG) {
|
||||||
|
ElMessage.error('上传图片只能是jpeg/jpg/png/gif 格式!')
|
||||||
|
}
|
||||||
|
const isLt2M = file.size / 1024 / 1024 < 2
|
||||||
|
if (!isLt2M) {
|
||||||
|
ElMessage.error('上传图片大小不能超过 2MB!')
|
||||||
|
}
|
||||||
|
return isLt2M && isJPG
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
const handleRemove = (file, fileList) => {
|
||||||
|
const pics = fileList.map(file => {
|
||||||
|
if (typeof file.response === 'string') {
|
||||||
|
return file.response
|
||||||
|
}
|
||||||
|
return file.response.data
|
||||||
|
}).join(',')
|
||||||
|
emit('update:modelValue', pics)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlePictureCardPreview = (file) => {
|
||||||
|
dialogImageUrl.value = file.url
|
||||||
|
dialogVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
@@ -2,23 +2,22 @@
|
|||||||
<div>
|
<div>
|
||||||
<el-upload
|
<el-upload
|
||||||
class="pic-uploader-component"
|
class="pic-uploader-component"
|
||||||
:action="http.adornUrl('/admin/file/upload/element')"
|
:action="uploadAction"
|
||||||
|
:headers="uploadHeaders"
|
||||||
accept=".png,.jpg,.jpeg,.gif"
|
accept=".png,.jpg,.jpeg,.gif"
|
||||||
:headers="{Authorization: $cookie.get('Authorization')}"
|
|
||||||
:show-file-list="false"
|
:show-file-list="false"
|
||||||
:on-success="handleUploadSuccess"
|
:on-success="handleUploadSuccess"
|
||||||
:before-upload="beforeAvatarUpload"
|
:before-upload="beforeAvatarUpload"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
v-if="value"
|
v-if="modelValue"
|
||||||
alt=""
|
alt=""
|
||||||
:src="resourcesUrl + value"
|
:src="resourcesUrl + modelValue"
|
||||||
class="pic"
|
class="pic"
|
||||||
>
|
>
|
||||||
<i
|
<el-icon v-else>
|
||||||
v-else
|
<Plus />
|
||||||
class="el-icon-plus pic-uploader-icon"
|
</el-icon>
|
||||||
/>
|
|
||||||
</el-upload>
|
</el-upload>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -26,10 +25,12 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import $cookie from 'vue-cookies'
|
import $cookie from 'vue-cookies'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
|
const uploadHeaders = { Authorization: $cookie.get('Authorization') }
|
||||||
|
const uploadAction = http.adornUrl('/admin/file/upload/element')
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
value: {
|
modelValue: {
|
||||||
default: '',
|
default: '',
|
||||||
type: String
|
type: String
|
||||||
}
|
}
|
||||||
@@ -84,5 +85,4 @@ const beforeAvatarUpload = (file) => {
|
|||||||
.pic-uploader-component :deep(.el-upload:hover) {
|
.pic-uploader-component :deep(.el-upload:hover) {
|
||||||
border-color: #409EFF;
|
border-color: #409EFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -31,6 +31,11 @@ export const mainRoutes = {
|
|||||||
path: 'home',
|
path: 'home',
|
||||||
name: 'home',
|
name: 'home',
|
||||||
component: () => import('@/views/common/home/index.vue')
|
component: () => import('@/views/common/home/index.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/prodInfo',
|
||||||
|
name: 'prodInfo',
|
||||||
|
component: () => import('@/views/modules/prod/prodInfo/index.vue')
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
|||||||
@@ -5,50 +5,38 @@ export const scoreProdStore = defineStore('prod', {
|
|||||||
id: 0,
|
id: 0,
|
||||||
skuTags: [],
|
skuTags: [],
|
||||||
defalutSku: {
|
defalutSku: {
|
||||||
price: 0.01, // 销售价
|
price: 0, // 销售价
|
||||||
oriPrice: 0.01, // 市场价
|
oriPrice: 0, // 市场价
|
||||||
stocks: 0, // 库存
|
stocks: 0, // 库存
|
||||||
skuScore: 1, // 积分价格
|
|
||||||
properties: '', // 销售属性组合字符串
|
properties: '', // 销售属性组合字符串
|
||||||
skuName: '', // sku名称
|
skuName: '', // sku名称
|
||||||
prodName: '', // 商品名称
|
prodName: '', // 商品名称
|
||||||
partyCode: '', // 商品编码
|
|
||||||
weight: 0, // 商品重量
|
weight: 0, // 商品重量
|
||||||
volume: 0, // 商品体积
|
volume: 0, // 商品体积
|
||||||
status: 1, // 0 禁用 1 启用
|
status: 1 // 0 禁用 1 启用
|
||||||
stockWarning: 0, // 库存预警
|
|
||||||
prodNameCn: '',
|
|
||||||
prodNameEn: '',
|
|
||||||
skuSingleProds: []
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
updateSkuTags (skuTags) {
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
updateSkuTags (state, skuTags) {
|
||||||
this.skuTags = skuTags
|
this.skuTags = skuTags
|
||||||
},
|
},
|
||||||
updateSkuTag ({ skuTag, index }) {
|
// eslint-disable-next-line no-unused-vars
|
||||||
index = parseInt(index)
|
addSkuTag (state, skuTag) {
|
||||||
if (skuTag && typeof index === 'number') {
|
|
||||||
this.skuTags.splice(index, 1, skuTag)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
addSkuTag (skuTag) {
|
|
||||||
this.skuTags.push(skuTag)
|
this.skuTags.push(skuTag)
|
||||||
},
|
},
|
||||||
removeSkuTag (tagIndex) {
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
removeSkuTag (state, tagIndex) {
|
||||||
this.skuTags.splice(tagIndex, 1)
|
this.skuTags.splice(tagIndex, 1)
|
||||||
},
|
},
|
||||||
removeSkuTagItem ({ tagIndex, tagItemIndex }) {
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
removeSkuTagItem (state, { tagIndex, tagItemIndex }) {
|
||||||
this.skuTags[tagIndex].tagItems.splice(tagItemIndex, 1)
|
this.skuTags[tagIndex].tagItems.splice(tagItemIndex, 1)
|
||||||
},
|
},
|
||||||
addSkuTagItem ({ tagIndex, tagItem }) {
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
addSkuTagItem (state, { tagIndex, tagItem }) {
|
||||||
this.skuTags[tagIndex].tagItems.push(tagItem)
|
this.skuTags[tagIndex].tagItems.push(tagItem)
|
||||||
},
|
|
||||||
updateSkuTagItem ({ skuTagItem, tagIndex, tagItemIndex }) {
|
|
||||||
if (skuTagItem) {
|
|
||||||
this.skuTags[tagIndex].tagItems.splice(tagItemIndex, 1, skuTagItem)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -293,6 +293,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import DevyAdd from './order-devy.vue'
|
import DevyAdd from './order-devy.vue'
|
||||||
|
const resourcesUrl = import.meta.env.VITE_APP_RESOURCES_URL
|
||||||
const dataForm = ref({
|
const dataForm = ref({
|
||||||
orderId: 0,
|
orderId: 0,
|
||||||
orderNumber: '',
|
orderNumber: '',
|
||||||
|
|||||||
@@ -204,7 +204,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 空 -->
|
<!-- 空 -->
|
||||||
<div class="empty-tips">
|
<div
|
||||||
|
v-if="!dataList.length"
|
||||||
|
class="empty-tips"
|
||||||
|
>
|
||||||
暂无数据
|
暂无数据
|
||||||
</div>
|
</div>
|
||||||
<el-pagination
|
<el-pagination
|
||||||
|
|||||||
208
src/views/modules/prod/category/add-or-update.vue
Normal file
208
src/views/modules/prod/category/add-or-update.vue
Normal file
@@ -0,0 +1,208 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
v-model="visible"
|
||||||
|
:title="!dataForm.currentId ? '新增' : '修改'"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
>
|
||||||
|
<el-form
|
||||||
|
ref="dataFormRef"
|
||||||
|
:model="dataForm"
|
||||||
|
:rules="dataRule"
|
||||||
|
label-width="80px"
|
||||||
|
@keyup.enter="onSubmit()"
|
||||||
|
>
|
||||||
|
<el-form-item
|
||||||
|
v-if="dataForm.type !== 2"
|
||||||
|
label="分类图片"
|
||||||
|
prop="pic"
|
||||||
|
>
|
||||||
|
<pic-upload v-model="dataForm.pic" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
v-if="dataForm.type !== 2"
|
||||||
|
label="分类名称"
|
||||||
|
prop="categoryName"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="dataForm.categoryName"
|
||||||
|
controls-position="right"
|
||||||
|
:min="0"
|
||||||
|
label="分类名称"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="上级分类">
|
||||||
|
<el-cascader
|
||||||
|
v-model="selectedCategory"
|
||||||
|
expand-trigger="hover"
|
||||||
|
:options="categoryList"
|
||||||
|
:props="categoryTreeProps"
|
||||||
|
change-on-select
|
||||||
|
:clearable="true"
|
||||||
|
@change="handleChange"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
v-if="dataForm.type !== 2"
|
||||||
|
label="排序号"
|
||||||
|
prop="seq"
|
||||||
|
>
|
||||||
|
<el-input-number
|
||||||
|
v-model="dataForm.seq"
|
||||||
|
controls-position="right"
|
||||||
|
:min="0"
|
||||||
|
label="排序号"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="状态"
|
||||||
|
prop="status"
|
||||||
|
>
|
||||||
|
<el-radio-group v-model="dataForm.status">
|
||||||
|
<el-radio :label="0">
|
||||||
|
下线
|
||||||
|
</el-radio>
|
||||||
|
<el-radio :label="1">
|
||||||
|
正常
|
||||||
|
</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button @click="visible = false">
|
||||||
|
取消
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
@click="onSubmit()"
|
||||||
|
>
|
||||||
|
确定
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { treeDataTranslate, idList } from '@/utils'
|
||||||
|
import { Debounce } from '@/utils/debounce'
|
||||||
|
const emit = defineEmits(['refreshDataList'])
|
||||||
|
const visible = ref(false)
|
||||||
|
const dataForm = reactive({
|
||||||
|
categoryId: 0,
|
||||||
|
currentId: 0,
|
||||||
|
grade: 0,
|
||||||
|
categoryName: '',
|
||||||
|
seq: 1,
|
||||||
|
status: 1,
|
||||||
|
parentId: 0,
|
||||||
|
pic: ''
|
||||||
|
})
|
||||||
|
const dataRule = reactive({
|
||||||
|
categoryName: [
|
||||||
|
{ required: true, message: '分类名称不能为空', trigger: 'blur' },
|
||||||
|
{ pattern: /\s\S+|S+\s|\S/, message: '请输入正确的分类名称', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
pic: [
|
||||||
|
{ required: true, message: '分类图片不能为空', trigger: 'blur' }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
const categoryList = ref([])
|
||||||
|
const selectedCategory = ref([])
|
||||||
|
const categoryTreeProps = reactive({
|
||||||
|
value: 'categoryId',
|
||||||
|
label: 'categoryName'
|
||||||
|
})
|
||||||
|
const isSubmit = ref(false)
|
||||||
|
const dataFormRef = ref(null)
|
||||||
|
const init = (id) => {
|
||||||
|
dataForm.currentId = id || 0
|
||||||
|
dataForm.categoryId = id || 0
|
||||||
|
http({
|
||||||
|
url: http.adornUrl('/prod/category/listCategory'),
|
||||||
|
method: 'get',
|
||||||
|
params: http.adornParams()
|
||||||
|
})
|
||||||
|
.then(({ data }) => {
|
||||||
|
categoryList.value = treeDataTranslate(data, 'categoryId', 'parentId')
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
visible.value = true
|
||||||
|
nextTick(() => {
|
||||||
|
dataFormRef.value?.resetFields()
|
||||||
|
selectedCategory.value = []
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
if (dataForm.categoryId) {
|
||||||
|
// 修改
|
||||||
|
http({
|
||||||
|
url: http.adornUrl(`/prod/category/info/${dataForm.categoryId}`),
|
||||||
|
method: 'get',
|
||||||
|
params: http.adornParams()
|
||||||
|
})
|
||||||
|
.then(({ data }) => {
|
||||||
|
dataForm.categoryId = data.categoryId
|
||||||
|
dataForm.categoryName = data.categoryName
|
||||||
|
dataForm.seq = data.seq
|
||||||
|
dataForm.pic = data.pic
|
||||||
|
dataForm.parentId = data.parentId
|
||||||
|
dataForm.status = data.status
|
||||||
|
selectedCategory.value = idList(categoryList.value, data.parentId, 'categoryId', 'children').reverse()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
defineExpose({ init })
|
||||||
|
|
||||||
|
const handleChange = (val) => {
|
||||||
|
dataForm.parentId = val[val.length - 1]
|
||||||
|
}
|
||||||
|
// 表单提交
|
||||||
|
const onSubmit = Debounce(() => {
|
||||||
|
if (selectedCategory.value.length === 1) {
|
||||||
|
dataForm.grade = 0
|
||||||
|
}
|
||||||
|
if (selectedCategory.value.length === 2) {
|
||||||
|
dataForm.grade = 1
|
||||||
|
}
|
||||||
|
if (selectedCategory.value.length === 3) {
|
||||||
|
dataForm.grade = 2
|
||||||
|
}
|
||||||
|
dataFormRef.value?.validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
if (isSubmit.value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
isSubmit.value = true
|
||||||
|
http({
|
||||||
|
url: http.adornUrl('/prod/category'),
|
||||||
|
method: dataForm.categoryId ? 'put' : 'post',
|
||||||
|
data: http.adornData({
|
||||||
|
categoryId: dataForm.categoryId || undefined,
|
||||||
|
categoryName: dataForm.categoryName,
|
||||||
|
status: dataForm.status,
|
||||||
|
seq: dataForm.seq,
|
||||||
|
grade: dataForm.grade,
|
||||||
|
parentId: dataForm.parentId,
|
||||||
|
pic: dataForm.pic
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
ElMessage({
|
||||||
|
message: '操作成功',
|
||||||
|
type: 'success',
|
||||||
|
duration: 1000,
|
||||||
|
onClose: () => {
|
||||||
|
isSubmit.value = false
|
||||||
|
visible.value = false
|
||||||
|
emit('refreshDataList')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
178
src/views/modules/prod/category/index.vue
Normal file
178
src/views/modules/prod/category/index.vue
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
<template>
|
||||||
|
<div class="mod-category">
|
||||||
|
<el-form
|
||||||
|
:inline="true"
|
||||||
|
:model="dataForm"
|
||||||
|
>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button
|
||||||
|
v-if="isAuth('prod:category:save')"
|
||||||
|
type="primary"
|
||||||
|
icon="el-icon-plus"
|
||||||
|
@click="onAddOrUpdate()"
|
||||||
|
>
|
||||||
|
新增
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<el-table
|
||||||
|
:data="dataList"
|
||||||
|
border
|
||||||
|
row-key="categoryId"
|
||||||
|
style="width: 100%;"
|
||||||
|
>
|
||||||
|
<el-table-column
|
||||||
|
prop="categoryName"
|
||||||
|
header-align="center"
|
||||||
|
tree-key="categoryId"
|
||||||
|
width="150"
|
||||||
|
label="分类名称"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
prop="pic"
|
||||||
|
header-align="center"
|
||||||
|
align="center"
|
||||||
|
label="图片"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<img
|
||||||
|
alt=""
|
||||||
|
:src="resourcesUrl + scope.row.pic "
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
prop="status"
|
||||||
|
header-align="center"
|
||||||
|
align="center"
|
||||||
|
label="状态"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag
|
||||||
|
v-if="scope.row.status === 0"
|
||||||
|
type="danger"
|
||||||
|
>
|
||||||
|
下线
|
||||||
|
</el-tag>
|
||||||
|
<el-tag v-else>
|
||||||
|
正常
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
prop="seq"
|
||||||
|
header-align="center"
|
||||||
|
align="center"
|
||||||
|
label="排序号"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
header-align="center"
|
||||||
|
align="center"
|
||||||
|
label="操作"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
v-if="isAuth('prod:category:update')"
|
||||||
|
type="primary"
|
||||||
|
@click="onAddOrUpdate(scope.row.categoryId)"
|
||||||
|
>
|
||||||
|
修改
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
v-if="isAuth('prod:category:delete')"
|
||||||
|
type="danger"
|
||||||
|
@click="onDelete(scope.row.categoryId)"
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<!-- 弹窗, 新增 / 修改 -->
|
||||||
|
<add-or-update
|
||||||
|
v-if="addOrUpdateVisible"
|
||||||
|
ref="addOrUpdateRef"
|
||||||
|
@refresh-data-list="getDataList"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { isAuth, treeDataTranslate } from '@/utils'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import AddOrUpdate from './add-or-update.vue'
|
||||||
|
const resourcesUrl = import.meta.env.VITE_APP_RESOURCES_URL
|
||||||
|
const dataForm = ref({})
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getDataList()
|
||||||
|
})
|
||||||
|
|
||||||
|
const dataList = ref([])
|
||||||
|
const dataListLoading = ref(false)
|
||||||
|
/**
|
||||||
|
* 获取数据列表
|
||||||
|
*/
|
||||||
|
const getDataList = () => {
|
||||||
|
dataListLoading.value = true
|
||||||
|
http({
|
||||||
|
url: http.adornUrl('/prod/category/table'),
|
||||||
|
method: 'get',
|
||||||
|
params: http.adornParams()
|
||||||
|
})
|
||||||
|
.then(({ data }) => {
|
||||||
|
dataList.value = treeDataTranslate(data, 'categoryId', 'parentId')
|
||||||
|
dataListLoading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const addOrUpdateVisible = ref(false)
|
||||||
|
const addOrUpdateRef = ref(null)
|
||||||
|
/**
|
||||||
|
* 新增 / 修改
|
||||||
|
* @param id
|
||||||
|
*/
|
||||||
|
const onAddOrUpdate = (id) => {
|
||||||
|
addOrUpdateVisible.value = true
|
||||||
|
nextTick(() => {
|
||||||
|
addOrUpdateRef.value?.init(id)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 删除
|
||||||
|
* @param id
|
||||||
|
*/
|
||||||
|
const onDelete = (id) => {
|
||||||
|
ElMessageBox.confirm('确定进行删除操作?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
http({
|
||||||
|
url: http.adornUrl(`/prod/category/${id}`),
|
||||||
|
method: 'delete'
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
ElMessage({
|
||||||
|
message: '操作成功',
|
||||||
|
type: 'success',
|
||||||
|
duration: 1500,
|
||||||
|
onClose: () => {
|
||||||
|
getDataList()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.mod-category {
|
||||||
|
img {
|
||||||
|
height: 80px;
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
219
src/views/modules/prod/prodComm/add-or-update.vue
Normal file
219
src/views/modules/prod/prodComm/add-or-update.vue
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
v-model="visible"
|
||||||
|
:title="!dataForm.prodCommId ? '新增' : '修改'"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
>
|
||||||
|
<el-form
|
||||||
|
ref="dataFormRef"
|
||||||
|
:model="dataForm"
|
||||||
|
:rules="dataRule"
|
||||||
|
label-width="80px"
|
||||||
|
@keyup.enter="onSubmit()"
|
||||||
|
>
|
||||||
|
<div v-if="!isEdit">
|
||||||
|
<el-form-item
|
||||||
|
label="评论内容"
|
||||||
|
prop="userName"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="dataForm.content"
|
||||||
|
type="textarea"
|
||||||
|
:readonly="true"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item
|
||||||
|
label="评论图片"
|
||||||
|
prop="userName"
|
||||||
|
>
|
||||||
|
<div v-if="!dataForm.pics?.length">
|
||||||
|
无
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<img
|
||||||
|
v-for="item in dataForm.pics"
|
||||||
|
:key="item"
|
||||||
|
alt=""
|
||||||
|
max-width="100%"
|
||||||
|
:src="dialogImageUrl + item"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item
|
||||||
|
label="记录时间"
|
||||||
|
prop="userName"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="dataForm.recTime"
|
||||||
|
:readonly="true"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="回复时间"
|
||||||
|
prop="userName"
|
||||||
|
:readonly="true"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="dataForm.replyTime"
|
||||||
|
:readonly="true"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="IP来源"
|
||||||
|
prop="userName"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="dataForm.postip"
|
||||||
|
:readonly="true"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="得分"
|
||||||
|
prop="score"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="dataForm.score"
|
||||||
|
:readonly="true"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item
|
||||||
|
label="是否匿名"
|
||||||
|
prop="isAnonymous"
|
||||||
|
>
|
||||||
|
<el-radio-group
|
||||||
|
v-model="dataForm.isAnonymous"
|
||||||
|
:disabled="true"
|
||||||
|
>
|
||||||
|
<el-radio :label="1">
|
||||||
|
是
|
||||||
|
</el-radio>
|
||||||
|
<el-radio :label="0">
|
||||||
|
不是
|
||||||
|
</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-form-item
|
||||||
|
label="掌柜回复"
|
||||||
|
type="textarea"
|
||||||
|
prop="userName"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="dataForm.replyContent"
|
||||||
|
:readonly="!isEdit"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item
|
||||||
|
v-if="isEdit"
|
||||||
|
label="审核"
|
||||||
|
prop="status"
|
||||||
|
>
|
||||||
|
<el-radio-group
|
||||||
|
v-model="dataForm.status"
|
||||||
|
:readonly="true"
|
||||||
|
>
|
||||||
|
<el-radio :label="1">
|
||||||
|
审核通过
|
||||||
|
</el-radio>
|
||||||
|
<el-radio :label="-1">
|
||||||
|
不通过
|
||||||
|
</el-radio>
|
||||||
|
<el-radio :label="0">
|
||||||
|
等待审核
|
||||||
|
</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button @click="visible = false">
|
||||||
|
取消
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
v-if="isEdit"
|
||||||
|
type="primary"
|
||||||
|
@click="onSubmit()"
|
||||||
|
>
|
||||||
|
确定
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
const emit = defineEmits(['refreshDataList'])
|
||||||
|
const dataRule = ref({})
|
||||||
|
const isEdit = ref(false)
|
||||||
|
const dataForm = ref({
|
||||||
|
prodCommId: null,
|
||||||
|
prodId: null,
|
||||||
|
orderItemId: null,
|
||||||
|
userId: null,
|
||||||
|
content: null,
|
||||||
|
replyContent: null,
|
||||||
|
recTime: null,
|
||||||
|
replyTime: null,
|
||||||
|
replySts: null,
|
||||||
|
postip: null,
|
||||||
|
score: null,
|
||||||
|
usefulCounts: null,
|
||||||
|
photoJson: null,
|
||||||
|
isAnonymous: null,
|
||||||
|
status: null
|
||||||
|
})
|
||||||
|
const init = (prodCommId, isEditParam) => {
|
||||||
|
isEdit.value = isEditParam
|
||||||
|
dataForm.value.prodCommId = prodCommId || 0
|
||||||
|
visible.value = true
|
||||||
|
nextTick(() => {
|
||||||
|
dataFormRef.value?.resetFields()
|
||||||
|
if (dataForm.value.prodCommId) {
|
||||||
|
http({
|
||||||
|
url: http.adornUrl('/prod/prodComm/info/' + dataForm.value.prodCommId),
|
||||||
|
method: 'get',
|
||||||
|
params: http.adornParams()
|
||||||
|
})
|
||||||
|
.then(({ data }) => {
|
||||||
|
dataForm.value = data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
defineExpose({ init })
|
||||||
|
|
||||||
|
const visible = ref(false)
|
||||||
|
const dataFormRef = ref(null)
|
||||||
|
/**
|
||||||
|
* 表单提交
|
||||||
|
*/
|
||||||
|
const onSubmit = () => {
|
||||||
|
dataFormRef.value?.validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
http({
|
||||||
|
url: http.adornUrl('/prod/prodComm'),
|
||||||
|
method: dataForm.value.prodCommId ? 'put' : 'post',
|
||||||
|
data: http.adornData(dataForm.value)
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
ElMessage({
|
||||||
|
message: '操作成功',
|
||||||
|
type: 'success',
|
||||||
|
duration: 1500,
|
||||||
|
onClose: () => {
|
||||||
|
visible.value = false
|
||||||
|
emit('refreshDataList')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
122
src/views/modules/prod/prodComm/index.vue
Normal file
122
src/views/modules/prod/prodComm/index.vue
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
<template>
|
||||||
|
<div class="mod-prod-prodComm">
|
||||||
|
<avue-crud
|
||||||
|
ref="crudRef"
|
||||||
|
:page="page"
|
||||||
|
:data="dataList"
|
||||||
|
:table-loading="dataListLoading"
|
||||||
|
:option="tableOption"
|
||||||
|
@search-change="onSearch"
|
||||||
|
@on-load="getDataList"
|
||||||
|
@refresh-change="refreshChange"
|
||||||
|
@row-del="rowDel"
|
||||||
|
>
|
||||||
|
<template #nickName="scope">
|
||||||
|
{{ scope.row.user.nickName }}
|
||||||
|
</template>
|
||||||
|
<template #replyTime="scope">
|
||||||
|
{{ scope.row.replyTime ? scope.row.replyTime : '-' }}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #menu="scope">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
icon="el-icon-edit"
|
||||||
|
@click="onAddOrUpdate(scope.row.prodCommId,true)"
|
||||||
|
>
|
||||||
|
编辑
|
||||||
|
</el-button>
|
||||||
|
|
||||||
|
<el-button
|
||||||
|
type="success"
|
||||||
|
icon="el-icon-view"
|
||||||
|
@click="onAddOrUpdate(scope.row.prodCommId,false)"
|
||||||
|
>
|
||||||
|
查看
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</avue-crud>
|
||||||
|
<add-or-update
|
||||||
|
v-if="addOrUpdateVisible"
|
||||||
|
ref="addOrUpdateRef"
|
||||||
|
@refresh-data-list="refreshChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import { tableOption } from '@/crud/prod/prodComm.js'
|
||||||
|
import AddOrUpdate from './add-or-update.vue'
|
||||||
|
|
||||||
|
const dataList = ref([])
|
||||||
|
const page = reactive({
|
||||||
|
total: 0, // 总页数
|
||||||
|
currentPage: 1, // 当前页数
|
||||||
|
pageSize: 20 // 每页显示多少条
|
||||||
|
})
|
||||||
|
const dataListLoading = ref(false)
|
||||||
|
|
||||||
|
const getDataList = (pageParam, params, done) => {
|
||||||
|
dataListLoading.value = true
|
||||||
|
http({
|
||||||
|
url: http.adornUrl('/prod/prodComm/page'),
|
||||||
|
method: 'get',
|
||||||
|
params: http.adornParams(Object.assign({
|
||||||
|
current: pageParam == null ? page.currentPage : pageParam.currentPage,
|
||||||
|
size: pageParam == null ? page.pageSize : pageParam.pageSize
|
||||||
|
}, params))
|
||||||
|
})
|
||||||
|
.then(({ data }) => {
|
||||||
|
dataList.value = data.records
|
||||||
|
page.total = data.total
|
||||||
|
dataListLoading.value = false
|
||||||
|
if (done) done()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const addOrUpdateVisible = ref(false)
|
||||||
|
const addOrUpdateRef = ref(null)
|
||||||
|
/**
|
||||||
|
* 新增 / 修改
|
||||||
|
*/
|
||||||
|
const onAddOrUpdate = (id, isEdit) => {
|
||||||
|
addOrUpdateVisible.value = true
|
||||||
|
nextTick(() => {
|
||||||
|
addOrUpdateRef.value?.init(id, isEdit)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const rowDel = (row) => {
|
||||||
|
ElMessageBox.confirm('确定进行删除操作?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
http({
|
||||||
|
url: http.adornUrl('/prod/prodComm/' + row.prodCommId),
|
||||||
|
method: 'delete',
|
||||||
|
data: http.adornData({})
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
ElMessage({
|
||||||
|
message: '操作成功',
|
||||||
|
type: 'success',
|
||||||
|
duration: 1500,
|
||||||
|
onClose: () => {
|
||||||
|
getDataList()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}).catch(() => { })
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 刷新回调
|
||||||
|
*/
|
||||||
|
const refreshChange = () => {
|
||||||
|
getDataList(page)
|
||||||
|
}
|
||||||
|
const onSearch = (params, done) => {
|
||||||
|
getDataList(page, params, done)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
159
src/views/modules/prod/prodInfo/components/prod-transport.vue
Normal file
159
src/views/modules/prod/prodInfo/components/prod-transport.vue
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
<template>
|
||||||
|
<div class="mod-prod-prod-transport">
|
||||||
|
<el-form-item
|
||||||
|
label="运费设置"
|
||||||
|
:rules="[{ required: true, message: '运费模板不能为空'}]"
|
||||||
|
>
|
||||||
|
<el-select
|
||||||
|
v-model="transportId"
|
||||||
|
placeholder="请选择"
|
||||||
|
@change="changeTransport"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="transport in transportList"
|
||||||
|
:key="transport.transportId"
|
||||||
|
:label="transport.transName"
|
||||||
|
:value="transport.transportId"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-table
|
||||||
|
v-if="transportInfo.transfees"
|
||||||
|
:data="transportInfo.transfees"
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
<el-table-column
|
||||||
|
label="配送区域"
|
||||||
|
width="350"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<span v-if="!scope.row.cityList.length">所有地区</span>
|
||||||
|
<el-tag
|
||||||
|
v-for="city in scope.row.cityList"
|
||||||
|
v-else
|
||||||
|
:key="city.areaId"
|
||||||
|
>
|
||||||
|
{{ city.areaName }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
prop="firstPiece"
|
||||||
|
:label="tableTitle[0]"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
prop="firstFee"
|
||||||
|
:label="tableTitle[1]"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
prop="continuousPiece"
|
||||||
|
:label="tableTitle[2]"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
prop="continuousFee"
|
||||||
|
:label="tableTitle[3]"
|
||||||
|
/>
|
||||||
|
</el-table>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="transportInfo.hasFreeCondition === 1">
|
||||||
|
<el-table
|
||||||
|
:data="transportInfo.transfeeFrees"
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
<el-table-column
|
||||||
|
label="指定区域"
|
||||||
|
width="350"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag
|
||||||
|
v-for="city in scope.row.freeCityList"
|
||||||
|
:key="city.areaId"
|
||||||
|
>
|
||||||
|
{{ city.areaName }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
prop="freeType"
|
||||||
|
label="包邮条件"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<span v-if="scope.row.freeType === 0">满件/重量/体积包邮</span>
|
||||||
|
<span v-if="scope.row.freeType === 1">满金额包邮</span>
|
||||||
|
<span v-if="scope.row.freeType === 2">满件/重量/体积且满金额包邮</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="amount">
|
||||||
|
<template #default="scope">
|
||||||
|
<span v-if="scope.row.freeType === 1">满{{ scope.row.amount }}元金额包邮</span>
|
||||||
|
<span v-if="scope.row.freeType === 0">满{{ scope.row.piece }}件/重量/体积包邮</span>
|
||||||
|
<span v-if="scope.row.freeType === 2">满{{ scope.row.piece }}件/重量/体积且满{{ scope.row.amount }}元金额包邮</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
const props = defineProps({
|
||||||
|
modelValue: {
|
||||||
|
default: null,
|
||||||
|
type: Number
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const emit = defineEmits(['input', 'update:modelValue'])
|
||||||
|
const transportId = ref(null)
|
||||||
|
const tableTitle = computed(() => {
|
||||||
|
const titles = [['首件(个)', '运费(元)', '续件(个)', '续费(元)'], ['首重(kg)', '运费(元)', '续重(kg)', '续费(元)'], ['首体积(m³)', '运费(元)', '续体积(m³)', '续费(元)']]
|
||||||
|
if (transportInfo.value.chargeType) {
|
||||||
|
return titles[transportInfo.value.chargeType]
|
||||||
|
}
|
||||||
|
return titles[0]
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
(id) => {
|
||||||
|
transportId.value = id
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getTransportList()
|
||||||
|
})
|
||||||
|
|
||||||
|
const transportList = ref([{
|
||||||
|
transportId: null,
|
||||||
|
transName: ''
|
||||||
|
}])
|
||||||
|
const getTransportList = () => {
|
||||||
|
http({
|
||||||
|
url: http.adornUrl('/shop/transport/list'),
|
||||||
|
method: 'get',
|
||||||
|
params: http.adornParams({})
|
||||||
|
})
|
||||||
|
.then(({ data }) => {
|
||||||
|
transportList.value = data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const transportInfo = ref({
|
||||||
|
hasFreeCondition: false,
|
||||||
|
transfeeFrees: [{ freeCityList: [] }]
|
||||||
|
})
|
||||||
|
const changeTransport = (id) => {
|
||||||
|
emit('update:modelValue', id)
|
||||||
|
if (!id) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
http({
|
||||||
|
url: http.adornUrl(`/shop/transport/info/${transportId.value}`),
|
||||||
|
method: 'get',
|
||||||
|
params: http.adornParams({})
|
||||||
|
})
|
||||||
|
.then(({ data }) => {
|
||||||
|
transportInfo.value = data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
247
src/views/modules/prod/prodInfo/components/sku-table.vue
Normal file
247
src/views/modules/prod/prodInfo/components/sku-table.vue
Normal file
@@ -0,0 +1,247 @@
|
|||||||
|
<template>
|
||||||
|
<div class="mod-prod-sku-table">
|
||||||
|
<el-form-item>
|
||||||
|
<el-table
|
||||||
|
:data="modelValue"
|
||||||
|
border
|
||||||
|
style="width: 100%; margin-top: 20px"
|
||||||
|
>
|
||||||
|
<el-table-column
|
||||||
|
v-for="(leftTitle, index) in tableLeftTitles"
|
||||||
|
:key="index"
|
||||||
|
:label="leftTitle"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
{{ scope.row.properties.split(';')[index].split(':')[1] }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
v-if="tableLeftTitles.length"
|
||||||
|
prop="pic"
|
||||||
|
label="sku图片"
|
||||||
|
width="180"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<pic-upload v-model="scope.row.pic" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
v-if="tableLeftTitles.length"
|
||||||
|
prop="prodName"
|
||||||
|
label="商品名称"
|
||||||
|
width="250"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-input
|
||||||
|
v-model="scope.row.prodName"
|
||||||
|
type="textarea"
|
||||||
|
:disabled="!scope.row.status"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
prop="price"
|
||||||
|
label="销售价"
|
||||||
|
width="160"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-input-number
|
||||||
|
v-model="scope.row.price"
|
||||||
|
controls-position="right"
|
||||||
|
:precision="2"
|
||||||
|
:max="1000000000"
|
||||||
|
:min="0.01"
|
||||||
|
:disabled="!scope.row.status"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
prop="oriPrice"
|
||||||
|
label="市场价"
|
||||||
|
width="160"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-input-number
|
||||||
|
v-model="scope.row.oriPrice"
|
||||||
|
controls-position="right"
|
||||||
|
:precision="2"
|
||||||
|
:max="1000000000"
|
||||||
|
:min="0.01"
|
||||||
|
:disabled="!scope.row.status"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
prop="stocks"
|
||||||
|
label="库存"
|
||||||
|
width="160"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-input-number
|
||||||
|
v-model="scope.row.stocks"
|
||||||
|
:min="0"
|
||||||
|
controls-position="right"
|
||||||
|
type="number"
|
||||||
|
:disabled="!scope.row.status"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
prop="weight"
|
||||||
|
label="商品重量(kg)"
|
||||||
|
width="210"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-input-number
|
||||||
|
v-model="scope.row.weight"
|
||||||
|
:precision="2"
|
||||||
|
:min="0"
|
||||||
|
controls-position="right"
|
||||||
|
:disabled="!scope.row.status"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
prop="volume"
|
||||||
|
label="商品体积(m³)"
|
||||||
|
width="210"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-input-number
|
||||||
|
v-model="scope.row.volume"
|
||||||
|
:precision="2"
|
||||||
|
:min="0"
|
||||||
|
controls-position="right"
|
||||||
|
:disabled="!scope.row.status"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
label="操作"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
v-if="scope.row.status"
|
||||||
|
type="text"
|
||||||
|
@click="changeSkuStatus(`${scope.$index}`)"
|
||||||
|
>
|
||||||
|
禁用
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
v-else
|
||||||
|
type="text"
|
||||||
|
@click="changeSkuStatus(`${scope.$index}`)"
|
||||||
|
>
|
||||||
|
启用
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import PicUpload from '@/components/pic-upload'
|
||||||
|
import { scoreProdStore } from '@/stores/prod.js'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
modelValue: {
|
||||||
|
default: () => [],
|
||||||
|
type: Array
|
||||||
|
},
|
||||||
|
prodName: {
|
||||||
|
default: '',
|
||||||
|
type: String
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
const dbSpecs = ref([]) // 数据库中的规格
|
||||||
|
let initing = false
|
||||||
|
|
||||||
|
const tableLeftTitles = computed(() => {
|
||||||
|
const res = []
|
||||||
|
for (let i = 0; i < skuTags.length; i++) {
|
||||||
|
const skuTag = skuTags[i]
|
||||||
|
res.push(skuTag.tagName)
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
})
|
||||||
|
const prod = scoreProdStore()
|
||||||
|
const skuTags = computed({
|
||||||
|
get () { return prod.skuTags }
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(() => props.prodName,
|
||||||
|
() => {
|
||||||
|
skuAddProdName()
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
http({
|
||||||
|
url: http.adornUrl('/prod/spec/list'),
|
||||||
|
method: 'get',
|
||||||
|
params: http.adornParams()
|
||||||
|
})
|
||||||
|
.then(({ data }) => {
|
||||||
|
dbSpecs.value = data
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const init = () => {
|
||||||
|
initing = true
|
||||||
|
}
|
||||||
|
defineExpose({ init })
|
||||||
|
|
||||||
|
const changeSkuStatus = (tagIndex) => {
|
||||||
|
// eslint-disable-next-line vue/no-mutating-props
|
||||||
|
props.modelValue[tagIndex].status = props.modelValue[tagIndex].status ? 0 : 1
|
||||||
|
}
|
||||||
|
const skuAddProdName = () => {
|
||||||
|
if (initing) return
|
||||||
|
const skuList = []
|
||||||
|
for (let i = 0; i < props.modelValue.length; i++) {
|
||||||
|
const sku = Object.assign({}, props.modelValue[i])
|
||||||
|
if (!sku.properties) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sku.skuName = ''
|
||||||
|
const properties = sku.properties.split(';')
|
||||||
|
for (const propertiesKey in properties) {
|
||||||
|
sku.skuName += properties[propertiesKey].split(':')[1] + ' '
|
||||||
|
}
|
||||||
|
sku.prodName = props.prodName + ' ' + sku.skuName
|
||||||
|
skuList.push(sku)
|
||||||
|
}
|
||||||
|
emit('update:modelValue', skuList)
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.mod-prod-sku-table{
|
||||||
|
:deep(.pic-uploader-component .el-upload) {
|
||||||
|
border: 1px dashed #d9d9d9;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
.pic-uploader-icon {
|
||||||
|
font-size: 28px;
|
||||||
|
color: #8c939d;
|
||||||
|
width: 120px;
|
||||||
|
height: 120px;
|
||||||
|
line-height: 120px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.pic {
|
||||||
|
width: 120px;
|
||||||
|
height: 120px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
:deep(.pic-uploader-component .el-upload:hover) {
|
||||||
|
border-color: #409EFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
527
src/views/modules/prod/prodInfo/components/sku-tag.vue
Normal file
527
src/views/modules/prod/prodInfo/components/sku-tag.vue
Normal file
@@ -0,0 +1,527 @@
|
|||||||
|
<template>
|
||||||
|
<div class="mod-prod-sku-tag">
|
||||||
|
<el-form-item label="商品规格">
|
||||||
|
<el-button @click="shopTagInput()">
|
||||||
|
添加规格
|
||||||
|
</el-button>
|
||||||
|
<div
|
||||||
|
v-for="(tag, tagIndex) in skuTags"
|
||||||
|
:key="tagIndex"
|
||||||
|
>
|
||||||
|
<span>{{ tag.tagName }}</span>
|
||||||
|
<el-button
|
||||||
|
class="button-new-tag"
|
||||||
|
type="text"
|
||||||
|
icon="el-icon-delete"
|
||||||
|
@click="removeTag(tagIndex)"
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
<br>
|
||||||
|
<el-tag
|
||||||
|
v-for="(tagItem, tagItemIndex) in tag.tagItems"
|
||||||
|
:key="tagItem.valueId"
|
||||||
|
closable
|
||||||
|
:disable-transitions="false"
|
||||||
|
@close="handleTagClose(tagIndex, tagItemIndex)"
|
||||||
|
>
|
||||||
|
{{ tagItem.propValue }}
|
||||||
|
</el-tag>
|
||||||
|
<el-input
|
||||||
|
v-if="tagItemInputs[tagIndex] && tagItemInputs[tagIndex].visible"
|
||||||
|
:ref="`saveTagInput${tagIndex}`"
|
||||||
|
v-model="tagItemInputs[tagIndex].value"
|
||||||
|
class="input-new-tag"
|
||||||
|
@keyup.enter="handleInputConfirm(tagIndex)"
|
||||||
|
@blur="handleInputConfirm(tagIndex)"
|
||||||
|
/>
|
||||||
|
<el-button
|
||||||
|
v-else
|
||||||
|
class="button-new-tag"
|
||||||
|
@click="showTagInput(tagIndex)"
|
||||||
|
>
|
||||||
|
+ 添加
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
v-show="isShowTagInput"
|
||||||
|
label="规格名"
|
||||||
|
>
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-select
|
||||||
|
v-model="addTagInput.propName"
|
||||||
|
filterable
|
||||||
|
allow-create
|
||||||
|
default-first-option
|
||||||
|
placeholder="请选择"
|
||||||
|
@change="handleTagClick"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in unUseTags"
|
||||||
|
:key="item.propId"
|
||||||
|
:label="item.propName"
|
||||||
|
:value="item.propName"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-col>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
v-show="isShowTagInput"
|
||||||
|
label="规格值"
|
||||||
|
>
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-select
|
||||||
|
v-model="addTagInput.selectValues"
|
||||||
|
multiple
|
||||||
|
filterable
|
||||||
|
allow-create
|
||||||
|
default-first-option
|
||||||
|
placeholder="请选择"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in dbTagValues"
|
||||||
|
:key="item.valueId"
|
||||||
|
:label="item.propValue"
|
||||||
|
:value="item.propValue"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-col>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button
|
||||||
|
v-show="isShowTagInput"
|
||||||
|
type="primary"
|
||||||
|
@click="addTag()"
|
||||||
|
>
|
||||||
|
确定
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
v-show="isShowTagInput"
|
||||||
|
@click="hideTagInput()"
|
||||||
|
>
|
||||||
|
取消
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { scoreProdStore } from '@/stores/prod.js'
|
||||||
|
const prod = scoreProdStore()
|
||||||
|
const props = defineProps({
|
||||||
|
skuList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const emit = defineEmits(['change'])
|
||||||
|
const isShowTagInput = ref(false)
|
||||||
|
const addTagInput = ref({
|
||||||
|
propName: '',
|
||||||
|
selectValues: []
|
||||||
|
})
|
||||||
|
const type = ref(0)
|
||||||
|
const tagItemName = ref('')
|
||||||
|
const tagItemInputs = ref([])
|
||||||
|
const dbTagValues = ref([]) // 根据选定的规格所查询出来的规格值
|
||||||
|
let tagName = ''
|
||||||
|
let tagNameIndex = 0
|
||||||
|
let dbTags = [] // 数据库中的规格
|
||||||
|
let maxValueId = 0 // 规格值id最大
|
||||||
|
let maxPropId = 0 // 规格id 最大
|
||||||
|
let initing = false
|
||||||
|
|
||||||
|
const skuTags = computed({
|
||||||
|
get () { return prod.skuTags },
|
||||||
|
set (val) { prod.updateSkuTags(val) }
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 未使用的规格, 通过计算属性计算
|
||||||
|
*/
|
||||||
|
const unUseTags = computed(() => {
|
||||||
|
const res = []
|
||||||
|
for (let i = 0; i < dbTags.length; i++) {
|
||||||
|
const dbTag = dbTags[i]
|
||||||
|
const specIndex = skuTags.value?.findIndex(tag => tag.tagName === dbTag.propName)
|
||||||
|
if (specIndex === -1) {
|
||||||
|
res.push(dbTag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
})
|
||||||
|
|
||||||
|
const defalutSku = computed(() => {
|
||||||
|
return prod.defalutSku
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(() => skuTags.value,
|
||||||
|
(val) => {
|
||||||
|
if (initing) {
|
||||||
|
initing = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let skuList = []
|
||||||
|
if (type.value === 4) {
|
||||||
|
// 删除规格值
|
||||||
|
props.skuList?.forEach(sku => {
|
||||||
|
const propertiesArray = sku.properties.split(';')
|
||||||
|
if (tagItemName.value !== propertiesArray[tagNameIndex].split(':')[1]) {
|
||||||
|
skuList.push(sku)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else if (type.value === 2) {
|
||||||
|
// 添加规格值
|
||||||
|
const properties = tagName + ':' + tagItemName.value
|
||||||
|
// 增加或删除规格
|
||||||
|
let tempSkuList = []
|
||||||
|
val.forEach(tag => {
|
||||||
|
if (skuList.length === 0) {
|
||||||
|
if (tagName === tag.tagName) {
|
||||||
|
const sku = Object.assign({}, defalutSku.value)
|
||||||
|
sku.properties = properties // 销售属性组合字符串
|
||||||
|
skuList.push(sku)
|
||||||
|
} else {
|
||||||
|
tag.tagItems.forEach(tagItem => {
|
||||||
|
const sku = Object.assign({}, defalutSku.value)
|
||||||
|
sku.properties = `${tag.tagName}:${tagItem.propValue}` // 销售属性组合字符串
|
||||||
|
skuList.push(sku)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (val.length === 1) {
|
||||||
|
skuList = props.skuList.concat(skuList)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tempSkuList = []
|
||||||
|
if (tagName === tag.tagName) {
|
||||||
|
skuList.forEach(sku => {
|
||||||
|
if (sku.properties.indexOf(tagName) === -1) {
|
||||||
|
const newSku = Object.assign({}, sku)
|
||||||
|
newSku.properties = `${sku.properties};${properties}`
|
||||||
|
tempSkuList.push(newSku)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
tag.tagItems.forEach(tagItem => {
|
||||||
|
skuList.forEach(sku => {
|
||||||
|
if (sku.properties.indexOf(tag.tagName) === -1) {
|
||||||
|
const newSku = Object.assign({}, sku)
|
||||||
|
newSku.properties = `${sku.properties};${tag.tagName}:${tagItem.propValue}`
|
||||||
|
tempSkuList.push(newSku)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
skuList = props.skuList.concat(tempSkuList)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// 增加或删除规格
|
||||||
|
let tempSkuList = []
|
||||||
|
val.forEach(tag => {
|
||||||
|
if (skuList.length === 0) {
|
||||||
|
tag.tagItems.forEach(tagItem => {
|
||||||
|
const sku = Object.assign({}, defalutSku.value)
|
||||||
|
sku.properties = `${tag.tagName}:${tagItem.propValue}` // 销售属性组合字符串
|
||||||
|
skuList.push(sku)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
tempSkuList = []
|
||||||
|
tag.tagItems.forEach(tagItem => {
|
||||||
|
skuList.forEach(sku => {
|
||||||
|
const newSku = Object.assign({}, sku)
|
||||||
|
newSku.properties = `${sku.properties};${tag.tagName}:${tagItem.propValue}`
|
||||||
|
tempSkuList.push(newSku)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
skuList = tempSkuList
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (!skuList.length) {
|
||||||
|
skuList.push(Object.assign({}, defalutSku.value))
|
||||||
|
}
|
||||||
|
// debugger
|
||||||
|
emit('change', skuList)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
http({
|
||||||
|
url: http.adornUrl('/prod/spec/list'),
|
||||||
|
method: 'get',
|
||||||
|
params: http.adornParams()
|
||||||
|
})
|
||||||
|
.then(({ data }) => {
|
||||||
|
dbTags = data
|
||||||
|
if (data) {
|
||||||
|
maxPropId = Math.max.apply(Math, data.map(item => item.propId))
|
||||||
|
} else {
|
||||||
|
maxPropId = 0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
http({
|
||||||
|
url: http.adornUrl('/prod/spec/listSpecMaxValueId'),
|
||||||
|
method: 'get',
|
||||||
|
params: http.adornParams()
|
||||||
|
})
|
||||||
|
.then(({ data }) => {
|
||||||
|
if (data) {
|
||||||
|
maxValueId = data
|
||||||
|
} else {
|
||||||
|
maxValueId = 0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const init = (skuList) => {
|
||||||
|
if (!skuList || !skuList.length) {
|
||||||
|
skuTags.value = []
|
||||||
|
emit('change', [Object.assign({}, defalutSku.value)])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
initing = true
|
||||||
|
const skuTagsParam = []
|
||||||
|
for (let i = 0; i < skuList.length; i++) {
|
||||||
|
const sku = skuList[i]
|
||||||
|
if (!sku.properties) break
|
||||||
|
const propertiesArray = sku.properties.split(';')
|
||||||
|
for (const j in propertiesArray) {
|
||||||
|
const properties = propertiesArray[j].split(':')
|
||||||
|
if (!skuTagsParam[j]) {
|
||||||
|
skuTagsParam[j] = {
|
||||||
|
tagName: properties[0],
|
||||||
|
tagItems: []
|
||||||
|
}
|
||||||
|
tagItemInputs.value.push({ visible: false, value: '' })
|
||||||
|
}
|
||||||
|
const tagItemNameIndex = skuTagsParam[j].tagItems.findIndex((tagItemName) => tagItemName.propValue === properties[1])
|
||||||
|
if (tagItemNameIndex === -1) {
|
||||||
|
skuTagsParam[j].tagItems.push({ propValue: properties[1] })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
skuTags.value = skuTagsParam
|
||||||
|
}
|
||||||
|
defineExpose({ init })
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示规格名、规格值输入框
|
||||||
|
*/
|
||||||
|
const shopTagInput = () => {
|
||||||
|
isShowTagInput.value = !isShowTagInput.value
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 隐藏规格名、规格值输入框
|
||||||
|
*/
|
||||||
|
const hideTagInput = () => {
|
||||||
|
isShowTagInput.value = false
|
||||||
|
cleanTagInput()
|
||||||
|
}
|
||||||
|
const addTag = () => {
|
||||||
|
const selectValues = addTagInput.value.selectValues
|
||||||
|
if (!addTagInput.value.propName) {
|
||||||
|
ElMessage.error('请输入规格名')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!selectValues.length) {
|
||||||
|
ElMessage.error('请输入规格值')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
isShowTagInput.value = false
|
||||||
|
for (let i = 0; i < selectValues.length; i++) {
|
||||||
|
const element = selectValues[i]
|
||||||
|
const is = Object.prototype.toString.call(element) === '[object Object]'
|
||||||
|
if (!is) {
|
||||||
|
maxPropId = maxPropId + 1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const tagItems = []
|
||||||
|
for (let i = 0; i < selectValues.length; i++) {
|
||||||
|
const element = selectValues[i]
|
||||||
|
const is = Object.prototype.toString.call(element) === '[object Object]'
|
||||||
|
if (is) {
|
||||||
|
tagItems.push(element)
|
||||||
|
} else {
|
||||||
|
maxValueId = maxValueId + 1
|
||||||
|
tagItems.push({ propId: maxPropId, propValue: element, valueId: maxValueId })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 向规格中放入规格输入框内的数据
|
||||||
|
prod.addSkuTag({
|
||||||
|
tagName: addTagInput.value.propName,
|
||||||
|
tagItems
|
||||||
|
})
|
||||||
|
type.value = 1
|
||||||
|
cleanTagInput()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除规格值输入框内容
|
||||||
|
*/
|
||||||
|
const cleanTagInput = () => {
|
||||||
|
addTagInput.value = {
|
||||||
|
propName: '',
|
||||||
|
selectValues: []
|
||||||
|
}
|
||||||
|
dbTagValues.value = []
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 规格名输入框,选中规格事件
|
||||||
|
*/
|
||||||
|
const handleTagClick = () => {
|
||||||
|
// 清空规格值输入框
|
||||||
|
dbTagValues.value = []
|
||||||
|
addTagInput.value.selectValues = []
|
||||||
|
// 判断规格名输入的值是否为数据库中已有的值
|
||||||
|
const specsIndex = dbTags.findIndex(spec => spec.propName === addTagInput.value.propName)
|
||||||
|
// 如果不是,则说明为用户随便输入
|
||||||
|
if (specsIndex === -1) return
|
||||||
|
// 如果数据库已有该规格名,则获取根据id获取该规格名称含有的规格值
|
||||||
|
http({
|
||||||
|
url: http.adornUrl(`/prod/spec/listSpecValue/${dbTags[specsIndex].propId}`),
|
||||||
|
method: 'get',
|
||||||
|
params: http.adornParams()
|
||||||
|
}).then(({ data }) => {
|
||||||
|
dbTagValues.value = data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 规格名输入框,选中规格事件
|
||||||
|
*/
|
||||||
|
const handleTagClose = (tagIndex, tagItemIndex) => {
|
||||||
|
tagName = skuTags.value[tagIndex].tagName
|
||||||
|
tagNameIndex = tagIndex
|
||||||
|
tagItemName.value = skuTags.value[tagIndex].tagItems[tagItemIndex].propValue
|
||||||
|
if (skuTags.value[tagIndex].tagItems.length === 1) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
type.value = 4
|
||||||
|
prod.removeSkuTagItem(tagIndex, tagItemIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标签输入框确定时调用
|
||||||
|
*/
|
||||||
|
const handleInputConfirm = (tagIndex) => {
|
||||||
|
if (checkTagItem(tagIndex)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const tagItems = skuTags.value[tagIndex].tagItems
|
||||||
|
const itemValue = tagItemInputs.value[tagIndex].value
|
||||||
|
const index = tagItems.length - 1
|
||||||
|
tagName = skuTags.value[tagIndex].tagName
|
||||||
|
tagItemName.value = tagItemInputs.value[tagIndex].value
|
||||||
|
const maxValue = getMaxValueId(skuTags.value[tagIndex].tagItems)
|
||||||
|
const tagItem = { propId: index === -1 ? 0 : skuTags.value[tagIndex].tagItems[index].propId, propValue: itemValue, valueId: index === -1 ? 0 : (maxValue + 1) }
|
||||||
|
if (tagItem) {
|
||||||
|
prod.addSkuTagItem({ tagIndex, tagItem })
|
||||||
|
}
|
||||||
|
tagItemInputs.value[tagIndex].visible = false
|
||||||
|
tagItemInputs.value[tagIndex].value = ''
|
||||||
|
type.value = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示标签输入框
|
||||||
|
*/
|
||||||
|
const showTagInput = (tagIndex) => {
|
||||||
|
tagItemInputs.value.push({ visible: false, value: '' })
|
||||||
|
tagItemInputs.value[tagIndex].visible = true
|
||||||
|
nextTick(() => {
|
||||||
|
[`saveTagInput${tagIndex}`][0].value.input.focus()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取数据集合所有对象中某个属性的最大值
|
||||||
|
*/
|
||||||
|
const getMaxValueId = (list) => {
|
||||||
|
return Math.max.apply(Math, list.map(item => item.valueId))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除 规格
|
||||||
|
* @param tagIndex
|
||||||
|
*/
|
||||||
|
const removeTag = (tagIndex) => {
|
||||||
|
type.value = 3
|
||||||
|
prod.removeSkuTag(tagIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增规格值时,判断是否存在同名的规格值
|
||||||
|
*/
|
||||||
|
const checkTagItem = (tagIndex) => {
|
||||||
|
const tagItem = tagItemInputs.value[tagIndex].value
|
||||||
|
if (!tagItem) {
|
||||||
|
tagItemInputs.value[tagIndex].visible = false
|
||||||
|
tagItemInputs.value[tagIndex].value = ''
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
let isSame = false
|
||||||
|
skuTags.value?.forEach(tag => {
|
||||||
|
const arr = tag.tagItems.map((item) => {
|
||||||
|
return item.propValue
|
||||||
|
})
|
||||||
|
if (arr.indexOf(tagItem) > -1) {
|
||||||
|
isSame = true
|
||||||
|
ElMessage.error('product.specificationValue')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return isSame
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.mod-prod-sku-tag {
|
||||||
|
:deep(.el-tag + .el-tag) {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
.button-new-tag {
|
||||||
|
margin-left: 10px;
|
||||||
|
height: 32px;
|
||||||
|
line-height: 30px;
|
||||||
|
padding-top: 0;
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
.input-new-tag {
|
||||||
|
width: 90px;
|
||||||
|
margin-left: 10px;
|
||||||
|
vertical-align: bottom;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增规格外部边框
|
||||||
|
:deep(.sku-border) {
|
||||||
|
border: 1px solid #EBEEF5;
|
||||||
|
width:70%
|
||||||
|
}
|
||||||
|
:deep(.sku-background){
|
||||||
|
background-color: #F6f6f6;
|
||||||
|
margin: 12px 12px;
|
||||||
|
.el-button{
|
||||||
|
margin-left: 10px;
|
||||||
|
span{
|
||||||
|
color:#000 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.el-form-item__label{
|
||||||
|
padding:0 24px 0 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
:deep(.sku-tag){
|
||||||
|
margin: 12px 12px;
|
||||||
|
}
|
||||||
|
:deep(.tagTree){
|
||||||
|
margin-left: 18px;
|
||||||
|
padding-bottom:8px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
342
src/views/modules/prod/prodInfo/index.vue
Normal file
342
src/views/modules/prod/prodInfo/index.vue
Normal file
@@ -0,0 +1,342 @@
|
|||||||
|
<template>
|
||||||
|
<div class="mod-prod-info">
|
||||||
|
<el-form
|
||||||
|
ref="dataFormRef"
|
||||||
|
:model="dataForm"
|
||||||
|
label-width="100px"
|
||||||
|
>
|
||||||
|
<el-form-item label="产品图片">
|
||||||
|
<mul-pic-upload v-model="dataForm.imgs" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="状态">
|
||||||
|
<el-radio-group v-model="dataForm.status">
|
||||||
|
<el-radio :label="1">
|
||||||
|
上架
|
||||||
|
</el-radio>
|
||||||
|
<el-radio :label="0">
|
||||||
|
下架
|
||||||
|
</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="产品分类"
|
||||||
|
:rules="[{ required: true, message: '请选择产品分类'}]"
|
||||||
|
prop="categoryId"
|
||||||
|
>
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-cascader
|
||||||
|
v-model="category.selected"
|
||||||
|
expand-trigger="hover"
|
||||||
|
:options="category.list"
|
||||||
|
:props="category.props"
|
||||||
|
change-on-select
|
||||||
|
@change="handleCategoryChange"
|
||||||
|
/>
|
||||||
|
</el-col>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="产品分组"
|
||||||
|
:rules="[{ required: true, message: '请选择产品分组'}]"
|
||||||
|
>
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-select
|
||||||
|
v-model="dataForm.tagList"
|
||||||
|
multiple
|
||||||
|
style="width: 250px"
|
||||||
|
placeholder="请选择"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in tags"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.title"
|
||||||
|
:value="item.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-col>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="产品名称"
|
||||||
|
prop="prodName"
|
||||||
|
:rules="[
|
||||||
|
{ required: true, message: '产品名称不能为空'},
|
||||||
|
{ pattern: /\s\S+|S+\s|\S/, message: '请输入正确的产品名称', trigger: 'blur' }
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-input
|
||||||
|
v-model="dataForm.prodName"
|
||||||
|
placeholder="产品名称"
|
||||||
|
maxlength="50"
|
||||||
|
/>
|
||||||
|
</el-col>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="产品卖点"
|
||||||
|
prop="brief"
|
||||||
|
:rules="[
|
||||||
|
{ required: false, pattern: /\s\S+|S+\s|\S/, message: '请输入正确的产品卖点', trigger: 'blur' }
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-input
|
||||||
|
v-model="dataForm.brief"
|
||||||
|
type="textarea"
|
||||||
|
:autosize="{minRows: 2, maxRows: 4}"
|
||||||
|
placeholder="产品卖点"
|
||||||
|
/>
|
||||||
|
</el-col>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="配送方式">
|
||||||
|
<el-checkbox v-model="dataForm.deliveryMode.hasShopDelivery">
|
||||||
|
商家配送
|
||||||
|
</el-checkbox>
|
||||||
|
<el-checkbox v-model="dataForm.deliveryMode.hasUserPickUp">
|
||||||
|
用户自提
|
||||||
|
</el-checkbox>
|
||||||
|
</el-form-item>
|
||||||
|
<prod-transport
|
||||||
|
v-show="dataForm.deliveryMode.hasShopDelivery"
|
||||||
|
v-model="dataForm.deliveryTemplateId"
|
||||||
|
/>
|
||||||
|
<sku-tag
|
||||||
|
ref="skuTagRef"
|
||||||
|
:sku-list="dataForm.skuList"
|
||||||
|
@change="skuTagChangeSkuHandler"
|
||||||
|
/>
|
||||||
|
<sku-table
|
||||||
|
ref="skuTableRef"
|
||||||
|
v-model="dataForm.skuList"
|
||||||
|
:prod-name="dataForm.prodName"
|
||||||
|
/>
|
||||||
|
<el-form-item
|
||||||
|
label="产品详情"
|
||||||
|
prop="content"
|
||||||
|
>
|
||||||
|
<tiny-mce
|
||||||
|
ref="contentRef"
|
||||||
|
v-model="dataForm.content"
|
||||||
|
style="width:1000px"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
@click="onSubmit()"
|
||||||
|
>
|
||||||
|
确定
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { treeDataTranslate, idList } from '@/utils'
|
||||||
|
import ProdTransport from './components/prod-transport.vue'
|
||||||
|
import SkuTag from './components/sku-tag.vue'
|
||||||
|
import SkuTable from './components/sku-table.vue'
|
||||||
|
import { Debounce } from '@/utils/debounce'
|
||||||
|
|
||||||
|
const emit = defineEmits(['refreshDataList'])
|
||||||
|
|
||||||
|
// 分类树展示与回显
|
||||||
|
const category = reactive({
|
||||||
|
list: [],
|
||||||
|
selected: [],
|
||||||
|
props: {
|
||||||
|
value: 'categoryId',
|
||||||
|
label: 'categoryName'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// 规格列表
|
||||||
|
const dataForm = ref({
|
||||||
|
prodName: '',
|
||||||
|
brief: '',
|
||||||
|
pic: '',
|
||||||
|
imgs: '',
|
||||||
|
categoryId: 0,
|
||||||
|
prodId: 0,
|
||||||
|
skuList: [],
|
||||||
|
tagList: [],
|
||||||
|
content: '',
|
||||||
|
status: 1,
|
||||||
|
deliveryMode: {
|
||||||
|
hasShopDelivery: false,
|
||||||
|
hasUserPickUp: false
|
||||||
|
},
|
||||||
|
deliveryTemplateId: null
|
||||||
|
})
|
||||||
|
const tags = ref([])
|
||||||
|
onMounted(() => {
|
||||||
|
dataForm.value.prodId = useRoute().query.prodId
|
||||||
|
getDataList()
|
||||||
|
})
|
||||||
|
|
||||||
|
const skuTableRef = ref(null)
|
||||||
|
const skuTagRef = ref(null)
|
||||||
|
/**
|
||||||
|
* 获取分类数据
|
||||||
|
*/
|
||||||
|
const getDataList = () => {
|
||||||
|
getTagList()
|
||||||
|
getCategoryList().then(() => {
|
||||||
|
if (dataForm.value.prodId) {
|
||||||
|
// 获取产品数据
|
||||||
|
http({
|
||||||
|
url: http.adornUrl(`/prod/prod/info/${dataForm.value.prodId}`),
|
||||||
|
method: 'get',
|
||||||
|
params: http.adornParams()
|
||||||
|
})
|
||||||
|
.then(({ data }) => {
|
||||||
|
dataForm.value = data
|
||||||
|
dataForm.value.deliveryMode = JSON.parse(data.deliveryMode)
|
||||||
|
skuTagRef.value?.init(data.skuList)
|
||||||
|
skuTableRef.value?.init()
|
||||||
|
category.selected = idList(category.list, dataForm.value.categoryId, 'categoryId', 'children').reverse()
|
||||||
|
dataForm.value.tagList = data.tagList
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
nextTick(() => {
|
||||||
|
dataFormRef.value?.resetFields()
|
||||||
|
skuTagRef.value?.init()
|
||||||
|
dataForm.value.pic = ''
|
||||||
|
dataForm.value.imgs = ''
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 获取分类信息
|
||||||
|
*/
|
||||||
|
const getCategoryList = () => {
|
||||||
|
return http({
|
||||||
|
url: http.adornUrl('/prod/category/listCategory'),
|
||||||
|
method: 'get',
|
||||||
|
params: http.adornParams()
|
||||||
|
})
|
||||||
|
.then(({ data }) => {
|
||||||
|
category.list = treeDataTranslate(data, 'categoryId', 'parentId')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 选择分类改变事件
|
||||||
|
* @param val
|
||||||
|
*/
|
||||||
|
const handleCategoryChange = (val) => {
|
||||||
|
dataForm.value.categoryId = val[val.length - 1]
|
||||||
|
}
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const dataFormRef = ref(null)
|
||||||
|
/**
|
||||||
|
* 表单提交
|
||||||
|
*/
|
||||||
|
const onSubmit = Debounce(() => {
|
||||||
|
dataFormRef.value?.validate((valid) => {
|
||||||
|
if (!valid) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!dataForm.value.imgs) {
|
||||||
|
errorMsg('请选择图片上传')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!dataForm.value.deliveryMode) {
|
||||||
|
errorMsg('请选择配送方式')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (dataForm.value.deliveryMode.hasShopDelivery && !dataForm.value.deliveryTemplateId) {
|
||||||
|
errorMsg('请选择运费模板')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const param = Object.assign({}, dataForm.value)
|
||||||
|
// 设置价格和库存
|
||||||
|
paramSetPriceAndStocks(param)
|
||||||
|
|
||||||
|
param.deliveryMode = undefined
|
||||||
|
param.deliveryModeVo = dataForm.value.deliveryMode
|
||||||
|
// 商品主图
|
||||||
|
param.pic = dataForm.value.imgs.split(',')[0]
|
||||||
|
http({
|
||||||
|
url: http.adornUrl('/prod/prod'),
|
||||||
|
method: param.prodId ? 'put' : 'post',
|
||||||
|
data: http.adornData(param)
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
ElMessage({
|
||||||
|
message: '操作成功',
|
||||||
|
type: 'success',
|
||||||
|
duration: 1500,
|
||||||
|
onClose: () => {
|
||||||
|
router.push({ name: 'prod-prodList' })
|
||||||
|
emit('refreshDataList')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const paramSetPriceAndStocks = (param) => {
|
||||||
|
// 商品库存
|
||||||
|
param.totalStocks = 0
|
||||||
|
// 商品价格
|
||||||
|
param.price = 0
|
||||||
|
// 商品原价
|
||||||
|
param.oriPrice = 0
|
||||||
|
// 商品实际库存
|
||||||
|
for (let i = 0; i < param.skuList.length; i++) {
|
||||||
|
const element = param.skuList[i]
|
||||||
|
if (element.status !== 1) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (param.price === 0) {
|
||||||
|
param.price = element.price ? Number.parseFloat(element.price) : 0
|
||||||
|
}
|
||||||
|
// 商品价格为最低价的那件商品的价格
|
||||||
|
param.price = Math.min(param.price, element.price)
|
||||||
|
if (param.price === element.price) {
|
||||||
|
param.oriPrice = element.oriPrice ? Number.parseFloat(element.oriPrice) : 0
|
||||||
|
}
|
||||||
|
param.totalStocks += element.stocks ? Number.parseInt(element.stocks) : 0
|
||||||
|
}
|
||||||
|
// 如果sku没有商品名称,则使用商品的商品名称
|
||||||
|
if (param.skuList.length === 1) {
|
||||||
|
param.skuList[0].prodName = dataForm.value.prodName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const skuTagChangeSkuHandler = (skuList) => {
|
||||||
|
const prodName = dataForm.value.prodName
|
||||||
|
skuList.forEach(sku => {
|
||||||
|
if (sku.properties) {
|
||||||
|
sku.skuName = ''
|
||||||
|
const properties = sku.properties.split(';')
|
||||||
|
for (const propertiesKey in properties) {
|
||||||
|
sku.skuName += properties[propertiesKey].split(':')[1] + ' '
|
||||||
|
}
|
||||||
|
sku.prodName = prodName + ' ' + sku.skuName
|
||||||
|
}
|
||||||
|
})
|
||||||
|
dataForm.value.skuList = skuList
|
||||||
|
}
|
||||||
|
const errorMsg = (message) => {
|
||||||
|
ElMessage({
|
||||||
|
message,
|
||||||
|
type: 'error',
|
||||||
|
duration: 1500
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有的分组信息
|
||||||
|
*/
|
||||||
|
const getTagList = () => {
|
||||||
|
http({
|
||||||
|
url: http.adornUrl('/prod/prodTag/listTagList'),
|
||||||
|
method: 'get',
|
||||||
|
params: http.adornParams()
|
||||||
|
})
|
||||||
|
.then(({ data }) => {
|
||||||
|
tags.value = data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
181
src/views/modules/prod/prodList/index.vue
Normal file
181
src/views/modules/prod/prodList/index.vue
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
<template>
|
||||||
|
<div class="mod-prod">
|
||||||
|
<avue-crud
|
||||||
|
ref="crudRef"
|
||||||
|
:page="page"
|
||||||
|
:data="dataList"
|
||||||
|
:table-loading="dataListLoading"
|
||||||
|
:permission="permission"
|
||||||
|
:option="tableOption"
|
||||||
|
@search-change="onSearch"
|
||||||
|
@selection-change="selectionChange"
|
||||||
|
@on-load="getDataList"
|
||||||
|
>
|
||||||
|
<template #menu-left>
|
||||||
|
<el-button
|
||||||
|
v-if="isAuth('shop:pickAddr:save')"
|
||||||
|
type="primary"
|
||||||
|
icon="el-icon-plus"
|
||||||
|
@click.stop="onAddOrUpdate()"
|
||||||
|
>
|
||||||
|
新增
|
||||||
|
</el-button>
|
||||||
|
|
||||||
|
<el-button
|
||||||
|
v-if="isAuth('shop:pickAddr:delete')"
|
||||||
|
type="danger"
|
||||||
|
:disabled="dataListSelections.length <= 0"
|
||||||
|
@click="onDelete()"
|
||||||
|
>
|
||||||
|
批量删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #status="scope">
|
||||||
|
<el-tag v-if="scope.row.status === 1">
|
||||||
|
上架
|
||||||
|
</el-tag>
|
||||||
|
<el-tag v-else>
|
||||||
|
未上架
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #menu="scope">
|
||||||
|
<el-button
|
||||||
|
v-if="isAuth('prod:prod:update')"
|
||||||
|
type="primary"
|
||||||
|
icon="el-icon-edit"
|
||||||
|
@click="onAddOrUpdate(scope.row.prodId)"
|
||||||
|
>
|
||||||
|
修改
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
v-if="isAuth('prod:prod:delete')"
|
||||||
|
type="danger"
|
||||||
|
icon="el-icon-delete"
|
||||||
|
@click="onDelete(scope.row.prodId)"
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</avue-crud>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { isAuth } from '@/utils'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import { tableOption } from '@/crud/prod/prodList.js'
|
||||||
|
const permission = reactive({
|
||||||
|
delBtn: isAuth('prod:prod:delete')
|
||||||
|
})
|
||||||
|
const dataList = ref([])
|
||||||
|
const page = reactive({
|
||||||
|
total: 0, // 总页数
|
||||||
|
currentPage: 1, // 当前页数
|
||||||
|
pageSize: 10 // 每页显示多少条
|
||||||
|
})
|
||||||
|
const dataListLoading = ref(false)
|
||||||
|
/**
|
||||||
|
* 获取数据列表
|
||||||
|
*/
|
||||||
|
const getDataList = (pageParam, params, done) => {
|
||||||
|
dataListLoading.value = true
|
||||||
|
http({
|
||||||
|
url: http.adornUrl('/prod/prod/page'),
|
||||||
|
method: 'get',
|
||||||
|
params: http.adornParams(
|
||||||
|
Object.assign(
|
||||||
|
{
|
||||||
|
current: pageParam == null ? page.currentPage : pageParam.currentPage,
|
||||||
|
size: pageParam == null ? page.pageSize : pageParam.pageSize
|
||||||
|
},
|
||||||
|
params
|
||||||
|
)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.then(({ data }) => {
|
||||||
|
dataList.value = data.records
|
||||||
|
for (const key in dataList.value) {
|
||||||
|
// eslint-disable-next-line no-prototype-builtins
|
||||||
|
if (dataList.value.hasOwnProperty(key)) {
|
||||||
|
const element = dataList.value[key]
|
||||||
|
element.imgs = element.imgs.split(',')[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
page.total = data.total
|
||||||
|
dataListLoading.value = false
|
||||||
|
if (done) done()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const router = useRouter()
|
||||||
|
/**
|
||||||
|
* 新增 / 修改
|
||||||
|
* @param id
|
||||||
|
*/
|
||||||
|
const onAddOrUpdate = (id) => {
|
||||||
|
router.push({
|
||||||
|
path: '/prodInfo',
|
||||||
|
query: { prodId: id }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 删除和批量删除
|
||||||
|
* @param id
|
||||||
|
*/
|
||||||
|
const onDelete = (id) => {
|
||||||
|
const prodIds = getSeleProdIds()
|
||||||
|
if (id) {
|
||||||
|
prodIds.push(id)
|
||||||
|
}
|
||||||
|
ElMessageBox.confirm(`确定进行[${id ? '删除' : '批量删除'}]操作?`, '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
http({
|
||||||
|
url: http.adornUrl('/prod/prod'),
|
||||||
|
method: 'delete',
|
||||||
|
data: http.adornData(prodIds, false)
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
ElMessage({
|
||||||
|
message: '操作成功',
|
||||||
|
type: 'success',
|
||||||
|
duration: 1500,
|
||||||
|
onClose: () => {
|
||||||
|
getDataList(page)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch(() => { })
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 条件查询
|
||||||
|
* @param params
|
||||||
|
* @param done
|
||||||
|
*/
|
||||||
|
const onSearch = (params, done) => {
|
||||||
|
getDataList(page, params, done)
|
||||||
|
}
|
||||||
|
|
||||||
|
const dataListSelections = ref([])
|
||||||
|
/**
|
||||||
|
* 多选变化
|
||||||
|
* @param val
|
||||||
|
*/
|
||||||
|
const selectionChange = (val) => {
|
||||||
|
dataListSelections.value = val
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 获取选中的商品Id列表
|
||||||
|
*/
|
||||||
|
const getSeleProdIds = () => {
|
||||||
|
return dataListSelections.value?.map(item => {
|
||||||
|
return item.prodId
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
145
src/views/modules/prod/prodTag/add-or-update.vue
Normal file
145
src/views/modules/prod/prodTag/add-or-update.vue
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
v-model="visible"
|
||||||
|
:title="!dataForm.id ? '新增' : '修改'"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
>
|
||||||
|
<el-form
|
||||||
|
ref="dataFormRef"
|
||||||
|
:model="dataForm"
|
||||||
|
:rules="dataRule"
|
||||||
|
label-width="80px"
|
||||||
|
@keyup.enter="onSubmit()"
|
||||||
|
>
|
||||||
|
<el-form-item
|
||||||
|
label="标签名称"
|
||||||
|
:rules="[
|
||||||
|
{ required: true, message: '标签名称不能为空', trigger: 'blur' },
|
||||||
|
{ pattern: /\s\S+|S+\s|\S/, message: '请输入正确的标签名称', trigger: 'blur' }
|
||||||
|
]"
|
||||||
|
prop="title"
|
||||||
|
>
|
||||||
|
<el-input v-model="dataForm.title" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="状态"
|
||||||
|
prop="status"
|
||||||
|
>
|
||||||
|
<el-radio-group v-model="dataForm.status">
|
||||||
|
<el-radio :label="1">
|
||||||
|
正常
|
||||||
|
</el-radio>
|
||||||
|
<el-radio :label="0">
|
||||||
|
禁用
|
||||||
|
</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="列表样式"
|
||||||
|
prop="style"
|
||||||
|
>
|
||||||
|
<el-radio-group v-model="dataForm.style">
|
||||||
|
<el-radio :label="0">
|
||||||
|
一列一个
|
||||||
|
</el-radio>
|
||||||
|
<el-radio :label="1">
|
||||||
|
一列两个
|
||||||
|
</el-radio>
|
||||||
|
<el-radio :label="2">
|
||||||
|
一列三个
|
||||||
|
</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item
|
||||||
|
label="排序"
|
||||||
|
prop="seq"
|
||||||
|
>
|
||||||
|
<el-input-number
|
||||||
|
v-model="dataForm.seq"
|
||||||
|
controls-position="right"
|
||||||
|
:min="0"
|
||||||
|
label="排序号"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button @click="visible = false">
|
||||||
|
取消
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
@click="onSubmit()"
|
||||||
|
>
|
||||||
|
确定
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { Debounce } from '@/utils/debounce'
|
||||||
|
const emit = defineEmits(['refreshDataList'])
|
||||||
|
const visible = ref(false)
|
||||||
|
const dataRule = {}
|
||||||
|
const dataForm = ref({
|
||||||
|
id: null,
|
||||||
|
title: null,
|
||||||
|
shopId: null,
|
||||||
|
status: 1,
|
||||||
|
isDefault: null,
|
||||||
|
prodCount: null,
|
||||||
|
seq: null,
|
||||||
|
style: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
const init = (id) => {
|
||||||
|
dataForm.value.id = id || 0
|
||||||
|
visible.value = true
|
||||||
|
nextTick(() => {
|
||||||
|
dataFormRef.value?.resetFields()
|
||||||
|
if (dataForm.value.id) {
|
||||||
|
http({
|
||||||
|
url: http.adornUrl('/prod/prodTag/info/' + dataForm.value.id),
|
||||||
|
method: 'get',
|
||||||
|
params: http.adornParams()
|
||||||
|
})
|
||||||
|
.then(({ data }) => {
|
||||||
|
dataForm.value = data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
defineExpose({ init })
|
||||||
|
|
||||||
|
const dataFormRef = ref(null)
|
||||||
|
/**
|
||||||
|
* 表单提交
|
||||||
|
*/
|
||||||
|
const onSubmit = Debounce(() => {
|
||||||
|
dataFormRef.value?.validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
http({
|
||||||
|
url: http.adornUrl('/prod/prodTag'),
|
||||||
|
method: dataForm.value.id ? 'put' : 'post',
|
||||||
|
data: http.adornData(dataForm.value)
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
ElMessage({
|
||||||
|
message: '操作成功',
|
||||||
|
type: 'success',
|
||||||
|
duration: 1500,
|
||||||
|
onClose: () => {
|
||||||
|
visible.value = false
|
||||||
|
emit('refreshDataList')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
156
src/views/modules/prod/prodTag/index.vue
Normal file
156
src/views/modules/prod/prodTag/index.vue
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
<template>
|
||||||
|
<div class="mod-prod-prodTag">
|
||||||
|
<avue-crud
|
||||||
|
ref="crudRef"
|
||||||
|
:page="page"
|
||||||
|
:data="dataList"
|
||||||
|
:table-loading="dataListLoading"
|
||||||
|
:option="tableOption"
|
||||||
|
@search-change="onSearch"
|
||||||
|
@on-load="getDataList"
|
||||||
|
@refresh-change="refreshChange"
|
||||||
|
>
|
||||||
|
<template #menu-left>
|
||||||
|
<el-button
|
||||||
|
v-if="isAuth('prod:prodTag:save')"
|
||||||
|
type="primary"
|
||||||
|
icon="el-icon-plus"
|
||||||
|
@click="onAddOrUpdate()"
|
||||||
|
>
|
||||||
|
新增
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
<template #title="scope">
|
||||||
|
{{ scope.row.title || '-' }}
|
||||||
|
</template>
|
||||||
|
<template #status="scope">
|
||||||
|
<el-tag
|
||||||
|
v-if="scope.row.status === 0"
|
||||||
|
type="danger"
|
||||||
|
>
|
||||||
|
禁用
|
||||||
|
</el-tag>
|
||||||
|
<el-tag v-else>
|
||||||
|
正常
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #isDfault="scope">
|
||||||
|
<el-tag v-if="scope.row.isDefault === 0">
|
||||||
|
自定义类型
|
||||||
|
</el-tag>
|
||||||
|
<el-tag v-else>
|
||||||
|
默认类型
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #menu="scope">
|
||||||
|
<el-button
|
||||||
|
v-if="isAuth('prod:prodTag:update')"
|
||||||
|
type="primary"
|
||||||
|
icon="el-icon-edit"
|
||||||
|
@click="onAddOrUpdate(scope.row.id)"
|
||||||
|
>
|
||||||
|
修改
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
v-if="isAuth('prod:prodTag:delete')"
|
||||||
|
type="danger"
|
||||||
|
icon="el-icon-delete"
|
||||||
|
@click.stop="onDelete(scope.row.id)"
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</avue-crud>
|
||||||
|
<add-or-update
|
||||||
|
v-if="addOrUpdateVisible"
|
||||||
|
ref="addOrUpdateRef"
|
||||||
|
@refresh-data-list="refreshChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { isAuth } from '@/utils'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import { tableOption } from '@/crud/prod/prodTag.js'
|
||||||
|
import AddOrUpdate from './add-or-update.vue'
|
||||||
|
|
||||||
|
const dataList = ref([])
|
||||||
|
const page = reactive({
|
||||||
|
total: 0, // 总页数
|
||||||
|
currentPage: 1, // 当前页数
|
||||||
|
pageSize: 10 // 每页显示多少条
|
||||||
|
})
|
||||||
|
const dataListLoading = ref(false)
|
||||||
|
|
||||||
|
const getDataList = (pageParam, params, done) => {
|
||||||
|
dataListLoading.value = true
|
||||||
|
http({
|
||||||
|
url: http.adornUrl('/prod/prodTag/page'),
|
||||||
|
method: 'get',
|
||||||
|
params: http.adornParams(Object.assign({
|
||||||
|
current: pageParam == null ? page.currentPage : pageParam.currentPage,
|
||||||
|
size: pageParam == null ? page.pageSize : pageParam.pageSize
|
||||||
|
}, params))
|
||||||
|
})
|
||||||
|
.then(({ data }) => {
|
||||||
|
dataList.value = data.records
|
||||||
|
page.total = data.total
|
||||||
|
dataListLoading.value = false
|
||||||
|
if (done) done()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const addOrUpdateVisible = ref(false)
|
||||||
|
const addOrUpdateRef = ref(null)
|
||||||
|
/**
|
||||||
|
* 新增 / 修改
|
||||||
|
* @param id
|
||||||
|
*/
|
||||||
|
const onAddOrUpdate = (id) => {
|
||||||
|
addOrUpdateVisible.value = true
|
||||||
|
nextTick(() => {
|
||||||
|
addOrUpdateRef.value?.init(id)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const onDelete = (id) => {
|
||||||
|
ElMessageBox.confirm('确定进行删除操作?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
http({
|
||||||
|
url: http.adornUrl('/prod/prodTag/' + id),
|
||||||
|
method: 'delete',
|
||||||
|
data: http.adornData({})
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
ElMessage({
|
||||||
|
message: '操作成功',
|
||||||
|
type: 'success',
|
||||||
|
duration: 1500,
|
||||||
|
onClose: () => {
|
||||||
|
getDataList(page)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}).catch(() => { })
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 刷新回调
|
||||||
|
*/
|
||||||
|
const refreshChange = () => {
|
||||||
|
getDataList(page)
|
||||||
|
}
|
||||||
|
const onSearch = (params, done) => {
|
||||||
|
getDataList(page, params, done)
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
</style>
|
||||||
190
src/views/modules/prod/spec/add-or-update.vue
Normal file
190
src/views/modules/prod/spec/add-or-update.vue
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
v-model="visible"
|
||||||
|
:title="!dataList[0].propId ? '新增' : '修改'"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
>
|
||||||
|
<el-table
|
||||||
|
:data="dataList"
|
||||||
|
border
|
||||||
|
style="width: 100%;"
|
||||||
|
>
|
||||||
|
<el-table-column
|
||||||
|
prop="propName"
|
||||||
|
header-align="center"
|
||||||
|
align="center"
|
||||||
|
label="属性名称"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-input
|
||||||
|
v-model="scope.row.propName"
|
||||||
|
placeholder="请输入内容"
|
||||||
|
maxlength="10"
|
||||||
|
show-word-limit
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
prop="prodPropValues"
|
||||||
|
header-align="center"
|
||||||
|
align="center"
|
||||||
|
label="属性值"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-col
|
||||||
|
v-for="item in scope.row.prodPropValues"
|
||||||
|
:key="item.valueId"
|
||||||
|
:span="12"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="item.propValue"
|
||||||
|
placeholder="请输入内容"
|
||||||
|
class="prop-value-input"
|
||||||
|
maxlength="20"
|
||||||
|
show-word-limit
|
||||||
|
clearable
|
||||||
|
@clear="clearProdPropValues"
|
||||||
|
/>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="4">
|
||||||
|
<el-button
|
||||||
|
v-show="scope.row.prodPropValues[scope.row.prodPropValues.length-1].propValue"
|
||||||
|
type="primary"
|
||||||
|
class="add-input"
|
||||||
|
icon="el-icon-circle-plus"
|
||||||
|
@click="addInput()"
|
||||||
|
/>
|
||||||
|
</el-col>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button @click="visible = false">
|
||||||
|
取消
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
@click="onSubmit()"
|
||||||
|
>
|
||||||
|
确定
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { Debounce } from '@/utils/debounce'
|
||||||
|
const emit = defineEmits(['refreshDataList'])
|
||||||
|
|
||||||
|
const visible = ref(false)
|
||||||
|
const dataList = ref([{ propId: 0, propName: '', prodPropValues: [{ valueId: 0 }] }])
|
||||||
|
const page = {
|
||||||
|
total: 0, // 总页数
|
||||||
|
currentPage: 1, // 当前页数
|
||||||
|
pageSize: 10 // 每页显示多少条
|
||||||
|
}
|
||||||
|
|
||||||
|
const init = (val) => {
|
||||||
|
if (val) {
|
||||||
|
dataList.value = [JSON.parse(JSON.stringify(val))]
|
||||||
|
} else {
|
||||||
|
dataList.value = [
|
||||||
|
{ propId: 0, propName: '', prodPropValues: [{ valueId: 0 }] }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
visible.value = true
|
||||||
|
}
|
||||||
|
defineExpose({ init })
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表单提交
|
||||||
|
*/
|
||||||
|
const onSubmit = Debounce(() => {
|
||||||
|
if (dataList.value[0].prodPropValues) {
|
||||||
|
const temp = []
|
||||||
|
for (const key in dataList.value[0].prodPropValues) {
|
||||||
|
// eslint-disable-next-line no-prototype-builtins
|
||||||
|
if (dataList.value[0].prodPropValues.hasOwnProperty(key)) {
|
||||||
|
const element = dataList.value[0].prodPropValues[key]
|
||||||
|
if (element.propValue) {
|
||||||
|
temp.push(dataList.value[0].prodPropValues[key])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dataList.value[0].prodPropValues = temp
|
||||||
|
}
|
||||||
|
if (!dataList.value[0].propName.trim()) {
|
||||||
|
dataList.value[0].propName = ''
|
||||||
|
ElMessage.error('属性名不能为空')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (dataList.value[0].prodPropValues.length < 1) {
|
||||||
|
dataList.value[0].prodPropValues = [{ valueId: 0 }]
|
||||||
|
ElMessage.error('规格项不能为空')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (dataList.value[0].propName.length > 10) {
|
||||||
|
ElMessage.error('属性名称长度不能大于10')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (dataList.value[0].prodPropValues.find(el => !el.propValue.trim())) {
|
||||||
|
ElMessage.error('属性值不能为空')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (dataList.value[0].prodPropValues.find(el => el.propValue.length > 20)) {
|
||||||
|
ElMessage.error('属性值长度不能大于20')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
http({
|
||||||
|
url: http.adornUrl('/prod/spec'),
|
||||||
|
method: dataList.value[0].propId ? 'put' : 'post',
|
||||||
|
data: http.adornData({
|
||||||
|
propId: dataList.value[0].propId || undefined,
|
||||||
|
propName: dataList.value[0].propName,
|
||||||
|
prodPropValues: dataList.value[0].prodPropValues
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
ElMessage({
|
||||||
|
message: '操作成功',
|
||||||
|
type: 'success',
|
||||||
|
duration: 1500,
|
||||||
|
onClose: () => {
|
||||||
|
visible.value = false
|
||||||
|
emit('refreshDataList', page)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const clearProdPropValues = () => {
|
||||||
|
if (dataList.value[0].prodPropValues.length === 1) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for (let i = 0; i < dataList.value[0].prodPropValues.length; i++) {
|
||||||
|
const element = dataList.value[0].prodPropValues[i]
|
||||||
|
if (!element.propValue) {
|
||||||
|
dataList.value[0].prodPropValues.splice(i, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const addInput = () => {
|
||||||
|
const temp = dataList.value[0].prodPropValues
|
||||||
|
if (temp[temp.length - 1].propValue) {
|
||||||
|
temp.push({})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.prop-value-input {
|
||||||
|
padding: 3px;
|
||||||
|
}
|
||||||
|
.add-input {
|
||||||
|
margin: 3px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
161
src/views/modules/prod/spec/index.vue
Normal file
161
src/views/modules/prod/spec/index.vue
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
<template>
|
||||||
|
<div class="mod-prod">
|
||||||
|
<avue-crud
|
||||||
|
ref="crudRef"
|
||||||
|
:page="page"
|
||||||
|
:data="dataList"
|
||||||
|
:option="tableOption"
|
||||||
|
:permission="permission"
|
||||||
|
@search-change="onSearch"
|
||||||
|
@on-load="getDataList"
|
||||||
|
>
|
||||||
|
<template #prodPropValues="scope">
|
||||||
|
<el-tag
|
||||||
|
v-for="item in scope.row.prodPropValues"
|
||||||
|
:key="item.valueId"
|
||||||
|
>
|
||||||
|
{{ item.propValue }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
<template #menu-left>
|
||||||
|
<el-button
|
||||||
|
v-if="isAuth('shop:pickAddr:save')"
|
||||||
|
type="primary"
|
||||||
|
icon="el-icon-plus"
|
||||||
|
|
||||||
|
@click.stop="onAddOrUpdate()"
|
||||||
|
>
|
||||||
|
新增
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
<template #menu="scope">
|
||||||
|
<el-button
|
||||||
|
v-if="isAuth('prod:spec:update')"
|
||||||
|
type="primary"
|
||||||
|
icon="el-icon-edit"
|
||||||
|
|
||||||
|
@click.stop="onAddOrUpdate(scope.row)"
|
||||||
|
>
|
||||||
|
编辑
|
||||||
|
</el-button>
|
||||||
|
|
||||||
|
<el-button
|
||||||
|
v-if="isAuth('prod:spec:delete')"
|
||||||
|
type="danger"
|
||||||
|
icon="el-icon-delete"
|
||||||
|
|
||||||
|
@click.stop="onDelete(scope.row.propId)"
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</avue-crud>
|
||||||
|
<!-- 弹窗, 新增 / 修改 -->
|
||||||
|
<add-or-update
|
||||||
|
v-if="addOrUpdateVisible"
|
||||||
|
ref="addOrUpdateRef"
|
||||||
|
@refresh-data-list="getDataList"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { isAuth } from '@/utils'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import AddOrUpdate from './add-or-update.vue'
|
||||||
|
import { tableOption } from '@/crud/prod/spec.js'
|
||||||
|
const permission = ref({
|
||||||
|
delBtn: isAuth('prod:prod:delete')
|
||||||
|
})
|
||||||
|
const dataList = ref([])
|
||||||
|
const dataListLoading = ref(false)
|
||||||
|
const dataListSelections = ref([])
|
||||||
|
const page = reactive({
|
||||||
|
total: 0, // 总页数
|
||||||
|
currentPage: 1, // 当前页数
|
||||||
|
pageSize: 10 // 每页显示多少条
|
||||||
|
})
|
||||||
|
/**
|
||||||
|
* 获取数据列表
|
||||||
|
*/
|
||||||
|
const getDataList = (pageParam, params, done) => {
|
||||||
|
dataListLoading.value = true
|
||||||
|
http({
|
||||||
|
url: http.adornUrl('/prod/spec/page'),
|
||||||
|
method: 'get',
|
||||||
|
params: http.adornParams(
|
||||||
|
Object.assign(
|
||||||
|
{
|
||||||
|
current: pageParam == null ? page.currentPage : pageParam.currentPage,
|
||||||
|
size: pageParam == null ? page.pageSize : pageParam.pageSize
|
||||||
|
},
|
||||||
|
params
|
||||||
|
)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.then(({ data }) => {
|
||||||
|
dataList.value = data.records
|
||||||
|
page.total = data.total
|
||||||
|
dataListLoading.value = false
|
||||||
|
if (done) done()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const addOrUpdateVisible = ref(false)
|
||||||
|
const addOrUpdateRef = ref(null)
|
||||||
|
/**
|
||||||
|
* 新增 / 修改
|
||||||
|
* @param val
|
||||||
|
*/
|
||||||
|
const onAddOrUpdate = (val) => {
|
||||||
|
addOrUpdateVisible.value = true
|
||||||
|
nextTick(() => {
|
||||||
|
addOrUpdateRef.value?.init(val)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除
|
||||||
|
* @param id
|
||||||
|
*/
|
||||||
|
const onDelete = (id) => {
|
||||||
|
const ids = id ? [id] : dataListSelections.value?.map(item => {
|
||||||
|
return item.propId
|
||||||
|
})
|
||||||
|
ElMessageBox.confirm(`确定进行[${id ? '删除' : '批量删除'}]操作?`, '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
http({
|
||||||
|
url: http.adornUrl(`/prod/spec/${ids}`),
|
||||||
|
method: 'delete',
|
||||||
|
data: http.adornData(ids, false)
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
ElMessage({
|
||||||
|
message: '操作成功',
|
||||||
|
type: 'success',
|
||||||
|
duration: 1500,
|
||||||
|
onClose: () => {
|
||||||
|
getDataList(page)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch(() => { })
|
||||||
|
}
|
||||||
|
|
||||||
|
const onSearch = (params, done) => {
|
||||||
|
getDataList(page, params, done)
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
:deep(.prop-value) {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0 3px 3px 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -82,8 +82,6 @@ const page = reactive({
|
|||||||
pageSize: 10 // 每页显示多少条
|
pageSize: 10 // 每页显示多少条
|
||||||
})
|
})
|
||||||
const dataListLoading = ref(false)
|
const dataListLoading = ref(false)
|
||||||
const dataListSelections = ref([])
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取数据列表
|
* 获取数据列表
|
||||||
*/
|
*/
|
||||||
@@ -106,7 +104,7 @@ const getDataList = (pageParam, params, done) => {
|
|||||||
if (done) done()
|
if (done) done()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
const dataListSelections = ref([])
|
||||||
/**
|
/**
|
||||||
* 多选回调
|
* 多选回调
|
||||||
* @param list
|
* @param list
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
是
|
是
|
||||||
</el-tag>
|
</el-tag>
|
||||||
</template>
|
</template>
|
||||||
<template #menuLeft>
|
<template #menu-left>
|
||||||
<el-button
|
<el-button
|
||||||
v-if="isAuth('shop:notice:save')"
|
v-if="isAuth('shop:notice:save')"
|
||||||
type="primary"
|
type="primary"
|
||||||
|
|||||||
@@ -16,10 +16,14 @@
|
|||||||
prop="pic"
|
prop="pic"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
|
v-if="dataForm.pic"
|
||||||
:src="dataForm.pic"
|
:src="dataForm.pic"
|
||||||
class="image"
|
class="image"
|
||||||
alt=""
|
alt=""
|
||||||
>
|
>
|
||||||
|
<div v-else>
|
||||||
|
无
|
||||||
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item
|
<el-form-item
|
||||||
label="用户昵称"
|
label="用户昵称"
|
||||||
|
|||||||
@@ -14,10 +14,7 @@
|
|||||||
v-if="scope.row.pic"
|
v-if="scope.row.pic"
|
||||||
class="avue-crud__img"
|
class="avue-crud__img"
|
||||||
>
|
>
|
||||||
<i
|
<el-icon><Document /></el-icon>
|
||||||
:src="scope.row.pic"
|
|
||||||
class="el-icon-document"
|
|
||||||
/>
|
|
||||||
</span>
|
</span>
|
||||||
<span v-else>-</span>
|
<span v-else>-</span>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Reference in New Issue
Block a user