增加门店管理,会员管理,订单管理模块

master
wyy 2 years ago
parent 552513a534
commit 96310a0deb

@ -0,0 +1,88 @@
<template>
<div>
<el-upload
class="pic-uploader-component"
:action="http.adornUrl('/admin/file/upload/element')"
accept=".png,.jpg,.jpeg,.gif"
:headers="{Authorization: $cookie.get('Authorization')}"
:show-file-list="false"
:on-success="handleUploadSuccess"
:before-upload="beforeAvatarUpload"
>
<img
v-if="value"
alt=""
:src="resourcesUrl + value"
class="pic"
>
<i
v-else
class="el-icon-plus pic-uploader-icon"
/>
</el-upload>
</div>
</template>
<script setup>
import $cookie from 'vue-cookies'
import { ElMessage } from 'element-plus'
const emit = defineEmits(['update:modelValue'])
// eslint-disable-next-line no-unused-vars
const props = defineProps({
value: {
default: '',
type: String
}
})
const resourcesUrl = import.meta.env.VITE_APP_RESOURCES_URL
/**
* 图片上传
*/
// eslint-disable-next-line no-unused-vars
const handleUploadSuccess = (response, file) => {
emit('update:modelValue', file.response.data)
}
/**
* 限制图片上传大小
*/
const beforeAvatarUpload = (file) => {
const isLt2M = file.size / 1024 / 1024 < 2
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 格式!')
}
if (!isLt2M) {
ElMessage.error('上传图片大小不能超过 2MB!')
}
return isLt2M && isJPG
}
</script>
<style lang="scss" scoped>
.pic-uploader-component :deep(.el-upload) {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
.pic-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
line-height: 178px;
text-align: center;
}
.pic {
width: 178px;
height: 178px;
display: block;
}
}
.pic-uploader-component :deep(.el-upload:hover) {
border-color: #409EFF;
}
</style>

@ -0,0 +1,226 @@
<template>
<el-dialog
v-model="visible"
title="选择商品"
:modal="false"
:close-on-click-modal="false"
>
<el-table
ref="prodTableRef"
v-loading="dataListLoading"
:data="dataList"
border
style="width: 100%;"
@selection-change="selectChangeHandle"
>
<el-table-column
v-if="isSingle"
width="50"
header-align="center"
align="center"
>
<template #default="scope">
<div>
<el-radio
v-model="singleSelectProdId"
:label="scope.row.prodId"
@change="getSelectProdRow(scope.row)"
>
&nbsp;
</el-radio>
</div>
</template>
</el-table-column>
<el-table-column
v-if="!isSingle"
type="selection"
header-align="center"
align="center"
width="50"
/>
<el-table-column
prop="prodName"
header-align="center"
align="center"
label="产品名称"
/>
<el-table-column
align="center"
width="140"
label="产品图片"
>
<template #default="scope">
<img
alt=""
:src="scope.row.pic"
width="100"
height="100"
>
</template>
</el-table-column>
</el-table>
<el-pagination
:current-page="pageIndex"
:page-sizes="[10, 20, 50, 100]"
:page-size="pageSize"
:total="totalPage"
layout="total, sizes, prev, pager, next, jumper"
@size-change="sizeChangeHandle"
@current-change="currentChangeHandle"
/>
<template #footer>
<span>
<el-button @click="visible = false">取消</el-button>
<el-button
type="primary"
@click="submitProds()"
>确定</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import { ElMessage } from 'element-plus'
const emit = defineEmits(['refreshSelectProds'])
// eslint-disable-next-line no-unused-vars
const props = defineProps({
isSingle: {
default: false,
type: Boolean
}
})
const visible = ref(false)
const dataForm = reactive({
product: ''
})
const singleSelectProdId = ref(0)
const selectProds = ref([])
const dataList = ref([])
const pageIndex = ref(1)
const pageSize = ref(10)
const totalPage = ref(0)
const dataListLoading = ref(false)
const dataListSelections = ref([])
onMounted(() => {
getDataList()
})
/**
* 获取数据列表
*/
const init = (selectProdParam) => {
selectProds.value = selectProdParam
visible.value = true
dataListLoading.value = true
if (selectProds.value) {
selectProds.value?.forEach(row => {
dataListSelections.value.push(row)
})
}
getDataList()
}
defineExpose({ init })
const prodTableRef = ref(null)
const getDataList = () => {
http({
url: http.adornUrl('/prod/prod/page'),
method: 'get',
params: http.adornParams(
Object.assign(
{
current: pageIndex.value,
size: pageSize.value
},
{
prodName: dataForm.prodName
}
)
)
})
.then(({ data }) => {
dataList.value = data.records
totalPage.value = data.total
dataListLoading.value = false
if (selectProds.value) {
nextTick(() => {
selectProds.value?.forEach(row => {
const index = dataList.value?.findIndex((prodItem) => prodItem.prodId === row.prodId)
prodTableRef.value?.toggleRowSelection(dataList.value[index])
})
})
}
})
}
/**
* 每页数
* @param val
*/
const sizeChangeHandle = (val) => {
pageSize.value = val
pageIndex.value = 1
getDataList()
}
/**
* 当前页
* @param val
*/
const currentChangeHandle = (val) => {
pageIndex.value = val
getDataList()
}
/**
* 单选商品事件
* @param row
*/
const getSelectProdRow = (row) => {
dataListSelections.value = [row]
}
/**
* 多选点击事件
* @param selection
*/
const selectChangeHandle = (selection) => {
dataList.value?.forEach((tableItem) => {
const selectedProdIndex = selection.findIndex((selectedProd) => {
if (!selectedProd) {
return false
}
return selectedProd.prodId === tableItem.prodId
})
const dataSelectedProdIndex = dataListSelections.value?.findIndex((dataSelectedProd) => dataSelectedProd.prodId === tableItem.prodId)
if (selectedProdIndex > -1 && dataSelectedProdIndex === -1) {
dataListSelections.value.push(tableItem)
} else if (selectedProdIndex === -1 && dataSelectedProdIndex > -1) {
dataListSelections.value.splice(dataSelectedProdIndex, 1)
}
})
}
/**
* 确定事件
*/
const submitProds = () => {
if (!dataListSelections.value.length) {
ElMessage({
message: '请选择商品',
type: 'error',
duration: 1000,
onClose: () => {}
})
return
}
const prods = []
dataListSelections.value.forEach(item => {
const prodIndex = prods.findIndex((prod) => prod.prodId === item.prodId)
if (prodIndex === -1) {
prods.push({ prodId: item.prodId, prodName: item.prodName, pic: item.pic })
}
})
emit('refreshSelectProds', prods)
dataListSelections.value = []
visible.value = false
}
</script>

@ -0,0 +1,81 @@
<template>
<div class="components-add-or-upload">
<el-tooltip
v-if="props.tinymceUploadType === 'prod'"
content="内容"
placement="top"
>
<el-button
type="primary"
@click="clickUpload()"
>
<el-icon><UploadFilled /></el-icon>
上传图片
</el-button>
</el-tooltip>
<el-button
v-else
:style="{ background: props.color, borderColor: props.color }"
type="primary"
@click="clickUpload()"
>
<el-icon><UploadFilled /></el-icon>
上传图片
</el-button>
<!-- 弹窗, 新增图片 -->
<!-- <elx-imgbox-->
<!-- v-if="elxImgboxVisible"-->
<!-- ref="elxImgboxRef"-->
<!-- @refresh-pic="refreshPic"-->
<!-- />-->
</div>
</template>
<script setup>
// const emit = defineEmits(['successCBK'])
const props = defineProps({
color: {
type: String,
default: '#155bd4'
},
tinymceUploadType: {
default: '',
type: String
}
})
const elxImgboxVisible = ref(false)
// const maxNum = ref(15) //
const imgUrls = ref([])
// const resourcesUrl = import.meta.env.VITE_APP_RESOURCES_URL
// const elxImgboxRef = ref()
// const dialogVisible = ref()
/**
* 打开图片选择窗
*/
const clickUpload = () => {
imgUrls.value = ''
elxImgboxVisible.value = true
// nextTick(() => {
// elxImgboxRef.value?.init(0, maxNum.value)
// })
}
/**
* 接收回调的图片数据
*/
// const refreshPic = imagePath => {
// const imageArray = imagePath.split(',')
// const data = []
// imageArray.forEach(img => {
// data.push(resourcesUrl + img)
// })
// imgUrls.value = ''
// dialogVisible.value = false
// emit('successCBK', data)
// }
</script>
<style lang="scss" scoped>
.components-add-or-upload{
margin-bottom: 20px;
}
</style>

@ -0,0 +1,163 @@
<template>
<div class="components-tiny-mce">
<div
class="tinymce-container"
:class="{ 'tox-fullscreen': toxFullscreen }"
>
<Editor
:id="props.id"
:init="init"
api-key="i6mv006qcwsfu1t7ebisntg5w261vpowkwirnx9cnse3ho5o"
/>
<div
class="add-or-upload"
>
<add-or-upload
v-show="isLoaded"
@success-c-b-k="imageSuccessCBK"
/>
</div>
</div>
</div>
</template>
<script setup>
import Editor from '@tinymce/tinymce-vue'
import AddOrUpload from './add-or-upload.vue'
const props = defineProps({
modelValue: {
type: String,
default: ''
},
id: {
type: String,
default: function () {
return 'vue-tinymce-' + +new Date() + ((Math.random() * 1000).toFixed(0) + '')
}
},
height: {
type: [Number, String],
required: false,
default: 360
},
width: {
type: [Number, String],
required: false,
default: 'auto'
}
})
const toxFullscreen = ref(false)
const hasChange = ref(false)
const hasInit = ref(false)
watch(
() => props.modelValue,
val => {
if (!hasChange.value && hasInit.value) {
nextTick(() => window.tinymce.get(props.id).setContent(val || ''))
}
}
)
const language = computed(() => {
return localStorage.getItem('b2cLang') || 'zh_CN'
})
watch(language, () => {
destroyTinymce()
})
const emit = defineEmits(['update:modelValue'])
const isLoaded = ref(false)
const init = reactive({
plugins: 'preview anchor autolink codesample emoticons image link lists media searchreplace table visualblocks pagebreak insertdatetime fullscreen', // preview anchor autolink codesample emoticons image link lists media searchreplace table visualblocks wordcount pagebreak insertdatetime fullscreen
content_css: 'default', // tinymce-5-dark || tinymce-5 || default || writer || document || dark
custom_undo_redo_levels: 50, // 退
end_container_on_empty_block: true, //
keep_styles: false, // code: tinycomments
menubar: true, // > false | 'edit insert view format table tools help' | :https://www.tiny.cloud/docs/tinymce/6/menus-configuration-options/
toolbar_mode: 'wrap', // > | wrap | scrolling | sliding
toolbar: ' searchreplace bold italic underline strikethrough alignleft aligncenter alignright outdent indent blockquote undo redo removeformat subscript superscript code codesample hr numlist link image preview anchor pagebreak insertdatetime media table emoticons forecolor backcolor fullscreen',
powerpaste_word_import: 'clean',
toolbar_location: 'top', // > bottom | top
style_formats_merge: true, //
elementpath: false, // > p span
advlist_bullet_styles: 'square',
resize: true, // > true | false | both
language, //
init_instance_callback: editor => {
isLoaded.value = true
if (props.modelValue) {
editor.setContent(props.modelValue)
}
hasInit.value = true
editor.on('NodeChange Change KeyUp SetContent', () => {
hasChange.value = true
emit('update:modelValue', editor.getContent())
})
},
setup (editor) {
const addOrupload = document.querySelectorAll('.components-tiny-mce .tinymce-container .add-or-upload')
addOrupload.forEach(v => {
v.style.zIndex = 10
})
editor.on('FullscreenStateChanged', e => {
toxFullscreen.value = e.state
handleFullScreenStateChange(e.state)
})
}
})
const handleFullScreenStateChange = (isFullscreen) => {
const addOrupload = document.querySelectorAll('.components-tiny-mce .tinymce-container .add-or-upload')
if (isFullscreen) {
//
addOrupload.forEach(v => {
v.style.zIndex = 0
})
} else {
// 退
addOrupload.forEach(v => {
v.style.zIndex = 10
})
}
}
const destroyTinymce = () => {
const tinymce = window.tinymce?.get(props.id)
if (toxFullscreen.value) {
tinymce.execCommand('mceFullScreen')
}
onUnmounted(() => {
destroyTinymce()
})
if (tinymce) {
tinymce.destroy()
}
}
const imageSuccessCBK = (arr) => {
arr.forEach(v => {
window.tinymce.get(props.id).insertContent(`<img alt="" class="wscnph" src="${v}" >`)
})
}
</script>
<!--eslint-disable-next-line vue-scoped-css/enforce-style-type -->
<style lang="scss">
.components-tiny-mce {
.tox-fullscreen .add-or-upload {
z-index: 9999 !important;
position:fixed !important;
right: 0;
top: 0;
}
.tinymce-container {
position: relative;
.add-or-upload {
position: absolute;
top: 10px;
right: 10px;
}
}
}
.tox-tinymce-aux {
z-index: 9999 !important;
}
</style>

@ -0,0 +1,253 @@
<template>
<div class="mod-index-img">
<el-dialog
v-model="visible"
:title="!dataForm.imgId ? '新增' : '修改'"
:close-on-click-modal="false"
>
<el-form
ref="dataFormRef"
:model="dataForm"
:rules="dataRule"
label-width="100px"
>
<el-form-item
label="轮播图片"
prop="imgUrl"
>
<pic-upload v-model="dataForm.imgUrl" />
</el-form-item>
<el-form-item
label="顺序"
prop="seq"
:rules="[
{ required: false, pattern: /\s\S+|S+\s|\S/, message: '请输入正确的顺序', trigger: 'blur' }
]"
>
<el-input v-model="dataForm.seq" />
</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-item label="类型">
<el-radio-group
v-model="dataForm.type"
@change="deleteRelation"
>
<el-radio :label="-1">
</el-radio>
<el-radio :label="0">
商品
</el-radio>
</el-radio-group>
<div v-if="dataForm.relation!=null">
<el-card
:body-style="{ padding: '0px' }"
style="height: 160px;width: 120px"
>
<img
alt=""
:src="card.pic"
style="height:104px;width:100%"
>
<div class="card-prod-bottom">
<span class="card-prod-name">{{ card.name }}</span>
<el-button
type="text"
class="card-prod-name-button"
@click="deleteRelation"
>
删除
</el-button>
</div>
</el-card>
</div>
<div v-if="dataForm.relation==null">
<el-button
v-if=" dataForm.type == 0"
@click="addProd"
>
选择商品
</el-button>
</div>
</el-form-item>
<el-form-item>
<el-button
type="primary"
@click="onSubmit()"
>
确定
</el-button>
</el-form-item>
</el-form>
</el-dialog>
<!-- 商品选择弹窗-->
<prods-select
v-if="prodsSelectVisible"
ref="prodsSelectRef"
:is-single="true"
@refresh-select-prods="selectCouponProds"
/>
</div>
</template>
<script setup>
import { ElMessage } from 'element-plus'
import { Debounce } from '@/utils/debounce'
const emit = defineEmits(['refreshDataList'])
const dataForm = ref({
status: 1,
des: '',
imgUrl: '',
seq: 0,
imgId: 0,
type: -1,
relation: null
})
const dataRule = reactive({
imgUrl: [
{ required: true, message: '轮播图片不能为空', trigger: 'blur' }
]
})
//
const card = ref({
id: 0,
pic: '',
name: '',
realData: {
prod: [],
shop: [],
activity: []
}
})
const page = reactive({
total: 0, //
currentPage: 1, //
pageSize: 10 //
})
const prodsSelectVisible = ref(false)
const visible = ref(false)
const dataFormRef = ref(null)
/**
* 获取分类数据
* @param id
*/
const init = (id) => {
visible.value = true
dataForm.value.imgId = id || 0
if (dataForm.value.imgId) {
//
http({
url: http.adornUrl(`/admin/indexImg/info/${dataForm.value.imgId}`),
method: 'get'
})
.then(({ data }) => {
dataForm.value = data
if (data.relation) {
card.value.pic = data.pic
card.value.name = data.prodName
card.value.id = data.relation
}
})
} else {
nextTick(() => {
dataFormRef.value?.resetFields()
dataForm.value.imgUrl = ''
})
}
}
defineExpose({ init })
/**
* 表单提交
*/
const onSubmit = Debounce(() => {
dataFormRef.value?.validate((valid) => {
if (!valid) {
return
}
const param = dataForm.value
http({
url: http.adornUrl('/admin/indexImg'),
method: param.imgId ? 'put' : 'post',
data: http.adornData(param)
})
.then(() => {
ElMessage({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
visible.value = false
emit('refreshDataList', page)
}
})
})
})
})
/**
* 删除关联数据
*/
const deleteRelation = () => {
dataForm.value.relation = null
}
const prodsSelectRef = ref(null)
/**
* 打开选择商品
*/
const addProd = () => {
prodsSelectVisible.value = true
nextTick(() => {
prodsSelectRef.value?.init(card.value.realData.prod)
})
}
/**
* 添加指定商品
*/
const selectCouponProds = (prods) => {
card.value.realData.prods = prods
if (prods.length) {
const selectProd = prods[0]
dataForm.value.relation = selectProd.prodId
card.value.pic = selectProd.pic
card.value.name = selectProd.prodName
card.value.id = selectProd.prodId
} else {
card.value = {}
}
}
</script>
<style lang="scss" scoped>
//card
.card-prod-bottom {
position: relative;
text-align: left;
.card-prod-name {
margin: auto;
padding: 0 6px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 118px;
display: inline-block;
}
.card-prod-name-button {
position: absolute;
top: 24px;
right: 10px;
}
}
</style>

@ -0,0 +1,177 @@
<template>
<div class="mod-prod">
<avue-crud
ref="crudRef"
:page="page"
:data="dataList"
:table-loading="dataListLoading"
:option="tableOption"
@search-change="onSearch"
@selection-change="selectionChange"
@on-load="getDataList"
>
<template #menu-left>
<el-button
v-if="isAuth('admin:indexImg:save')"
type="primary"
icon="el-icon-plus"
@click.stop="onAddOrUpdate()"
>
新增
</el-button>
<el-button
v-if="isAuth('admin:indexImg:delete')"
type="danger"
:disabled="dataListSelections.length <= 0"
@click="onDelete()"
>
批量删除
</el-button>
</template>
<template #imgUrl="scope">
<img
v-if="scope.row.imgUrl"
alt=""
:src="scope.row.imgUrl"
width="100"
height="100"
>
<img
v-else
alt=""
src="~@/assets/img/def.png"
width="100"
height="100"
>
</template>
<template #menu="scope">
<el-button
v-if="isAuth('admin:indexImg:update')"
type="primary"
icon="el-icon-edit"
@click="onAddOrUpdate(scope.row.imgId)"
>
修改
</el-button>
<el-button
v-if="isAuth('admin:indexImg:delete')"
type="danger"
icon="el-icon-delete"
@click="onDelete(scope.row.imgId)"
>
删除
</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/admin/indexImg.js'
const dataList = ref([])
const dataListLoading = ref(false)
const dataListSelections = ref([])
const resourcesUrl = import.meta.env.VITE_APP_RESOURCES_URL
const page = reactive({
total: 0, //
currentPage: 1, //
pageSize: 10 //
})
/**
* 获取数据列表
*/
const getDataList = (pageParam, params, done) => {
dataListLoading.value = true
http({
url: http.adornUrl('/admin/indexImg/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 }) => {
data.records.forEach(item => {
item.imgUrl = item.imgUrl ? resourcesUrl + item.imgUrl : ''
})
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)
})
}
/**
* 删除
* @param id
*/
const onDelete = (id) => {
const ids = id ? [id] : dataListSelections.value?.map(item => {
return item.imgId
})
ElMessageBox.confirm(`确定进行[${id ? '删除' : '批量删除'}]操作?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
.then(() => {
http({
url: http.adornUrl('/admin/indexImg'),
method: 'delete',
data: http.adornData(ids, false)
})
.then(() => {
ElMessage({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
getDataList()
}
})
})
})
}
/**
* 条件查询
*/
const onSearch = (params, done) => {
getDataList(page, params, done)
}
/**
* 多选变化
*/
const selectionChange = (val) => {
dataListSelections.value = val
}
</script>

@ -0,0 +1,101 @@
<template>
<!-- 发货信息用于导出代发货订单的excel交给快递公司 -->
<el-dialog
v-model="visible"
:modal="false"
title="请输入发货信息"
:close-on-click-modal="false"
width="38%"
>
<el-form
ref="dataFormRef"
:model="dataForm"
:rules="dataRule"
label-width="120px"
@keyup.enter="onSubmit()"
>
<el-form-item
label="发件人姓名"
prop="consignmentName"
>
<el-input
v-model="dataForm.consignmentName"
controls-position="right"
label="发件人姓名"
/>
</el-form-item>
<el-form-item
label="发货人手机号"
prop="consignmentMobile"
>
<el-input
v-model="dataForm.consignmentMobile"
controls-position="right"
label="发货人手机号"
/>
</el-form-item>
<el-form-item
label="发货地址"
prop="consignmentAddr"
>
<el-input
v-model="dataForm.consignmentAddr"
controls-position="right"
label="发货地址"
/>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button
@click="visible = false"
>取消</el-button>
<el-button
type="primary"
@click="onSubmit()"
>确定</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
const emit = defineEmits(['inputCallback'])
const visible = ref(false)
const dataForm = reactive({
consignmentName: '',
consignmentMobile: '',
consignmentAddr: ''
})
const dataRule = {
consignmentName: [
{ required: true, message: '不能为空', trigger: 'blur' }
],
consignmentMobile: [
{ required: true, message: '不能为空', trigger: 'blur' }
],
consignmentAddr: [
{ required: true, message: '不能为空', trigger: 'blur' }
]
}
const dataFormRef = ref(null)
const init = () => {
visible.value = true
nextTick(() => {
dataFormRef.value?.resetFields()
})
}
defineExpose({ init })
/**
* 表单提交
*/
const onSubmit = () => {
dataFormRef.value?.validate((valid) => {
if (valid) {
visible.value = false
emit('inputCallback', dataForm)
}
})
}
</script>

@ -0,0 +1,124 @@
<template>
<el-dialog
v-model="visible"
:modal="false"
title="选择发货地址"
:close-on-click-modal="false"
>
<el-form
ref="dataFormRef"
:model="dataForm"
:rules="dataRule"
label-width="80px"
@keyup.enter="onSubmit()"
>
<el-form-item label="快递公司">
<el-select
v-model="dataForm.dvyId"
placeholder="请选择"
>
<el-option
v-for="item in dataForm.dvyNames"
:key="item.dvyId"
:label="item.dvyName"
:value="item.dvyId"
/>
</el-select>
</el-form-item>
<el-form-item
label="快递单号"
prop="dvyFlowId"
>
<el-input
v-model="dataForm.dvyFlowId"
controls-position="right"
:min="0"
label="快递单号"
/>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="visible = false">取消</el-button>
<el-button
type="primary"
@click="onSubmit()"
>确定</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import { ElMessage } from 'element-plus'
const emit = defineEmits(['refreshDataList'])
// eslint-disable-next-line no-unused-vars
const validDvyFlowId = (rule, value, callback) => {
if (!value.trim()) {
callback(new Error('不能为空'))
} else {
callback()
}
}
const dataRule = {
dvyFlowId: [
{ required: true, message: '不能为空', trigger: 'blur' },
{ validator: validDvyFlowId, trigger: 'blur' }
]
}
const visible = ref(false)
const dataForm = reactive({
dvyId: '',
dvyFlowId: 0,
dvyNames: [],
orderNumber: 0
})
const init = (orderNumber, dvyId, dvyFlowId) => {
visible.value = true
dataForm.orderNumber = orderNumber || ''
dataForm.dvyId = dvyId || ''
dataForm.dvyFlowId = dvyFlowId || ''
http({
url: http.adornUrl('/admin/delivery/list'),
method: 'get',
params: http.adornParams()
}).then(({ data }) => {
dataForm.dvyNames = data
})
}
defineExpose({ init })
const dataFormRef = ref(null)
/**
* 表单提交
*/
const onSubmit = () => {
dataFormRef.value?.validate((valid) => {
if (valid) {
http({
url: http.adornUrl('/order/order/delivery'),
method: 'put',
data: http.adornData({
orderNumber: dataForm.orderNumber,
dvyId: dataForm.dvyId,
dvyFlowId: dataForm.dvyFlowId
})
})
.then(() => {
ElMessage({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
visible.value = false
emit('refreshDataList')
}
})
})
}
})
}
</script>

@ -0,0 +1,514 @@
<template>
<el-dialog
v-model="visible"
:title="!dataForm.orderNumber ? '新增' : '查看'"
:close-on-click-modal="false"
width="80%"
>
<el-form
ref="dataFormRef"
:model="dataForm"
label-width="80px"
@keyup.enter="onSubmit()"
>
<div class="main">
<div class="content">
<div class="order-number">
<div class="num-cont">
<el-form-item label="订单编号:">
<span class="text">{{ dataForm.orderNumber }}</span>
</el-form-item>
<el-form-item>
<el-steps
:active="stepsStatus"
align-center
:process-status="dataForm.status == 6 ? 'error':'wait'"
>
<el-step
title="提交订单"
:description="dataForm.orderTime"
/>
<el-step
title="买家已付款"
:description="dataForm.payTime"
/>
<el-step
v-if="dataForm.orderType !== 1"
title="卖家已发货"
:description="dataForm.dvyTime"
/>
<el-step
v-if="dataForm.orderType !== 1"
title="买家已收货"
:description="dataForm.finallyTime"
/>
</el-steps>
</el-form-item>
</div>
</div>
<div class="order-state">
<div class="state-cont">
<div class="state-title">
<el-form-item label="订单状态:">
<template #default>
<el-tag
v-if="dataForm.status === 1"
type="warning"
>
待付款
</el-tag>
<el-tag
v-if="dataForm.status === 2 && dataForm.orderType !== 1"
type="warning"
>
待发货
</el-tag>
<el-tag
v-if="dataForm.status === 3 && dataForm.orderType !== 1"
type="warning"
>
待收货
</el-tag>
<el-tag
v-if="dataForm.status === 4 && dataForm.orderType !== 1"
type="warning"
>
待评价
</el-tag>
<el-tag
v-if="dataForm.status === 5"
type="success"
>
成功
</el-tag>
<el-tag
v-if="dataForm.status === 6"
type="danger"
>
失败
</el-tag>
</template>
</el-form-item>
<el-form-item>
<el-row>
<el-button
v-if="dataForm.status === 2 && dataForm.orderType !== 1"
type="primary"
plain
@click="changeOrder(dataForm.orderNumber)"
>
发货
</el-button>
</el-row>
</el-form-item>
</div>
<div class="order-info">
<div class="order-details">
<div class="detail-title">
<img
src="~@/assets/img/car.png"
alt=""
>
<span class="prompt">买家付款后才可以发货</span>
</div>
<div class="detail-cont">
<div class="detail01">
<img
src="~@/assets/img/address.png"
alt=""
>
<div class="text-width">
<el-form-item label="收货人:">
<span>{{ dataForm.userAddrOrder.receiver }}</span>
</el-form-item>
<el-form-item label="手机:">
<span>{{ dataForm.userAddrOrder.mobile }}</span>
</el-form-item>
<el-form-item label="收货地址">
<span>{{ dataForm.userAddrOrder.province }}{{ dataForm.userAddrOrder.city }}{{ dataForm.userAddrOrder.area }}{{ dataForm.userAddrOrder.addr }}</span>
</el-form-item>
</div>
</div>
<div class="detail01">
<img
src="~@/assets/img/invoice.png"
alt=""
>
<span>不开发票</span>
</div>
</div>
</div>
<div class="buyers">
<div class="detail-title">
<img
src="~@/assets/img/buyer.png"
alt=""
style="margin-right:15px"
>
<el-form-item
label="买家:"
style="margin-top:22px"
label-width="44px"
>
<span>{{ dataForm.nickName }}</span>
</el-form-item>
</div>
<div class="buyers-info">
<div class="detail02">
<img
src="~@/assets/img/message.png"
alt=""
>
<div class="text-width">
<span>买家备注:</span>
<br>
<span>{{ dataForm.remarks }}</span>
</div>
</div>
</div>
</div>
</div>
<div class="item-list">
<el-table
:data="dataForm.orderItems"
border
>
<el-table-column
prop=""
label="商品"
>
<template #default="scope">
<div class="prod-con">
<img
:src="resourcesUrl + scope.row.pic"
class="prod-img"
>
<span>{{ scope.row.prodName }}</span>
</div>
</template>
</el-table-column>
<el-table-column
prop="price"
label="单价"
width="180"
align="center"
>
<template #default="scope">
<span>{{ scope.row.price }}</span>
</template>
</el-table-column>
<el-table-column
prop="count"
label="数量"
width="180"
align="center"
>
<template #default="scope">
<span>{{ scope.row.prodCount }}</span>
</template>
</el-table-column>
<el-table-column
prop="totalPrice"
label="总价"
width="180"
align="center"
>
<template #default="scope">
<span>{{ scope.row.productTotalAmount }}</span>
</template>
</el-table-column>
</el-table>
</div>
<div class="item-info">
<el-form-item label="商品总价:">
<span class="text">{{ dataForm.total }}</span>
</el-form-item>
<el-form-item
v-if="dataForm.freightAmount"
label="配送费用:"
>
<span class="text">{{ dataForm.freightAmount }}</span>
</el-form-item>
<el-form-item label="应付金额:">
<span class="text">{{ dataForm.actualTotal }}</span>
</el-form-item>
</div>
</div>
</div>
<div class="order-log">
<div class="log-title">
<span>订单日志</span>
</div>
<div class="log-cont">
<el-form-item
v-if="dataForm.orderTime"
label-width="10px"
>
<span>{{ dataForm.orderTime }} {{ dataForm.nickName }} 创建订单成功</span>
</el-form-item>
<el-form-item
v-if="dataForm.updateTime"
label-width="10px"
>
<span>{{ dataForm.updateTime }} {{ dataForm.nickName }} 订单更新成功</span>
</el-form-item>
<el-form-item
v-if="dataForm.payTime"
label-width="10px"
>
<span>{{ dataForm.payTime }} {{ dataForm.nickName }} 订单付款成功</span>
</el-form-item>
<el-form-item
v-if="dataForm.dvyTime"
label-width="10px"
>
<span>{{ dataForm.dvyTime }} {{ dataForm.nickName }} 订单发货成功</span>
</el-form-item>
<el-form-item
v-if="dataForm.finallyTime"
label-width="10px"
>
<span>{{ dataForm.finallyTime }} {{ dataForm.nickName }} 完成订单成功</span>
</el-form-item>
<el-form-item
v-if="dataForm.cancelTime"
label-width="10px"
>
<span>{{ dataForm.cancelTime }} {{ dataForm.nickName }} 取消订单成功</span>
</el-form-item>
</div>
</div>
</div>
</div>
</el-form>
<!-- 弹窗, 新增 / 修改 -->
<devy-add
v-if="devyVisible"
ref="devyAddRef"
@refresh-data-list="getDataList"
/>
</el-dialog>
</template>
<script setup>
import DevyAdd from './order-devy.vue'
const dataForm = ref({
orderId: 0,
orderNumber: '',
remarks: '',
total: 0,
actualTotal: 0,
dvyType: '',
status: 1,
addrOrderId: 0,
nickName: '',
orderItems: [],
orderTime: '',
updateTime: '',
payTime: '',
dvyTime: '',
finallyTime: '',
cancelTime: '',
userAddrOrder: {}
})
const stepsStatus = computed(() => {
if (dataForm.value.finallyTime) {
return 4
}
if (dataForm.value.dvyTime) {
return 3
}
if (dataForm.value.payTime) {
return 2
}
if (dataForm.value.orderTime) {
return 1
}
})
const visible = ref(false)
const devyVisible = ref(false)
watch(
() => visible.value,
() => {
if (!visible.value) {
devyVisible.value = false
}
}
)
const dataFormRef = ref(null)
const init = (orderNumber) => {
dataForm.value.orderNumber = orderNumber || 0
visible.value = true
nextTick(() => {
dataFormRef.value?.resetFields()
})
if (dataForm.value.orderNumber) {
//
http({
url: http.adornUrl(`/order/order/orderInfo/${dataForm.value.orderNumber}`),
method: 'get',
params: http.adornParams()
})
.then(({ data }) => {
dataForm.value = data
})
}
}
defineExpose({ init })
const getDataList = () => {
http({
url: http.adornUrl(`/order/order/orderInfo/${dataForm.value.orderNumber}`),
method: 'get',
params: http.adornParams()
})
.then(({ data }) => {
dataForm.value = data
})
}
const devyAddRef = ref(null)
/**
* 发货
* @param orderNumber
*/
const changeOrder = (orderNumber) => {
devyVisible.value = true
nextTick(() => {
devyAddRef.value?.init(orderNumber, dataForm.value.dvyId, dataFormRef.value?.dvyFlowId)
})
}
</script>
<style scoped lang="scss">
.main {
height: 100%;
width: 100%;
font: 14px Arial, "PingFang SC", "Hiragino Sans GB", STHeiti, "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif;
color: #495060;
}
.content {
margin: 0 20px;
.order-state {
position: relative;
margin-top: 50px;
border-bottom: 1px solid #e9eaec;
}
}
.order-number {
.text {
font-size: 14px;
color: #8a8a8a;
}
}
.order-state {
.state-title {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
}
.order-info {
width: 100%;
border-top: 1px solid #e9eaec;
margin: 50px 0;
display: flex;
}
.item-info {
padding-left: 80%;
margin: 25px 0;
}
}
.order-info {
img {
width: 18px !important;
height: 18px !important;
margin-right: 15px;
}
.detail-title {
height: 50px;
line-height: 50px;
display: flex;
align-items: center;
}
.order-details {
width: 50%;
border-right: 1px solid #e9eaec;
}
.detail-cont {
position: relative;
border-top: 1px dashed #e9eaec;
margin: 15px 20px 0 0;
}
.buyers {
width: 50%;
margin-left: 20px;
}
}
.detail-cont {
.detail01 {
display: flex;
height: 100%;
line-height: 25px;
margin-top: 15px;
}
}
.detail01 {
.text-width {
width: 100%;
}
}
.detail02 {
.text-width {
width: 100%;
}
}
.buyers {
.buyers-info {
border-top: 1px dashed #e9eaec;
margin-top: 15px;
position: relative;
}
.detail02 {
display: flex;
height: 100%;
line-height: 25px;
margin-top: 15px;
}
}
.item-info {
span {
margin-bottom: 15px;
line-height: 30px;
}
.text {
position: absolute;
right: 0;
}
}
.order-log {
.log-title {
height: 50px;
width: 100%;
line-height: 50px;
font-weight: bold;
}
.log-cont {
color: #4395ff;
}
}
.item-list {
.prod-con {
display: flex;
.prod-img {
width: 100px;
height: 100px;
margin-right: 8px;
}
}
}
</style>

@ -0,0 +1,556 @@
<template>
<div class="mod-order-order">
<el-form
:inline="true"
:model="dataForm"
@keyup.enter="getDataList(page)"
>
<el-form-item label="订单编号:">
<el-input
v-model="dataForm.orderNumber"
placeholder="订单编号"
clearable
/>
</el-form-item>
<el-form-item label="下单时间:">
<el-date-picker
v-model="dateRange"
type="datetimerange"
range-separator="至"
value-format="yyyy-MM-dd HH:mm:ss"
start-placeholder="开始日期"
end-placeholder="结束日期"
/>
</el-form-item>
<el-form-item label="订单状态:">
<el-select
v-model="dataForm.status"
clearable
placeholder="请选择订单状态"
>
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button
type="primary"
icon="el-icon-search"
@click="getDataList()"
>
查询
</el-button>
<el-button
v-if="isAuth('order:order:waitingConsignmentExcel')"
type="primary"
@click="showConsignmentInfo()"
>
导出待发货订单
</el-button>
<el-button
v-if="isAuth('order:order:soldExcel')"
type="primary"
@click="getSoldExcel()"
>
导出销售记录
</el-button>
<el-button @click="clearDatas()">
清空
</el-button>
</el-form-item>
</el-form>
<div class="main">
<div class="content">
<div class="tit">
<el-row style="width:100%">
<el-col :span="10">
<span class="item product">商品</span>
</el-col>
<el-col :span="3">
<span class="item">成交单价/购买数量</span>
</el-col>
<el-col :span="3">
<span class="item">实付金额</span>
</el-col>
<el-col :span="3">
<span class="item">支付方式</span>
</el-col>
<el-col :span="3">
<span class="item">订单状态</span>
</el-col>
<el-col :span="2">
<span class="item">操作</span>
</el-col>
</el-row>
</div>
<div
v-for="order in dataList"
:key="order.orderId"
class="prod"
>
<div class="prod-tit">
<span>订单编号{{ order.orderNumber }}</span>
<span>下单时间{{ order.createTime }}</span>
</div>
<div class="prod-cont">
<el-row style="width:100%">
<el-col :span="12">
<div class="prod-item">
<div
v-for="orderItem in order.orderItems"
:key="orderItem.orderItemId"
class="items name"
>
<div class="prod-image">
<img
alt=""
:src="resourcesUrl + orderItem.pic"
style="height:100px;width: 100px;"
>
</div>
<div class="prod-name">
<span>{{ orderItem.prodName }}</span>
<span class="prod-info">{{ orderItem.skuName }}</span>
</div>
<div class="prod-price">
<span>{{ orderItem.price }}</span>
<span>×{{ orderItem.prodCount }}</span>
</div>
</div>
</div>
</el-col>
<el-col
:span="3"
style="height: 100%;"
>
<div class="item">
<div>
<span class="totalprice">{{ order.actualTotal }}</span>
<span v-if="order.freightAmount">{{ order.freightAmount }}</span>
<span>{{ order.productNums }}</span>
</div>
</div>
</el-col>
<el-col
:span="3"
style="height: 100%;"
>
<div class="item">
<div>
<span v-if="order.payType === 1"></span>
<span v-else-if="order.payType === 2">支付宝</span>
<span v-else></span>
</div>
</div>
</el-col>
<el-col
:span="3"
style="height: 100%;"
>
<div class="item">
<span
v-if="order.status === 1"
type="danger"
>待付款</span>
<span
v-else-if="order.status === 2"
type="danger"
>待发货</span>
<span
v-else-if="order.status === 3"
type="danger"
>待收货</span>
<span
v-else-if="order.status === 4"
type="danger"
>待评价</span>
<span
v-else-if="order.status === 5"
type="danger"
>成功</span>
<span
v-else
>失败</span>
</div>
</el-col>
<el-col
:span="3"
style="height: 100%;"
>
<div class="item">
<div class="operate">
<el-button
v-if="isAuth('order:order:update')"
type="text"
@click="onAddOrUpdate(order.orderNumber)"
>
查看
</el-button>
</div>
</div>
</el-col>
</el-row>
</div>
<div class="remark">
<div class="buyer-remark">
<span>备注:{{ order.remarks }}</span>
</div>
</div>
</div>
</div>
</div>
<!-- -->
<div class="empty-tips">
暂无数据
</div>
<el-pagination
:current-page="page.pageIndex"
:page-sizes="[10, 20, 50, 100]"
:page-size="page.pageSize"
:total="page.total"
layout="total, sizes, prev, pager, next, jumper"
@size-change="sizeChangeHandle"
@current-change="currentChangeHandle"
/>
<!-- 弹窗, 新增 / 修改 -->
<add-or-update
v-if="addOrUpdateVisible"
ref="addOrUpdateRef"
@refresh-data-list="getDataList"
/>
<consignment-info
v-if="consignmentInfoVisible"
ref="consignmentInfoRef"
@input-callback="getWaitingConsignmentExcel"
/>
</div>
</template>
<script setup>
import AddOrUpdate from './components/order-info.vue'
import ConsignmentInfo from './components/consignment-info.vue'
import { isAuth } from '@/utils'
const resourcesUrl = import.meta.env.VITE_APP_RESOURCES_URL
const dataForm = ref({})
const dateRange = ref([])
const options = [{
value: 1,
label: '待付款'
},
{
value: 2,
label: '待发货'
},
{
value: 3,
label: '待收货'
},
{
value: 4,
label: '待评价'
},
{
value: 5,
label: '成功'
},
{
value: 6,
label: '失败'
}]
const dataList = ref([])
const page = reactive({
total: 0, //
currentPage: 1, //
pageSize: 10 //
})
onMounted(() => {
getDataList(page)
})
/**
* 获取数据列表
*/
const getDataList = (pageParam, params, done) => {
pageParam = (pageParam === undefined ? page : pageParam)
http({
url: http.adornUrl('/order/order/page'),
method: 'get',
params: http.adornParams(
Object.assign(
{
current: pageParam == null ? page.currentPage : pageParam.currentPage,
size: pageParam == null ? page.pageSize : pageParam.pageSize,
orderNumber: dataForm.value.orderNumber,
status: dataForm.value.status,
startTime: dateRange.value === null ? null : dateRange.value[0], //
endTime: dateRange.value === null ? null : dateRange.value[1] //
},
params
), false
)
})
.then(({ data }) => {
dataList.value = data.records
page.total = data.total
if (done) done()
})
}
/**
* 清除数据
*/
const clearDatas = () => {
dataForm.value = {}
dateRange.value = []
}
/**
* 每页数
* @param val
*/
const sizeChangeHandle = (val) => {
page.pageSize = val
page.currentPage = 1
getDataList(page)
}
/**
* 当前页
* @param val
*/
const currentChangeHandle = (val) => {
page.currentPage = val
getDataList(page)
}
const addOrUpdateRef = ref(null)
const addOrUpdateVisible = ref(false)
/**
* 新增 / 修改
* @param val
*/
const onAddOrUpdate = (val) => {
addOrUpdateVisible.value = true
nextTick(() => {
addOrUpdateRef.value?.init(val)
})
}
const consignmentInfoRef = ref(null)
const consignmentInfoVisible = ref(false)
const showConsignmentInfo = () => {
consignmentInfoVisible.value = true
nextTick(() => {
consignmentInfoRef.value?.init()
})
}
const getWaitingConsignmentExcel = (consignmentInfo) => {
http({
url: http.adornUrl('/order/order/waitingConsignmentExcel'),
method: 'get',
params: http.adornParams({
consignmentName: consignmentInfo.consignmentName,
consignmentMobile: consignmentInfo.consignmentMobile,
consignmentAddr: consignmentInfo.consignmentAddr,
startTime: dateRange.value === null ? null : dateRange.value[0], //
endTime: dateRange.value === null ? null : dateRange.value[1] //
}),
responseType: 'blob' //
})
.then(({ data }) => {
const blob = new Blob([data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8' })
const fileName = '待发货信息整理.xls'
const elink = document.createElement('a')
if ('download' in elink) { // IE
elink.download = fileName
elink.style.display = 'none'
elink.href = URL.createObjectURL(blob)
document.body.appendChild(elink)
elink.click()
URL.revokeObjectURL(elink.href) // URL
document.body.removeChild(elink)
} else { // IE10+
navigator.msSaveBlob(blob, fileName)
}
})
}
const getSoldExcel = () => {
http({
url: http.adornUrl('/order/order/soldExcel'),
method: 'get',
params: http.adornParams({
startTime: dateRange.value === null ? null : dateRange.value[0], //
endTime: dateRange.value === null ? null : dateRange.value[1] //
}),
responseType: 'blob' //
})
.then(({ data }) => {
const blob = new Blob([data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8' })
const fileName = '销售信息整理.xls'
const elink = document.createElement('a')
if ('download' in elink) { // IE
elink.download = fileName
elink.style.display = 'none'
elink.href = URL.createObjectURL(blob)
document.body.appendChild(elink)
elink.click()
URL.revokeObjectURL(elink.href) // URL
document.body.removeChild(elink)
} else { // IE10+
navigator.msSaveBlob(blob, fileName)
}
})
}
</script>
<style lang="scss" scoped>
.mod-order-order {
.tit {
display: flex;
height: 45px;
align-items: center;
}
.tit {
.item {
padding: 0 10px;
width: 10%;
text-align: center;
}
.product {
width: 25%;
}
}
.prod-tit {
padding: 10px;
background: #f8f8f9;
border-left: 1px solid #dddee1;
border-top: 1px solid #dddee1;
border-right: 1px solid #dddee1;
span {
margin-right: 15px;
}
}
.prod-cont {
display: flex;
border-top: 1px solid #dddee1;
border-bottom: 1px solid #dddee1;
border-left: 1px solid #dddee1;
color: #495060;
.item {
display: flex;
display: -webkit-flex;
align-items: center;
justify-content: center;
padding: 10px;
border-right: 1px solid #dddee1;
text-align: center;
height: 100%;
span {
display: block;
}
}
.prod-item {
display: flex;
flex-direction: column;
border-right: 1px solid #dddee1;
}
.items.name {
display: flex;
position: relative;
padding: 20px;
border-bottom: 1px solid #dddee1;
&:last-child {
border-bottom: none;
}
}
}
.prod-name {
width: 55%;
text-align: left;
.prod-info {
display: block;
color: #80848f;
margin-top: 30px;
}
}
.prod-price {
position: absolute;
right: 40px;
text-align: right;
span {
display: block;
margin-bottom: 10px;
}
}
.prod-image {
margin-right: 20px;
width: 100px;
height: 100px;
img {
width: 100px;
height: 100px;
}
}
.item {
span {
display: block;
margin-bottom: 10px;
}
.operate {
color: #2d8cf0;
}
.totalprice {
color: #c00;
}
}
.prod {
.remark {
width: 100%;
height: 50px;
line-height: 50px;
background-color: #e8f7f6;
border-left: 1px solid #dddee1;
border-right: 1px solid #dddee1;
border-bottom: 1px solid #dddee1;
margin-bottom: 20px;
}
}
.buyer-remark {
padding: 0 20px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.empty-tips {
display: block;
width: 100%;
text-align: center;
margin: 50px 0;
color: #999;
}
}
</style>

@ -0,0 +1,163 @@
<template>
<div class="mod-hotSearch-add-or-update">
<el-dialog
v-model="visible"
:title="!dataForm.hotSearchId ? '新增' : '修改'"
:close-on-click-modal="false"
>
<el-form
ref="dataFormRef"
:model="dataForm"
:rules="dataRule"
label-width="80px"
@keyup.enter="onSubmit()"
>
<el-form-item
label="标题"
prop="title"
>
<el-input
v-model="dataForm.title"
controls-position="right"
:min="0"
maxlength="50"
show-word-limit
label="标题"
/>
</el-form-item>
<el-form-item
label="内容"
prop="content"
>
<el-input
v-model="dataForm.content"
controls-position="right"
type="textarea"
:min="0"
maxlength="255"
show-word-limit
label="内容"
/>
</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-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>
</div>
</template>
<script setup>
import { ElMessage } from 'element-plus'
import { Debounce } from '@/utils/debounce'
const emit = defineEmits(['refreshDataList'])
const dataForm = ref({
hotSearchId: 0,
title: '',
content: '',
recDate: '',
seq: 0,
status: 0
})
const page = reactive({
total: 0, //
currentPage: 1, //
pageSize: 10 //
})
const visible = ref(false)
const dataRule = {
title: [
{ required: true, message: '标题不能为空', trigger: 'blur' },
{ min: 1, max: 50, message: '长度在1到50个字符内', trigger: 'blur' },
{ pattern: /\s\S+|S+\s|\S/, message: '标题不能为空', trigger: 'blur' }
],
content: [
{ required: true, message: '内容不能为空', trigger: 'blur' },
{ min: 1, max: 255, message: '长度在1到255个字符内', trigger: 'blur' },
{ pattern: /\s\S+|S+\s|\S/, message: '内容不能为空', trigger: 'blur' }
]
}
const dataFormRef = ref(null)
const init = (id) => {
dataForm.value.hotSearchId = id || 0
visible.value = true
nextTick(() => {
dataFormRef.value?.resetFields()
if (dataForm.value.hotSearchId) {
http({
url: http.adornUrl('/admin/hotSearch/info/' + dataForm.value.hotSearchId),
method: 'get',
params: http.adornParams()
})
.then(({ data }) => {
dataForm.value = data
})
}
})
}
defineExpose({ init })
/**
* 表单提交
*/
const onSubmit = Debounce(() => {
dataFormRef.value?.validate(valid => {
if (valid) {
const param = dataForm.value
http({
url: http.adornUrl('/admin/hotSearch'),
method: param.hotSearchId ? 'put' : 'post',
data: http.adornData(param)
})
.then(() => {
ElMessage({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
visible.value = false
emit('refreshDataList', page)
}
})
})
}
})
})
</script>

@ -0,0 +1,172 @@
<template>
<div class="mod-hotSearcch">
<avue-crud
ref="crudRef"
:page="page"
:data="dataList"
:table-loading="dataListLoading"
:option="tableOption"
@search-change="onSearch"
@on-load="getDataList"
@refresh-change="refreshChange"
@selection-change="selectionChange"
>
<template #menu-left>
<el-button
v-if="isAuth('admin:hotSearch:save')"
type="primary"
icon="el-icon-plus"
@click="onAddOrUpdate()"
>
新增
</el-button>
<el-button
type="danger"
:disabled="dataListSelections.length <= 0"
@click.stop="onDeconste"
>
批量删除
</el-button>
</template>
<template #status="scope">
<el-tag
v-if="scope.row.status === 0"
type="danger"
>
未启用
</el-tag>
<el-tag v-else>
启用
</el-tag>
</template>
<template #menu="scope">
<el-button
v-if="isAuth('admin:hotSearch:update')"
type="primary"
icon="el-icon-edit"
@click="onAddOrUpdate(scope.row.hotSearchId)"
>
修改
</el-button>
<el-button
v-if="isAuth('admin:hotSearch:deconste')"
type="danger"
icon="el-icon-deconste"
@click.stop="onDeconste(scope.row,scope.index)"
>
删除
</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 { tableOption } from '@/crud/shop/hotSearch.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 dataListSelections = ref([])
/**
* 获取数据列表
*/
const getDataList = (pageParam, params, done) => {
dataListLoading.value = true
http({
url: http.adornUrl('/admin/hotSearch/page'),
method: 'get',
params: http.adornParams(Object.assign({
current: pageParam ? pageParam.currentPage : 1,
size: pageParam ? pageParam.pageSize : 20
}, params))
})
.then(({ data }) => {
page.total = data.total
page.pageSize = data.size
page.currentPage = data.current
dataList.value = data.records
dataListLoading.value = false
if (done) done()
})
}
/**
* 多选回调
* @param list
*/
const selectionChange = (list) => {
dataListSelections.value = list
}
const addOrUpdateVisible = ref(false)
const addOrUpdateRef = ref(null)
/**
* 新增 / 修改
* @param id
*/
const onAddOrUpdate = (id) => {
addOrUpdateVisible.value = true
nextTick(() => {
addOrUpdateRef.value?.init(id)
})
}
/**
* 点击查询
*/
const onSearch = (params, done) => {
getDataList(page, params, done)
}
/**
* 删除
*/
const onDeconste = (row) => {
const ids = row.hotSearchId ? [row.hotSearchId] : dataListSelections.value?.map(item => {
return item.hotSearchId
})
ElMessageBox.confirm(`确定进行[${row.hotSearchId ? '删除' : '批量删除'}]操作?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
.then(() => {
http({
url: http.adornUrl('/admin/hotSearch'),
method: 'delete',
data: http.adornData(ids, false)
})
.then(() => {
ElMessage({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
getDataList()
}
})
})
}).catch(() => { })
}
const refreshChange = () => {
getDataList(page)
}
</script>

@ -0,0 +1,148 @@
<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="公告标题"
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="isTop"
>
<el-radio-group v-model="dataForm.isTop">
<el-radio :label="1">
</el-radio>
<el-radio :label="0">
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
label="公告内容"
prop="content"
>
<TinyMce
ref="contentEnRef"
v-model="dataForm.content"
/>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button
@click="visible = false"
>取消</el-button>
<el-button
type="primary"
@click="onSubmit()"
>确定</el-button>
</span>
</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 contentEnRef = ref(null)
// eslint-disable-next-line no-unused-vars
const validateTitle = (rule, value, callback) => {
if (!value.trim()) {
dataForm.value.title = ''
callback(new Error('公告标题不能为空'))
} else {
callback()
}
}
const dataRule = {
title: [
{ required: true, message: '公告标题不能为空', trigger: 'blur' },
{ validator: validateTitle, trigger: 'blur' }
]
}
const dataForm = ref({
title: null,
content: null,
url: null,
status: 1,
isTop: 0
})
const dataFormRef = ref(null)
const init = (id) => {
dataForm.value.id = id || 0
visible.value = true
nextTick(() => {
dataFormRef.value?.resetFields()
if (dataForm.value.id) {
http({
url: http.adornUrl('/shop/notice/info/' + dataForm.value.id),
method: 'get',
params: http.adornParams()
})
.then(({ data }) => {
dataForm.value = data
})
}
})
}
defineExpose({ init })
/**
* 表单提交
*/
const onSubmit = Debounce(() => {
dataFormRef.value?.validate((valid) => {
if (valid) {
http({
url: http.adornUrl('/shop/notice'),
method: dataForm.value.id ? 'put' : 'post',
data: http.adornData(dataForm.value)
})
.then(() => {
ElMessage({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
visible.value = false
emit('refreshDataList')
dataForm.value.content = ''
}
})
})
}
})
})
</script>

@ -0,0 +1,152 @@
<template>
<div class="mod-shop-notice">
<avue-crud
ref="crudRef"
:page="page"
:data="dataList"
:table-loading="dataListLoading"
:option="tableOption"
@search-change="onSearch"
@on-load="getDataList"
@refresh-change="refreshChange"
>
<template #status="scope">
<el-tag
v-if="scope.row.status === 0"
type="danger"
>
撤销
</el-tag>
<el-tag v-else>
公布
</el-tag>
</template>
<template #isTop="scope">
<el-tag v-if="scope.row.isTop === 0">
</el-tag>
<el-tag v-else>
</el-tag>
</template>
<template #menuLeft>
<el-button
v-if="isAuth('shop:notice:save')"
type="primary"
icon="el-icon-plus"
@click="onAddOrUpdate()"
>
新增
</el-button>
</template>
<template #menu="scope">
<el-button
v-if="isAuth('shop:notice:update')"
type="primary"
icon="el-icon-edit"
@click="onAddOrUpdate(scope.row.id)"
>
修改
</el-button>
<el-button
v-if="isAuth('shop:notice: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/shop/notice'
import AddOrUpdate from './add-or-update.vue'
const dataList = ref([])
const page = reactive({
total: 0, //
currentPage: 1, //
pageSize: 10 //
})
const dataListLoading = ref(false)
const addOrUpdateVisible = ref(false)
const getDataList = (pageParam, params, done) => {
dataListLoading.value = true
http({
url: http.adornUrl('/shop/notice/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 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('/shop/notice/' + id),
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>
<style lang="scss" scoped>
</style>

@ -0,0 +1,289 @@
<template>
<el-dialog
v-model="visible"
:title="!dataForm.addrId ? '新增' : '修改'"
:close-on-click-modal="false"
>
<el-form
ref="dataFormRef"
:model="dataForm"
:rules="dataRule"
label-width="80px"
@keyup.enter="onSubmit()"
>
<el-form-item
label="名称"
prop="addrName"
>
<el-input
v-model="dataForm.addrName"
placeholder="自提点名称"
/>
</el-form-item>
<el-form-item label="省份">
<el-col :span="8">
<el-form-item prop="province">
<el-select
v-model="dataForm.provinceId"
placeholder="请选择"
@change="selectProvince"
>
<el-option
v-for="province in provinceList"
:key="province.areaId"
:label="province.areaName"
:value="province.areaId"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item prop="city">
<el-select
v-model="dataForm.cityId"
placeholder="请选择"
@change="selectCity"
>
<el-option
v-for="city in cityList"
:key="city.areaId"
:label="city.areaName"
:value="city.areaId"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item prop="area">
<el-select
v-model="dataForm.areaId"
placeholder="请选择"
>
<el-option
v-for="area in areaList"
:key="area.areaId"
:label="area.areaName"
:value="area.areaId"
/>
</el-select>
</el-form-item>
</el-col>
</el-form-item>
<el-form-item
label="地址"
prop="addr"
>
<el-input
v-model="dataForm.addr"
placeholder="地址"
/>
</el-form-item>
<el-form-item
label="手机号"
prop="mobile"
>
<el-input
v-model="dataForm.mobile"
maxlength="11"
placeholder="手机号"
/>
</el-form-item>
</el-form>
<template #footer>
<span
class="dialog-footer"
>
<el-button @click="visible = false">取消</el-button>
<el-button
type="primary"
@click="onSubmit()"
>确定</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import { ElMessage } from 'element-plus'
import { isMobile } from '@/utils/validate'
import { Debounce } from '@/utils/debounce'
const emit = defineEmits(['refreshDataList'])
const visible = ref(false)
// eslint-disable-next-line no-unused-vars
const validateMobile = (rule, value, callback) => {
if (!isMobile(value)) {
callback(new Error('手机号格式错误'))
} else {
callback()
}
}
const dataRule = {
addrName: [
{ required: true, message: '自提点名称不能为空', trigger: 'blur' },
{ pattern: /\s\S+|S+\s|\S/, message: '请输入正确的自提点名称', trigger: 'blur' }
],
addr: [
{ required: true, message: '地址不能为空', trigger: 'blur' },
{ pattern: /\s\S+|S+\s|\S/, message: '请输入正确的地址', trigger: 'blur' }
],
city: [{ required: true, message: '城市不能为空', trigger: 'blur' }],
province: [
{ required: true, message: '省份不能为空', trigger: 'blur' }
],
area: [{ required: true, message: '区/县不能为空', trigger: 'blur' }],
mobile: [
{ required: true, message: '手机号不能为空', trigger: 'blur' },
{ validator: validateMobile, trigger: 'blur' }
]
}
const provinceList = ref([])
const dataFormRef = ref(null)
const cityList = ref([])
const areaList = ref([])
const dataForm = reactive({
addrId: 0,
addr: '',
addrName: '',
mobile: '',
area: '',
city: '',
province: '',
areaId: null,
cityId: null,
provinceId: null
})
const page = reactive({
total: 0, //
currentPage: 1, //
pageSize: 10 //
})
const init = (id) => {
dataForm.addrId = id || 0
visible.value = true
nextTick(() => {
dataFormRef.value?.resetFields()
cityList.value = []
areaList.value = []
dataForm.provinceId = null
dataForm.cityId = null
dataForm.areaId = null
})
listAreaByParentId().then(({ data }) => {
provinceList.value = data
})
if (dataForm.addrId) {
http({
url: http.adornUrl(
`/shop/pickAddr/info/${dataForm.addrId}`
),
method: 'get',
params: http.adornParams()
})
.then(({ data }) => {
dataForm.addr = data.addr
dataForm.mobile = data.mobile
dataForm.addrName = data.addrName
dataForm.areaId = data.areaId
dataForm.cityId = data.cityId
dataForm.provinceId = data.provinceId
listAreaByParentId(data.provinceId).then(({ data }) => {
cityList.value = data
})
listAreaByParentId(data.cityId).then(({ data }) => {
areaList.value = data
})
})
}
}
defineExpose({ init })
const listAreaByParentId = (pid) => {
if (!pid) pid = 0
return http({
url: http.adornUrl('/admin/area/listByPid'),
method: 'get',
params: http.adornParams({ pid })
})
}
/**
* 选择省
* @param val
*/
const selectProvince = (val) => {
dataForm.cityId = null
dataForm.city = ''
// select
listAreaByParentId(val).then(({ data }) => {
cityList.value = data
})
}
/**
* 选择市
* @param val
*/
const selectCity = (val) => {
dataForm.areaId = null
dataForm.area = ''
// select
listAreaByParentId(val).then(({ data }) => {
areaList.value = data
})
}
/**
* 表单提交
*/
const onSubmit = Debounce(() => {
for (let i = 0; i < provinceList.value.length; i++) {
if (provinceList.value[i].areaId === dataForm.provinceId) {
//
dataForm.province = provinceList.value[i].areaName
}
}
for (let i = 0; i < cityList.value.length; i++) {
if (cityList.value[i].areaId === dataForm.cityId) {
//
dataForm.city = cityList.value[i].areaName
}
}
for (let i = 0; i < areaList.value.length; i++) {
if (areaList.value[i].areaId === dataForm.areaId) {
//
dataForm.area = areaList.value[i].areaName
}
}
dataFormRef.value?.validate(valid => {
if (valid) {
http({
url: http.adornUrl('/shop/pickAddr'),
method: dataForm.addrId ? 'put' : 'post',
data: http.adornData({
addrId: dataForm.addrId || undefined,
addr: dataForm.addr,
addrName: dataForm.addrName,
mobile: dataForm.mobile,
area: dataForm.area,
city: dataForm.city,
province: dataForm.province,
areaId: dataForm.areaId,
cityId: dataForm.cityId,
provinceId: dataForm.provinceId
})
})
.then(() => {
ElMessage({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
visible.value = false
emit('refreshDataList', page)
}
})
})
}
})
})
</script>

@ -0,0 +1,166 @@
<template>
<div class="mod-pickAddr">
<avue-crud
ref="crudRef"
:page="page"
:data="dataList"
:option="tableOption"
:permission="permission"
@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 #menu="scope">
<el-button
v-if="isAuth('shop:pickAddr:update')"
type="primary"
icon="el-icon-edit"
@click.stop="onAddOrUpdate(scope.row.addrId)"
>
编辑
</el-button>
<el-button
v-if="isAuth('shop:pickAddr:delete')"
type="danger"
icon="el-icon-delete"
@click.stop="onDelete(scope.row.addrId)"
>
删除
</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/shop/pickAddr.js'
const permission = {
delBtn: isAuth('prod:prod:delete')
}
const dataList = ref([])
const dataListSelections = ref([])
const page = reactive({
total: 0, //
currentPage: 1, //
pageSize: 10 //
})
/**
* 获取数据列表
*/
const getDataList = (pageParam, params, done) => {
http({
url: http.adornUrl('/shop/pickAddr/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
if (done) done()
})
}
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) => {
const ids = id ? [id] : dataListSelections.value?.map(item => {
return item.addrId
})
ElMessageBox.confirm(
'确定进行删除操作?', '提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
)
.then(() => {
http({
url: http.adornUrl('/shop/pickAddr'),
method: 'delete',
data: http.adornData(ids, false)
})
.then(() => {
ElMessage({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
getDataList(page)
}
})
})
})
.catch(() => { })
}
/**
* 条件查询
* @param params
* @param done
*/
const onSearch = (params, done) => {
getDataList(page, params, done)
}
/**
* 多选变化
* @param val
*/
const selectionChange = (val) => {
dataListSelections.value = val
}
</script>

@ -0,0 +1,568 @@
<template>
<el-dialog
v-model="visible"
:title="!dataForm.transportId ? '新增' : '修改'"
:close-on-click-modal="false"
width="1400px"
class="transport-dialog"
>
<el-form
ref="dataFormRef"
:model="dataForm"
label-width="80px"
@keyup.enter="onSubmit()"
>
<el-form-item
label="模板名称"
prop="transName"
:rules="[
{ required: true, message: '模板名称不能为空'},
{ pattern: /\s\S+|S+\s|\S/, message: '请输入正确的模板名称', trigger: 'blur' }
]"
>
<el-input
v-model="dataForm.transName"
placeholder="模板名称"
/>
</el-form-item>
<el-form-item
label="模板类型"
prop="isFreeFee"
required="required"
>
<el-radio-group
v-model="dataForm.isFreeFee"
@change="changeFreeFee"
>
<el-radio :label="0">
买家承担运费
</el-radio>
<el-radio :label="1">
卖家包邮
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
label="收费方式"
prop="chargeType"
>
<el-radio-group
v-model="dataForm.chargeType"
:disabled="dataForm.isFreeFee == 1"
>
<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-table
:data="dataForm.transfees"
border
style="width: 100%;"
class="table-con"
>
<el-table-column
header-align="center"
align="center"
width="450"
label="可配送区域"
>
<template #default="scope">
<span v-if="scope.$index == 0"></span>
<span v-if="(!scope.row.cityList || !scope.row.cityList.length) && scope.$index > 0"></span>
<span v-if="scope.$index > 0">
<el-tag
v-for="city in scope.row.cityList"
:key="city.areaId"
>{{ city.areaName }}</el-tag>
</span>
<el-button
v-if="isAuth('shop:transfee:update') && scope.$index > 0"
type="text"
@click="onAddOrUpdate(`${scope.$index}`)"
>
编辑
</el-button>
<el-button
v-if="isAuth('shop:transfee:delete') && scope.$index > 0"
type="text"
@click="onDelete(`${scope.$index}`)"
>
删除
</el-button>
</template>
</el-table-column>
<el-table-column
header-align="center"
align="center"
width="180"
:label="tableTitle[0]"
>
<template #default="scope">
<el-form-item
:prop="`transfees.${scope.$index}.firstPiece`"
label-width="0px"
:rules="[{ required: true, message: `${tableTitle[0]}不能为空`}]"
>
<el-input
v-model="scope.row.firstPiece"
type="number"
:disabled="!scope.row.status && scope.$index === 0"
@change="checkNumber(scope.row, 1)"
/>
</el-form-item>
</template>
</el-table-column>
<el-table-column
header-align="center"
align="center"
:label="tableTitle[1]"
>
<template #default="scope">
<el-form-item
:prop="`transfees.${scope.$index}.firstFee`"
label-width="0px"
:rules="[{ required: true, message: `${tableTitle[1]}不能为空`}]"
>
<el-input
v-model="scope.row.firstFee"
type="number"
:min="0"
:disabled="!scope.row.status && scope.$index === 0"
/>
</el-form-item>
</template>
</el-table-column>
<el-table-column
header-align="center"
align="center"
:label="tableTitle[2]"
>
<template #default="scope">
<el-form-item
:prop="`transfees.${scope.$index}.continuousPiece`"
label-width="0px"
:rules="[{ required: true, message: `${tableTitle[2]}不能为空`}]"
>
<el-input
v-model="scope.row.continuousPiece"
type="number"
:disabled="!scope.row.status && scope.$index === 0"
@change="checkNumber(scope.row, 3)"
/>
</el-form-item>
</template>
</el-table-column>
<el-table-column
header-align="center"
align="center"
:label="tableTitle[3]"
>
<template #default="scope">
<el-form-item
:prop="`transfees.${scope.$index}.continuousFee`"
label-width="0px"
:rules="[{ required: true, message: `${tableTitle[3]}不能为空`}]"
>
<el-input
v-model="scope.row.continuousFee"
type="number"
:min="0"
:disabled="!scope.row.status && scope.$index === 0"
/>
</el-form-item>
</template>
</el-table-column>
</el-table>
<div
v-if="dataForm.isFreeFee == 0"
style="margin-top: 20px"
>
<el-button
type="primary"
icon="el-icon-location-outline"
@click="addTransfee()"
>
点击添加可配送的区域和运费
</el-button>
</div>
<el-checkbox
v-if="!dataForm.isFreeFee"
v-model="dataForm.hasFreeCondition"
style="margin-top:10px;font-size:50px"
>
指定条件包邮
</el-checkbox>
<el-table
v-if="dataForm.hasFreeCondition && !dataForm.isFreeFee"
:data="dataForm.transfeeFrees"
border
style="width: 100%;"
>
<el-table-column
header-align="center"
align="center"
width="350"
label="指定区域"
>
<template #default="scope">
<span v-if="!scope.row.freeCityList || !scope.row.freeCityList.length"></span>
<el-tag
v-for="city in scope.row.freeCityList"
:key="city.areaId"
>
{{ city.areaName }}
</el-tag>
<el-button
v-if="isAuth('shop:transfee:update')"
type="text"
@click="addOrUpdateTransfeeFree(`${scope.$index}`)"
>
编辑
</el-button>
<el-button
v-if="isAuth('shop:transfee:delete')"
type="text"
@click="deleteTransfeeFree(`${scope.$index}`)"
>
删除
</el-button>
</template>
</el-table-column>
<el-table-column
header-align="center"
align="center"
width="600"
label="设置包邮条件"
>
<template #default="scope">
<el-radio-group v-model="scope.row.freeType">
<el-radio :label="0">
满件/重量/体积包邮
</el-radio>
<el-radio :label="1">
满金额包邮
</el-radio>
<el-radio :label="2">
满件/重量/体积且满金额包邮
</el-radio>
</el-radio-group>
</template>
</el-table-column>
<el-table-column
header-align="center"
align="left"
>
<template #default="scope">
<el-form-item
v-if="scope.row.freeType == 1 || scope.row.freeType == 2"
:prop="`transfeeFrees.${scope.$index}.amount`"
label-width="0px"
:rules="[{ required: true, message: `不能为空`}]"
>
<el-input
v-model="scope.row.amount"
style="width:100px"
/>
</el-form-item>
<el-form-item
v-if="scope.row.freeType == 0 || scope.row.freeType == 2"
:prop="`transfeeFrees.${scope.$index}.piece`"
label-width="0px"
:rules="[{ required: true, message: `不能为空`}]"
>
<el-input
v-model="scope.row.piece"
style="width:100px"
/> //
</el-form-item>
</template>
</el-table-column>
</el-table>
<div
v-if="dataForm.isFreeFee == 0"
style="margin-top: 20px"
>
<el-button
type="primary"
icon="el-icon-location-outline"
@click="addTransfeeFree()"
>
点击添加指定包邮条件
</el-button>
</div>
</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>
<!-- 弹窗, 新增 / 修改 -->
<add-or-update
v-if="addOrUpdateVisible"
ref="addOrUpdateRef"
@refresh-data-list="getDataList"
/>
</el-dialog>
</template>
<script setup>
import { isAuth } from '@/utils'
import { ElMessage } from 'element-plus'
import { Debounce } from '@/utils/debounce'
import AddOrUpdate from './add-or-update.vue'
const emit = defineEmits(['refreshDataList'])
const hasFreeCondition = ref(0)
const visible = ref(false)
const dataForm = ref({
hasFreeCondition: false,
transName: '',
createTime: '',
chargeType: 0,
transportId: 0,
isFreeFee: 0,
transfees: [{ cityList: [], status: 1 }],
transfeeFrees: [{ freeCityList: [], freeType: 0 }]
})
const page = reactive({
total: 0, //
currentPage: 1, //
pageSize: 10 //
})
const tableTitle = computed(() => {
const titles = [['首件(个)', '运费(元)', '续件(个)', '续费(元)'], ['首重(kg)', '运费(元)', '续重(kg)', '续费(元)'], ['首体积(m³)', '运费(元)', '续体积(m³)', '续费(元)']]
if (dataForm.value.chargeType) {
return titles[dataForm.value.chargeType]
}
return titles[0]
})
//
watch(
() => visible.value,
(val) => {
if (!val) {
addOrUpdateVisible.value = false
}
}
)
const addOrUpdateVisible = ref(false)
const dataFormRef = ref(null)
const init = (id) => {
visible.value = true
dataForm.value.transportId = id || 0
nextTick(() => {
dataFormRef.value?.resetFields()
dataForm.value = {
hasFreeCondition: false,
transName: '',
createTime: '',
chargeType: 0,
transportId: 0,
isFreeFee: 0,
transfees: [{ cityList: [], status: 1 }],
transfeeFrees: [{ freeCityList: [], freeType: 0 }]
}
})
if (dataForm.value.transportId) {
http({
//
url: http.adornUrl(`/shop/transport/info/${dataForm.value.transportId}`),
method: 'get'
})
.then(({ data }) => {
if (data.isFreeFee) {
data.transfees[0].status = 0
} else {
data.transfees[0].status = 1
}
dataForm.value = data
dataForm.value.hasFreeCondition = !!data.hasFreeCondition
})
}
}
defineExpose({ init })
const getDataList = (row, cityList, type) => {
if (type === 0) {
dataForm.value.transfees[row].cityList = cityList
}
if (type === 1) {
dataForm.value.transfeeFrees[row].freeCityList = cityList
}
}
/**
* 添加运费项
*/
const addTransfee = () => {
dataForm.value.transfees.push({ cityList: [], status: 1 })
}
/**
* 删除运费项
*/
const onDelete = (rowIndex) => {
dataForm.value.transfees.splice(rowIndex, 1)
}
const addOrUpdateRef = ref(null)
/**
* 可配送区域和运费编辑
*/
const onAddOrUpdate = (rowIndex) => {
addOrUpdateVisible.value = true
let allSelectCityList = []
for (let i = 1; i < dataForm.value.transfees.length; i++) {
const cityList = dataForm.value.transfees[i].cityList
allSelectCityList = allSelectCityList.concat(cityList)
}
nextTick(() => {
addOrUpdateRef.value?.init(rowIndex, dataForm.value.transfees[rowIndex].cityList || [], allSelectCityList, 0)
})
}
/**
* 添加指定包邮条件
*/
const addTransfeeFree = () => {
if (dataForm.value.hasFreeCondition) {
dataForm.value.transfeeFrees?.push({ freeCityList: [], freeType: 0 })
}
}
/**
* 删除指定包邮条件
*/
const deleteTransfeeFree = (rowIndex) => {
dataForm.value.transfeeFrees?.splice(rowIndex, 1)
}
/**
* 指定包邮条件编辑
*/
const addOrUpdateTransfeeFree = (rowIndex) => {
addOrUpdateVisible.value = true
let allSelectCityList = []
for (let i = 1; i < dataForm.value.transfeeFrees.length; i++) {
const freeCityList = dataForm.value.transfeeFrees[i].freeCityList
allSelectCityList = allSelectCityList.concat(freeCityList)
}
nextTick(() => {
addOrUpdateRef.value?.init(rowIndex, dataForm.value.transfeeFrees[rowIndex].freeCityList || [], allSelectCityList, 1)
})
}
/**
* 改变模板类型 0 买家承担运费 1 卖家包邮
* @param val
*/
const changeFreeFee = (val) => {
dataForm.value.hasFreeCondition = false
if (val) {
dataForm.value.chargeType = 0
dataForm.value.transfees = [{ cityList: [], status: 0, firstPiece: 1, firstFee: 0, continuousPiece: 1, continuousFee: 0 }]
} else {
dataForm.value.transfees = [{ cityList: [], status: 1 }]
}
}
/**
* 校验输入的数字
*/
const checkNumber = (row, type) => {
if (type === 1) {
row.firstPiece = getNumber(row.firstPiece)
row.firstPiece = row.firstPiece === 0 ? 1 : row.firstPiece
} else if (type === 3) {
row.continuousPiece = getNumber(row.continuousPiece)
row.continuousPiece = row.continuousPiece === 0 ? 1 : row.continuousPiece
}
}
/**
* 保留整数并小于零的数设为0
*/
const getNumber = (num) => {
num = Math.round(num)
return num < 0 ? 0 : num
}
/**
* 表单提交
*/
const onSubmit = Debounce(() => {
dataFormRef.value?.validate((valid) => {
if (valid) {
for (let i = 1; i < dataForm.value.transfees.length; i++) {
const transfee = dataForm.value.transfees[i]
if (transfee.cityList.length === 0) {
ElMessage({
message: '请选择可配送区域',
type: 'error',
duration: 1500
})
return
}
}
if (dataForm.value.hasFreeCondition) {
hasFreeCondition.value = 1
} else {
hasFreeCondition.value = 0
}
dataForm.value.transfees[0].cityList = []
http({
url: http.adornUrl('/shop/transport'),
method: dataForm.value.transportId ? 'put' : 'post',
data: http.adornData({
transportId: dataForm.value.transportId || undefined,
transName: dataForm.value.transName,
chargeType: dataForm.value.chargeType,
isFreeFee: dataForm.value.isFreeFee,
transfees: dataForm.value.transfees,
transfeeFrees: dataForm.value.transfeeFrees,
hasFreeCondition: hasFreeCondition.value
})
})
.then(() => {
ElMessage({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
visible.value = false
emit('refreshDataList', page)
}
})
})
}
})
})
</script>
<style scoped>
.transport-dialog .table-con :deep(.el-form-item) {
margin-top: 16px;
margin-bottom: 16px!important;
}
</style>

@ -0,0 +1,167 @@
<template>
<div class="mod-transport">
<avue-crud
ref="crudRef"
:page="page"
:data="dataList"
:option="tableOption"
@search-change="onSearch"
@selection-change="selectionChange"
@on-load="getDataList"
>
<template #prod-prop-values="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:transport:save')"
type="primary"
icon="el-icon-plus"
@click.stop="onAddOrUpdate()"
>
新增
</el-button>
<el-button
v-if="isAuth('shop:transport:delete')"
type="danger"
:disabled="dataListSelections.length <= 0"
@click="onDelete()"
>
批量删除
</el-button>
</template>
<template #menu="scope">
<el-button
v-if="isAuth('shop:transport:update')"
type="primary"
icon="el-icon-edit"
@click.stop="onAddOrUpdate(scope.row.transportId)"
>
修改
</el-button>
<el-button
v-if="isAuth('shop:transport:delete')"
type="danger"
icon="el-icon-delete"
@click.stop="onDelete(scope.row.transportId)"
>
删除
</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 { tableOption } from '@/crud/shop/transport'
import AddOrUpdate from './add-or-update.vue'
const dataList = ref([])
const dataListSelections = ref([])
const page = ref({
total: 0, //
currentPage: 1, //
pageSize: 10 //
})
/**
* 获取数据列表
*/
const getDataList = (pageParam, params, done) => {
http({
url: http.adornUrl('/shop/transport/page'),
method: 'get',
params: http.adornParams(
Object.assign(
{
current: pageParam == null ? page.value.currentPage : pageParam.currentPage,
size: pageParam == null ? page.value.pageSize : pageParam.pageSize
},
params
)
)
})
.then(({ data }) => {
dataList.value = data.records
page.value.total = data.total
if (done) done()
})
}
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) => {
const ids = id ? [id] : dataListSelections.value?.map(item => item.transportId)
ElMessageBox.confirm(
`确定进行[${id ? '删除' : '批量删除'}]操作?`,
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
)
.then(() => {
http({
url: http.adornUrl('/shop/transport'),
method: 'delete',
data: http.adornData(ids, false)
})
.then(() => {
ElMessage({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
getDataList()
}
})
})
}).catch(() => {})
}
/**
* 条件查询
*/
const onSearch = (params, done) => {
getDataList(page.value, params, done)
}
/**
* 多选变化
*/
const selectionChange = (val) => {
dataListSelections.value = val
}
</script>

@ -0,0 +1,135 @@
<template>
<el-dialog
v-model="visible"
:title="!dataForm.userId ? '新增' : '修改'"
:close-on-click-modal="false"
>
<el-form
ref="dataFormRef"
:model="dataForm"
:rules="dataRule"
label-width="80px"
@keyup.enter="onSubmit()"
>
<el-form-item
label="用户头像"
prop="pic"
>
<img
:src="dataForm.pic"
class="image"
alt=""
>
</el-form-item>
<el-form-item
label="用户昵称"
prop="nickName"
>
<el-input
v-model="dataForm.nickName"
:disabled="true"
placeholder="用户昵称"
/>
</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>
<span
class="dialog-footer"
>
<el-button @click="visible = false">取消</el-button>
<el-button
type="primary"
@click="onSubmit()"
>确定</el-button>
</span>
</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 dataForm = ref({
userId: 0,
nickName: '',
pic: '',
status: 1
})
const page = reactive({
total: 0, //
currentPage: 1, //
pageSize: 10 //
})
const dataRule = {
nickName: [
{ required: true, message: '用户名不能为空', trigger: 'blur' }
]
}
const dataFormRef = ref(null)
const init = (id) => {
dataForm.value.userId = id || 0
visible.value = true
nextTick(() => {
dataFormRef.value?.resetFields()
})
if (dataForm.value.userId) {
http({
url: http.adornUrl(`/admin/user/info/${dataForm.value.userId}`),
method: 'get',
params: http.adornParams()
})
.then(({ data }) => {
dataForm.value = data
})
}
}
defineExpose({ init })
/**
* 表单提交
*/
const onSubmit = Debounce(() => {
dataFormRef.value?.validate(valid => {
if (valid) {
http({
url: http.adornUrl('/admin/user'),
method: dataForm.value.userId ? 'put' : 'post',
data: http.adornData({
userId: dataForm.value.userId || undefined,
nickName: dataForm.value.nickName,
status: dataForm.value.status
})
})
.then(() => {
ElMessage({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
visible.value = false
emit('refreshDataList', page)
}
})
})
}
})
})
</script>

@ -0,0 +1,123 @@
<template>
<div class="mod-user">
<avue-crud
ref="crudRef"
:page="page"
:data="dataList"
:option="tableOption"
@search-change="onSearch"
@selection-change="selectionChange"
@on-load="getDataList"
>
<template #pic="scope">
<span
v-if="scope.row.pic"
class="avue-crud__img"
>
<i
:src="scope.row.pic"
class="el-icon-document"
/>
</span>
<span v-else>-</span>
</template>
<template #status="scope">
<el-tag
v-if="scope.row.status === 0"
type="danger"
>
禁用
</el-tag>
<el-tag v-else>
正常
</el-tag>
</template>
<template #menu="scope">
<el-button
v-if="isAuth('admin:user:update')"
type="primary"
icon="el-icon-edit"
@click.stop="onAddOrUpdate(scope.row.userId)"
>
编辑
</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 { tableOption } from '@/crud/user/user.js'
import AddOrUpdate from './add-or-update.vue'
const dataList = ref([])
const dataListLoading = ref(false)
const dataListSelections = ref([])
const addOrUpdateVisible = ref(false)
const page = reactive({
total: 0, //
currentPage: 1, //
pageSize: 10 //
})
/**
* 获取数据列表
*/
const getDataList = (pageParam, params, done) => {
dataListLoading.value = true
http({
url: http.adornUrl('/admin/user/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 addOrUpdateRef = ref(null)
/**
* 新增 / 修改
* @param id
*/
const onAddOrUpdate = (id) => {
addOrUpdateVisible.value = true
nextTick(() => {
addOrUpdateRef.value?.init(id)
})
}
/**
* 条件查询
*/
const onSearch = (params, done) => {
getDataList(page, params, done)
}
/**
* 多选变化
*/
const selectionChange = (val) => {
dataListSelections.value = val
}
</script>
Loading…
Cancel
Save