Compare commits

..

2 Commits

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

@ -1,114 +1,62 @@
<template> <template>
<div class="mod-home"> <div class="mod-home">
<!-- 简介段落 -->
<p>一个基于spring bootspring oauth2.0mybatisredis的轻量级前后端分离拥有完整sku和下单流程的完全开源商城</p> <p>一个基于spring bootspring oauth2.0mybatisredis的轻量级前后端分离拥有完整sku和下单流程的完全开源商城</p>
<p>&nbsp;</p> <p>&nbsp;</p>
<!-- 版权声明 -->
<p>该项目仅供学习参考可供个人学习使用如需商用联系作者进行授权否则必将追究法律责任</p> <p>该项目仅供学习参考可供个人学习使用如需商用联系作者进行授权否则必将追究法律责任</p>
<p>&nbsp;</p> <p>&nbsp;</p>
<h2>前言</h2> <h2>前言</h2>
<p> <p>
<code>mall4j商城</code>项目致力于为中小企业打造一个完整易于维护的开源电商系统采用现阶段流行技术实现后台管理系统包含商品管理订单管理运费模板规格管理会员管理运营管理内容管理统计报表权限管理设置等模块 <code>mall4j商城</code>项目致力于为中小企业打造一个完整易于维护的开源电商系统采用现阶段流行技术实现后台管理系统包含商品管理订单管理运费模板规格管理会员管理运营管理内容管理统计报表权限管理设置等模块
</p> </p>
<p>&nbsp;</p> <p>&nbsp;</p>
<h2>技术选型</h2> <h2>技术选型</h2>
<figure> <figure>
<table <table border="1" cellspacing="0" cellpadding="5px">
border="1"
cellspacing="0"
cellpadding="5px"
>
<thead> <thead>
<tr> <tr>
<th>技术</th> <th>技术</th>
<th>版本</th> <th>版本</th>
<th>说明</th> <th>说明</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr> <!-- 列出所用技术及其版本和简要说明 -->
<td>Spring Boot</td> <tr v-for="(tech, index) in technologies" :key="index">
<td>3.0.4</td> <td>{{ tech.name }}</td>
<td>MVC核心框架</td> <td>{{ tech.version }}</td>
</tr> <td>{{ tech.description }}</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> </tbody>
</table> </table>
</figure> </figure>
<p>&nbsp;</p> <p>&nbsp;</p>
<h2>部署教程</h2> <h2>部署教程</h2>
<p>&nbsp;</p> <p>&nbsp;</p>
<h3>1.开发环境</h3> <h3>1.开发环境</h3>
<figure> <figure>
<table <table border="1" cellspacing="0" cellpadding="5px">
border="1"
cellspacing="0"
cellpadding="5px"
>
<thead> <thead>
<tr> <tr>
<th>工具</th> <th>工具</th>
<th>版本</th> <th>版本</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr> <!-- 列出所需的开发环境工具及其版本 -->
<td>jdk</td> <tr v-for="(tool, index) in developmentTools" :key="index">
<td>17</td> <td>{{ tool.name }}</td>
</tr> <td>{{ tool.version }}</td>
<tr> </tr>
<td>mysql</td>
<td>5.7+</td>
</tr>
<tr>
<td>redis</td>
<td>3.2+</td>
</tr>
</tbody> </tbody>
</table> </table>
</figure> </figure>
<h3>2.启动</h3> <h3>2.启动</h3>
<ul> <ul>
<li>推荐使用idea安装lombok插件使用idea导入maven项目</li> <li>推荐使用idea安装lombok插件使用idea导入maven项目</li>
@ -127,8 +75,31 @@
</div> </div>
</template> </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> <style lang="scss" scoped>
.mod-home { .mod-home {
line-height: 1.5; line-height: 1.5; //
} }
</style> </style>

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

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

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

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

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

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

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

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

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

@ -1,156 +1,130 @@
<template> <template>
<!-- 商品评论管理对话框 -->
<el-dialog <el-dialog
v-model="visible" v-model="visible"
:title="!dataForm.prodCommId ? '新增' : '修改'" :title="!dataForm.prodCommId ? '新增' : '修改'"
:close-on-click-modal="false" :close-on-click-modal="false"
> >
<el-form <el-form
ref="dataFormRef" ref="dataFormRef"
:model="dataForm" :model="dataForm"
:rules="dataRule" :rules="dataRule"
label-width="80px" label-width="80px"
@keyup.enter="onSubmit()" @keyup.enter="onSubmit()"
> >
<div v-if="!isEdit"> <!-- 如果不是编辑状态则显示评论详情 -->
<el-form-item <div v-if="!isEdit">
label="评论内容" <!-- 评论内容表单项只读 -->
prop="userName" <el-form-item label="评论内容" prop="content">
> <el-input
<el-input v-model="dataForm.content"
v-model="dataForm.content" type="textarea"
type="textarea" :readonly="true"
:readonly="true" />
/> </el-form-item>
</el-form-item>
<!-- 评论图片表单项 -->
<el-form-item <el-form-item label="评论图片" prop="pics">
label="评论图片" <div v-if="!dataForm.pics?.length"></div>
prop="userName" <div v-else>
> <img
<div v-if="!dataForm.pics?.length"> v-for="item in dataForm.pics"
:key="item"
</div> alt=""
<div v-else> max-width="100%"
<img :src="dialogImageUrl + item"
v-for="item in dataForm.pics"
:key="item"
alt=""
max-width="100%"
:src="dialogImageUrl + item"
>
</div>
</el-form-item>
<el-form-item
label="记录时间"
prop="userName"
>
<el-input
v-model="dataForm.recTime"
:readonly="true"
/>
</el-form-item>
<el-form-item
label="回复时间"
prop="userName"
:readonly="true"
>
<el-input
v-model="dataForm.replyTime"
:readonly="true"
/>
</el-form-item>
<el-form-item
label="IP来源"
prop="userName"
>
<el-input
v-model="dataForm.postip"
:readonly="true"
/>
</el-form-item>
<el-form-item
label="得分"
prop="score"
>
<el-input
v-model="dataForm.score"
:readonly="true"
/>
</el-form-item>
<el-form-item
label="是否匿名"
prop="isAnonymous"
> >
<el-radio-group
v-model="dataForm.isAnonymous"
:disabled="true"
>
<el-radio :label="1">
</el-radio>
<el-radio :label="0">
不是
</el-radio>
</el-radio-group>
</el-form-item>
</div> </div>
</el-form-item>
<el-form-item <!-- 记录时间表单项只读 -->
label="掌柜回复" <el-form-item label="记录时间" prop="recTime">
type="textarea" <el-input
prop="userName" v-model="dataForm.recTime"
:readonly="true"
/>
</el-form-item>
<!-- 回复时间表单项只读 -->
<el-form-item label="回复时间" prop="replyTime">
<el-input
v-model="dataForm.replyTime"
:readonly="true"
/>
</el-form-item>
<!-- IP来源表单项只读 -->
<el-form-item label="IP来源" prop="postip">
<el-input
v-model="dataForm.postip"
:readonly="true"
/>
</el-form-item>
<!-- 得分表单项只读 -->
<el-form-item label="得分" prop="score">
<el-input
v-model="dataForm.score"
:readonly="true"
/>
</el-form-item>
<!-- 是否匿名表单项只读 -->
<el-form-item label="是否匿名" prop="isAnonymous">
<el-radio-group
v-model="dataForm.isAnonymous"
:disabled="true"
> >
<el-input <el-radio :label="1"></el-radio>
v-model="dataForm.replyContent" <el-radio :label="0">不是</el-radio>
:readonly="!isEdit" </el-radio-group>
/> </el-form-item>
</el-form-item> </div>
<el-form-item <!-- 掌柜回复表单项非编辑状态下只读 -->
<el-form-item label="掌柜回复" prop="replyContent">
<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-radio-group>
</el-form-item>
</el-form>
<!-- 对话框底部的操作按钮 -->
<template #footer>
<div class="dialog-footer">
<el-button @click="visible = false">取消</el-button> <!-- -->
<el-button
v-if="isEdit" v-if="isEdit"
label="审核" type="primary"
prop="status" @click="onSubmit()"
> >确定</el-button> <!-- -->
<el-radio-group </div>
v-model="dataForm.status" </template>
:readonly="true"
>
<el-radio :label="1">
审核通过
</el-radio>
<el-radio :label="-1">
不通过
</el-radio>
<el-radio :label="0">
等待审核
</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="visible = false">
取消
</el-button>
<el-button
v-if="isEdit"
type="primary"
@click="onSubmit()"
>
确定
</el-button>
</div>
</template>
</el-dialog> </el-dialog>
</template> </template>
<script setup> <script setup>
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
const emit = defineEmits(['refreshDataList']) const emit = defineEmits(['refreshDataList'])
//
const dataRule = ref({}) const dataRule = ref({})
//
const isEdit = ref(false) const isEdit = ref(false)
// 使ref
const dataForm = ref({ const dataForm = ref({
prodCommId: null, prodCommId: null,
prodId: null, prodId: null,
@ -168,52 +142,54 @@ const dataForm = ref({
isAnonymous: null, isAnonymous: null,
status: null status: null
}) })
// prodCommIdisEditParam
const init = (prodCommId, isEditParam) => { const init = (prodCommId, isEditParam) => {
isEdit.value = isEditParam isEdit.value = isEditParam //
dataForm.value.prodCommId = prodCommId || 0 dataForm.value.prodCommId = prodCommId || 0
visible.value = true visible.value = true //
nextTick(() => { nextTick(() => {
dataFormRef.value?.resetFields() dataFormRef.value?.resetFields() //
if (dataForm.value.prodCommId) { if (dataForm.value.prodCommId) {
// ID
http({ http({
url: http.adornUrl('/prod/prodComm/info/' + dataForm.value.prodCommId), url: http.adornUrl('/prod/prodComm/info/' + dataForm.value.prodCommId),
method: 'get', method: 'get',
params: http.adornParams() params: http.adornParams()
}) })
.then(({ data }) => { .then(({ data }) => {
dataForm.value = data Object.assign(dataForm.value, data) // dataForm
}) })
} }
}) })
} }
defineExpose({ init })
defineExpose({ init }) // init
//
const visible = ref(false) const visible = ref(false)
//
const dataFormRef = ref(null) const dataFormRef = ref(null)
/** /**
* 表单提交 * 表单提交方法
*/ */
const onSubmit = () => { const onSubmit = () => {
dataFormRef.value?.validate((valid) => { dataFormRef.value?.validate((valid) => {
if (valid) { if (valid) {
//
http({ http({
url: http.adornUrl('/prod/prodComm'), url: http.adornUrl('/prod/prodComm'),
method: dataForm.value.prodCommId ? 'put' : 'post', method: dataForm.value.prodCommId ? 'put' : 'post',
data: http.adornData(dataForm.value) data: http.adornData(dataForm.value)
}) })
.then(() => { .then(() => {
ElMessage({ ElMessage.success('操作成功')
message: '操作成功', visible.value = false //
type: 'success', emit('refreshDataList') //
duration: 1500,
onClose: () => {
visible.value = false
emit('refreshDataList')
}
})
}) })
} }
}) })
} }
</script> </script>

@ -1,41 +1,49 @@
<template> <template>
<!-- 商品评论管理模块 -->
<div class="mod-prod-prodComm"> <div class="mod-prod-prodComm">
<!-- 使用avue-crud组件构建表格 -->
<avue-crud <avue-crud
ref="crudRef" ref="crudRef"
:page="page" :page="page" <!-- 分页配置 -->
:data="dataList" :data="dataList" <!-- 表格数据 -->
:table-loading="dataListLoading" :table-loading="dataListLoading" <!-- 表格加载状态 -->
:option="tableOption" :option="tableOption" <!-- 表格选项 -->
@search-change="onSearch" @search-change="onSearch" <!-- 搜索条件改变时触发 -->
@on-load="getDataList" @on-load="getDataList" <!-- 加载分页数据时触发 -->
@refresh-change="refreshChange" @refresh-change="refreshChange" <!-- 刷新表格时触发 -->
@row-del="rowDel" @row-del="rowDel" <!-- 删除行时触发 -->
> >
<template #nickName="scope"> <!-- 自定义模板显示用户昵称 -->
{{ scope.row.user.nickName }} <template #nickName="scope">
</template> {{ scope.row.user.nickName }}
<template #replyTime="scope"> </template>
{{ scope.row.replyTime ? scope.row.replyTime : '-' }}
</template>
<template #menu="scope"> <!-- 自定义模板显示回复时间如果为空则显示'-' -->
<el-button <template #replyTime="scope">
type="primary" {{ scope.row.replyTime ? scope.row.replyTime : '-' }}
icon="el-icon-edit" </template>
@click="onAddOrUpdate(scope.row.prodCommId,true)"
>
编辑
</el-button>
<el-button <!-- 自定义菜单栏提供编辑和查看按钮 -->
type="success" <template #menu="scope">
icon="el-icon-view" <el-button
@click="onAddOrUpdate(scope.row.prodCommId,false)" type="primary"
> icon="el-icon-edit"
查看 @click="onAddOrUpdate(scope.row.prodCommId, true)"
</el-button> >
</template> 编辑
</el-button>
<el-button
type="success"
icon="el-icon-view"
@click="onAddOrUpdate(scope.row.prodCommId, false)"
>
查看
</el-button>
</template>
</avue-crud> </avue-crud>
<!-- 弹窗, 新增 / 修改 -->
<add-or-update <add-or-update
v-if="addOrUpdateVisible" v-if="addOrUpdateVisible"
ref="addOrUpdateRef" ref="addOrUpdateRef"
@ -46,19 +54,31 @@
<script setup> <script setup>
import { ElMessage, ElMessageBox } from 'element-plus' 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' import AddOrUpdate from './add-or-update.vue'
//
const dataList = ref([]) const dataList = ref([])
//
const page = reactive({ const page = reactive({
total: 0, // total: 0, //
currentPage: 1, // currentPage: 1, //
pageSize: 20 // pageSize: 20 //
}) })
//
const dataListLoading = ref(false) const dataListLoading = ref(false)
/**
* 获取数据列表方法
* @param pageParam - 分页参数
* @param params - 搜索参数
* @param done - 完成回调函数
*/
const getDataList = (pageParam, params, done) => { const getDataList = (pageParam, params, done) => {
dataListLoading.value = true dataListLoading.value = true // true
http({ http({
url: http.adornUrl('/prod/prodComm/page'), url: http.adornUrl('/prod/prodComm/page'),
method: 'get', method: 'get',
@ -68,24 +88,33 @@ const getDataList = (pageParam, params, done) => {
}, params)) }, params))
}) })
.then(({ data }) => { .then(({ data }) => {
dataList.value = data.records dataList.value = data.records // dataList
page.total = data.total page.total = data.total //
dataListLoading.value = false dataListLoading.value = false // false
if (done) done() if (done) done() //
}) })
} }
//
const addOrUpdateVisible = ref(false) const addOrUpdateVisible = ref(false)
const addOrUpdateRef = ref(null) const addOrUpdateRef = ref(null)
/** /**
* 新增 / 修改 * 新增或修改操作
* @param id - 商品评论ID新增时为null
* @param isEdit - 是否是编辑模式
*/ */
const onAddOrUpdate = (id, isEdit) => { const onAddOrUpdate = (id, isEdit) => {
addOrUpdateVisible.value = true addOrUpdateVisible.value = true //
nextTick(() => { nextTick(() => {
addOrUpdateRef.value?.init(id, isEdit) addOrUpdateRef.value?.init(id, isEdit) //
}) })
} }
/**
* 删除行
* @param row - 要删除的行数据
*/
const rowDel = (row) => { const rowDel = (row) => {
ElMessageBox.confirm('确定进行删除操作?', '提示', { ElMessageBox.confirm('确定进行删除操作?', '提示', {
confirmButtonText: '确定', confirmButtonText: '确定',
@ -94,7 +123,7 @@ const rowDel = (row) => {
}) })
.then(() => { .then(() => {
http({ http({
url: http.adornUrl('/prod/prodComm/' + row.prodCommId), url: http.adornUrl(`/prod/prodComm/${row.prodCommId}`),
method: 'delete', method: 'delete',
data: http.adornData({}) data: http.adornData({})
}) })
@ -104,19 +133,26 @@ const rowDel = (row) => {
type: 'success', type: 'success',
duration: 1500, duration: 1500,
onClose: () => { onClose: () => {
getDataList() getDataList() //
} }
}) })
}) })
}).catch(() => { }) }).catch(() => { /* 用户取消操作 */ })
} }
/** /**
* 刷新回调 * 刷新回调重新加载数据列表
*/ */
const refreshChange = () => { const refreshChange = () => {
getDataList(page) getDataList(page) //
} }
/**
* 搜索条件改变时重新加载数据列表
* @param params - 搜索参数
* @param done - 完成回调函数
*/
const onSearch = (params, done) => { const onSearch = (params, done) => {
getDataList(page, params, done) getDataList(page, params, done) //
} }
</script> </script>

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

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

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

@ -1,28 +1,22 @@
<template> <template>
<div class="mod-prod-info"> <div class="mod-prod-info">
<el-form <!-- 使用el-form构建一个表单并设置引用 -->
ref="dataFormRef" <el-form ref="dataFormRef" :model="dataForm" label-width="100px">
:model="dataForm" <!-- 产品图片上传 -->
label-width="100px"
>
<el-form-item label="产品图片"> <el-form-item label="产品图片">
<mul-pic-upload v-model="dataForm.imgs" /> <mul-pic-upload v-model="dataForm.imgs" />
</el-form-item> </el-form-item>
<!-- 产品上架/下架状态选择 -->
<el-form-item label="状态"> <el-form-item label="状态">
<el-radio-group v-model="dataForm.status"> <el-radio-group v-model="dataForm.status">
<el-radio :label="1"> <el-radio :label="1">上架</el-radio>
上架 <el-radio :label="0">下架</el-radio>
</el-radio>
<el-radio :label="0">
下架
</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item
label="产品分类" <!-- 产品分类选择 -->
:rules="[{ required: true, message: '请选择产品分类'}]" <el-form-item label="产品分类" prop="categoryId" :rules="[...rules]">
prop="categoryId"
>
<el-col :span="8"> <el-col :span="8">
<el-cascader <el-cascader
v-model="category.selected" v-model="category.selected"
@ -34,97 +28,53 @@
/> />
</el-col> </el-col>
</el-form-item> </el-form-item>
<el-form-item
label="产品分组" <!-- 产品分组多选 -->
:rules="[{ required: true, message: '请选择产品分组'}]" <el-form-item label="产品分组" prop="tagList" :rules="[...rules]">
>
<el-col :span="8"> <el-col :span="8">
<el-select <el-select v-model="dataForm.tagList" multiple style="width: 250px" placeholder="请选择">
v-model="dataForm.tagList" <el-option v-for="item in tags" :key="item.id" :label="item.title" :value="item.id" />
multiple
style="width: 250px"
placeholder="请选择"
>
<el-option
v-for="item in tags"
:key="item.id"
:label="item.title"
:value="item.id"
/>
</el-select> </el-select>
</el-col> </el-col>
</el-form-item> </el-form-item>
<el-form-item
label="产品名称" <!-- 产品名称输入框 -->
prop="prodName" <el-form-item label="产品名称" prop="prodName" :rules="[...rules]">
:rules="[
{ required: true, message: '产品名称不能为空'},
{ pattern: /\s\S+|S+\s|\S/, message: '请输入正确的产品名称', trigger: 'blur' }
]"
>
<el-col :span="8"> <el-col :span="8">
<el-input <el-input v-model="dataForm.prodName" placeholder="产品名称" maxlength="50" />
v-model="dataForm.prodName"
placeholder="产品名称"
maxlength="50"
/>
</el-col> </el-col>
</el-form-item> </el-form-item>
<el-form-item
label="产品卖点" <!-- 产品卖点文本域 -->
prop="brief" <el-form-item label="产品卖点" prop="brief" :rules="[...rules]">
:rules="[
{ required: false, pattern: /\s\S+|S+\s|\S/, message: '请输入正确的产品卖点', trigger: 'blur' }
]"
>
<el-col :span="8"> <el-col :span="8">
<el-input <el-input v-model="dataForm.brief" type="textarea" :autosize="{minRows: 2, maxRows: 4}" placeholder="产品卖点" />
v-model="dataForm.brief"
type="textarea"
:autosize="{minRows: 2, maxRows: 4}"
placeholder="产品卖点"
/>
</el-col> </el-col>
</el-form-item> </el-form-item>
<!-- 配送方式选择 -->
<el-form-item label="配送方式"> <el-form-item label="配送方式">
<el-checkbox v-model="dataForm.deliveryMode.hasShopDelivery"> <el-checkbox v-model="dataForm.deliveryMode.hasShopDelivery"></el-checkbox>
商家配送 <el-checkbox v-model="dataForm.deliveryMode.hasUserPickUp"></el-checkbox>
</el-checkbox>
<el-checkbox v-model="dataForm.deliveryMode.hasUserPickUp">
用户自提
</el-checkbox>
</el-form-item> </el-form-item>
<prod-transport
v-show="dataForm.deliveryMode.hasShopDelivery" <!-- 商家配送模板选择仅当选择了商家配送时显示 -->
v-model="dataForm.deliveryTemplateId" <prod-transport v-show="dataForm.deliveryMode.hasShopDelivery" v-model="dataForm.deliveryTemplateId" />
/>
<sku-tag <!-- SKU标签组件用于管理商品规格 -->
ref="skuTagRef" <sku-tag ref="skuTagRef" :sku-list="dataForm.skuList" @change="skuTagChangeSkuHandler" />
:sku-list="dataForm.skuList"
@change="skuTagChangeSkuHandler" <!-- SKU表格组件展示SKU详细信息 -->
/> <sku-table ref="skuTableRef" v-model="dataForm.skuList" :prod-name="dataForm.prodName" />
<sku-table
ref="skuTableRef" <!-- 产品详情富文本编辑器 -->
v-model="dataForm.skuList" <el-form-item label="产品详情" prop="content">
:prod-name="dataForm.prodName" <tiny-mce ref="contentRef" v-model="dataForm.content" style="width:1000px" />
/>
<el-form-item
label="产品详情"
prop="content"
>
<tiny-mce
ref="contentRef"
v-model="dataForm.content"
style="width:1000px"
/>
</el-form-item> </el-form-item>
<!-- 提交按钮 -->
<el-form-item> <el-form-item>
<el-button <el-button type="primary" @click="onSubmit()"></el-button>
type="primary"
@click="onSubmit()"
>
确定
</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
</div> </div>
@ -138,18 +88,20 @@ import SkuTag from './components/sku-tag.vue'
import SkuTable from './components/sku-table.vue' import SkuTable from './components/sku-table.vue'
import { Debounce } from '@/utils/debounce' import { Debounce } from '@/utils/debounce'
//
const emit = defineEmits(['refreshDataList']) const emit = defineEmits(['refreshDataList'])
// //
const category = reactive({ const category = reactive({
list: [], list: [], //
selected: [], selected: [], //
props: { props: {
value: 'categoryId', value: 'categoryId',
label: 'categoryName' label: 'categoryName'
} }
}) })
//
//
const dataForm = ref({ const dataForm = ref({
prodName: '', prodName: '',
brief: '', brief: '',
@ -167,22 +119,28 @@ const dataForm = ref({
}, },
deliveryTemplateId: null deliveryTemplateId: null
}) })
//
const tags = ref([]) const tags = ref([])
// ID
onMounted(() => { onMounted(() => {
dataForm.value.prodId = useRoute().query.prodId dataForm.value.prodId = useRoute().query.prodId
getDataList() getDataList()
}) })
//
const skuTableRef = ref(null) const skuTableRef = ref(null)
const skuTagRef = ref(null) const skuTagRef = ref(null)
/** /**
* 获取分类数据 * 获取所有必要的数据列表
*/ */
const getDataList = () => { const getDataList = () => {
getTagList() getTagList()
getCategoryList().then(() => { getCategoryList().then(() => {
if (dataForm.value.prodId) { if (dataForm.value.prodId) {
// // ID
http({ http({
url: http.adornUrl(`/prod/prod/info/${dataForm.value.prodId}`), url: http.adornUrl(`/prod/prod/info/${dataForm.value.prodId}`),
method: 'get', method: 'get',
@ -198,6 +156,7 @@ const getDataList = () => {
}) })
} else { } else {
nextTick(() => { nextTick(() => {
//
dataFormRef.value?.resetFields() dataFormRef.value?.resetFields()
skuTagRef.value?.init() skuTagRef.value?.init()
dataForm.value.pic = '' dataForm.value.pic = ''
@ -206,49 +165,48 @@ const getDataList = () => {
} }
}) })
} }
/** /**
* 获取分类信息 * 获取分类信息并转换为树形结构
*/ */
const getCategoryList = () => { const getCategoryList = () => {
return http({ return http({
url: http.adornUrl('/prod/category/listCategory'), url: http.adornUrl('/prod/category/listCategory'),
method: 'get', method: 'get',
params: http.adornParams() params: http.adornParams()
}).then(({ data }) => {
category.list = treeDataTranslate(data, 'categoryId', 'parentId')
}) })
.then(({ data }) => {
category.list = treeDataTranslate(data, 'categoryId', 'parentId')
})
} }
/** /**
* 选择分类改变事件 * 处理分类改变事件更新分类ID
* @param val
*/ */
const handleCategoryChange = (val) => { const handleCategoryChange = (val) => {
dataForm.value.categoryId = val[val.length - 1] dataForm.value.categoryId = val[val.length - 1]
} }
//
const router = useRouter() const router = useRouter()
//
const dataFormRef = ref(null) const dataFormRef = ref(null)
/** /**
* 表单提交 * 提交表单添加或更新产品信息
*/ */
const onSubmit = Debounce(() => { const onSubmit = Debounce(() => {
dataFormRef.value?.validate((valid) => { dataFormRef.value?.validate((valid) => {
if (!valid) { if (!valid) {
return return
} }
if (!dataForm.value.imgs) { //
errorMsg('请选择图片上传') if (!dataForm.value.imgs || !dataForm.value.deliveryMode ||
return (dataForm.value.deliveryMode.hasShopDelivery && !dataForm.value.deliveryTemplateId)) {
} errorMsg('请检查表单内容')
if (!dataForm.value.deliveryMode) {
errorMsg('请选择配送方式')
return
}
if (dataForm.value.deliveryMode.hasShopDelivery && !dataForm.value.deliveryTemplateId) {
errorMsg('请选择运费模板')
return return
} }
const param = Object.assign({}, dataForm.value) const param = Object.assign({}, dataForm.value)
// //
paramSetPriceAndStocks(param) paramSetPriceAndStocks(param)
@ -257,69 +215,73 @@ const onSubmit = Debounce(() => {
param.deliveryModeVo = dataForm.value.deliveryMode param.deliveryModeVo = dataForm.value.deliveryMode
// //
param.pic = dataForm.value.imgs.split(',')[0] param.pic = dataForm.value.imgs.split(',')[0]
// HTTP
http({ http({
url: http.adornUrl('/prod/prod'), url: http.adornUrl('/prod/prod'),
method: param.prodId ? 'put' : 'post', method: param.prodId ? 'put' : 'post',
data: http.adornData(param) data: http.adornData(param)
}) }).then(() => {
.then(() => { ElMessage({
ElMessage({ message: '操作成功',
message: '操作成功', type: 'success',
type: 'success', duration: 1500,
duration: 1500, onClose: () => {
onClose: () => { //
router.push({ router.push({ path: '/prod/prodList' })
path: '/prod/prodList' emit('refreshDataList')
}) }
emit('refreshDataList')
}
})
}) })
})
}) })
}) })
/**
* 设置商品价格和库存总量
*/
const paramSetPriceAndStocks = (param) => { const paramSetPriceAndStocks = (param) => {
//
param.totalStocks = 0 param.totalStocks = 0
//
param.price = 0 param.price = 0
//
param.oriPrice = 0 param.oriPrice = 0
//
for (let i = 0; i < param.skuList.length; i++) { for (let i = 0; i < param.skuList.length; i++) {
const element = param.skuList[i] const element = param.skuList[i]
if (element.status !== 1) { if (element.status !== 1) continue
continue
} if (param.price === 0) param.price = Number.parseFloat(element.price || 0)
if (param.price === 0) {
param.price = element.price ? Number.parseFloat(element.price) : 0
}
//
param.price = Math.min(param.price, element.price) param.price = Math.min(param.price, element.price)
if (param.price === element.price) { if (param.price === element.price) param.oriPrice = Number.parseFloat(element.oriPrice || 0)
param.oriPrice = element.oriPrice ? Number.parseFloat(element.oriPrice) : 0
} param.totalStocks += Number.parseInt(element.stocks || 0)
param.totalStocks += element.stocks ? Number.parseInt(element.stocks) : 0
} }
// sku使
if (param.skuList.length === 1) { if (param.skuList.length === 1) {
param.skuList[0].prodName = dataForm.value.prodName param.skuList[0].prodName = dataForm.value.prodName
} }
} }
/**
* 处理SKU变化事件更新SKU名称
*/
const skuTagChangeSkuHandler = (skuList) => { const skuTagChangeSkuHandler = (skuList) => {
const prodName = dataForm.value.prodName const prodName = dataForm.value.prodName
skuList.forEach(sku => { skuList.forEach(sku => {
if (sku.properties) { if (sku.properties) {
sku.skuName = '' sku.skuName = ''
const properties = sku.properties.split(';') const properties = sku.properties.split(';')
for (const propertiesKey in properties) { for (const property of properties) {
sku.skuName += properties[propertiesKey].split(':')[1] + ' ' sku.skuName += property.split(':')[1] + ' '
} }
sku.prodName = prodName + ' ' + sku.skuName sku.prodName = prodName + ' ' + sku.skuName.trim()
} }
}) })
dataForm.value.skuList = skuList dataForm.value.skuList = skuList
} }
/**
* 显示错误消息
*/
const errorMsg = (message) => { const errorMsg = (message) => {
ElMessage({ ElMessage({
message, message,
@ -336,9 +298,8 @@ const getTagList = () => {
url: http.adornUrl('/prod/prodTag/listTagList'), url: http.adornUrl('/prod/prodTag/listTagList'),
method: 'get', method: 'get',
params: http.adornParams() params: http.adornParams()
}).then(({ data }) => {
tags.value = data
}) })
.then(({ data }) => {
tags.value = data
})
} }
</script> </script>

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

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

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

@ -4,11 +4,13 @@
:title="!dataList[0].propId ? '新增' : '修改'" :title="!dataList[0].propId ? '新增' : '修改'"
:close-on-click-modal="false" :close-on-click-modal="false"
> >
<!-- 属性表格 -->
<el-table <el-table
:data="dataList" :data="dataList"
border border
style="width: 100%;" style="width: 100%;"
> >
<!-- 属性名称列 -->
<el-table-column <el-table-column
prop="propName" prop="propName"
header-align="center" header-align="center"
@ -25,6 +27,8 @@
/> />
</template> </template>
</el-table-column> </el-table-column>
<!-- 属性值列 -->
<el-table-column <el-table-column
prop="prodPropValues" prop="prodPropValues"
header-align="center" header-align="center"
@ -47,6 +51,7 @@
@clear="clearProdPropValues" @clear="clearProdPropValues"
/> />
</el-col> </el-col>
<!-- 添加新输入框按钮 -->
<el-col :span="4"> <el-col :span="4">
<el-button <el-button
v-show="scope.row.prodPropValues[scope.row.prodPropValues.length-1].propValue" v-show="scope.row.prodPropValues[scope.row.prodPropValues.length-1].propValue"
@ -59,37 +64,46 @@
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<!-- 对话框底部按钮 -->
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
<el-button @click="visible = false"> <el-button @click="visible = false">取消</el-button>
取消 <el-button type="primary" @click="onSubmit()"></el-button>
</el-button>
<el-button
type="primary"
@click="onSubmit()"
>
确定
</el-button>
</div> </div>
</template> </template>
</el-dialog> </el-dialog>
</template> </template>
<script setup> <script setup>
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { Debounce } from '@/utils/debounce' import { Debounce } from '@/utils/debounce'
import { ref, defineEmits, defineExpose } from 'vue'
//
const emit = defineEmits(['refreshDataList']) const emit = defineEmits(['refreshDataList'])
// /
const visible = ref(false) const visible = ref(false)
//
const dataList = ref([{ propId: 0, propName: '', prodPropValues: [{ valueId: 0 }] }]) const dataList = ref([{ propId: 0, propName: '', prodPropValues: [{ valueId: 0 }] }])
// 使
const page = { const page = {
total: 0, // total: 0, //
currentPage: 1, // currentPage: 1, //
pageSize: 10 // pageSize: 10 //
} }
/**
* 初始化方法接受可选的产品属性对象作为参数
* @param val - 产品属性对象如果存在则为编辑模式
*/
const init = (val) => { const init = (val) => {
if (val) { if (val) {
dataList.value = [JSON.parse(JSON.stringify(val))] dataList.value = [JSON.parse(JSON.stringify(val))] //
} else { } else {
dataList.value = [ dataList.value = [
{ propId: 0, propName: '', prodPropValues: [{ valueId: 0 }] } { propId: 0, propName: '', prodPropValues: [{ valueId: 0 }] }
@ -103,10 +117,10 @@ defineExpose({ init })
* 表单提交 * 表单提交
*/ */
const onSubmit = Debounce(() => { const onSubmit = Debounce(() => {
//
if (dataList.value[0].prodPropValues) { if (dataList.value[0].prodPropValues) {
const temp = [] const temp = []
for (const key in dataList.value[0].prodPropValues) { for (const key in dataList.value[0].prodPropValues) {
// eslint-disable-next-line no-prototype-builtins
if (dataList.value[0].prodPropValues.hasOwnProperty(key)) { if (dataList.value[0].prodPropValues.hasOwnProperty(key)) {
const element = dataList.value[0].prodPropValues[key] const element = dataList.value[0].prodPropValues[key]
if (element.propValue) { if (element.propValue) {
@ -116,13 +130,13 @@ const onSubmit = Debounce(() => {
} }
dataList.value[0].prodPropValues = temp dataList.value[0].prodPropValues = temp
} }
//
if (!dataList.value[0].propName.trim()) { if (!dataList.value[0].propName.trim()) {
dataList.value[0].propName = ''
ElMessage.error('属性名不能为空') ElMessage.error('属性名不能为空')
return return
} }
if (dataList.value[0].prodPropValues.length < 1) { if (dataList.value[0].prodPropValues.length < 1) {
dataList.value[0].prodPropValues = [{ valueId: 0 }]
ElMessage.error('规格项不能为空') ElMessage.error('规格项不能为空')
return return
} }
@ -138,6 +152,8 @@ const onSubmit = Debounce(() => {
ElMessage.error('属性值长度不能大于20') ElMessage.error('属性值长度不能大于20')
return return
} }
// HTTPID
http({ http({
url: http.adornUrl('/prod/spec'), url: http.adornUrl('/prod/spec'),
method: dataList.value[0].propId ? 'put' : 'post', method: dataList.value[0].propId ? 'put' : 'post',
@ -160,6 +176,9 @@ const onSubmit = Debounce(() => {
}) })
}) })
/**
* 清除属性值时的处理逻辑
*/
const clearProdPropValues = () => { const clearProdPropValues = () => {
if (dataList.value[0].prodPropValues.length === 1) { if (dataList.value[0].prodPropValues.length === 1) {
return return
@ -172,10 +191,13 @@ const clearProdPropValues = () => {
} }
} }
/**
* 添加新的属性值输入框
*/
const addInput = () => { const addInput = () => {
const temp = dataList.value[0].prodPropValues const temp = dataList.value[0].prodPropValues
if (temp[temp.length - 1].propValue) { if (temp[temp.length - 1].propValue) {
temp.push({}) temp.push({}) //
} }
} }
</script> </script>

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

Loading…
Cancel
Save