Compare commits

..

No commits in common. 'c25697f0bc7c6bef4711ceab4e35265d7dc48551' and '07db23c09e234ac55c8e9fe3f0593f58054d2671' have entirely different histories.

@ -1,21 +1,16 @@
<template>
<!-- 400页面模板 -->
<div class="site-wrapper site-page--not-found">
<!-- 内容包裹器用于设置页面整体布局 -->
<div class="site-content__wrapper">
<!-- 页面内容主体 -->
<div class="site-content">
<!-- 显示错误代码 -->
<h2 class="not-found-title">400</h2>
<!-- 描述错误信息 -->
<h2 class="not-found-title">
400
</h2>
<p class="not-found-desc">
抱歉您访问的页面<em>失联</em> ...
</p>
<!-- 返回上一页按钮 -->
<el-button @click="$router.go(-1)">
返回上一页
</el-button>
<!-- 进入首页按钮 -->
<el-button
type="primary"
class="not-found-btn-gohome"
@ -29,29 +24,22 @@
</template>
<script setup>
//
// 使 <script setup> VueAPI
</script>
<style lang="scss" scoped>
.site-wrapper.site-page--not-found {
/* 设置页面占据整个浏览器窗口 */
.site-wrapper.site-page--not-found {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
overflow: hidden;
.site-content__wrapper {
/* 移除默认的内边距和外边距,并设置背景颜色 */
padding: 0;
margin: 0;
background-color: #fff;
}
.site-content {
/* 固定定位,使内容居中显示 */
position: fixed;
top: 15%;
left: 50%;
@ -59,33 +47,25 @@
padding: 30px;
text-align: center;
transform: translate(-50%, 0);
}
.not-found-title {
/* 错误代码样式 */
margin: 20px 0 15px;
font-size: 10em;
font-weight: 400;
color: rgb(55, 71, 79);
}
.not-found-desc {
/* 错误描述样式 */
margin: 0 0 30px;
font-size: 26px;
text-transform: uppercase;
color: rgb(118, 131, 143);
> em {
/* 强调文字样式 */
font-style: normal;
color: #ee8145;
}
}
.not-found-btn-gohome {
/* 首页按钮样式,增加左边距以与返回按钮间隔开 */
margin-left: 30px;
}
}
}
</style>

@ -1,21 +1,21 @@
<template>
<div class="mod-home">
<!-- 简介段落 -->
<p>一个基于spring bootspring oauth2.0mybatisredis的轻量级前后端分离拥有完整sku和下单流程的完全开源商城</p>
<p>&nbsp;</p>
<!-- 版权声明 -->
<p>该项目仅供学习参考可供个人学习使用如需商用联系作者进行授权否则必将追究法律责任</p>
<p>&nbsp;</p>
<h2>前言</h2>
<p>
<code>mall4j商城</code>项目致力于为中小企业打造一个完整易于维护的开源电商系统采用现阶段流行技术实现后台管理系统包含商品管理订单管理运费模板规格管理会员管理运营管理内容管理统计报表权限管理设置等模块
<code>mall4j商城</code>项目致力于为中小企业打造一个完整易于维护的开源电商系统采用现阶段流行技术实现后台管理系统包含商品管理订单管理运费模板规格管理会员管理运营管理内容管理统计报表权限管理设置等模块
</p>
<p>&nbsp;</p>
<h2>技术选型</h2>
<figure>
<table border="1" cellspacing="0" cellpadding="5px">
<table
border="1"
cellspacing="0"
cellpadding="5px"
>
<thead>
<tr>
<th>技术</th>
@ -24,23 +24,69 @@
</tr>
</thead>
<tbody>
<!-- 列出所用技术及其版本和简要说明 -->
<tr v-for="(tech, index) in technologies" :key="index">
<td>{{ tech.name }}</td>
<td>{{ tech.version }}</td>
<td>{{ tech.description }}</td>
<tr>
<td>Spring Boot</td>
<td>3.0.4</td>
<td>MVC核心框架</td>
</tr>
<tr>
<td>MyBatis</td>
<td>3.5.0</td>
<td>ORM框架</td>
</tr>
<tr>
<td>MyBatisPlus</td>
<td>3.5.3.1</td>
<td>基于mybatis使用lambda表达式的</td>
</tr>
<tr>
<td>Swagger-UI</td>
<td>4.0.0</td>
<td>文档生产工具</td>
</tr>
<tr>
<td>redisson</td>
<td>3.19.3</td>
<td>对redis进行封装集成分布式锁等</td>
</tr>
<tr>
<td>hikari</td>
<td>3.2.0</td>
<td>数据库连接池</td>
</tr>
<tr>
<td>log4j2</td>
<td>2.17.2</td>
<td>更快的log日志工具</td>
</tr>
<tr>
<td>lombok</td>
<td>1.18.8</td>
<td>简化对象封装工具</td>
</tr>
<tr>
<td>hutool</td>
<td>5.8.15</td>
<td>更适合国人的java工具集</td>
</tr>
<tr>
<td>xxl-job</td>
<td>2.3.1</td>
<td>定时任务</td>
</tr>
</tbody>
</table>
</figure>
<p>&nbsp;</p>
<h2>部署教程</h2>
<p>&nbsp;</p>
<h3>1.开发环境</h3>
<figure>
<table border="1" cellspacing="0" cellpadding="5px">
<table
border="1"
cellspacing="0"
cellpadding="5px"
>
<thead>
<tr>
<th>工具</th>
@ -48,15 +94,21 @@
</tr>
</thead>
<tbody>
<!-- 列出所需的开发环境工具及其版本 -->
<tr v-for="(tool, index) in developmentTools" :key="index">
<td>{{ tool.name }}</td>
<td>{{ tool.version }}</td>
<tr>
<td>jdk</td>
<td>17</td>
</tr>
<tr>
<td>mysql</td>
<td>5.7+</td>
</tr>
<tr>
<td>redis</td>
<td>3.2+</td>
</tr>
</tbody>
</table>
</figure>
<h3>2.启动</h3>
<ul>
<li>推荐使用idea安装lombok插件使用idea导入maven项目</li>
@ -75,31 +127,8 @@
</div>
</template>
<script setup>
//
const technologies = [
{ name: 'Spring Boot', version: '3.0.4', description: 'MVC核心框架' },
{ name: 'MyBatis', version: '3.5.0', description: 'ORM框架' },
{ name: 'MyBatisPlus', version: '3.5.3.1', description: '基于mybatis使用lambda表达式的增强' },
{ name: 'Swagger-UI', version: '4.0.0', description: '文档生产工具' },
{ name: 'redisson', version: '3.19.3', description: '对redis进行封装、集成分布式锁等' },
{ name: 'hikari', version: '3.2.0', description: '数据库连接池' },
{ name: 'log4j2', version: '2.17.2', description: '更快的log日志工具' },
{ name: 'lombok', version: '1.18.8', description: '简化对象封装工具' },
{ name: 'hutool', version: '5.8.15', description: '更适合国人的java工具集' },
{ name: 'xxl-job', version: '2.3.1', description: '定时任务' }
];
//
const developmentTools = [
{ name: 'jdk', version: '17' },
{ name: 'mysql', version: '5.7+' },
{ name: 'redis', version: '3.2+' }
];
</script>
<style lang="scss" scoped>
.mod-home {
line-height: 1.5; //
line-height: 1.5;
}
</style>

@ -1,14 +1,14 @@
<template>
<div class="login">
<!-- 登录框 -->
<div class="login-box">
<!-- 顶部区域包含logo -->
<div class="top">
<div class="logo">
<img src="~@/assets/img/login-logo.png" alt="Logo">
<img
src="~@/assets/img/login-logo.png"
alt=""
>
</div>
</div>
<!-- 中间区域包含登录表单 -->
<div class="mid">
<el-form
ref="dataFormRef"
@ -17,7 +17,6 @@
status-icon
@keyup.enter="dataFormSubmit()"
>
<!-- 用户名输入框 -->
<el-form-item prop="userName">
<el-input
v-model="dataForm.userName"
@ -25,7 +24,6 @@
placeholder="帐号"
/>
</el-form-item>
<!-- 密码输入框 -->
<el-form-item prop="password">
<el-input
v-model="dataForm.password"
@ -34,7 +32,6 @@
placeholder="密码"
/>
</el-form-item>
<!-- 登录按钮 -->
<el-form-item>
<div class="item-btn">
<input
@ -46,12 +43,10 @@
</el-form-item>
</el-form>
</div>
<!-- 底部版权信息 -->
<div class="bottom">
Copyright © 2019 广州市蓝海创新科技有限公司
</div>
</div>
<!-- 验证组件用于显示图形验证码 -->
<Verify
ref="verifyRef"
:captcha-type="'blockPuzzle'"
@ -62,23 +57,17 @@
</template>
<script setup>
import { encrypt } from '@/utils/crypto' //
import { getUUID } from '@/utils' //
import Verify from '@/components/verifition/Verify.vue' //
import cookie from 'vue-cookies' // cookies
import { ref, onMounted, onBeforeUnmount } from 'vue'
import { useRouter } from 'vue-router'
import http from '@/api/http'
import { encrypt } from '@/utils/crypto'
import { getUUID } from '@/utils'
import Verify from '@/components/verifition/Verify.vue'
import cookie from 'vue-cookies'
//
const dataForm = ref({
userName: '',
password: '',
uuid: '',
captcha: ''
})
//
const dataRule = {
userName: [
{ required: true, message: '帐号不能为空', trigger: 'blur' }
@ -91,51 +80,39 @@ const dataRule = {
]
}
//
onBeforeUnmount(() => {
document.removeEventListener('keyup', handerKeyup)
})
//
onMounted(() => {
getCaptcha()
document.addEventListener('keyup', handerKeyup)
})
//
const handerKeyup = (e) => {
const keycode = e.which || e.keyCode
const keycode = document.all ? event.keyCode : e.which
if (keycode === 13) {
dataFormSubmit()
this.dataFormSubmit()
}
}
//
const verifyRef = ref(null)
//
const dataFormRef = ref(null)
let isSubmit = false
/**
* 提交表单
*/
const dataFormSubmit = () => {
dataFormRef.value?.validate((valid) => {
if (valid) {
verifyRef.value?.show() //
verifyRef.value?.show()
}
})
}
//
const router = useRouter()
/**
* 登录逻辑处理
* @param {Object} verifyResult - 验证成功后的结果
*/
const login = (verifyResult) => {
if (isSubmit) return
if (isSubmit) {
return
}
isSubmit = true
http({
url: http.adornUrl('/adminLogin'),
@ -146,8 +123,8 @@ const login = (verifyResult) => {
captchaVerification: verifyResult.captchaVerification
})
}).then(({ data }) => {
cookie.set('Authorization', data.accessToken) // cookie
router.replace({ name: 'home' }) //
cookie.set('Authorization', data.accessToken)
router.replace({ name: 'home' })
}).catch(() => {
isSubmit = false
})
@ -157,8 +134,9 @@ const login = (verifyResult) => {
* 获取验证码
*/
const getCaptcha = () => {
dataForm.value.uuid = getUUID() // uuid
dataForm.value.uuid = getUUID()
}
</script>
<style lang="scss" scoped>
@ -168,37 +146,30 @@ const getCaptcha = () => {
background: url('../../../assets/img/login-bg.png') no-repeat;
background-size: cover;
position: fixed;
.login-box {
position: absolute;
left: 50%;
transform: translateX(-50%);
height: 100%;
padding-top: 10%;
.top {
margin-bottom: 30px;
text-align: center;
.logo {
font-size: 0;
max-width: 50%;
margin: 0 auto;
}
&:deep(.company) {
font-size: 16px;
margin-top: 10px;
}
}
.mid {
font-size: 14px;
.item-btn {
width: 410px;
margin-top: 20px;
input {
border: 0;
width: 100%;
@ -209,7 +180,6 @@ const getCaptcha = () => {
}
}
}
.bottom {
position: absolute;
bottom: 10%;
@ -220,11 +190,9 @@ const getCaptcha = () => {
}
}
}
.info {
width: 410px;
}
:deep(.login-captcha) {
height: 40px;
}

@ -1,6 +1,5 @@
<template>
<div class="mod-index-img">
<!-- 对话框组件用于显示新增或修改轮播图片的表单 -->
<el-dialog
v-model="visible"
:title="!dataForm.imgId ? '新增' : '修改'"
@ -12,14 +11,12 @@
: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"
@ -29,27 +26,32 @@
>
<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 :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 :label="-1">
</el-radio>
<el-radio :label="0">
商品
</el-radio>
</el-radio-group>
<!-- 显示关联的商品卡片 -->
<div v-if="dataForm.relation != null">
<div v-if="dataForm.relation!=null">
<el-card
:body-style="{ padding: '0px' }"
style="height: 160px;width: 120px"
@ -71,12 +73,15 @@
</div>
</el-card>
</div>
<!-- 如果没有关联商品则显示选择商品按钮 -->
<div v-if="dataForm.relation == null && dataForm.type === 0">
<el-button @click="addProd"></el-button>
<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"
@ -87,7 +92,7 @@
</el-form-item>
</el-form>
</el-dialog>
<!-- 商品选择弹窗 -->
<!-- 商品选择弹窗-->
<prods-select
v-if="prodsSelectVisible"
ref="prodsSelectRef"
@ -100,13 +105,7 @@
<script setup>
import { ElMessage } from 'element-plus'
import { Debounce } from '@/utils/debounce'
import { ref, reactive, nextTick, defineExpose, defineEmits } from 'vue'
import http from '@/api/http'
//
const emit = defineEmits(['refreshDataList'])
//
const dataForm = ref({
status: 1,
des: '',
@ -116,15 +115,12 @@ const dataForm = ref({
type: -1,
relation: null
})
//
const dataRule = reactive({
imgUrl: [
{ required: true, message: '轮播图片不能为空', trigger: 'blur' }
]
})
//
//
const card = ref({
id: 0,
pic: '',
@ -135,32 +131,23 @@ const card = ref({
activity: []
}
})
//
const page = reactive({
total: 0, //
currentPage: 1, //
pageSize: 10 //
})
//
const prodsSelectVisible = ref(false)
//
const visible = ref(false)
//
const dataFormRef = ref(null)
/**
* 初始化数据
* @param id - 要编辑的数据ID若为新增则传入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'
@ -174,19 +161,16 @@ const init = (id) => {
}
})
} else {
//
nextTick(() => {
dataFormRef.value?.resetFields()
dataForm.value.imgUrl = ''
})
}
}
// init
defineExpose({ init })
/**
* 表单提交使用防抖功能防止快速多次点击
* 表单提交
*/
const onSubmit = Debounce(() => {
dataFormRef.value?.validate((valid) => {
@ -212,19 +196,16 @@ const onSubmit = Debounce(() => {
})
})
})
/**
* 删除关联商品
* 删除关联数据
*/
const deleteRelation = () => {
dataForm.value.relation = null
}
//
const prodsSelectRef = ref(null)
/**
* 打开选择商品窗口
* 打开选择商品
*/
const addProd = () => {
prodsSelectVisible.value = true
@ -232,10 +213,8 @@ const addProd = () => {
prodsSelectRef.value?.init(card.value.realData.prod)
})
}
/**
* 添加指定商品到轮播图关联
* @param prods - 已选中的商品列表
* 添加指定商品
*/
const selectCouponProds = (prods) => {
card.value.realData.prods = prods
@ -252,7 +231,7 @@ const selectCouponProds = (prods) => {
</script>
<style lang="scss" scoped>
//
//card
.card-prod-bottom {
position: relative;
text-align: left;

@ -1,6 +1,5 @@
<template>
<div class="mod-prod">
<!-- Avue-Crud 组件用于创建数据表格 -->
<avue-crud
ref="crudRef"
:page="page"
@ -11,9 +10,7 @@
@selection-change="selectionChange"
@on-load="getDataList"
>
<!-- 自定义左侧菜单按钮 -->
<template #menu-left>
<!-- 新增按钮仅当用户有权限时显示 -->
<el-button
v-if="isAuth('admin:indexImg:save')"
type="primary"
@ -23,7 +20,6 @@
新增
</el-button>
<!-- 批量删除按钮仅当用户有权限且选中至少一项时启用 -->
<el-button
v-if="isAuth('admin:indexImg:delete')"
type="danger"
@ -34,9 +30,7 @@
</el-button>
</template>
<!-- 自定义图片展示列 -->
<template #imgUrl="scope">
<!-- 如果存在图片链接则显示图片否则显示默认图片 -->
<img
v-if="scope.row.imgUrl"
alt=""
@ -52,10 +46,7 @@
height="100"
>
</template>
<!-- 自定义操作菜单 -->
<template #menu="scope">
<!-- 修改按钮仅当用户有权限时显示 -->
<el-button
v-if="isAuth('admin:indexImg:update')"
type="primary"
@ -64,7 +55,6 @@
>
修改
</el-button>
<!-- 删除按钮仅当用户有权限时显示 -->
<el-button
v-if="isAuth('admin:indexImg:delete')"
type="danger"
@ -76,7 +66,7 @@
</template>
</avue-crud>
<!-- 弹窗组件用于新增或修改轮播图信息 -->
<!-- 弹窗, 新增 / 修改 -->
<add-or-update
v-if="addOrUpdateVisible"
ref="addOrUpdateRef"
@ -90,33 +80,18 @@ import { isAuth } from '@/utils'
import { ElMessage, ElMessageBox } from 'element-plus'
import AddOrUpdate from './add-or-update.vue'
import { tableOption } from '@/crud/admin/indexImg.js'
import { ref, reactive, nextTick } from 'vue'
import http from '@/api/http'
//
const dataList = ref([])
//
const dataListLoading = ref(false)
//
const dataListSelections = ref([])
// URL
const resourcesUrl = import.meta.env.VITE_APP_RESOURCES_URL
//
const page = reactive({
total: 0, //
currentPage: 1, //
pageSize: 10 //
pageSize: 10 //
})
/**
* 获取数据列表
* @param {Object} pageParam - 分页参数对象
* @param {Object} params - 查询条件
* @param {Function} done - 完成回调函数
*/
const getDataList = (pageParam, params, done) => {
dataListLoading.value = true
@ -144,15 +119,11 @@ const getDataList = (pageParam, params, done) => {
})
}
//
const addOrUpdateVisible = ref(false)
//
const addOrUpdateRef = ref(null)
/**
* 新增或修改操作
* @param {Number} id - 要编辑的数据ID若为新增则传入null或不传
* 新增 / 修改
* @param id
*/
const onAddOrUpdate = (id) => {
addOrUpdateVisible.value = true
@ -160,13 +131,14 @@ const onAddOrUpdate = (id) => {
addOrUpdateRef.value?.init(id)
})
}
/**
* 删除操作支持单个删除和批量删除
* @param {Number} id - 单个删除时传入的数据ID
* 删除
* @param id
*/
const onDelete = (id) => {
const ids = id ? [id] : dataListSelections.value?.map(item => item.imgId)
const ids = id ? [id] : dataListSelections.value?.map(item => {
return item.imgId
})
ElMessageBox.confirm(`确定进行[${id ? '删除' : '批量删除'}]操作?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
@ -190,19 +162,14 @@ const onDelete = (id) => {
})
})
}
/**
* 条件查询事件处理
* @param {Object} params - 查询条件
* @param {Function} done - 完成回调函数
* 条件查询
*/
const onSearch = (params, done) => {
getDataList(page, params, done)
}
/**
* 多选变化事件处理
* @param {Array} val - 当前选中的数据项数组
* 多选变化
*/
const selectionChange = (val) => {
dataListSelections.value = val

@ -1,5 +1,5 @@
<template>
<!-- 发货信息对话框用于导出待发货订单的Excel交给快递公司 -->
<!-- 发货信息用于导出代发货订单的excel交给快递公司 -->
<el-dialog
v-model="visible"
:modal="false"
@ -7,7 +7,6 @@
:close-on-click-modal="false"
width="38%"
>
<!-- 表单组件包含发件人姓名手机号和地址的输入项 -->
<el-form
ref="dataFormRef"
:model="dataForm"
@ -15,7 +14,6 @@
label-width="120px"
@keyup.enter="onSubmit()"
>
<!-- 发件人姓名输入项 -->
<el-form-item
label="发件人姓名"
prop="consignmentName"
@ -23,11 +21,9 @@
<el-input
v-model="dataForm.consignmentName"
controls-position="right"
placeholder="请输入发件人姓名"
label="发件人姓名"
/>
</el-form-item>
<!-- 发货人手机号输入项 -->
<el-form-item
label="发货人手机号"
prop="consignmentMobile"
@ -35,11 +31,9 @@
<el-input
v-model="dataForm.consignmentMobile"
controls-position="right"
placeholder="请输入发货人手机号"
label="发货人手机号"
/>
</el-form-item>
<!-- 发货地址输入项 -->
<el-form-item
label="发货地址"
prop="consignmentAddr"
@ -47,74 +41,60 @@
<el-input
v-model="dataForm.consignmentAddr"
controls-position="right"
placeholder="请输入发货地址"
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>
<el-button
@click="visible = false"
>取消</el-button>
<el-button
type="primary"
@click="onSubmit()"
>确定</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import { ref, reactive, nextTick, defineEmits, defineExpose } from 'vue'
//
const emit = defineEmits(['inputCallback'])
//
const visible = ref(false)
//
const dataForm = reactive({
consignmentName: '', //
consignmentMobile: '', //
consignmentAddr: '' //
consignmentName: '',
consignmentMobile: '',
consignmentAddr: ''
})
//
const dataRule = {
consignmentName: [
{ required: true, message: '发件人姓名不能为空', trigger: 'blur' }
{ required: true, message: '不能为空', trigger: 'blur' }
],
consignmentMobile: [
{ required: true, message: '发货人手机号不能为空', trigger: 'blur' }
{ required: true, message: '不能为空', trigger: 'blur' }
],
consignmentAddr: [
{ required: true, message: '发货地址不能为空', trigger: 'blur' }
{ required: true, message: '不能为空', trigger: 'blur' }
]
}
//
const dataFormRef = ref(null)
/**
* 初始化方法打开对话框并重置表单
*/
const init = () => {
visible.value = true
nextTick(() => {
dataFormRef.value?.resetFields()
})
}
defineExpose({ init }) // init
defineExpose({ init })
/**
* 表单提交方法
* 表单提交
*/
const onSubmit = () => {
dataFormRef.value?.validate((valid) => {
if (valid) {
visible.value = false //
emit('inputCallback', dataForm) //
visible.value = false
emit('inputCallback', dataForm)
}
})
}

@ -1,12 +1,10 @@
<template>
<!-- 发货地址选择对话框 -->
<el-dialog
v-model="visible"
:modal="false"
title="选择发货地址"
:close-on-click-modal="false"
>
<!-- 表单组件用于选择快递公司和填写快递单号 -->
<el-form
ref="dataFormRef"
:model="dataForm"
@ -14,13 +12,11 @@
label-width="80px"
@keyup.enter="onSubmit()"
>
<!-- 快递公司选择项 -->
<el-form-item label="快递公司">
<el-select
v-model="dataForm.dvyId"
placeholder="请选择"
>
<!-- 动态生成选项列表 dataForm.dvyNames 中获取 -->
<el-option
v-for="item in dataForm.dvyNames"
:key="item.dvyId"
@ -29,8 +25,6 @@
/>
</el-select>
</el-form-item>
<!-- 快递单号输入项 -->
<el-form-item
label="快递单号"
prop="dvyFlowId"
@ -39,37 +33,26 @@
v-model="dataForm.dvyFlowId"
controls-position="right"
:min="0"
placeholder="请输入快递单号"
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>
<el-button
type="primary"
@click="onSubmit()"
>确定</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import { ElMessage } from 'element-plus'
import { ref, reactive, defineEmits, defineExpose } from 'vue'
import http from '@/api/http' // HTTP
//
const emit = defineEmits(['refreshDataList'])
/**
* 自定义验证规则确保快递单号不为空且已正确填写
* @param {Object} rule - 验证规则对象
* @param {String} value - 输入值
* @param {Function} callback - 回调函数用于通知验证结果
*/
// eslint-disable-next-line no-unused-vars
const validDvyFlowId = (rule, value, callback) => {
if (!value.trim()) {
callback(new Error('不能为空'))
@ -77,8 +60,6 @@ const validDvyFlowId = (rule, value, callback) => {
callback()
}
}
// 使
const dataRule = {
dvyFlowId: [
{ required: true, message: '不能为空', trigger: 'blur' },
@ -86,30 +67,19 @@ const dataRule = {
]
}
//
const visible = ref(false)
// ID
const dataForm = reactive({
dvyId: '', // ID
dvyFlowId: '', //
dvyNames: [], //
orderNumber: '' //
dvyId: '',
dvyFlowId: 0,
dvyNames: [],
orderNumber: 0
})
/**
* 初始化方法打开对话框并加载快递公司列表
* @param {String} orderNumber - 订单编号
* @param {String} [dvyId] - 默认快递公司ID可选
* @param {String} [dvyFlowId] - 默认快递单号可选
*/
const init = (orderNumber, dvyId, dvyFlowId) => {
visible.value = true
dataForm.orderNumber = orderNumber || ''
dataForm.dvyId = dvyId || ''
dataForm.dvyFlowId = dvyFlowId || ''
// dataForm.dvyNames
http({
url: http.adornUrl('/admin/delivery/list'),
method: 'get',
@ -118,18 +88,15 @@ const init = (orderNumber, dvyId, dvyFlowId) => {
dataForm.dvyNames = data
})
}
defineExpose({ init }) // init
defineExpose({ init })
//
const dataFormRef = ref(null)
/**
* 表单提交方法验证表单并提交数据
* 表单提交
*/
const onSubmit = () => {
dataFormRef.value?.validate((valid) => {
if (valid) {
//
http({
url: http.adornUrl('/order/order/delivery'),
method: 'put',
@ -145,12 +112,13 @@ const onSubmit = () => {
type: 'success',
duration: 1500,
onClose: () => {
visible.value = false //
emit('refreshDataList') //
visible.value = false
emit('refreshDataList')
}
})
})
}
})
}
</script>

@ -1,12 +1,10 @@
<template>
<!-- el-dialog组件用于创建一个对话框用于展示订单相关信息 -->
<el-dialog
v-model="visible"
:title="!dataForm.orderNumber? '新增' : '查看'"
:title="!dataForm.orderNumber ? '新增' : '查看'"
:close-on-click-modal="false"
width="80%"
>
<!-- el-form组件用于创建表单这里用于承载订单信息的展示和相关操作表单 -->
<el-form
ref="dataFormRef"
:model="dataForm"
@ -17,36 +15,30 @@
<div class="content">
<div class="order-number">
<div class="num-cont">
<!-- el-form-item用于在表单中定义一个表单项这里展示订单编号 -->
<el-form-item label="订单编号:">
<span class="text">{{ dataForm.orderNumber }}</span>
</el-form-item>
<el-form-item>
<!-- el-steps组件用于展示步骤条展示订单的不同流程状态 -->
<el-steps
:active="stepsStatus"
align-center
:process-status="dataForm.status == 6? 'error':'wait'"
:process-status="dataForm.status == 6 ? 'error':'wait'"
>
<!-- el-step定义步骤条中的每一个步骤这里展示提交订单步骤及其时间描述 -->
<el-step
title="提交订单"
:description="dataForm.orderTime"
/>
<!-- 展示买家已付款步骤及其时间描述 -->
<el-step
title="买家已付款"
:description="dataForm.payTime"
/>
<!-- 根据订单类型判断是否展示卖家已发货步骤及其时间描述 -->
<el-step
v-if="dataForm.orderType!== 1"
v-if="dataForm.orderType !== 1"
title="卖家已发货"
:description="dataForm.dvyTime"
/>
<!-- 根据订单类型判断是否展示买家已收货步骤及其时间描述 -->
<el-step
v-if="dataForm.orderType!== 1"
v-if="dataForm.orderType !== 1"
title="买家已收货"
:description="dataForm.finallyTime"
/>
@ -57,7 +49,6 @@
<div class="order-state">
<div class="state-cont">
<div class="state-title">
<!-- 展示订单状态的el-form-item通过不同条件判断显示不同状态的标签 -->
<el-form-item label="订单状态:">
<template #default>
<el-tag
@ -67,19 +58,19 @@
待付款
</el-tag>
<el-tag
v-if="dataForm.status === 2 && dataForm.orderType!== 1"
v-if="dataForm.status === 2 && dataForm.orderType !== 1"
type="warning"
>
待发货
</el-tag>
<el-tag
v-if="dataForm.status === 3 && dataForm.orderType!== 1"
v-if="dataForm.status === 3 && dataForm.orderType !== 1"
type="warning"
>
待收货
</el-tag>
<el-tag
v-if="dataForm.status === 4 && dataForm.orderType!== 1"
v-if="dataForm.status === 4 && dataForm.orderType !== 1"
type="warning"
>
待评价
@ -100,9 +91,8 @@
</el-form-item>
<el-form-item>
<el-row>
<!-- 根据订单状态和类型判断是否显示发货按钮点击可触发changeOrder方法 -->
<el-button
v-if="dataForm.status === 2 && dataForm.orderType!== 1"
v-if="dataForm.status === 2 && dataForm.orderType !== 1"
type="primary"
plain
@click="changeOrder(dataForm.orderNumber)"
@ -128,7 +118,6 @@
alt=""
>
<div class="text-width">
<!-- 展示收货人相关信息的el-form-item -->
<el-form-item label="收货人:">
<span>{{ dataForm.userAddrOrder.receiver }}</span>
</el-form-item>
@ -180,7 +169,6 @@
</div>
</div>
<div class="item-list">
<!-- el-table组件用于展示商品列表相关信息如商品名称价格数量总价等 -->
<el-table
:data="dataForm.orderItems"
border
@ -232,7 +220,6 @@
</el-table>
</div>
<div class="item-info">
<!-- 展示商品总价的el-form-item -->
<el-form-item label="商品总价:">
<span class="text">{{ dataForm.total }}</span>
</el-form-item>
@ -253,7 +240,6 @@
<span>订单日志</span>
</div>
<div class="log-cont">
<!-- 根据不同的订单时间相关属性展示对应的订单日志信息 -->
<el-form-item
v-if="dataForm.orderTime"
label-width="10px"
@ -296,7 +282,7 @@
</div>
</el-form>
<!-- 弹窗组件用于新增或修改相关操作当devyVisible为真时显示可通过@refresh-data-list监听事件刷新数据列表 -->
<!-- 弹窗, 新增 / 修改 -->
<devy-add
v-if="devyVisible"
ref="devyAddRef"
@ -307,74 +293,55 @@
<script setup>
import DevyAdd from './order-devy.vue'
import { ref, computed, watch, nextTick } from 'vue'
import http from '@/api/http' // HTTP
import { ElMessage } from 'element-plus'
// URL
const resourcesUrl = import.meta.env.VITE_APP_RESOURCES_URL
//
const dataForm = ref({
orderId: 0, // ID
orderNumber: '', //
remarks: '', //
total: 0, //
actualTotal: 0, //
dvyType: '', //
status: 1, //
addrOrderId: 0, // ID
nickName: '', //
orderItems: [], //
orderTime: '', //
updateTime: '', //
payTime: '', //
dvyTime: '', //
finallyTime: '', //
cancelTime: '', //
userAddrOrder: {} //
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 //
return 4
}
if (dataForm.value.dvyTime) {
return 3 //
return 3
}
if (dataForm.value.payTime) {
return 2 //
return 2
}
if (dataForm.value.orderTime) {
return 1 //
return 1
}
return '' //
return ''
})
//
const visible = ref(false)
//
const devyVisible = ref(false)
// `visible`
watch(
() => visible.value,
(newValue) => {
if (!newValue) {
() => {
if (!visible.value) {
devyVisible.value = false
}
}
)
//
const dataFormRef = ref(null)
/**
* 初始化方法打开对话框并加载订单信息
* @param {String|Number} orderNumber - 订单编号
*/
const init = (orderNumber) => {
dataForm.value.orderNumber = orderNumber || 0
visible.value = true
@ -382,37 +349,34 @@ const init = (orderNumber) => {
dataFormRef.value?.resetFields()
})
if (dataForm.value.orderNumber) {
//
//
http({
url: http.adornUrl(`/order/order/orderInfo/${dataForm.value.orderNumber}`),
method: 'get',
params: http.adornParams()
}).then(({ data }) => {
})
.then(({ data }) => {
dataForm.value = data
})
}
}
defineExpose({ init }) // init
defineExpose({ init })
/**
* 获取订单信息的方法
*/
const getDataList = () => {
http({
url: http.adornUrl(`/order/order/orderInfo/${dataForm.value.orderNumber}`),
method: 'get',
params: http.adornParams()
}).then(({ data }) => {
})
.then(({ data }) => {
dataForm.value = data
})
}
//
const devyAddRef = ref(null)
/**
* 发货操作打开发货对话框并初始化数据
* @param {String|Number} orderNumber - 订单编号
* 发货
* @param orderNumber
*/
const changeOrder = (orderNumber) => {
devyVisible.value = true
@ -423,172 +387,133 @@ const changeOrder = (orderNumber) => {
</script>
<style scoped lang="scss">
//
.main {
height: 100%; // 100%
width: 100%; // 100%
font: 14px Arial, "PingFang SC", "Hiragino Sans GB", STHeiti, "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif; //
color: #495060; //
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; // 20px
//
margin: 0 20px;
.order-state {
position: relative; //
margin-top: 50px; // 50px
border-bottom: 1px solid #e9eaec; //
position: relative;
margin-top: 50px;
border-bottom: 1px solid #e9eaec;
}
}
//
.order-number {
.text {
font-size: 14px; //
color: #8a8a8a; //
font-size: 14px;
color: #8a8a8a;
}
}
//
.order-state {
//
.state-title {
width: 100%; // 100%
display: flex; // 使
justify-content: space-between; //
align-items: center; //
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
}
//
.order-info {
width: 100%; // 100%
border-top: 1px solid #e9eaec; //
margin: 50px 0; // 50px
display: flex; // 使
width: 100%;
border-top: 1px solid #e9eaec;
margin: 50px 0;
display: flex;
}
//
.item-info {
padding-left: 80%; // 80%
margin: 25px 0; // 25px
padding-left: 80%;
margin: 25px 0;
}
}
//
.order-info {
img {
width: 18px !important; // 18px
height: 18px !important; // 18px
margin-right: 15px; // 15px
width: 18px !important;
height: 18px !important;
margin-right: 15px;
}
//
.detail-title {
height: 50px; // 50px
line-height: 50px; // 50px
display: flex; // 使
align-items: center; //
height: 50px;
line-height: 50px;
display: flex;
align-items: center;
}
//
.order-details {
width: 50%; // 50%
border-right: 1px solid #e9eaec; //
width: 50%;
border-right: 1px solid #e9eaec;
}
//
.detail-cont {
position: relative; //
border-top: 1px dashed #e9eaec; // 线
margin: 15px 20px 0 0; //
position: relative;
border-top: 1px dashed #e9eaec;
margin: 15px 20px 0 0;
}
//
.buyers {
width: 50%; // 50%
margin-left: 20px; // 20px
width: 50%;
margin-left: 20px;
}
}
//
.detail-cont {
.detail01 {
display: flex; // 使
height: 100%; // 100%
line-height: 25px; // 25px
margin-top: 15px; // 15px
display: flex;
height: 100%;
line-height: 25px;
margin-top: 15px;
}
}
.detail01 {
.text-width {
width: 100%;
}
}
//
.detail01,
.detail02 {
.text-width {
width: 100%; // 100%
width: 100%;
}
}
//
.buyers {
.buyers-info {
border-top: 1px dashed #e9eaec; // 线
margin-top: 15px; // 15px
position: relative; //
border-top: 1px dashed #e9eaec;
margin-top: 15px;
position: relative;
}
.detail02 {
display: flex; // 使
height: 100%; // 100%
line-height: 25px; // 25px
margin-top: 15px; // 15px
display: flex;
height: 100%;
line-height: 25px;
margin-top: 15px;
}
}
//
.item-info {
span {
margin-bottom: 15px; // 15px
line-height: 30px; // 30px
margin-bottom: 15px;
line-height: 30px;
}
.text {
position: absolute; //
right: 0; //
position: absolute;
right: 0;
}
}
//
.order-log {
.log-title {
height: 50px; // 50px
width: 100%; // 100%
line-height: 50px; // 50px
font-weight: bold; //
height: 50px;
width: 100%;
line-height: 50px;
font-weight: bold;
}
.log-cont {
color: #4395ff; //
color: #4395ff;
}
}
//
.item-list {
.prod-con {
display: flex; // 使
//
display: flex;
.prod-img {
width: 100px; // 100px
height: 100px; // 100px
margin-right: 8px; // 8px
width: 100px;
height: 100px;
margin-right: 8px;
}
}
}
//
:deep(.el-steps--horizontal) {
flex: 25% 1 1; // flex
flex: 25% 1 1;
}
</style>

@ -1,14 +1,17 @@
<template>
<!-- 定义一个订单模块的容器 -->
<div class="mod-order-order">
<!-- 表单区域用于输入筛选条件 -->
<el-form :inline="true" :model="dataForm" @keyup.enter="getDataList(page)">
<!-- 输入框订单编号 -->
<el-form
:inline="true"
:model="dataForm"
@keyup.enter="getDataList(page)"
>
<el-form-item label="订单编号:">
<el-input v-model="dataForm.orderNumber" placeholder="订单编号" clearable />
<el-input
v-model="dataForm.orderNumber"
placeholder="订单编号"
clearable
/>
</el-form-item>
<!-- 时间选择器下单时间范围 -->
<el-form-item label="下单时间:">
<el-date-picker
v-model="dateRange"
@ -19,59 +22,95 @@
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
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
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
v-if="isAuth('order:order:soldExcel')"
type="primary"
@click="getSoldExcel()"
>
导出销售记录
</el-button>
<el-button @click="clearDatas()"></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-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
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
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;" />
<img
alt=""
:src="resourcesUrl + orderItem.pic"
style="height:100px;width: 100px;"
>
</div>
<div class="prod-name">
<span>{{ orderItem.prodName }}</span>
@ -84,9 +123,10 @@
</div>
</div>
</el-col>
<!-- 实付金额 -->
<el-col :span="3" style="height: 100%;">
<el-col
:span="3"
style="height: 100%;"
>
<div class="item">
<div>
<span class="totalprice">{{ order.actualTotal }}</span>
@ -95,9 +135,10 @@
</div>
</div>
</el-col>
<!-- 支付方式 -->
<el-col :span="3" style="height: 100%;">
<el-col
:span="3"
style="height: 100%;"
>
<div class="item">
<div>
<span v-if="order.payType === 1"></span>
@ -106,24 +147,47 @@
</div>
</div>
</el-col>
<!-- 订单状态 -->
<el-col :span="3" style="height: 100%;">
<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>
<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%;">
<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
v-if="isAuth('order:order:update')"
type="text"
@click="onAddOrUpdate(order.orderNumber)"
>
查看
</el-button>
</div>
@ -131,8 +195,6 @@
</el-col>
</el-row>
</div>
<!-- 备注信息 -->
<div class="remark">
<div class="buyer-remark">
<span>备注:{{ order.remarks }}</span>
@ -141,15 +203,15 @@
</div>
</div>
</div>
<!-- 当没有数据时显示的提示信息 -->
<div v-if="!dataList.length" class="empty-tips">
<!-- -->
<div
v-if="!dataList.length"
class="empty-tips"
>
暂无数据
</div>
<!-- 分页组件 -->
<el-pagination
:current-page="page.currentPage"
:current-page="page.pageIndex"
:page-sizes="[10, 20, 50, 100]"
:page-size="page.pageSize"
:total="page.total"
@ -157,73 +219,63 @@
@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" />
<!-- 弹窗, 新增 / 修改 -->
<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'
// URL
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)
})
/**
* 发起HTTP请求获取订单数据列表并更新页面上的数据显示
* @param pageParam - 分页参数默认使用全局page对象
* @param params - 额外的查询参数
* @param done - 回调函数在获取数据完成后调用
* 获取数据列表
*/
const getDataList = (pageParam, params, done) => {
pageParam = (pageParam === undefined ? page : pageParam)
@ -250,42 +302,36 @@ const getDataList = (pageParam, params, done) => {
if (done) done()
})
}
/**
* 清除所有表单数据和日期范围选择
* 清除数据
*/
const clearDatas = () => {
dataForm.value = {}
dateRange.value = []
}
/**
* 当分页组件中每页显示量改变时触发此方法
* @param val - 新的每页显示数量
* 每页数
* @param val
*/
const sizeChangeHandle = (val) => {
page.pageSize = val
page.currentPage = 1
getDataList(page)
}
/**
* 分页组件中当前页码改变时触发此方法
* @param val - 新的当前页码
* 前页
* @param val
*/
const currentChangeHandle = (val) => {
page.currentPage = val
getDataList(page)
}
// AddOrUpdate
const addOrUpdateRef = ref(null)
// AddOrUpdate
const addOrUpdateVisible = ref(false)
/**
* 打开订单信息编辑或新增弹窗
* @param val - 可选参数当存在时表示编辑指定ID的订单
* 新增 / 修改
* @param val
*/
const onAddOrUpdate = (val) => {
addOrUpdateVisible.value = true
@ -294,25 +340,14 @@ const onAddOrUpdate = (val) => {
})
}
// ConsignmentInfo
const consignmentInfoRef = ref(null)
// ConsignmentInfo
const consignmentInfoVisible = ref(false)
/**
* 显示发货信息弹窗
*/
const showConsignmentInfo = () => {
consignmentInfoVisible.value = true
nextTick(() => {
consignmentInfoRef.value?.init()
})
}
/**
* 下载待发货信息整理Excel文件
* @param consignmentInfo - 发货信息对象
*/
const getWaitingConsignmentExcel = (consignmentInfo) => {
http({
url: http.adornUrl('/order/order/waitingConsignmentExcel'),
@ -321,44 +356,42 @@ const getWaitingConsignmentExcel = (consignmentInfo) => {
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]
startTime: dateRange.value === null ? null : dateRange.value[0], //
endTime: dateRange.value === null ? null : dateRange.value[1] //
}),
responseType: 'blob'
responseType: 'blob' //
})
.then(({ data }) => {
const blob = new Blob([data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8' })
const fileName = '待发货信息整理.xls'
downloadFile(blob, fileName)
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)
}
})
}
/**
* 下载销售信息整理Excel文件
*/
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]
startTime: dateRange.value === null ? null : dateRange.value[0], //
endTime: dateRange.value === null ? null : dateRange.value[1] //
}),
responseType: 'blob'
responseType: 'blob' //
})
.then(({ data }) => {
const blob = new Blob([data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8' })
const fileName = '销售信息整理.xls'
downloadFile(blob, fileName)
})
}
/**
* 文件下载辅助函数
* @param blob - 文件内容
* @param fileName - 文件名
*/
function downloadFile(blob, fileName) {
const elink = document.createElement('a')
if ('download' in elink) { // IE
elink.download = fileName
@ -366,38 +399,35 @@ function downloadFile(blob, fileName) {
elink.href = URL.createObjectURL(blob)
document.body.appendChild(elink)
elink.click()
URL.revokeObjectURL(elink.href)
URL.revokeObjectURL(elink.href) // URL
document.body.removeChild(elink)
} else { // IE10+
navigator.msSaveBlob(blob, fileName)
}
})
}
</script>
<style lang="scss" scoped>
// 使scoped
.mod-order-order {
// flex45px
.tit {
display: flex;
height: 45px;
align-items: center;
}
// 10px10%
.tit {
.item {
padding: 0 10px;
width: 10%;
text-align: center;
}
// 25%
.product {
width: 25%;
}
}
//
.prod-tit {
padding: 10px;
background: #f8f8f9;
@ -405,13 +435,11 @@ function downloadFile(blob, fileName) {
border-top: 1px solid #dddee1;
border-right: 1px solid #dddee1;
// span15px
span {
margin-right: 15px;
}
}
// flex
.prod-cont {
display: flex;
border-top: 1px solid #dddee1;
@ -419,7 +447,6 @@ function downloadFile(blob, fileName) {
border-left: 1px solid #dddee1;
color: #495060;
// flex
.item {
display: flex;
display: -webkit-flex;
@ -430,39 +457,33 @@ function downloadFile(blob, fileName) {
text-align: center;
height: 100%;
// span
span {
display: block;
}
}
// 使flex
.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;
}
}
}
// 55%
.prod-name {
width: 55%;
text-align: left;
// 30px
.prod-info {
display: block;
color: #80848f;
@ -470,20 +491,17 @@ function downloadFile(blob, fileName) {
}
}
// 40px
.prod-price {
position: absolute;
right: 40px;
text-align: right;
// span10px
span {
display: block;
margin-bottom: 10px;
}
}
//
.prod-image {
margin-right: 20px;
width: 100px;
@ -495,25 +513,21 @@ function downloadFile(blob, fileName) {
}
}
// span10px
.item {
span {
display: block;
margin-bottom: 10px;
}
//
.operate {
color: #2d8cf0;
}
//
.totalprice {
color: #c00;
}
}
//
.prod {
.remark {
width: 100%;
@ -527,7 +541,6 @@ function downloadFile(blob, fileName) {
}
}
//
.buyer-remark {
padding: 0 20px;
overflow: hidden;
@ -535,7 +548,6 @@ function downloadFile(blob, fileName) {
text-overflow: ellipsis;
}
// 50px
.empty-tips {
display: block;
width: 100%;

@ -1,32 +1,26 @@
<template>
<!-- 使用el-dialog组件创建一个对话框v-model绑定visible控制显示/隐藏 -->
<el-dialog
v-model="visible"
:title="!dataForm.currentId ? '新增' : '修改'" <!-- 根据currentId判断是新增还是修改 -->
:close-on-click-modal="false" <!-- 点击模态框区域不关闭对话框 -->
:title="!dataForm.currentId ? '新增' : '修改'"
:close-on-click-modal="false"
>
<!-- el-form表单ref引用dataFormRef以便在脚本中操作表单 -->
<el-form
ref="dataFormRef"
:model="dataForm"
:rules="dataRule"
label-width="80px"
@keyup.enter="onSubmit()" <!-- 按下Enter键时触发提交 -->
@keyup.enter="onSubmit()"
>
<!-- 分类图片表单项仅当type不是2时显示 -->
<el-form-item
v-if="dataForm.type !== 2"
label="分类图片"
prop="pic"
>
<!-- 自定义的图片上传组件 -->
<pic-upload
v-model="dataForm.pic"
@update:model-value="checkPic" <!-- 图片更新后重新校验 -->
@update:model-value="checkPic"
/>
</el-form-item>
<!-- 分类名称表单项仅当type不是2时显示 -->
<el-form-item
v-if="dataForm.type !== 2"
label="分类名称"
@ -39,8 +33,6 @@
label="分类名称"
/>
</el-form-item>
<!-- 上级分类选择器 -->
<el-form-item label="上级分类">
<el-cascader
v-model="selectedCategory"
@ -49,11 +41,9 @@
:props="categoryTreeProps"
change-on-select
:clearable="true"
@change="handleChange" <!-- 上级分类变化时触发 -->
@change="handleChange"
/>
</el-form-item>
<!-- 排序号表单项仅当type不是2时显示 -->
<el-form-item
v-if="dataForm.type !== 2"
label="排序号"
@ -66,41 +56,42 @@
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 :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> <!-- 点击确定提交表单 -->
<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'
// emit
import { ElMessage } from 'element-plus'
import { treeDataTranslate, idList } from '@/utils'
import { Debounce } from '@/utils/debounce'
const emit = defineEmits(['refreshDataList'])
//
const visible = ref(false)
// 使reactive
const dataForm = reactive({
categoryId: 0,
currentId: 0,
@ -111,109 +102,108 @@ const dataForm = reactive({
parentId: 0,
pic: ''
})
//
const dataRule = reactive({
categoryName: [
{required: true, message: '分类名称不能为空', trigger: 'blur'},
{pattern: /\s\S+|S+\s|\S/, message: '请输入正确的分类名称', trigger: 'blur'}
{ required: true, message: '分类名称不能为空', trigger: 'blur' },
{ pattern: /\s\S+|S+\s|\S/, message: '请输入正确的分类名称', trigger: 'blur' }
],
pic: [
{required: true, message: '分类图片不能为空', trigger: 'blur'}
{ required: true, message: '分类图片不能为空', trigger: 'blur' }
]
})
//
const categoryList = ref([])
//
const selectedCategory = ref([])
//
const categoryTreeProps = reactive({
value: 'categoryId',
label: 'categoryName',
checkStrictly: true
})
//
const isSubmit = ref(false)
//
const dataFormRef = ref(null)
// id
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}) => {
.then(({ data }) => {
categoryList.value = treeDataTranslate(data, 'categoryId', 'parentId')
})
.then(() => {
visible.value = true
nextTick(() => {
dataFormRef.value?.resetFields() //
dataFormRef.value?.resetFields()
selectedCategory.value = []
})
})
.then(() => {
if (dataForm.categoryId) {
// ID
//
http({
url: http.adornUrl(`/prod/category/info/${dataForm.categoryId}`),
method: 'get',
params: http.adornParams()
})
.then(({data}) => {
Object.assign(dataForm, data)
.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 })
// init
defineExpose({init})
//
const handleChange = (val) => {
dataForm.parentId = val[val.length - 1]
}
// 使
//
const onSubmit = Debounce(() => {
// grade
dataForm.grade = selectedCategory.value.length - 1
//
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 //
if (isSubmit.value) {
return
}
isSubmit.value = true
//
http({
url: http.adornUrl('/prod/category'),
method: dataForm.categoryId ? 'put' : 'post',
data: http.adornData({
...dataForm
categoryId: dataForm.categoryId || undefined,
categoryName: dataForm.categoryName,
status: dataForm.status,
seq: dataForm.seq,
grade: dataForm.grade,
parentId: dataForm.parentId,
pic: dataForm.pic
})
})
.then(() => {
ElMessage.success('操作成功')
ElMessage({
message: '操作成功',
type: 'success',
duration: 1000,
onClose: () => {
isSubmit.value = false
visible.value = false
emit('refreshDataList') //
emit('refreshDataList')
}
})
.catch(() => {
isSubmit.value = false
})
}
})

@ -1,10 +1,10 @@
<template>
<!-- 分类管理模块 -->
<div class="mod-category">
<!-- 搜索表单使用inline布局让表单项在同一行显示 -->
<el-form :inline="true" :model="dataForm">
<el-form
:inline="true"
:model="dataForm"
>
<el-form-item>
<!-- 新增按钮只有拥有相应权限的用户可以点击 -->
<el-button
v-if="isAuth('prod:category:save')"
type="primary"
@ -15,10 +15,12 @@
</el-button>
</el-form-item>
</el-form>
<!-- 数据表格展示分类列表 -->
<el-table :data="dataList" border row-key="categoryId" style="width: 100%;">
<!-- 分类名称列 -->
<el-table
:data="dataList"
border
row-key="categoryId"
style="width: 100%;"
>
<el-table-column
prop="categoryName"
header-align="center"
@ -26,8 +28,6 @@
width="150"
label="分类名称"
/>
<!-- 图片列自定义模板显示图片 -->
<el-table-column
prop="pic"
header-align="center"
@ -35,11 +35,12 @@
label="图片"
>
<template #default="scope">
<img alt="" :src="checkFileUrl(scope.row.pic)" style="height:80px;width:200px;">
<img
alt=""
:src="checkFileUrl(scope.row.pic)"
>
</template>
</el-table-column>
<!-- 状态列根据状态值显示不同颜色的标签 -->
<el-table-column
prop="status"
header-align="center"
@ -47,27 +48,29 @@
label="状态"
>
<template #default="scope">
<el-tag v-if="scope.row.status === 0" type="danger">线</el-tag>
<el-tag v-else></el-tag>
<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"
@ -75,7 +78,6 @@
>
修改
</el-button>
<!-- 删除按钮只有拥有相应权限的用户可以点击 -->
<el-button
v-if="isAuth('prod:category:delete')"
type="danger"
@ -86,8 +88,7 @@
</template>
</el-table-column>
</el-table>
<!-- 弹窗, 新增 / 修改通过addOrUpdateVisible控制显示隐藏 -->
<!-- 弹窗, 新增 / 修改 -->
<add-or-update
v-if="addOrUpdateVisible"
ref="addOrUpdateRef"
@ -101,64 +102,53 @@
import { checkFileUrl, isAuth, treeDataTranslate } from '@/utils'
import { ElMessage, ElMessageBox } from 'element-plus'
import AddOrUpdate from './add-or-update.vue'
//
const dataForm = ref({})
//
onMounted(() => {
getDataList()
})
//
const dataList = ref([])
const dataListLoading = ref(false)
/**
* 获取数据列表方法
* 获取数据列表
*/
const getDataList = () => {
dataListLoading.value = true // true
dataListLoading.value = true
http({
url: http.adornUrl('/prod/category/table'),
method: 'get',
params: http.adornParams()
})
.then(({ data }) => {
// dataList
dataList.value = treeDataTranslate(data, 'categoryId', 'parentId')
dataListLoading.value = false // false
dataListLoading.value = false
})
}
//
const addOrUpdateVisible = ref(false)
const addOrUpdateRef = ref(null)
/**
* 新增或修改分类
* @param id - 如果提供id则为修改否则为新增
* 新增 / 修改
* @param id
*/
const onAddOrUpdate = (id) => {
addOrUpdateVisible.value = true //
addOrUpdateVisible.value = true
nextTick(() => {
addOrUpdateRef.value?.init(id) //
addOrUpdateRef.value?.init(id)
})
}
/**
* 删除分类
* @param id - 要删除的分类ID
* 删除
* @param id
*/
const onDelete = (id) => {
//
ElMessageBox.confirm('确定进行删除操作?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
.then(() => {
// HTTP DELETE
http({
url: http.adornUrl(`/prod/category/${id}`),
method: 'delete'
@ -169,19 +159,20 @@ const onDelete = (id) => {
type: 'success',
duration: 1500,
onClose: () => {
getDataList() //
getDataList()
}
})
})
})
}
</script>
<style lang="scss" scoped>
.mod-category {
img {
height: 80px;
width: 200px; //
width: 200px;
}
}
</style>

@ -1,5 +1,4 @@
<template>
<!-- 商品评论管理对话框 -->
<el-dialog
v-model="visible"
:title="!dataForm.prodCommId ? '新增' : '修改'"
@ -12,10 +11,11 @@
label-width="80px"
@keyup.enter="onSubmit()"
>
<!-- 如果不是编辑状态则显示评论详情 -->
<div v-if="!isEdit">
<!-- 评论内容表单项只读 -->
<el-form-item label="评论内容" prop="content">
<el-form-item
label="评论内容"
prop="userName"
>
<el-input
v-model="dataForm.content"
type="textarea"
@ -23,9 +23,13 @@
/>
</el-form-item>
<!-- 评论图片表单项 -->
<el-form-item label="评论图片" prop="pics">
<div v-if="!dataForm.pics?.length"></div>
<el-form-item
label="评论图片"
prop="userName"
>
<div v-if="!dataForm.pics?.length">
</div>
<div v-else>
<img
v-for="item in dataForm.pics"
@ -37,78 +41,106 @@
</div>
</el-form-item>
<!-- 记录时间表单项只读 -->
<el-form-item label="记录时间" prop="recTime">
<el-form-item
label="记录时间"
prop="userName"
>
<el-input
v-model="dataForm.recTime"
:readonly="true"
/>
</el-form-item>
<!-- 回复时间表单项只读 -->
<el-form-item label="回复时间" prop="replyTime">
<el-form-item
label="回复时间"
prop="userName"
:readonly="true"
>
<el-input
v-model="dataForm.replyTime"
:readonly="true"
/>
</el-form-item>
<!-- IP来源表单项只读 -->
<el-form-item label="IP来源" prop="postip">
<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-form-item
label="得分"
prop="score"
>
<el-input
v-model="dataForm.score"
:readonly="true"
/>
</el-form-item>
<!-- 是否匿名表单项只读 -->
<el-form-item label="是否匿名" prop="isAnonymous">
<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 :label="1">
</el-radio>
<el-radio :label="0">
不是
</el-radio>
</el-radio-group>
</el-form-item>
</div>
<!-- 掌柜回复表单项非编辑状态下只读 -->
<el-form-item label="掌柜回复" prop="replyContent">
<el-form-item
label="掌柜回复"
type="textarea"
prop="userName"
>
<el-input
v-model="dataForm.replyContent"
type="textarea"
:readonly="!isEdit"
/>
</el-form-item>
<!-- 审核表单项仅在编辑状态下显示 -->
<el-form-item v-if="isEdit" label="审核" prop="status">
<el-radio-group v-model="dataForm.status">
<el-radio :label="1">审核通过</el-radio>
<el-radio :label="-1">不通过</el-radio>
<el-radio :label="0">等待审核</el-radio>
<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 @click="visible = false">
取消
</el-button>
<el-button
v-if="isEdit"
type="primary"
@click="onSubmit()"
>确定</el-button> <!-- -->
>
确定
</el-button>
</div>
</template>
</el-dialog>
@ -117,14 +149,8 @@
<script setup>
import { ElMessage } from 'element-plus'
const emit = defineEmits(['refreshDataList'])
//
const dataRule = ref({})
//
const isEdit = ref(false)
// 使ref
const dataForm = ref({
prodCommId: null,
prodId: null,
@ -142,54 +168,52 @@ const dataForm = ref({
isAnonymous: null,
status: null
})
// prodCommIdisEditParam
const init = (prodCommId, isEditParam) => {
isEdit.value = isEditParam //
isEdit.value = isEditParam
dataForm.value.prodCommId = prodCommId || 0
visible.value = true //
visible.value = true
nextTick(() => {
dataFormRef.value?.resetFields() //
dataFormRef.value?.resetFields()
if (dataForm.value.prodCommId) {
// ID
http({
url: http.adornUrl('/prod/prodComm/info/' + dataForm.value.prodCommId),
method: 'get',
params: http.adornParams()
})
.then(({ data }) => {
Object.assign(dataForm.value, data) // dataForm
dataForm.value = data
})
}
})
}
defineExpose({ init })
defineExpose({ init }) // 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.success('操作成功')
visible.value = false //
emit('refreshDataList') //
ElMessage({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
visible.value = false
emit('refreshDataList')
}
})
})
}
})
}
</script>

@ -1,34 +1,28 @@
<template>
<!-- 商品评论管理模块 -->
<div class="mod-prod-prodComm">
<!-- 使用avue-crud组件构建表格 -->
<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" <!-- 删除行时触发 -->
: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)"
@click="onAddOrUpdate(scope.row.prodCommId,true)"
>
编辑
</el-button>
@ -36,14 +30,12 @@
<el-button
type="success"
icon="el-icon-view"
@click="onAddOrUpdate(scope.row.prodCommId, false)"
@click="onAddOrUpdate(scope.row.prodCommId,false)"
>
查看
</el-button>
</template>
</avue-crud>
<!-- 弹窗, 新增 / 修改 -->
<add-or-update
v-if="addOrUpdateVisible"
ref="addOrUpdateRef"
@ -54,31 +46,19 @@
<script setup>
import { ElMessage, ElMessageBox } from 'element-plus'
import { tableOption } from '@/crud/prod/prodComm.js' //
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 //
total: 0, //
currentPage: 1, //
pageSize: 20 //
})
//
const dataListLoading = ref(false)
/**
* 获取数据列表方法
* @param pageParam - 分页参数
* @param params - 搜索参数
* @param done - 完成回调函数
*/
const getDataList = (pageParam, params, done) => {
dataListLoading.value = true // true
dataListLoading.value = true
http({
url: http.adornUrl('/prod/prodComm/page'),
method: 'get',
@ -88,33 +68,24 @@ const getDataList = (pageParam, params, done) => {
}, params))
})
.then(({ data }) => {
dataList.value = data.records // dataList
page.total = data.total //
dataListLoading.value = false // false
if (done) done() //
dataList.value = data.records
page.total = data.total
dataListLoading.value = false
if (done) done()
})
}
//
const addOrUpdateVisible = ref(false)
const addOrUpdateRef = ref(null)
/**
* 新增或修改操作
* @param id - 商品评论ID新增时为null
* @param isEdit - 是否是编辑模式
* 新增 / 修改
*/
const onAddOrUpdate = (id, isEdit) => {
addOrUpdateVisible.value = true //
addOrUpdateVisible.value = true
nextTick(() => {
addOrUpdateRef.value?.init(id, isEdit) //
addOrUpdateRef.value?.init(id, isEdit)
})
}
/**
* 删除行
* @param row - 要删除的行数据
*/
const rowDel = (row) => {
ElMessageBox.confirm('确定进行删除操作?', '提示', {
confirmButtonText: '确定',
@ -123,7 +94,7 @@ const rowDel = (row) => {
})
.then(() => {
http({
url: http.adornUrl(`/prod/prodComm/${row.prodCommId}`),
url: http.adornUrl('/prod/prodComm/' + row.prodCommId),
method: 'delete',
data: http.adornData({})
})
@ -133,26 +104,19 @@ const rowDel = (row) => {
type: 'success',
duration: 1500,
onClose: () => {
getDataList() //
getDataList()
}
})
})
}).catch(() => { /* 用户取消操作 */ })
}).catch(() => { })
}
/**
* 刷新回调重新加载数据列表
* 刷新回调
*/
const refreshChange = () => {
getDataList(page) //
getDataList(page)
}
/**
* 搜索条件改变时重新加载数据列表
* @param params - 搜索参数
* @param done - 完成回调函数
*/
const onSearch = (params, done) => {
getDataList(page, params, done) //
getDataList(page, params, done)
}
</script>

@ -1,12 +1,9 @@
<template>
<!-- 商品运费设置模块 -->
<div class="mod-prod-prod-transport">
<!-- 运费设置表单项 -->
<el-form-item
label="运费设置"
:rules="[{ required: true, message: '运费模板不能为空'}]"
>
<!-- 运费模板选择框 -->
<el-select
v-model="transportId"
placeholder="请选择"
@ -20,8 +17,6 @@
/>
</el-select>
</el-form-item>
<!-- 展示运费详情表格 -->
<el-form-item>
<el-table
v-if="transportInfo.transfees"
@ -33,9 +28,7 @@
width="350"
>
<template #default="scope">
<!-- 如果没有城市列表则显示所有地区 -->
<span v-if="!scope.row.cityList.length"></span>
<!-- 否则显示具体城市 -->
<el-tag
v-for="city in scope.row.cityList"
v-else
@ -45,7 +38,6 @@
</el-tag>
</template>
</el-table-column>
<!-- 动态列标题根据chargeType改变 -->
<el-table-column
prop="firstPiece"
:label="tableTitle[0]"
@ -64,8 +56,6 @@
/>
</el-table>
</el-form-item>
<!-- 展示包邮条件 -->
<el-form-item v-if="transportInfo.hasFreeCondition === 1">
<el-table
:data="transportInfo.transfeeFrees"
@ -107,58 +97,36 @@
</template>
<script setup>
import { computed, ref, watch, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
// propsmodelValue
const props = defineProps({
modelValue: {
default: null,
type: Number
}
})
// emitsmodelValue
const emit = defineEmits(['input', 'update:modelValue'])
// ID
const transportId = ref(null)
//
const tableTitle = computed(() => {
const titles = [
['首件(个)', '运费(元)', '续件(个)', '续费(元)'],
['首重(kg)', '运费(元)', '续重(kg)', '续费(元)'],
['首体积(m³)', '运费(元)', '续体积(m³)', '续费(元)']
]
if (transportInfo.value.chargeType !== undefined) {
const titles = [['首件(个)', '运费(元)', '续件(个)', '续费(元)'], ['首重(kg)', '运费(元)', '续重(kg)', '续费(元)'], ['首体积(m³)', '运费(元)', '续体积(m³)', '续费(元)']]
if (transportInfo.value.chargeType) {
return titles[transportInfo.value.chargeType]
}
return titles[0]
})
// props.modelValue
watch(
() => props.modelValue,
(id) => {
transportId.value = id
}
)
})
//
onMounted(() => {
getTransportList()
})
//
const transportList = ref([{
transportId: null,
transName: ''
}])
/**
* 获取运费模板列表
*/
const getTransportList = () => {
http({
url: http.adornUrl('/shop/transport/list'),
@ -169,19 +137,12 @@ const getTransportList = () => {
transportList.value = data
})
}
//
const transportInfo = ref({
hasFreeCondition: false,
transfeeFrees: [{ freeCityList: [] }]
})
/**
* 当运费模板选择发生变化时触发
* @param id - 选中的运费模板ID
*/
const changeTransport = (id) => {
emit('update:modelValue', id) // modelValue
emit('update:modelValue', id)
if (!id) {
return
}
@ -191,7 +152,8 @@ const changeTransport = (id) => {
params: http.adornParams({})
})
.then(({ data }) => {
transportInfo.value = data //
transportInfo.value = data
})
}
</script>

@ -1,155 +1,136 @@
<template>
<!-- 商品SKU信息管理模块 -->
<div class="mod-prod-sku-table">
<el-form-item>
<!-- SKU信息表格 -->
<el-table
:data="modelValue" <!-- 绑定表格数据 -->
border <!-- 显示边框 -->
:data="modelValue"
border
style="width: 100%; margin-top: 20px"
>
<!-- 动态生成左侧标题列 -->
<el-table-column
v-for="(leftTitle, index) in tableLeftTitles" <!-- 循环创建列 -->
v-for="(leftTitle, index) in tableLeftTitles"
:key="index"
:label="leftTitle" <!-- 列标题 -->
:label="leftTitle"
>
<template #default="scope"> <!-- 自定义单元格内容 -->
{{ scope.row.properties.split(';')[index].split(':')[1] }} <!-- 根据properties字段显示属性值 -->
<template #default="scope">
{{ scope.row.properties.split(';')[index].split(':')[1] }}
</template>
</el-table-column>
<!-- SKU图片列 -->
<el-table-column
v-if="tableLeftTitles.length" <!-- 如果有左侧标题则显示 -->
v-if="tableLeftTitles.length"
prop="pic"
label="sku图片"
width="180"
>
<template #default="scope"> <!-- 自定义单元格内容 -->
<pic-upload v-model="scope.row.pic" /> <!-- 使用自定义上传组件 -->
<template #default="scope">
<pic-upload v-model="scope.row.pic" />
</template>
</el-table-column>
<!-- 商品名称列 -->
<el-table-column
v-if="tableLeftTitles.length" <!-- 如果有左侧标题则显示 -->
v-if="tableLeftTitles.length"
prop="prodName"
label="商品名称"
width="250"
>
<template #default="scope"> <!-- 自定义单元格内容 -->
<template #default="scope">
<el-input
v-model="scope.row.prodName" <!-- 绑定输入框值 -->
type="textarea" <!-- 设置为文本区域类型 -->
:disabled="!scope.row.status" <!-- 状态控制是否可编辑 -->
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"> <!-- 自定义单元格内容 -->
<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" <!-- 状态控制是否可编辑 -->
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"> <!-- 自定义单元格内容 -->
<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" <!-- 状态控制是否可编辑 -->
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"> <!-- 自定义单元格内容 -->
<template #default="scope">
<el-input-number
v-model="scope.row.stocks" <!-- 绑定输入框值 -->
:min="0" <!-- 最小值 -->
controls-position="right" <!-- 控制按钮位置 -->
type="number" <!-- 类型 -->
:disabled="!scope.row.status" <!-- 状态控制是否可编辑 -->
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"> <!-- 自定义单元格内容 -->
<template #default="scope">
<el-input-number
v-model="scope.row.weight" <!-- 绑定输入框值 -->
:precision="2" <!-- 小数点后保留位数 -->
:min="0" <!-- 最小值 -->
controls-position="right" <!-- 控制按钮位置 -->
:disabled="!scope.row.status" <!-- 状态控制是否可编辑 -->
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"> <!-- 自定义单元格内容 -->
<template #default="scope">
<el-input-number
v-model="scope.row.volume" <!-- 绑定输入框值 -->
:precision="2" <!-- 小数点后保留位数 -->
:min="0" <!-- 最小值 -->
controls-position="right" <!-- 控制按钮位置 -->
:disabled="!scope.row.status" <!-- 状态控制是否可编辑 -->
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"> <!-- 自定义单元格内容 -->
<template #default="scope">
<el-button
v-if="scope.row.status" <!-- 如果状态为启用则显示禁用按钮 -->
v-if="scope.row.status"
type="text"
@click="changeSkuStatus(`${scope.$index}`)" <!-- 点击事件触发改变状态 -->
@click="changeSkuStatus(`${scope.$index}`)"
>
禁用
</el-button>
<el-button
v-else <!-- 如果状态为禁用则显示启用按钮 -->
v-else
type="text"
@click="changeSkuStatus(`${scope.$index}`)" <!-- 点击事件触发改变状态 -->
@click="changeSkuStatus(`${scope.$index}`)"
>
启用
</el-button>
@ -161,10 +142,8 @@
</template>
<script setup>
import { computed, ref, watch, onMounted } from 'vue'
import { scoreProdStore } from '@/stores/prod.js'
// props
const props = defineProps({
modelValue: {
default: () => [],
@ -175,17 +154,10 @@ const props = defineProps({
type: String
}
})
// emits
const emit = defineEmits(['update:modelValue'])
//
const dbSpecs = ref([])
//
const dbSpecs = ref([]) //
let initing = false
//
const tableLeftTitles = computed(() => {
const res = []
for (let i = 0; i < skuTags.value.length; i++) {
@ -194,22 +166,16 @@ const tableLeftTitles = computed(() => {
}
return res
})
//
const prod = scoreProdStore()
// SKU
const skuTags = computed({
get () { return prod.skuTags }
})
// SKU
watch(() => props.prodName,
() => {
skuAddProdName()
})
//
onMounted(() => {
http({
url: http.adornUrl('/prod/spec/list'),
@ -217,58 +183,48 @@ onMounted(() => {
params: http.adornParams()
})
.then(({ data }) => {
dbSpecs.value = data //
dbSpecs.value = data
})
})
//
const init = () => {
initing = true
}
defineExpose({ init })
/**
* 改变SKU状态
* @param tagIndex - 要改变状态的SKU索引
*/
const changeSkuStatus = (tagIndex) => {
props.modelValue[tagIndex].status = props.modelValue[tagIndex].status ? 0 : 1 //
// eslint-disable-next-line vue/no-mutating-props
props.modelValue[tagIndex].status = props.modelValue[tagIndex].status ? 0 : 1
}
/**
* 添加或更新商品名称到每个SKU中
*/
const skuAddProdName = () => {
if (initing) return //
if (initing) return
const skuList = []
for (let i = 0; i < props.modelValue.length; i++) {
const sku = Object.assign({}, props.modelValue[i]) // SKU
const sku = Object.assign({}, props.modelValue[i])
if (!sku.properties) {
return // properties
return
}
sku.skuName = '' // SKU
const properties = sku.properties.split(';') // properties
sku.skuName = ''
const properties = sku.properties.split(';')
for (const propertiesKey in properties) {
sku.skuName += properties[propertiesKey].split(':')[1] + ' ' // SKU
sku.skuName += properties[propertiesKey].split(':')[1] + ' '
}
sku.prodName = props.prodName + ' ' + sku.skuName //
skuList.push(sku) // SKU
sku.prodName = props.prodName + ' ' + sku.skuName
skuList.push(sku)
}
emit('update:modelValue', skuList) // modelValue
emit('update:modelValue', skuList)
}
</script>
<style lang="scss" scoped>
.mod-prod-sku-table {
/* 图片上传样式 */
.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;
@ -277,15 +233,12 @@ const skuAddProdName = () => {
line-height: 120px;
text-align: center;
}
.pic {
width: 120px;
height: 120px;
display: block;
}
}
/* 鼠标悬停时的样式 */
:deep(.pic-uploader-component .el-upload:hover) {
border-color: #409EFF;
}

@ -1,18 +1,14 @@
<template>
<div class="mod-prod-sku-tag">
<!-- 商品规格表单项 -->
<el-form-item label="商品规格">
<!-- 添加规格按钮 -->
<el-button @click="shopTagInput()">
添加规格
</el-button>
<!-- 遍历skuTags数组显示每个规格及其值 -->
<div v-for="(tag, tagIndex) in skuTags" :key="tagIndex">
<!-- 显示规格名称 -->
<div
v-for="(tag, tagIndex) in skuTags"
:key="tagIndex"
>
<span>{{ tag.tagName }}</span>
<!-- 删除规格按钮 -->
<el-button
class="button-new-tag"
type="text"
@ -22,8 +18,6 @@
删除
</el-button>
<br>
<!-- 遍历当前规格下的所有值并以标签形式展示 -->
<el-tag
v-for="(tagItem, tagItemIndex) in tag.tagItems"
:key="tagItem.valueId"
@ -33,8 +27,6 @@
>
{{ tagItem.propValue }}
</el-tag>
<!-- 如果显示输入框则渲染一个用于新增规格值的输入框 -->
<el-input
v-if="tagItemInputs[tagIndex] && tagItemInputs[tagIndex].visible"
:ref="`saveTagInput${tagIndex}`"
@ -43,8 +35,6 @@
@keyup.enter="handleInputConfirm(tagIndex)"
@blur="handleInputConfirm(tagIndex)"
/>
<!-- 否则显示+ 添加按钮以切换到输入框 -->
<el-button
v-else
class="button-new-tag"
@ -54,11 +44,11 @@
</el-button>
</div>
</el-form-item>
<!-- 当isShowTagInput为true时显示规格名选择项 -->
<el-form-item v-show="isShowTagInput" label="规格名">
<el-form-item
v-show="isShowTagInput"
label="规格名"
>
<el-col :span="8">
<!-- 规格名选择器允许创建新选项 -->
<el-select
v-model="addTagInput.propName"
filterable
@ -67,7 +57,6 @@
placeholder="请选择"
@change="handleTagClick"
>
<!-- 遍历未使用的规格作为选择器的选项 -->
<el-option
v-for="item in unUseTags"
:key="item.propId"
@ -77,11 +66,11 @@
</el-select>
</el-col>
</el-form-item>
<!-- 当isShowTagInput为true时显示规格值选择项 -->
<el-form-item v-show="isShowTagInput" label="规格值">
<el-form-item
v-show="isShowTagInput"
label="规格值"
>
<el-col :span="8">
<!-- 规格值选择器支持多选并允许创建新选项 -->
<el-select
v-model="addTagInput.selectValues"
multiple
@ -90,7 +79,6 @@
default-first-option
placeholder="请选择"
>
<!-- 遍历数据库中的规格值作为选择器的选项 -->
<el-option
v-for="item in dbTagValues"
:key="item.valueId"
@ -100,10 +88,7 @@
</el-select>
</el-col>
</el-form-item>
<!-- 操作按钮组当isShowTagInput为true时显示 -->
<el-form-item>
<!-- 确认按钮点击后调用addTag方法 -->
<el-button
v-show="isShowTagInput"
type="primary"
@ -111,8 +96,6 @@
>
确定
</el-button>
<!-- 取消按钮点击后隐藏规格名/值输入框 -->
<el-button
v-show="isShowTagInput"
@click="hideTagInput()"
@ -126,54 +109,31 @@
<script setup>
import { ElMessage } from 'element-plus'
import { scoreProdStore } from '@/stores/prod.js'
import { ref, computed, watch, onMounted, nextTick } from 'vue'
//
const prod = scoreProdStore()
// props
const props = defineProps({
skuList: {
type: Array,
default: () => []
}
})
// emits
const emit = defineEmits(['change'])
// /
const isShowTagInput = ref(false)
//
const addTagInput = ref({
propName: '', //
selectValues: [] //
propName: '',
selectValues: []
})
//
const type = ref(0)
//
const tagItemName = ref('')
//
const tagItemInputs = ref([])
const dbTagValues = ref([]) //
const dbTags = ref([]) //
//
const dbTagValues = ref([])
//
const dbTags = ref([])
//
let tagName = ''
let tagNameIndex = 0
let maxValueId = 0 // id
let maxPropId = 0 // id
let initing = false
// SKU
const skuTags = computed({
get () { return prod.skuTags },
set (val) { prod.updateSkuTags(val) }
@ -194,10 +154,10 @@ const unUseTags = computed(() => {
return res
})
// SKU
const defalutSku = computed(() => prod.defalutSku)
const defalutSku = computed(() => {
return prod.defalutSku
})
// skuTagsSKU
watch(() => skuTags.value,
(val) => {
if (initing) {
@ -205,8 +165,6 @@ watch(() => skuTags.value,
return
}
let skuListArr = []
// typeSKU
if (type.value === 4) {
//
props.skuList?.forEach(sku => {
@ -217,19 +175,19 @@ watch(() => skuTags.value,
})
} else if (type.value === 2) {
//
const properties = `${tagName}:${tagItemName.value}`
const properties = tagName + ':' + tagItemName.value
//
let tempSkuList = []
val?.forEach(tag => {
if (skuListArr.length === 0) {
if (tagName === tag.tagName) {
const sku = Object.assign({}, defalutSku.value)
sku.properties = properties
sku.properties = properties //
skuListArr.push(sku)
} else {
tag.tagItems.forEach(tagItem => {
const sku = Object.assign({}, defalutSku.value)
sku.properties = `${tag.tagName}:${tagItem.propValue}`
sku.properties = `${tag.tagName}:${tagItem.propValue}` //
skuListArr.push(sku)
})
}
@ -267,7 +225,7 @@ watch(() => skuTags.value,
if (skuListArr.length === 0) {
tag.tagItems.forEach(tagItem => {
const sku = Object.assign({}, defalutSku.value)
sku.properties = `${tag.tagName}:${tagItem.propValue}`
sku.properties = `${tag.tagName}:${tagItem.propValue}` //
skuListArr.push(sku)
})
} else {
@ -283,26 +241,24 @@ watch(() => skuTags.value,
}
})
}
// SKUSKU
if (!skuListArr.length) {
skuListArr.push(Object.assign({}, defalutSku.value))
}
// SKU
// debugger
emit('change', skuListArr)
}, {
},
{
deep: true
})
}
)
//
onMounted(() => {
//
http({
url: http.adornUrl('/prod/spec/list'),
method: 'get',
params: http.adornParams()
}).then(({ data }) => {
})
.then(({ data }) => {
dbTags.value = data
if (data) {
maxPropId = Math.max.apply(Math, data.map(item => item.propId))
@ -310,13 +266,12 @@ onMounted(() => {
maxPropId = 0
}
})
// ID
http({
url: http.adornUrl('/prod/spec/listSpecMaxValueId'),
method: 'get',
params: http.adornParams()
}).then(({ data }) => {
})
.then(({ data }) => {
if (data) {
maxValueId = data
} else {
@ -325,10 +280,6 @@ onMounted(() => {
})
})
/**
* 初始化SKU标签和列表
* @param {Array} skuList - SKU列表
*/
const init = (skuList) => {
if (!skuList || !skuList.length) {
skuTags.value = []
@ -368,16 +319,12 @@ const shopTagInput = () => {
}
/**
* 隐藏规格名规格值输入框并清除内容
* 隐藏规格名规格值输入框
*/
const hideTagInput = () => {
isShowTagInput.value = false
cleanTagInput()
}
/**
* 添加新的规格和规格值
*/
const addTag = () => {
const selectValues = addTagInput.value.selectValues
if (!addTagInput.value.propName) {
@ -450,9 +397,7 @@ const handleTagClick = () => {
}
/**
* 删除规格值
* @param {number} tagIndex - 标签索引
* @param {number} tagItemIndex - 标签项索引
* 规格名输入框选中规格事件
*/
const handleTagClose = (tagIndex, tagItemIndex) => {
tagName = skuTags.value[tagIndex].tagName
@ -467,7 +412,6 @@ const handleTagClose = (tagIndex, tagItemIndex) => {
/**
* 标签输入框确定时调用
* @param {number} tagIndex - 标签索引
*/
const handleInputConfirm = (tagIndex) => {
if (checkTagItem(tagIndex)) {
@ -490,7 +434,6 @@ const handleInputConfirm = (tagIndex) => {
/**
* 显示标签输入框
* @param {number} tagIndex - 标签索引
*/
const showTagInput = (tagIndex) => {
tagItemInputs.value.push({ visible: false, value: '' })
@ -502,8 +445,6 @@ const showTagInput = (tagIndex) => {
/**
* 获取数据集合所有对象中某个属性的最大值
* @param {Array} list - 对象数组
* @returns {number} 最大值
*/
const getMaxValueId = (list) => {
return Math.max.apply(Math, list.map(item => item.valueId))
@ -511,7 +452,7 @@ const getMaxValueId = (list) => {
/**
* 删除 规格
* @param {number} tagIndex - 标签索引
* @param tagIndex
*/
const removeTag = (tagIndex) => {
type.value = 3
@ -520,8 +461,6 @@ const removeTag = (tagIndex) => {
/**
* 新增规格值时判断是否存在同名的规格值
* @param {number} tagIndex - 标签索引
* @returns {boolean} 是否存在重复
*/
const checkTagItem = (tagIndex) => {
const tagItem = tagItemInputs.value[tagIndex].value
@ -537,7 +476,7 @@ const checkTagItem = (tagIndex) => {
})
if (arr.indexOf(tagItem) > -1) {
isSame = true
ElMessage.error('product.specificationValue') //
ElMessage.error('product.specificationValue')
return false
}
})
@ -547,67 +486,49 @@ const checkTagItem = (tagIndex) => {
<style lang="scss" scoped>
.mod-prod-sku-tag {
// el-tag
:deep(.el-tag + .el-tag) {
margin-left: 10px; //
margin-left: 10px;
}
//
.button-new-tag {
margin-left: 10px; //
height: 32px; //
line-height: 30px; //
padding-top: 0; //
padding-bottom: 0; //
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; //
width: 90px;
margin-left: 10px;
vertical-align: bottom;
}
}
//
//
:deep(.sku-border) {
border: 1px solid #EBEEF5; //
width: 70%; //
border: 1px solid #EBEEF5;
width:70%
}
// SKU
:deep(.sku-background) {
background-color: #F6f6f6; //
margin: 12px 12px; //
//
.el-button {
margin-left: 10px; //
span {
color: #000 !important; //
: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; //
.el-form-item__label{
padding:0 24px 0 0
}
}
// SKU
:deep(.sku-tag) {
margin: 12px 12px; //
:deep(.sku-tag){
margin: 12px 12px;
}
//
:deep(.tagTree) {
margin-left: 18px; //
padding-bottom: 8px; //
:deep(.tagTree){
margin-left: 18px;
padding-bottom:8px;
}
// div
.el-form-item__content div {
width: 100%; // div100%
width: 100%;
}
</style>

@ -1,22 +1,28 @@
<template>
<div class="mod-prod-info">
<!-- 使用el-form构建一个表单并设置引用 -->
<el-form ref="dataFormRef" :model="dataForm" label-width="100px">
<!-- 产品图片上传 -->
<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 :label="1">
上架
</el-radio>
<el-radio :label="0">
下架
</el-radio>
</el-radio-group>
</el-form-item>
<!-- 产品分类选择 -->
<el-form-item label="产品分类" prop="categoryId" :rules="[...rules]">
<el-form-item
label="产品分类"
:rules="[{ required: true, message: '请选择产品分类'}]"
prop="categoryId"
>
<el-col :span="8">
<el-cascader
v-model="category.selected"
@ -28,53 +34,97 @@
/>
</el-col>
</el-form-item>
<!-- 产品分组多选 -->
<el-form-item label="产品分组" prop="tagList" :rules="[...rules]">
<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
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="[...rules]">
<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-input
v-model="dataForm.prodName"
placeholder="产品名称"
maxlength="50"
/>
</el-col>
</el-form-item>
<!-- 产品卖点文本域 -->
<el-form-item label="产品卖点" prop="brief" :rules="[...rules]">
<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-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-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标签组件用于管理商品规格 -->
<sku-tag ref="skuTagRef" :sku-list="dataForm.skuList" @change="skuTagChangeSkuHandler" />
<!-- SKU表格组件展示SKU详细信息 -->
<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" />
<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-button
type="primary"
@click="onSubmit()"
>
确定
</el-button>
</el-form-item>
</el-form>
</div>
@ -88,20 +138,18 @@ 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: [], //
list: [],
selected: [],
props: {
value: 'categoryId',
label: 'categoryName'
}
})
//
//
const dataForm = ref({
prodName: '',
brief: '',
@ -119,28 +167,22 @@ const dataForm = ref({
},
deliveryTemplateId: null
})
//
const tags = ref([])
// ID
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) {
// ID
//
http({
url: http.adornUrl(`/prod/prod/info/${dataForm.value.prodId}`),
method: 'get',
@ -156,7 +198,6 @@ const getDataList = () => {
})
} else {
nextTick(() => {
//
dataFormRef.value?.resetFields()
skuTagRef.value?.init()
dataForm.value.pic = ''
@ -165,48 +206,49 @@ const getDataList = () => {
}
})
}
/**
* 获取分类信息并转换为树形结构
* 获取分类信息
*/
const getCategoryList = () => {
return http({
url: http.adornUrl('/prod/category/listCategory'),
method: 'get',
params: http.adornParams()
}).then(({ data }) => {
})
.then(({ data }) => {
category.list = treeDataTranslate(data, 'categoryId', 'parentId')
})
}
/**
* 处理分类改变事件更新分类ID
* 选择分类改变事件
* @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 || !dataForm.value.deliveryMode ||
(dataForm.value.deliveryMode.hasShopDelivery && !dataForm.value.deliveryTemplateId)) {
errorMsg('请检查表单内容')
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)
@ -215,20 +257,20 @@ const onSubmit = Debounce(() => {
param.deliveryModeVo = dataForm.value.deliveryMode
//
param.pic = dataForm.value.imgs.split(',')[0]
// HTTP
http({
url: http.adornUrl('/prod/prod'),
method: param.prodId ? 'put' : 'post',
data: http.adornData(param)
}).then(() => {
})
.then(() => {
ElMessage({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
//
router.push({ path: '/prod/prodList' })
router.push({
path: '/prod/prodList'
})
emit('refreshDataList')
}
})
@ -236,52 +278,48 @@ const onSubmit = Debounce(() => {
})
})
/**
* 设置商品价格和库存总量
*/
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 = Number.parseFloat(element.price || 0)
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 = Number.parseFloat(element.oriPrice || 0)
param.totalStocks += Number.parseInt(element.stocks || 0)
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
}
}
/**
* 处理SKU变化事件更新SKU名称
*/
const skuTagChangeSkuHandler = (skuList) => {
const prodName = dataForm.value.prodName
skuList.forEach(sku => {
if (sku.properties) {
sku.skuName = ''
const properties = sku.properties.split(';')
for (const property of properties) {
sku.skuName += property.split(':')[1] + ' '
for (const propertiesKey in properties) {
sku.skuName += properties[propertiesKey].split(':')[1] + ' '
}
sku.prodName = prodName + ' ' + sku.skuName.trim()
sku.prodName = prodName + ' ' + sku.skuName
}
})
dataForm.value.skuList = skuList
}
/**
* 显示错误消息
*/
const errorMsg = (message) => {
ElMessage({
message,
@ -298,7 +336,8 @@ const getTagList = () => {
url: http.adornUrl('/prod/prodTag/listTagList'),
method: 'get',
params: http.adornParams()
}).then(({ data }) => {
})
.then(({ data }) => {
tags.value = data
})
}

@ -1,6 +1,5 @@
<template>
<div class="mod-prod">
<!-- 使用avue-crud构建一个CRUD表格并设置引用 -->
<avue-crud
ref="crudRef"
:page="page"
@ -12,7 +11,6 @@
@selection-change="selectionChange"
@on-load="getDataList"
>
<!-- 自定义左侧菜单按钮 -->
<template #menu-left>
<el-button
v-if="isAuth('shop:pickAddr:save')"
@ -33,7 +31,6 @@
</el-button>
</template>
<!-- 自定义状态列内容 -->
<template #status="scope">
<el-tag v-if="scope.row.status === 1">
上架
@ -43,7 +40,6 @@
</el-tag>
</template>
<!-- 自定义行操作菜单 -->
<template #menu="scope">
<el-button
v-if="isAuth('prod:prod:update')"
@ -70,29 +66,19 @@
import { isAuth } from '@/utils'
import { ElMessage, ElMessageBox } from 'element-plus'
import { tableOption } from '@/crud/prod/prodList.js'
//
const router = useRouter()
//
const permission = reactive({
delBtn: isAuth('prod:prod:delete')
})
//
const dataList = ref([])
//
const page = reactive({
total: 0, //
currentPage: 1, //
pageSize: 10 //
total: 0, //
currentPage: 1, //
pageSize: 10 //
})
//
const dataListLoading = ref(false)
//
/**
* 获取数据列表
*/
const getDataList = (pageParam, params, done) => {
dataListLoading.value = true
http({
@ -109,20 +95,23 @@ const getDataList = (pageParam, params, done) => {
)
})
.then(({ data }) => {
//
dataList.value = data.records.map(element => ({
...element,
imgs: element.imgs.split(',')[0]
}))
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 - 商品ID如果存在则为编辑否则为新增
* 新增 / 修改
* @param id
*/
const onAddOrUpdate = (id) => {
router.push({
@ -130,21 +119,19 @@ const onAddOrUpdate = (id) => {
query: { prodId: id }
})
}
/**
* 删除或批量删除商品信息
* @param id - 单个商品ID若不存在则执行批量删除
* 删除和批量删除
* @param id
*/
const onDelete = (id) => {
const prodIds = getSeleProdIds()
if (id) {
prodIds.push(id)
}
ElMessageBox.confirm(`确定进行[${id ? '删除' : '批量删除'}]操作?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: '警告'
type: 'warning'
})
.then(() => {
http({
@ -163,33 +150,32 @@ const onDelete = (id) => {
})
})
})
.catch(() => {})
.catch(() => { })
}
/**
* 条件查询触发的数据获取
* @param params - 查询参数
* @param done - 完成回调
* 条件查询
* @param params
* @param done
*/
const onSearch = (params, done) => {
getDataList(page, params, done)
}
//
const dataListSelections = ref([])
/**
* 多选变化事件处理器
* @param val - 选中的行数据
* 多选变化
* @param val
*/
const selectionChange = (val) => {
dataListSelections.value = val
}
/**
* 获取选中的商品ID列表
* 获取选中的商品Id列表
*/
const getSeleProdIds = () => {
return dataListSelections.value?.map(item => item.prodId) || []
return dataListSelections.value?.map(item => {
return item.prodId
})
}
</script>

@ -4,7 +4,6 @@
:title="!dataForm.id ? '新增' : '修改'"
:close-on-click-modal="false"
>
<!-- 表单 -->
<el-form
ref="dataFormRef"
:model="dataForm"
@ -12,37 +11,50 @@
label-width="80px"
@keyup.enter="onSubmit()"
>
<!-- 标签名称 -->
<el-form-item
label="标签名称"
prop="title"
:rules="[
{ required: true, message: '标签名称不能为空', trigger: 'blur' },
{ pattern: /\s\S+|\S\s|\S/, 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-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 :label="1">
正常
</el-radio>
<el-radio :label="0">
禁用
</el-radio>
</el-radio-group>
</el-form-item>
<!-- 列表样式选择 -->
<el-form-item label="列表样式" prop="style">
<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 :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-form-item
label="排序"
prop="seq"
>
<el-input-number
v-model="dataForm.seq"
controls-position="right"
@ -51,12 +63,17 @@
/>
</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>
<el-button @click="visible = false">
取消
</el-button>
<el-button
type="primary"
@click="onSubmit()"
>
确定
</el-button>
</div>
</template>
</el-dialog>
@ -65,76 +82,55 @@
<script setup>
import { ElMessage } from 'element-plus'
import { Debounce } from '@/utils/debounce'
import { ref, nextTick, defineEmits, defineExpose } from 'vue'
//
const emit = defineEmits(['refreshDataList'])
// /
const visible = ref(false)
//
const dataRule = {}
//
const dataForm = ref({
id: null,
title: null,
shopId: null,
status: 1, //
status: 1,
isDefault: null,
prodCount: null,
seq: 0, // 0
style: 0 //
seq: 0,
style: 0
})
// ID
const init = (id) => {
dataForm.value.id = id || 0
visible.value = true
nextTick(() => {
//
dataFormRef.value?.resetFields()
if (dataForm.value.id) {
// ID
http({
url: http.adornUrl('/prod/prodTag/info/' + dataForm.value.id),
method: 'get',
params: http.adornParams()
})
.then(({ data }) => {
Object.assign(dataForm.value, data)
dataForm.value = data
})
}
})
}
// init
defineExpose({ init })
//
const dataFormRef = ref(null)
/**
* 提交表单
* 表单提交
*/
const onSubmit = Debounce(() => {
// 0
if (!dataForm.value.seq) {
dataForm.value.seq = 0
}
//
dataFormRef.value?.validate((valid) => {
if (valid) {
// HTTPID
http({
url: http.adornUrl('/prod/prodTag'),
method: dataForm.value.id ? 'put' : 'post',
data: http.adornData(dataForm.value)
})
.then(() => {
//
ElMessage({
message: '操作成功',
type: 'success',
@ -148,4 +144,5 @@ const onSubmit = Debounce(() => {
}
})
})
</script>

@ -1,6 +1,5 @@
<template>
<div class="mod-prod-prodTag">
<!-- 使用avue-crud构建CRUD表格 -->
<avue-crud
ref="crudRef"
:page="page"
@ -11,7 +10,6 @@
@on-load="getDataList"
@refresh-change="refreshChange"
>
<!-- 自定义左侧菜单按钮 -->
<template #menu-left>
<el-button
v-if="isAuth('prod:prodTag:save')"
@ -22,15 +20,14 @@
新增
</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
v-if="scope.row.status === 0"
type="danger"
>
禁用
</el-tag>
<el-tag v-else>
@ -38,7 +35,6 @@
</el-tag>
</template>
<!-- 自定义默认类型列内容 -->
<template #isDfault="scope">
<el-tag v-if="scope.row.isDefault === 0">
自定义类型
@ -48,7 +44,6 @@
</el-tag>
</template>
<!-- 自定义行操作菜单 -->
<template #menu="scope">
<el-button
v-if="isAuth('prod:prodTag:update')"
@ -68,8 +63,6 @@
</el-button>
</template>
</avue-crud>
<!-- 引入用于添加或更新记录的子组件 -->
<add-or-update
v-if="addOrUpdateVisible"
ref="addOrUpdateRef"
@ -83,24 +76,15 @@ import { isAuth } from '@/utils'
import { ElMessage, ElMessageBox } from 'element-plus'
import { tableOption } from '@/crud/prod/prodTag.js'
import AddOrUpdate from './add-or-update.vue'
import { ref, reactive, nextTick } from 'vue'
//
const dataList = ref([])
//
const page = reactive({
total: 0, //
currentPage: 1, //
pageSize: 10 //
total: 0, //
currentPage: 1, //
pageSize: 10 //
})
//
const dataListLoading = ref(false)
/**
* 获取数据列表方法
*/
const getDataList = (pageParam, params, done) => {
dataListLoading.value = true
http({
@ -119,13 +103,11 @@ const getDataList = (pageParam, params, done) => {
})
}
// /
const addOrUpdateVisible = ref(false)
const addOrUpdateRef = ref(null)
/**
* 新增或编辑产品标签
* @param id - 标签ID如果存在则为编辑模式
* 新增 / 修改
* @param id
*/
const onAddOrUpdate = (id) => {
addOrUpdateVisible.value = true
@ -134,10 +116,6 @@ const onAddOrUpdate = (id) => {
})
}
/**
* 删除产品标签
* @param id - 标签ID
*/
const onDelete = (id) => {
ElMessageBox.confirm('确定进行删除操作?', '提示', {
confirmButtonText: '确定',
@ -160,22 +138,19 @@ const onDelete = (id) => {
}
})
})
}).catch(() => {})
}).catch(() => { })
}
/**
* 刷新回调用于重新加载数据列表
* 刷新回调
*/
const refreshChange = () => {
getDataList(page)
}
/**
* 条件查询触发的数据获取
* @param params - 查询参数
* @param done - 完成回调
*/
const onSearch = (params, done) => {
getDataList(page, params, done)
}
</script>
<style lang="scss" scoped>
</style>

@ -4,13 +4,11 @@
: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"
@ -27,8 +25,6 @@
/>
</template>
</el-table-column>
<!-- 属性值列 -->
<el-table-column
prop="prodPropValues"
header-align="center"
@ -51,7 +47,6 @@
@clear="clearProdPropValues"
/>
</el-col>
<!-- 添加新输入框按钮 -->
<el-col :span="4">
<el-button
v-show="scope.row.prodPropValues[scope.row.prodPropValues.length-1].propValue"
@ -64,46 +59,37 @@
</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>
<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'
import { ref, defineEmits, defineExpose } from 'vue'
//
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 //
}
/**
* 初始化方法接受可选的产品属性对象作为参数
* @param val - 产品属性对象如果存在则为编辑模式
*/
const init = (val) => {
if (val) {
dataList.value = [JSON.parse(JSON.stringify(val))] //
dataList.value = [JSON.parse(JSON.stringify(val))]
} else {
dataList.value = [
{ propId: 0, propName: '', prodPropValues: [{ valueId: 0 }] }
@ -117,10 +103,10 @@ 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) {
@ -130,13 +116,13 @@ const onSubmit = Debounce(() => {
}
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
}
@ -152,8 +138,6 @@ const onSubmit = Debounce(() => {
ElMessage.error('属性值长度不能大于20')
return
}
// HTTPID
http({
url: http.adornUrl('/prod/spec'),
method: dataList.value[0].propId ? 'put' : 'post',
@ -176,9 +160,6 @@ const onSubmit = Debounce(() => {
})
})
/**
* 清除属性值时的处理逻辑
*/
const clearProdPropValues = () => {
if (dataList.value[0].prodPropValues.length === 1) {
return
@ -191,13 +172,10 @@ const clearProdPropValues = () => {
}
}
/**
* 添加新的属性值输入框
*/
const addInput = () => {
const temp = dataList.value[0].prodPropValues
if (temp[temp.length - 1].propValue) {
temp.push({}) //
temp.push({})
}
}
</script>

@ -1,6 +1,5 @@
<template>
<div class="mod-prod">
<!-- 使用avue-crud构建CRUD表格 -->
<avue-crud
ref="crudRef"
:page="page"
@ -10,31 +9,31 @@
@search-change="onSearch"
@on-load="getDataList"
>
<!-- 自定义属性值列内容 -->
<template #prodPropValues="scope">
<el-tag v-for="item in scope.row.prodPropValues" :key="item.valueId">
<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)"
>
编辑
@ -44,14 +43,14 @@
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"
@ -65,31 +64,19 @@ import { isAuth } from '@/utils'
import { ElMessage, ElMessageBox } from 'element-plus'
import AddOrUpdate from './add-or-update.vue'
import { tableOption } from '@/crud/prod/spec.js'
import { ref, reactive, nextTick } from 'vue'
//
const permission = ref({
delBtn: isAuth('prod:prod:delete') //
delBtn: isAuth('prod:prod:delete')
})
//
const dataList = ref([])
//
const dataListLoading = ref(false)
//
const dataListSelections = ref([])
//
const page = reactive({
total: 0, //
currentPage: 1, //
pageSize: 10 //
total: 0, //
currentPage: 1, //
pageSize: 10 //
})
/**
* 获取数据列表方法
* 获取数据列表
*/
const getDataList = (pageParam, params, done) => {
dataListLoading.value = true
@ -114,13 +101,11 @@ const getDataList = (pageParam, params, done) => {
})
}
// /
const addOrUpdateVisible = ref(false)
const addOrUpdateRef = ref(null)
/**
* 新增或编辑产品规格
* @param val - 如果存在则为编辑模式否则为新增模式
* 新增 / 修改
* @param val
*/
const onAddOrUpdate = (val) => {
addOrUpdateVisible.value = true
@ -130,11 +115,13 @@ const onAddOrUpdate = (val) => {
}
/**
* 删除产品规格
* @param id - 规格ID如果不存在则表示批量删除
* 删除
* @param id
*/
const onDelete = (id) => {
const ids = id ? [id] : dataListSelections.value?.map(item => item.propId)
const ids = id ? [id] : dataListSelections.value?.map(item => {
return item.propId
})
ElMessageBox.confirm(`确定进行[${id ? '删除' : '批量删除'}]操作?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
@ -157,17 +144,13 @@ const onDelete = (id) => {
})
})
})
.catch(() => {})
.catch(() => { })
}
/**
* 条件查询触发的数据获取
* @param params - 查询参数
* @param done - 完成回调
*/
const onSearch = (params, done) => {
getDataList(page, params, done)
}
</script>
<style scoped lang="scss">

Loading…
Cancel
Save