Merge branch 'projects/mesxc-test' into projects/mesxc-dy

This commit is contained in:
helloDy
2024-03-29 14:38:26 +08:00
353 changed files with 2395 additions and 33367 deletions

View File

@@ -2,7 +2,7 @@
* @Author: zwq
* @Date: 2021-07-19 15:18:30
* @LastEditors: zhp
* @LastEditTime: 2024-03-26 09:57:35
* @LastEditTime: 2024-03-28 15:28:07
* @Description:
-->
<template>
@@ -16,35 +16,32 @@
许昌安彩AGV原片周转看板
<h3 class="unit">单位河南汇融数字科技有限公司</h3>
<h3 class="time">{{ times }}</h3>
<!-- <el-button
type="text"
class="title-button"
:style="{ right: 33 + 'px', top: 37 + 'px' }"
@click="changeFullScreen"
>
<svg-icon v-if="isFullScreen" icon-class="unFullScreenView" />
<svg-icon v-else icon-class="fullScreenView" />
</el-button> -->
<el-button type="text" class="title-button" :style="{ right: 18 + 'px', top: 26 + 'px', fontSize: '32px' }"
@click="changeFullScreen">
<svg-icon width="32" height="32" v-if="isFullScreen" icon-class="unFullScreenView" />
<svg-icon width="32" height="32" v-else icon-class="fullScreenView" />
</el-button>
</el-row>
<el-row class="container-main flex-col" type="flex">
<el-row :style="{ padding: '0 ' + 9 + 'px' }" :gutter="15 * beilv" type="flex" class="flex-1">
<el-col :style="{ margin: 8 + 'px' + ' 0' }" :span="12">
<base-container :beilv="1" :size="'middle'" :title="'搬运任务'" :title-icon="'task'" :back="'energy'">
<base-table1 :page="1" :limit="999" :show-index="false" :beilv="1" :table-config="qualityYearTableProps"
:table-data="qualityYearList" />
<base-container :beilv="1" :size="'eqStatus'" :title="'搬运任务'" :title-icon="'task'" :back="'energy'">
<!-- <base-table1 :page="1" :limit="999" :show-index="false" :beilv="1" :table-config="qualityYearTableProps"
:table-data="qualityYearList" /> -->
<dv-scroll-board :config="taskConfig" style="width:100%;height:350px" ref='taskScrollBoard' />
</base-container>
</el-col>
<el-col :style="{ margin: 8 + 'px' + ' 0' }" :span="12">
<base-container :beilv="1" :size="'middle'" :title="'周转进度'" :title-icon="'order'" :back="'energy'">
<div style="width:100%; overflow: hidden scroll;">
<base-container :beilv="1" :size="'eqStatus'" :title="'周转进度'" :title-icon="'order'" :back="'energy'">
<div class="order" style="width:100%; overflow: hidden scroll;height: 350px;">
<el-row v-for="op in orderProcessList" :key="op.id" style="margin-bottom: 1em">
<p v-if="op.outRate === 1" class="now-secondary-title" style="color:#4679FD">
<i class="el-icon-check" />
{{ op.name }}
</p>
<p v-else class="now-secondary-title">{{ op.name }}</p>
<el-progress define-back-color="rgba(32, 57, 96, 1)" text-color="white" :percentage="op.outRate * 100"
class="custom-progress-bar" />
<el-progress :stroke-width="10" define-back-color="rgba(32, 57, 96, 1)" text-color="white"
:percentage="op.outRate * 100" class="custom-progress-bar" />
</el-row>
</div>
</base-container>
@@ -53,17 +50,19 @@
<el-row :style="{ padding: '0 ' + 9 + 'px' }" :gutter="12 * beilv" type="flex" class="flex-1">
<el-col :style="{ margin: 8 + 'px' + ' 0' }" :span="12">
<base-container :beilv="1" :height="256" :size="'middle'" :title="'库存管理'" :title-icon="'productLine'"
<base-container :beilv="1" :height="280" :size="'eqStatus'" :title="'库存管理'" :title-icon="'productLine'"
:back="'energy'">
<base-table :page="1" :limit="999" :show-index="false" :beilv="1" :table-config="inventoryTableProps"
:table-data="inventoryList" />
<!-- <base-table :page="1" :limit="999" :show-index="false" :beilv="1" :table-config="inventoryTableProps"
:table-data="inventoryList" /> -->
<dv-scroll-board :config="inventoryConfig" style="width:100%;height:380px" ref='inventoryScrollBoard' />
</base-container>
</el-col>
<el-col :style="{ margin: 8 + 'px' + ' 0' }" :span="12">
<base-container :beilv="1" :height="318 + 338 + 16" :size="'middle'" :title="'库位信息'" :title-icon="'info'"
<base-container :beilv="1" :height="318 + 338 + 16" :size="'eqStatus'" :title="'库位信息'" :title-icon="'info'"
:back="'energy'">
<base-table2 :page="1" :limit="999" :show-index="false" :beilv="1" :table-config="locationTableProps"
:table-data="locationList" />
<!-- <base-table2 :page="1" :limit="999" :show-index="false" :beilv="1" :table-config="locationTableProps"
:table-data="locationList" /> -->
<dv-scroll-board :config="locationConfig" style="width:100%;height:380px" ref='locationScrollBoard' />
</base-container>
</el-col>
</el-row>
@@ -191,6 +190,15 @@ const inventoryList = [
{
eqName: ' 螺丝刀', eqCode: '备件库', level: '', product: ' 123', productCode: 'WLXX20220919000147', unit: '个', time: '2023年9月10日'
},
{
eqName: ' 清洗剂', eqCode: '备件库', level: '200mm', product: ' 123', productCode: 'WLXX20220919000147', unit: '瓶', time: '2023年9月10日'
},
{
eqName: ' 清洗剂', eqCode: '备件库', level: '200mm', product: ' 123', productCode: 'WLXX20220919000147', unit: '瓶', time: '2023年9月10日'
},
{
eqName: ' 清洗剂', eqCode: '备件库', level: '200mm', product: ' 123', productCode: 'WLXX20220919000147', unit: '瓶', time: '2023年9月10日'
},
{
eqName: ' 清洗剂', eqCode: '备件库', level: '200mm', product: ' 123', productCode: 'WLXX20220919000147', unit: '瓶', time: '2023年9月10日'
}
@@ -267,6 +275,39 @@ export default {
inventoryTableProps,
inventoryList,
locationList,
taskConfig: {
header: ['序号', '作业号', '执行叉车', '库位', '成品规格', '成品编码', '时间'],
headerBGC: 'rgba(32, 55, 96, 0.8)',
oddRowBGC: 'rgba(32, 55, 96, 0.8)',
evenRowBGC: 'rgba(14, 32, 62, 0.8)',
columnWidth: [80, 120, 90, 140, 140, 150,130],
align: ['center'],
data: [],
// index:true,
rowNum: 10
},
inventoryConfig: {
header: ['序号', '物件名称', '存放区域', '规格', '数量', '单位', '编码', '入库时间'],
headerBGC: 'rgba(32, 55, 96, 0.8)',
oddRowBGC: 'rgba(32, 55, 96, 0.8)',
evenRowBGC: 'rgba(14, 32, 62, 0.8)',
columnWidth: [80, 100, 90, 140, 80, 70, 180, 120],
align: ['center'],
data: [],
// index:true,
rowNum: 10
},
locationConfig: {
header: ['序号', '库位名称', '存放物件名称', '数量', '存放位置', '编码', '单位', '入库时间'],
headerBGC: 'rgba(32, 55, 96, 0.8)',
oddRowBGC: 'rgba(32, 55, 96, 0.8)',
evenRowBGC: 'rgba(14, 32, 62, 0.8)',
columnWidth: [80, 100, 120, 80, 100, 180, 70, 120],
align: ['center'],
data: [],
// index:true,
rowNum: 10
},
locationTableProps,
qualityYearList,
isFullScreen: false,
@@ -294,6 +335,20 @@ export default {
{
name: '作业号RQ20230911',
outRate: '0.4'
},
{
name: '作业号RQ20230911',
outRate: '0.4'
},
{
name: '作业号RQ20230911',
outRate: '0.4'
}, {
name: '作业号RQ20230911',
outRate: '0.4'
}, {
name: '作业号RQ20230911',
outRate: '0.4'
}
],
// qualityTableProps1,
@@ -316,6 +371,16 @@ export default {
},
watch: {
isFullScreen: function (val) {
if (val) {
const _this = this;
_this.beilv2 = document.documentElement.clientWidth / 1920
console.log(_this.beilv2);
} else {
const _this = this;
_this.beilv2 = document.documentElement.clientWidth / 1920
}
},
clientWidth(val) {
if (!this.timer) {
this.clientWidth = val
@@ -343,6 +408,9 @@ export default {
}, 86400000)
},
mounted() {
this.getTask()
this.getInventoryData()
this.getLocationData()
const _this = this;
window.onresize = () => {
return (() => {
@@ -355,6 +423,60 @@ export default {
getTimes() {
setInterval(this.getTimesInterval, 1000);
},
getTask() {
let eqArr = this.qualityYearList.map((item, index) => [
`<span style="color:rgba(255,255,255,0.5)" >${index + 1 || ''}
</span>`,
// formatDate(item.planStartTime) || '',
`
<span style="color:rgba(255,255,255,0.5)" >${item.eqName || ''}
</span>`,
`<span style="color:rgba(255,255,255,0.5)">${item.eqCode || ''}</span>`,
`<span style="color:rgba(255,255,255,0.5)">${item.level || ''}</span>`,
`<span style="color:rgba(255,255,255,0.5)">${item.product || ''}</span>`,
`<span style="color:rgba(255,255,255,0.5)">${item.productCode || ''}</span>`,
`<span style="color:rgba(255,255,255,0.5)">${item.time || ''}</span>`,
])
this.taskConfig.data = eqArr
this.$refs['taskScrollBoard'].updateRows(eqArr)
},
getInventoryData() {
let eqArr = this.inventoryList.map((item, index) => [
`<span style="color:rgba(255,255,255,0.5)" >${index + 1 || ''}
</span>`,
// formatDate(item.planStartTime) || '',
`
<span style="color:rgba(255,255,255,0.5)" >${item.eqName || ''}
</span>`,
`<span style="color:rgba(255,255,255,0.5)">${item.eqCode || ''}</span>`,
`<span style="color:rgba(255,255,255,0.5)">${item.level || ''}</span>`,
`<span style="color:rgba(255,255,255,0.5)">${item.product || ''}</span>`,
`<span style="color:rgba(255,255,255,0.5)">${item.unit || ''}</span>`,
`<span style="color:rgba(255,255,255,0.5)">${item.productCode || ''}</span>`,
`<span style="color:rgba(255,255,255,0.5)">${item.time || ''}</span>`,
])
this.inventoryConfig.data = eqArr
this.$refs['inventoryScrollBoard'].updateRows(eqArr)
},
getLocationData() {
let eqArr = this.locationList.map((item, index) => [
`<span style="color:rgba(255,255,255,0.5)" >${index + 1 || ''}
</span>`,
// formatDate(item.planStartTime) || '',
`
<span style="color:rgba(255,255,255,0.5)" >${item.eqName || ''}
</span>`,
`<span style="color:rgba(255,255,255,0.5)">${item.eqCode || ''}</span>`,
`<span style="color:rgba(255,255,255,0.5)">${item.product || ''}</span>`,
`<span style="color:rgba(255,255,255,0.5)">${item.level || ''}</span>`,
`<span style="color:rgba(255,255,255,0.5)">${item.productCode || ''}</span>`,
`<span style="color:rgba(255,255,255,0.5)">${item.unit || ''}</span>`,
`<span style="color:rgba(255,255,255,0.5)">${item.time || ''}</span>`,
])
this.locationConfig.data = eqArr
this.$refs['locationScrollBoard'].updateRows(eqArr)
},
getTimesInterval: function () {
let _this = this;
let year = new Date().getFullYear(); //获取当前时间的年份
@@ -383,25 +505,25 @@ export default {
this.isFullScreen = screenfull.isFullscreen
},
init() {
if (screenfull.enabled) {
if (screenfull.isEnabled) {
screenfull.on('change', this.change)
}
},
destroy() {
if (screenfull.enabled) {
if (screenfull.isEnabled) {
screenfull.off('change', this.change)
}
},
changeFullScreen() {
console.log(this.beilv);
if (!screenfull.enabled) {
if (!screenfull.isEnabled) {
this.$message({
message: 'you browser can not work',
type: 'warning'
})
return false
}
screenfull.toggle(this.$refs.container)
screenfull.toggle()
}
}
}
@@ -410,6 +532,9 @@ export default {
<style lang="scss" scoped>
.visual-container {
width: 1920px;
height: 1080px;
@@ -418,26 +543,29 @@ export default {
// opacity: .8;
.container-title {
width: 100%;
background: url('../../assets/img/OperationalOverview/title.png') no-repeat;
background: url('../../assets/img/OperationalOverview/AGV.png') no-repeat;
background-size: 100% 100%;
color: #00fff0;
text-align: center;
word-spacing:8px;
letter-spacing: 8px;
// word-spacing: 8px;
position: relative;
.unit{
position: absolute;
left: 290px;
top:25px;
color: rgba($color: #ffffff, $alpha: 8);
font-size: 20px;
}
.time{
position: absolute;
left: 1360px;
top:25px;
color: rgba($color: #ffffff, $alpha: 8);
font-size: 20px;
}
.unit {
position: absolute;
left: 240px;
top: 25px;
color: rgba(255, 255, 255, 0.80);
font-size: 20px;
letter-spacing: 0px;
}
.time {
position: absolute;
left: 1400px;
top: 25px;
color: rgba(255, 255, 255, 0.80);
font-size: 20px;
letter-spacing: 0px;
}
.title-button {
color: #00fff0;
font-size: 20px;
@@ -489,7 +617,9 @@ export default {
// .container-main {
// padding: 5px;
// }
.order::-webkit-scrollbar{
display: none
}
</style>
<style lang="scss" >

View File

@@ -1,7 +1,7 @@
<!--
* @Author: zhp
* @Date: 2024-01-29 16:50:26
* @LastEditTime: 2024-03-26 16:30:12
* @LastEditTime: 2024-03-28 16:35:49
* @LastEditors: zhp
* @Description:
-->
@@ -17,15 +17,11 @@
许昌安彩冷端看板
<h3 class="unit">单位河南汇融数字科技有限公司</h3>
<h3 class="time">{{ times }}</h3>
<!-- <el-button
type="text"
class="title-button"
:style="{ right: 33 + 'px', top: 37 + 'px' }"
@click="changeFullScreen"
>
<svg-icon v-if="isFullScreen" icon-class="unFullScreenView" />
<svg-icon v-else icon-class="fullScreenView" />
</el-button> -->
<el-button type="text" class="title-button" :style="{ right: 18 + 'px', top: 26 + 'px',fontSize:'32px'}"
@click="changeFullScreen">
<svg-icon width="32" height="32" v-if="isFullScreen" icon-class="unFullScreenView" />
<svg-icon width="32" height="32" v-else icon-class="fullScreenView" />
</el-button>
</el-row>
<el-row class="container-main flex-col" type="flex">
<el-row :style="{ padding: '0 ' + 9 + 'px' }" :gutter="15" type="flex" class="flex-1">
@@ -61,15 +57,26 @@
<el-row :style="{ padding: '0 ' + 9 + 'px' }" :gutter="10" type="flex" class="flex-1">
<el-col :style="{ margin: 8 + 'px' + ' 0' }" :span="12">
<base-container :no-content-padding="true" :height="256" :title="'废片 缺陷数据'" :size="'middle'"
<base-container :no-content-padding="true" :height="256" :title="'废片 缺陷数据'" :size="'eqStatus'"
:title-icon="'scrap'">
<!-- <div style="width: 45%;position: absolute; top: 3em; right: 3em;">
<top-radio-group />
</div> -->
<!-- <el-row :gutter="9"> -->
<!-- <el-col :style="{ margin: 8 + 'px' + ' 0' }" :span="24"> -->
<pile-bar-chart ref="pileChart" :name-list="EnergyMonitoringNameList" :data-list="EnergyMonitoringList"
:height="359" />
<pile-bar-chart style="margin-bottom: -15px" ref="firstPileChart" :height="90" />
<second-pile-bar-chart style="margin-bottom: -15px" id=" 'second' " ref="secondPileChart" :height="90" />
<third-pile-bar-chart style="margin-bottom: -15px" id=" 'third' " ref="thirdPileChart" :height="90" />
<fourth-pile-bar-chart style="margin-bottom: -15px" id=" 'fourth' " ref="fourthPileChart" :height="90" />
<fifth-pile-bar-chart style="margin-bottom: -15px" id=" 'fifth' " ref="fifthPileChart" :height="100" />
<!-- <pile-bar-chart ref="secondPileChart"
:height="90" />
<pile-bar-chart ref="thirdPileChart"
:height="90" />
<pile-bar-chart ref="fourthPileChart"
:height="90" />
<pile-bar-chart ref="fifthPileChart"
:height="90" /> -->
<!-- </el-col> -->
<!-- <el-col :style="{ margin: 8 + 'px' + ' 0' }" :span="12">
<base-table3
@@ -90,21 +97,21 @@
</div> -->
<!-- 像下面这样表格里的limit值也许可以用js动态计算出来 -->
<el-col :span="8" style="">
<div style="font-size:20px;margin: 5px 0 10px 0">熔化风机</div>
<div style="color:rgba(82, 255, 248, 1);font-size:20px;margin: 5px 0 10px 0">熔化风机</div>
<!-- <base-table1 :page="1" :limit="999" :show-index="false" :table-config="EqMonitoringPropsFun"
:table-data="funList" /> -->
<dv-scroll-board :config="funConfig" style="width:100%;height:310px" ref='funScrollBoard' />
</el-col>
<span class="eqLine"></span>
<el-col :span="8">
<div style="font-size:20px;margin: 5px 0 10px 0;">退火风机</div>
<div style="color:rgba(82, 255, 248, 1);font-size:20px;margin: 5px 0 10px 0;">退火风机</div>
<!-- <base-table1 :page="1" :limit="999" :show-index="false" :table-config="EqMonitoringPropsFun"
:table-data="annealFunList" /> -->
<dv-scroll-board :config="annealFunConfig" style="width:100%;height:310px" ref='annealFunScrollBoard' />
</el-col>
<span class="eqLineTwo"></span>
<el-col :span="8" style="float: right;">
<div style="font-size:20px;margin: 5px 0 10px 0;">产线设备</div>
<div style="color:rgba(82, 255, 248, 1);font-size:20px;margin: 5px 0 10px 0;">产线设备</div>
<!-- <base-table1 :page="1" :limit="999" :show-index="false" :table-config="EqMonitoringProps"
:table-data="realEqList" /> -->
<dv-scroll-board :config="realEqConfig" style="width:100%;height:310px" ref='realEqScrollBoard' />
@@ -136,6 +143,11 @@ import baseContainer from './components/baseContainer'
import baseTable1 from './components/baseTable'
import screenfull from 'screenfull'
import pileBarChart from './components/pileBarChart'
import secondPileBarChart from './components/pileBarChart'
import thirdPileBarChart from './components/pileBarChart'
import fourthPileBarChart from './components/pileBarChart'
import fifthPileBarChart from './components/pileBarChart'
import colorDiv from './components/colorDiv'
import moment from "moment";
import doubleYChart from './components/coldDoubleYChart'
@@ -152,7 +164,11 @@ export default {
pileBarChart,
doubleYChart,
LinearBarChart,
ISRAChart
ISRAChart,
secondPileBarChart,
thirdPileBarChart ,
fourthPileBarChart,
fifthPileBarChart ,
},
data() {
return {
@@ -256,6 +272,16 @@ export default {
},
watch: {
isFullScreen:function (val) {
if (val) {
const _this = this;
_this.beilv2 = document.documentElement.clientWidth / 1920
console.log(_this.beilv2);
} else {
const _this = this;
_this.beilv2 = document.documentElement.clientWidth / 1920
}
},
clientWidth(val) {
if (!this.timer) {
this.clientWidth = val
@@ -275,6 +301,7 @@ export default {
setTimeout(() => {
window.location.reload()
}, 86400000)
// this.getData()
},
destroyed() {
this.CutWebsocketClose()
@@ -287,21 +314,96 @@ export default {
}
},
mounted() {
console.log(moment(this.logoutTime - 28800000).format('HH:mm:ss'));
let nameList = []
let nameWasteList = []
let topNameList = []
let productList = []
let wasteList = []
let yieldList = []
// let sumAreaList = []
// let yieldList = []
// this.cutTableDataList =
let coldDetData = [
{
"first": 0.8834,
"lineName": "Y61",
"product": 0.8834,
"productArea": 35069.28,
"second": 0,
'sumArea': 38400,
'wastArea': 3330.72,
"yield": 0.9133
},
{
"first": 0.847,
"lineName": "Y62",
"product": 0.847,
"productArea": 26765.46,
"second": 0,
'sumArea': 1111111,
'wastArea': 22222,
"yield": 0.9133
},
{
"first": 0.8668,
"lineName": "Y63",
"product": 0.8668,
"productArea": 26448.46,
"second": 0,
'sumArea': 1111111,
'wastArea': 22222,
"yield": 0.9133
},
{
"first": 0.9064,
"lineName": "Y64",
"product": 0.9064,
"productArea": 26667.32,
"second": 0,
'sumArea': 1111111,
'wastArea': 22222,
"yield": 0.9133
},
{
"first": 0.8838,
"lineName": "Y65",
"product": 0.8838,
"productArea": 26554.32,
"second": 0,
'sumArea': 1111111,
'wastArea': 22222,
"yield": 0.9133
}
]
coldDetData.forEach((ele, index) => {
nameList.push(ele.lineName)
topNameList.push('产线: ' + ele.lineName + ' ' + '总面积:' + ele.sumArea)
productList.push(ele.productArea)
wasteList.push(ele.wastArea)
nameWasteList.push('缺陷面积:' + ele.wastArea)
yieldList.push({
name: '良品',
yield:ele.yield
})
// sumAreaList.push(ele.sumArea)
// yieldList.push((ele.yield * 100).toFixed(3))
})
this.$refs.firstPileChart.initChart(nameList, [topNameList[0]], [nameWasteList[0]], [productList[0]], [wasteList[0]])
this.$refs.secondPileChart.initChart(nameList, [topNameList[1]], [nameWasteList[1]], [productList[1]], [wasteList[1]])
this.$refs.thirdPileChart.initChart(nameList, [topNameList[2]], [nameWasteList[2]], [productList[2]], [wasteList[2]])
this.$refs.fourthPileChart.initChart(nameList, [topNameList[3]], [nameWasteList[3]], [productList[3]], [wasteList[3]])
this.$refs.fifthPileChart.initChart(nameList, [topNameList[4]], [nameWasteList[4]], [productList[4]], [wasteList[4]])
// this.$refs.productChart.initChart(nameList, yieldList, sumAreaList)
this.getTimes()
// console.log(moment(this.logoutTime - 28800000).format('HH:mm:ss'));
this.funInitWebSocket()
// if (num > 0) {
if (!this.wsIsOpen) {
getDcsMsg()
this.wsIsOpen = true
console.log('开启websocket==========')
}
// } else {
// }
this.CutInitWebSocket()
this.SJGInitWebSocket()
// this.getList()
this.getTimes()
const _this = this;
_this.beilv2 = document.documentElement.clientWidth / 1920
window.onresize = () => {
@@ -312,6 +414,39 @@ export default {
}
},
methods: {
// getData() {
// let detData = [
// {
// name: '1',
// run: '运行',
// },
// {
// name: '2',
// run: '未运行',
// },
// {
// name: '3',
// run: '运行',
// },
// {
// name: '4',
// run: '未运行',
// },
// {
// name: '1',
// run: '运行',
// },
// ]
// this.realEqList = detData.map((item, index) => [
// // console.log(item)
// `<span style="color:rgba(255,255,255,0.5)" >${index + 1 || ''}
// </span>`,
// `<span style="color:rgba(255,255,255,0.5)">${item.name || ''}</span>`,
// `<span style="color:rgba(255,255,255,0.5)"><div style="${item.run == '运行' ? 'box-shadow: 0px 0px 2px 1px #2760FF;width:6px;height:6px;border-radius: 50%;background-color: #2760FF;float:left;margin:10px 10px 0 0 ' : 'box-shadow: 0px 0px 2px 1px #FFBD02;width:6px;height:6px;border-radius: 50%; background-color: #FFBD02;float:left;margin:10px 10px 0 0 '}"></div> ${item.run || ''}</span>`,
// ])
// this.realEqConfig.data = this.realEqList
// this.$refs['realEqScrollBoard'].updateRows(this.realEqList)
// },
CutWebsocketClose(e) {
this.Cutws.ws.onclose = (event) => { console.log(event );}
},
@@ -381,16 +516,19 @@ export default {
})
this.$refs['ISRAChart'].updateChart(chartData)
} else if (this.SJGWsData.type === 'equipment') {
this.realEqList = this.SJGWsData.detData.map((ele, index) => [
this.realEqList = this.SJGWsData.detData.map((item, index) => [
// console.log(item)
`<span style="color:rgba(255,255,255,0.5)" >${index + 1 || ''}
</span>`,
`<span style="color:rgba(255,255,255,0.5)">${ele.name || ''}</span>`,
`<span style="color:rgba(255,255,255,0.5)">${ele.run || ''}</span>`,
`<span style="color:rgba(255,255,255,0.5)">${item.name || ''}</span>`,
`<span style="color:rgba(255,255,255,0.5)"><div style="${item.run == '运行' ? 'box-shadow: 0px 0px 2px 1px #2760FF;width:6px;height:6px;border-radius: 50%;background-color: #2760FF;float:left;margin:10px 10px 0 0 ' : 'box-shadow: 0px 0px 2px 1px #FFBD02;width:6px;height:6px;border-radius: 50%; background-color: #FFBD02;float:left;margin:10px 10px 0 0 '}"></div> ${item.run || ''}</span>`,
])
}
this.realEqConfig.data = this.realEqList
this.$refs['realEqScrollBoard'].updateRows(this.realEqList)
this.$nextTick(() => {
this.$refs['realEqScrollBoard'].updateRows(this.realEqList)
})
};
// if (typeof (WebSocket) === 'undefined') {
// alert('您的浏览器不支持WebSocket')
@@ -451,9 +589,13 @@ export default {
wasteList.push(ele.wastArea)
nameWasteList.push('缺陷面积:' + ele.wastArea)
sumAreaList.push(ele.sumArea)
yieldList.push((ele.yield * 100).toFixed(3))
yieldList.push(parseFloat((ele.yield * 100).toFixed(3)))
})
this.$refs.pileChart.initChart(nameList, topNameList, nameWasteList, productList, wasteList)
this.$refs.firstPileChart.initChart(nameList, [topNameList[0]], [nameWasteList[0]], [productList[0]], [wasteList[0]])
this.$refs.secondPileChart.initChart(nameList, [topNameList[1]], [nameWasteList[1]], [productList[1]], [wasteList[1]])
this.$refs.thirdPileChart.initChart(nameList, [topNameList[0]], [nameWasteList[0]], [productList[0]], [wasteList[0]])
this.$refs.fourthPileChart.initChart(nameList, [topNameList[0]], [nameWasteList[0]], [productList[0]], [wasteList[0]])
this.$refs.fifthPileChart.initChart(nameList, [topNameList[0]], [nameWasteList[0]], [productList[0]], [wasteList[0]])
this.$refs.productChart.initChart(nameList, yieldList, sumAreaList)
}
};
@@ -495,7 +637,8 @@ export default {
</span>`,
`<span style="color:rgba(255,255,255,0.5)" >${i || ''}
</span>`,
`<span style="color:rgba(255,255,255,0.5)">${this.funWsData.data.annealFanInfo[i] || ''}</span>`,
`<span style="color:rgba(255,255,255,0.5)">${item.name || ''}</span>`,
`<span style="color:rgba(255,255,255,0.5)"><div style="${item.run == '运行' ? 'box-shadow: 0px 0px 2px 1px #2760FF;width:6px;height:6px;border-radius: 50%;background-color: #2760FF;float:left;margin:10px 10px 0 0 ' : 'box-shadow: 0px 0px 2px 1px #FFBD02;width:6px;height:6px;border-radius: 50%; background-color: #FFBD02;float:left;margin:10px 10px 0 0 '}"></div> ${item.run || ''}</span>`,
])
}
this.annealFunConfig.data = arr
@@ -509,7 +652,8 @@ export default {
arr.push([
`<span style="color:rgba(255,255,255,0.5)" >${index || ''}</span>`,
`<span style="color:rgba(255,255,255,0.5)" >${i || ''}</span>`,
`<span style="color:rgba(255,255,255,0.5)">${this.funWsData.data.fanInfo[i] || ''}</span>`,
`<span style="color:rgba(255,255,255,0.5)">${item.name || ''}</span>`,
`<span style="color:rgba(255,255,255,0.5)"><div style="${item.run == '运行' ? 'box-shadow: 0px 0px 2px 1px #2760FF;width:6px;height:6px;border-radius: 50%;background-color: #2760FF;float:left;margin:10px 10px 0 0 ' : 'box-shadow: 0px 0px 2px 1px #FFBD02;width:6px;height:6px;border-radius: 50%; background-color: #FFBD02;float:left;margin:10px 10px 0 0 '}"></div> ${item.run || ''}</span>`,
])
}
this.funConfig.data = arr
@@ -594,106 +738,34 @@ export default {
// console.log(size[0] + '*' + size[1] + '*' + size[2]);
return size[0] + '*' + size[1] + '*' + size[2]
},
// 数据接收
// cutWebsocketOnMessage(e) {
// this.cutWsData = e?.data ? JSON.parse(e?.data) : {}
// if (this.cutWsData.type === 'cutting' && this.cutWsData.name === 'table') {
// let cutArr = this.cutWsData.productHourData.map((item, index) => [
// `<span style="color:rgba(255,255,255,0.7)" >${index + 1 || ''}
// </span>`,
// `<span style="color:rgba(255,255,255,0.7)" >${item.lineName || ''}
// </span>`,
// `<span style="color:rgba(255,255,255,0.7)">${this.formatTime(item.time) || ''}</span>`,
// `<span style="color:rgba(255,255,255,0.7)">${this.getSize(item.size) || ''}</span>`,
// `<span style="color:rgba(255,255,255,0.7)">${item.productArea + '㎡' || ''}</span>`,
// `<span style="color:rgba(255,255,255,0.7)">${item.wasteArea + '㎡' || ''}</span>`,
// `<div style = "${(item.product * 100).toFixed(2) > 91 ? 'display:block;color:#00FFF7' : 'display:none;'}">${(item.product * 100).toFixed(2) + '%' || ''}</div>
// <div style = "${(item.product * 100).toFixed(2) < 91 ? 'display:block; color:rgba(255, 209, 96, 1)' : 'display:none;'}">${(item.product * 100).toFixed(2) + '%' || ''}</div>`
// ])
// this.cutConfig.data = cutArr
// this.$refs['cutScrollBoard'].updateRows(cutArr)
// } else if (this.cutWsData.type === 'cutting' && this.cutWsData.name === 'chart' && this.cutWsData.dateType === 'day') {
// let nameList = []
// let nameWasteList = []
// let topNameList = []
// let productList = []
// let wasteList = []
// let sumAreaList = []
// let yieldList = []
// // this.cutTableDataList =
// this.cutWsData.coldDetData.forEach((ele, index) => {
// nameList.push(ele.lineName)
// topNameList.push('产线: ' + ele.lineName + ' ' + '总面积:' + ele.sumArea)
// productList.push(ele.productArea)
// wasteList.push(ele.wastArea)
// nameWasteList.push('缺陷面积:' + ele.wastArea)
// sumAreaList.push(ele.sumArea)
// yieldList.push((ele.yield *100).toFixed(3))
// })
// this.$refs.pileChart.initChart(nameList, topNameList, nameWasteList, productList, wasteList)
// this.$refs.productChart.initChart(nameList, yieldList, sumAreaList)
// }
// },
// SJGWebsocketOnOpen() {
// console.log('socket连接成功')
// this.SJGWebsocket.onmessage()
// },
// 连接建立失败重连
// SJGWebsocketOnError(e) {
// this.SJGInitWebSocket()
// },
// 数据接收
// SJGWebsocketOnMessage(e) {
// this.SJGWsData = e?.data ? JSON.parse(e?.data) : {}
// if (this.SJGWsData.type === 'isra') {
// console.log('222222', this.SJGWsData.detData)
// console.log(this.SJGWsData.detData);
// let chartData = this.SJGWsData.detData.map((item, index) => {
// return {
// name: item.type,
// num:item.num
// }
// })
// this.$refs['ISRAChart'].updateChart(chartData)
// } else if (this.SJGWsData.type === 'equipment') {
// this.realEqList = this.SJGWsData.detData.map((ele, index) =>[
// // console.log(item)
// `<span style="color:rgba(255,255,255,0.5)" >${index + 1 || ''}
// </span>`,
// `<span style="color:rgba(255,255,255,0.5)">${ele.name || ''}</span>`,
// `<span style="color:rgba(255,255,255,0.5)">${ele.run || ''}</span>`,
// ])
// }
// this.realEqConfig.data = this.realEqList
// this.$refs['realEqScrollBoard'].updateRows(this.realEqList)
// },
windowWidth(value) {
this.clientWidth = value
this.beilv2 = this.clientWidth / 1920
},
change() {
console.log(this.isFullScreen);
this.isFullScreen = screenfull.isFullscreen
},
init() {
if (screenfull.enabled) {
if (screenfull.isEnabled) {
screenfull.on('change', this.change)
}
},
destroy() {
if (screenfull.enabled) {
if (screenfull.isEnabled) {
screenfull.off('change', this.change)
}
},
changeFullScreen() {
this.containerWidth = 223223223
if (!screenfull.enabled) {
// this.containerWidth = 223223223
if (!screenfull.isEnabled) {
this.$message({
message: 'you browser can not work',
type: 'warning'
})
return false
}
screenfull.toggle(this.$refs.container)
screenfull.toggle()
}
}
}
@@ -714,14 +786,16 @@ export default {
background-size: 100% 100%;
color: rgba(0, 255, 247, 1);
text-align: center;
letter-spacing: 8px;
word-spacing: 8px;
.unit {
position: absolute;
left: 260px;
left: 290px;
top: 25px;
color: rgba(255, 255, 255, 0.80);
font-size: 20px;
}
letter-spacing: 0px;
}
.time {
position: absolute;
@@ -729,6 +803,7 @@ export default {
color: rgba(255, 255, 255, 0.80);
top: 25px;
font-size: 20px;
letter-spacing: 0px;
}
.title-button {
@@ -780,6 +855,10 @@ export default {
top: 2em;
}
.screen-btn {
color: #00fff0;
font-size: 32px;
}
// .container-main {
// padding: 5px;
// }
@@ -868,6 +947,7 @@ background: linear-gradient(to bottom,rgba(60,
margin-left: 3em;
}
}
</style>
<style scoped>
@@ -926,4 +1006,5 @@ background: linear-gradient(to bottom,rgba(60,
.tooltip:hover .tooltiptext {
visibility: visible;
}
</style>

View File

@@ -1,8 +1,8 @@
<!--
* @Author: zwq
* @Date: 2022-01-21 14:43:06
* @LastEditors: zwq
* @LastEditTime: 2022-01-24 13:27:41
* @LastEditors: zhp
* @LastEditTime: 2024-03-28 16:30:54
* @Description:
-->
<template>
@@ -61,7 +61,7 @@ export default {
},
mounted() {
this.$nextTick(() => {
this.initChart()
// this.initChart()
})
},
beforeDestroy() {

View File

@@ -2,7 +2,7 @@
* @Author: zwq
* @Date: 2022-01-21 14:43:06
* @LastEditors: zhp
* @LastEditTime: 2024-01-25 17:41:44
* @LastEditTime: 2024-03-28 16:45:24
* @Description:
-->
<template>
@@ -90,6 +90,7 @@ export default {
bottom: 0,
itemHeight: 10,
itemWidth: 10,
icon:'roundRect',
formatter: function(name) {
let pieLegendVale = {}
lData.filter((item, index) => {

View File

@@ -2,7 +2,7 @@
* @Author: gtz
* @Date: 2022-01-19 15:58:17
* @LastEditors: zhp
* @LastEditTime: 2024-02-22 10:26:59
* @LastEditTime: 2024-03-27 10:26:35
* @Description: file content
* @FilePath: \mt-bus-fe\src\views\OperationalOverview\components\baseContainer\index.vue
-->
@@ -158,6 +158,7 @@ export default {
<style lang="scss" scoped>
.base-container {
color: #fff;
width: 100%;
@@ -192,7 +193,7 @@ export default {
backdrop-filter: blur(5px);
}
&__eqStatus {
background: url(../../../../assets/img/high.png) no-repeat;
background: url(../../../../assets/img/large.png) no-repeat;
background-size: 100% 100%;
backdrop-filter: blur(5px);
}

View File

@@ -1,7 +1,7 @@
<!--
* @Author: zhp
* @Date: 2023-09-21 09:06:28
* @LastEditTime: 2024-03-26 16:16:50
* @LastEditTime: 2024-03-28 16:31:20
* @LastEditors: zhp
* @Description:
-->
@@ -78,7 +78,7 @@ export default {
this.$el.addEventListener('resize', () => {
console.log('resziing.....');
});
this.initChart()
// this.initChart()
},
beforeDestroy() {
if (!this.chart) {
@@ -112,7 +112,7 @@ export default {
}
// barBorderRadius: this.borderRadius
},
barWidth: 12,
barWidth: 18,
data: outputNumList
},
{
@@ -147,72 +147,40 @@ export default {
]
// const colors = ['#5470C6', '#91CC75', '#EE6666']
this.chart = echarts.init(document.getElementById(this.id))
this.chart.setOption({
// color: colors,
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross'
}
},
grid: { top: 90, right: 60, bottom: 20, left: 90 },
legend: {
itemWidth: 10,
itemHeight: 10,
top: '0%',
right: '20px',
data: ['产线产量', '产线良品率'],
textStyle: {
fontSize: 12 * this.beilv,
color: '#ced1d5'
}
},
xAxis: {
type: 'category',
axisLine: {
lineStyle: {
type: 'solid',
color: '#25528f',
width: '1' // 坐标线的宽度
if (!nameList && !passRateList && !outputNumList) {
// 隐藏echarts图表
this.chart.clear(); // 清空图表
this.chart.setOption({ // 设置空的option
title: {
show: false
},
series: []
});
} else {
// 显示echarts图表
this.chart.setOption({
// color: colors,
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross'
}
},
axisLabel: {
color: "#fff",
fontSize: 12,
// formatter: '{value}'
// textStyle: {
// color: 'rgba(255,255,255,0.5)' // 坐标值得具体的颜色
// }
},
splitLine: {
lineStyle: {
color: '#25528f'
grid: { top: 90, right: 60, bottom: 20, left: 20, containLabel: true },
legend: {
itemWidth: 10,
itemHeight: 10,
top: '0%',
right: '20px',
data: ['产线产量', '产线良品率'],
textStyle: {
fontSize: 12 * this.beilv,
color: '#ced1d5'
}
},
data: nameList
},
// yAxis: {
// },
yAxis: [
{
// min: function() { // 取最小值向下取整为最小刻度
// return 0
// },
// max: function(value) { // 取最大值向上取整为最大刻度
// return Math.ceil(value.max)
// },
scale: true,
type: 'value',
name: '良品率/%',
nameTextStyle: {// y轴上方单位的颜色
color: '#fff',
align: "left",
},
position: 'right',
alignTicks: true,
xAxis: {
type: 'category',
axisLine: {
show: true,
lineStyle: {
type: 'solid',
color: '#25528f',
@@ -223,72 +191,118 @@ export default {
color: "#fff",
fontSize: 12,
// formatter: '{value}'
formatter: '{value}%'
// }
},
splitLine: {
lineStyle: {
color: '#25528f'
}
}
// type: 'value'
// axisLine: {
// show: true,
// lineStyle: {
// color: colors[0]
// }
// },
},
{
// min: function() { // 取最小值向下取整为最小刻度
// return 0
// },
// max: function(value) { // 取最大值向上取整为最大刻度
// return Math.ceil(value.max)
// },
scale: true,
type: 'value',
name: '产量/片', // y轴上方的单位
nameTextStyle: {
color: "#fff",
// fontSize: 10,
align: "right",
},
// position: 'left',
alignTicks: true,
axisLine: {
show: true,
lineStyle: {
type: 'solid',
color: '#25528f', // 左边线的颜色
width: '1' // 坐标线的宽度
}
},
axisLabel: {
// textStyle: {
// color: 'rgba(255,255,255,0.5)', // 坐标值得具体的颜色
color: "#fff",
fontSize: 12,
// formatter: '{value}'
formatter: '{value} 片'
// color: 'rgba(255,255,255,0.5)' // 坐标值得具体的颜色
// }
},
splitLine: {
lineStyle: {
color: '#25528f'
}
},
data: nameList
},
// yAxis: {
// },
yAxis: [
{
// min: function() { // 取最小值向下取整为最小刻度
// return 0
// },
// max: function(value) { // 取最大值向上取整为最大刻度
// return Math.ceil(value.max)
// },
scale: true,
type: 'value',
name: '良品率/%',
nameTextStyle: {// y轴上方单位的颜色
color: '#fff',
align: "left",
},
position: 'right',
alignTicks: true,
axisLine: {
show: true,
lineStyle: {
// type: 'solid',
color: '#25528f',
// width: '1' // 坐标线的宽度
}
},
axisLabel: {
color: "#fff",
fontSize: 12,
// formatter: '{value}'
formatter: '{value}%'
// }
},
splitLine: {
lineStyle: {
color: '#25528f'
}
}
// type: 'value'
// axisLine: {
// show: true,
// lineStyle: {
// color: colors[0]
// }
// },
},
{
// min: function() { // 取最小值向下取整为最小刻度
// return 0
// },
// max: function(value) { // 取最大值向上取整为最大刻度
// return Math.ceil(value.max)
// },
scale: true,
type: 'value',
name: '产量/片', // y轴上方的单位
nameTextStyle: {
color: "#fff",
// fontSize: 10,
align: "right",
},
// position: 'left',
alignTicks: true,
axisLine: {
show: true,
lineStyle: {
// type: 'solid',
color: '#25528f', // 左边线的颜色
// width: '1' // 坐标线的宽度
}
},
axisLabel: {
// textStyle: {
// color: 'rgba(255,255,255,0.5)', // 坐标值得具体的颜色
color: "#fff",
// show: true,
lineStyle: {
color: "#25528f",
},
// }
},
splitLine: {
lineStyle: {
color: '#25528f'
}
}
// type: 'value'
// axisLine: {
// show: true,
// lineStyle: {
// color: colors[1]
// }jik078u7uut9890999999999999999999999999999999999999999999999999999999999999995u8
// },
}
// type: 'value'
// axisLine: {
// show: true,
// lineStyle: {
// color: colors[1]
// }jik078u7uut9890999999999999999999999999999999999999999999999999999999999999995u8
// },
}
],
series: series
})
],
series: series
})
}
}
}
}

View File

@@ -70,7 +70,7 @@ export default {
// this.dataList.forEach(ele => {
// console.log(ele);
// this.series = []
this.initChart()
// this.initChart()
// this.$nextTick(() => {
// // this.initChart()
// })
@@ -87,8 +87,17 @@ export default {
// console.log('1111', dataList);
// console.log(1)
this.chart = echarts.init(document.getElementById(this.id))
// if (dataList.length !== 0) {
// this.$set(this.series, "data", dataList);
if (!nameList && !dataList) {
// 隐藏echarts图表
this.chart.clear(); // 清空图表
this.chart.setOption({ // 设置空的option
title: {
show: false
},
series: []
});
} else {
// 显示echarts图表
this.series = [{
type: 'bar',
data: dataList,
@@ -109,92 +118,96 @@ export default {
}
// barBorderRadius: this.borderRadius
},
barWidth: 12,
barWidth: 18,
}]
// }
// if (nameList.length !== 0) {
// }
// if (nameList.length !== 0) {
this.nameList = nameList
// }
this.chart.setOption({
tooltip: {
trigger: 'axis',
axisPointer: {
// 坐标轴指示器,坐标轴触发有效
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow'
}
},
grid: { top: 90, right: 60, bottom: 20, left: 90 },
// legend: {
// itemWidth: 10,
// itemHeight: 10,
// // right: '20px',
// data: nameList,
// textStyle: {
// fontSize: 12 * this.beilv,
// color: '#ced1d5'
// }
// },
xAxis: {
type: 'category',
axisLine: {
lineStyle: {
type: 'solid',
color: '#25528f', // 左边线的颜色
width: '1' // 坐标线的宽度
// }
this.chart.setOption({
tooltip: {
trigger: 'axis',
axisPointer: {
// 坐标轴指示器,坐标轴触发有效
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow'
}
},
axisLabel: {
color: "#fff",
fontSize: 12,
// formatter: '{value}'
grid: { top: 90, right: 60, bottom: 20, left: 30, containLabel: true },
// legend: {
// itemWidth: 10,
// itemHeight: 10,
// // right: '20px',
// data: nameList,
// textStyle: {
// fontSize: 12 * this.beilv,
// color: '#ced1d5'
// }
// },
xAxis: {
type: 'category',
axisLine: {
lineStyle: {
type: 'solid',
color: '#25528f', // 左边线的颜色
width: '1' // 坐标线的宽度
}
},
axisLabel: {
color: "#fff",
fontSize: 12,
// formatter: '{value}'
},
splitLine: {
lineStyle: {
color: '#25528f'
}
},
data: this.nameList
},
splitLine: {
lineStyle: {
color: '#25528f'
yAxis: {
name: '单位kwh',
nameTextStyle: {
color: '#fff',
fontSize: 10,
align: 'right',
},
type: 'value',
axisLabel: {
color: "#fff",
fontSize: 12,
// formatter: '{value}/kwh'
},
axisLine: {
show: true,
lineStyle: {
color: "#25528f",
},
},
splitLine: {
lineStyle: {
color: "#25528f",
},
}
},
data: this.nameList
},
yAxis: {
name: '单位kwh',
nameTextStyle: {
color: '#fff',
fontSize: 10,
align: 'right',
},
type: 'value',
axisLabel: {
color: "#fff",
fontSize: 12,
// formatter: '{value}/kwh'
},
axisLine: {
show: true,
lineStyle: {
color: "#25528f",
},
},
splitLine: {
lineStyle: {
color: "#25528f",
},
}
},
// legend: {
// itemHeight: 10,
// itemWidth: 10,
// x: 'center', // 可设定图例在左、右、居中
// y: 'top', // 可设定图例在上、下、居中
// show: this.showLegend,
// data: this.dataList,
// right: '1%',
// textStyle: {
// fontSize: 12 * this.beilv,
// color: '#ced1d5'
// }
// },
series: this.series
})
// legend: {
// itemHeight: 10,
// itemWidth: 10,
// x: 'center', // 可设定图例在左、右、居中
// y: 'top', // 可设定图例在上、下、居中
// show: this.showLegend,
// data: this.dataList,
// right: '1%',
// textStyle: {
// fontSize: 12 * this.beilv,
// color: '#ced1d5'
// }
// },
series: this.series
})
}
// if (dataList.length !== 0) {
// this.$set(this.series, "data", dataList);
}
}
}

View File

@@ -1,7 +1,7 @@
<!--
* @Author: zhp
* @Date: 2023-12-27 13:54:52
* @LastEditTime: 2024-03-26 10:50:30
* @LastEditTime: 2024-03-28 16:30:14
* @LastEditors: zhp
* @Description:
-->
@@ -15,6 +15,7 @@
import * as echarts from 'echarts';
import 'echarts/theme/macarons' // echarts theme
import resize from './mixins/resize'
import { raw } from 'body-parser';
export default {
name: 'OverviewBar',
@@ -42,7 +43,7 @@ export default {
},
height: {
type: Number,
default: 200
default: 100
},
showLegend: {
type: Boolean,
@@ -77,7 +78,7 @@ export default {
// this.dataList.forEach(ele => {
// console.log(ele);
// this.series = []
this.initChart()
// this.initChart()
// this.$nextTick(() => {
// // this.initChart()
// })
@@ -94,7 +95,8 @@ export default {
let rawData = []
let colors = ['#0fdedb', '#2359ec']
rawData.push(passRateList, wasteList)
const totalData = [];
const totalData = [];
// if (rawData.length != 0 && raw,Data,length != 0) {
for (let i = 0; i < rawData[0].length; ++i) {
let sum = 0;
for (let j = 0; j < rawData.length; ++j) {
@@ -102,7 +104,8 @@ export default {
}
totalData.push(sum);
}
console.log('total', totalData);
// }
console.log('total', totalData)
this.chart = echarts.init(document.getElementById(this.id))
const series = [
'良品',
@@ -116,34 +119,49 @@ export default {
name,
type: 'bar',
stack: 'total',
barWidth: 10,
label: {
show: true,
formatter: (params) => Math.round(params.value * 1000) / 10 + '%'
},
barWidth: 12,
// label: {
// show: true,
// formatter: (params) => Math.round(params.value * 1000) / 10 + '%'
// },
color:colors[sid],
data: rawData.length != 0 ? rawData[sid].map((d, did) =>
totalData[did] <= 0 ? 0 : d / totalData[did]
) : []
};
});
// this.charts.resize({
// //width: width,
// //height: height,
// // es6解构
// width,
// height
// })
this.chart.setOption({
legend: {
formatter: function (name) {
//通过name获取到数组对象中的单个对象
let singleData = series.filter(function (item) {
return item.name == name
})
return name + parseFloat((singleData[0].data * 100).toFixed(3)) + '%'
},
itemWidth: 12,
itemHeight: 12,
top:'10',
bottom: '20',
left: '20',
icon: 'rect',
textStyle: {
color: '#ffffff'
}
},
grid: {
top:'80',
top:'0',
left: '3%',
right: '4%',
// bottom: '3%',
width: 'auto',
height: '300',
height: '95',
containLabel: true
},
yAxis: [
@@ -170,7 +188,7 @@ export default {
verticalAlign: 'bottom',
fontSize: 12,
align: 'left',
padding: [0, 0, 15, -5]
padding: [0, 0, 10, -5]
}
},
data: topNameList
@@ -198,7 +216,7 @@ export default {
verticalAlign: 'bottom',
fontSize: 12,
align: 'right',
padding: [0, 0, 15, -5]
padding: [0, 0, 10, -5]
}
},
data: nameWasteList
@@ -210,6 +228,10 @@ export default {
},
series:series
})
this.chart.resize({
width: 'auto',
height: 90
});;
}
}
}

View File

@@ -2,7 +2,7 @@
* @Author: zwq
* @Date: 2021-07-19 15:18:30
* @LastEditors: zhp
* @LastEditTime: 2024-03-26 15:43:27
* @LastEditTime: 2024-03-28 16:10:47
* @Description:
-->
<template>
@@ -16,16 +16,19 @@
许昌安彩深加工看板
<h3 class="unit">单位河南汇融数字科技有限公司</h3>
<h3 class="time">{{ times }}</h3>
<el-button type="text" class="title-button" :style="{ right: 18 + 'px', top: 26 + 'px',fontSize:'32px'}"
@click="changeFullScreen">
<svg-icon width="32" height="32" v-if="isFullScreen" icon-class="unFullScreenView" />
<svg-icon width="32" height="32" v-else icon-class="fullScreenView" />
</el-button>
</el-row>
<el-row class="container-main flex-col" type="flex">
<el-row :style="{ padding: '0 ' + 9 + 'px' }" :gutter="15" type="flex" class="flex-1" style="height: 50%;">
<el-col :style="{ margin: 8 + 'px' + ' 0' }" :span="8" height="100%">
<base-container :title="'设备报警'" :size="'small'" :height="318" :title-icon="'eqAlarm'">
<dv-scroll-board class="eqTable" :config="eqConfig" style="width:100%;height:350px" ref='eqScrollBoard' />
</base-container>
</el-col>
<el-col :style="{ margin: 8 + 'px' + ' 0' }" :span="8" height="100%">
<base-container :title="'各工序缺陷汇总'" :size="'small'" :title-icon="'scrap'">
<dv-scroll-board :config="processConfig" style="width:100%;height:350px" ref='processScrollBoard' />
@@ -33,13 +36,14 @@
</el-col>
<el-col :style="{ margin: 8 + 'px' + ' 0' }" :span="8" height="100%">
<base-container :height="318" :size="'small'" :title="'工单监控'" :title-icon="'order'">
<div style="width:100%; overflow: hidden scroll;">
<base-container :height="318" :size="'small'" :title="'工单监控'" :title-icon="'eqMonitoring'">
<div class="order" style="width:100%; overflow: hidden scroll;height: 350px;">
<el-row v-for="op in orderList" :key="op.id" style="margin-bottom: 1em">
<!-- <el-col :span="12"> -->
<p class="now-secondary-title">{{ op.name }}</p>
<el-progress define-back-color="rgba(32, 57, 96, 1)" text-color="white"
:percentage="op.progressRate * 100" class="custom-progress-bar" />
<el-progress :stroke-width="10" define-back-color="rgba(32, 57, 96, 1)" text-color="white"
:percentage="op.progressRate" class="custom-progress-bar" />
</el-row>
</div>
<!-- </div> -->
@@ -49,8 +53,8 @@
<el-row :style="{ padding: '0 ' + 9 + 'px' }" :gutter="12" type="flex" class="flex-1">
<el-col :style="{ margin: 8 + 'px' + ' 0' }" :span="12">
<base-container :show-yes-time="true" :no-content-padding="true" :height="256" :size="'middle'" :title="'能源监控'"
:title-icon="'energyMonitoring'">
<base-container :show-yes-time="true" :no-content-padding="true" :height="256" :size="'eqStatus'"
:title="'能源监控'" :title-icon="'energyMonitoring'">
<el-row :gutter="9">
<el-col :style="{ margin: 8 + 'px' + ' 0' }" :span="24">
<linear-bar-chart ref="EnergyMonitoringChart" :name-list="EnergyMonitoringNameList"
@@ -60,7 +64,7 @@
</base-container>
</el-col>
<el-col :style="{ margin: 8 + 'px' + ' 0' }" :span="12">
<base-container :show-time="true" :no-content-padding="true" :height="318 + 338 + 16" :size="'middle'"
<base-container :show-time="true" :no-content-padding="true" :height="318 + 338 + 16" :size="'eqStatus'"
:title="'产线产量及良品率'" :title-icon="'productLine'">
<double-y-chart ref="productLineChart" :id=" 'doubleYChart' " :height="390" :show-legend="true" />
</base-container>
@@ -234,7 +238,6 @@ export default {
}
},
computed: {
// ...mapGetters(['sidebar']),
styles() {
const v = Math.floor(this.value * this.beilv2 * 100) / 10000
return {
@@ -243,8 +246,17 @@ export default {
}
}
},
watch: {
isFullScreen: function (val) {
if (val) {
const _this = this;
_this.beilv2 = document.documentElement.clientWidth / 1920
console.log(_this.beilv2);
} else {
const _this = this;
_this.beilv2 = document.documentElement.clientWidth / 1920
}
},
clientWidth(val) {
if (!this.timer) {
this.clientWidth = val
@@ -255,12 +267,10 @@ export default {
_this.timer = false
}, 500)
}
// 这里可以添加修改时的方法
this.windowWidth(val);
}
},
created() {
this.getData()
this.init()
setTimeout(() => {
window.location.reload()
@@ -271,20 +281,67 @@ export default {
this.SJGWebsocketClose()
},
mounted() {
let eqArr = this.qualityYearList.map((item, index) => [
`<span style="color:rgba(255,255,255,0.5)" >${index + 1 || ''}
</span>`,
// formatDate(item.planStartTime) || '',
`
<span style="color:rgba(255,255,255,0.5)" >${item.name || ''}
</span>`,
`<span style="color:rgba(255,255,255,0.5)">${item.code || ''}</span>`,
`<span style="color:rgba(255,255,255,0.5)">${item.status || ''}</span>`,
`<span style="color:rgba(255,255,255,0.5)">${item.error || ''}</span>`,
])
this.eqConfig.data = eqArr
this.$refs['eqScrollBoard'].updateRows(eqArr)
this.$refs.productLineChart.initChart(['D61', 'D62', 'D63', 'D64', 'D65',], [98, 97, 98.7, 98.5, 98.3,], [3134, 2323, 3232, 3233, 2321])
// let detData = [
// {
// id:'1',
// name: '测试工单',
// progressRate:0.933333
// },
// {
// id: '2',
// name: '测试工单2',
// progressRate: 0.932323
// }, {
// id: '3',
// name: '测试工单3',
// progressRate: 0.23232
// }, {
// id: '4',
// name: '测试工单4',
// progressRate: 0.32323
// }
// // {
// // id: '5',
// // name: '测试工单',
// // progressRate: 0.5555
// // }, {
// // id: '6',
// // name: '测试工单',
// // progressRate: 0.66564
// // },
// ]
// this.orderList = detData.map((ele, index) => {
// if (ele.progressRate && ele.progressRate != 1) {
// return {
// id: ele.id,
// name: ele.name,
// progressRate: parseFloat((ele.progressRate * 100).toFixed(3))
// }
// }
// });
// let EnergyNameList = ['Y61', 'Y62', 'Y63', 'Y64', 'Y65',]
// this.EnergyMonitoringNameList = EnergyNameList
// let EnergyDataList = [1, 2, 3, 4, 5]
// // this.wsData.data.forEach((ele) => {
// // EnergyDataList.push(ele.useQuantity)
// // })
// this.EnergyMonitoringList = EnergyDataList
// // console.log(EnergyDataList)
// this.$refs.EnergyMonitoringChart.initChart(this.EnergyMonitoringNameList, this.EnergyMonitoringList)
// let eqArr = this.qualityYearList.map((item, index) => [
// `<span style="color:rgba(255,255,255,0.5)" >${index + 1 || ''}
// </span>`,
// // formatDate(item.planStartTime) || '',
// `
// <span style="color:rgba(255,255,255,0.5)" >${item.name || ''}
// </span>`,
// `<span style="color:rgba(255,255,255,0.5)">${item.code || ''}</span>`,
// `<span style="color:rgba(255,255,255,0.5)">${item.status || ''}</span>`,
// `<span style="color:rgba(255,255,255,0.5)">${item.error || ''}</span>`,
// ])
// this.eqConfig.data = eqArr
// this.$refs['eqScrollBoard'].updateRows(eqArr)
// this.$refs.productLineChart.initChart(['D61', 'D62', 'D63', 'D64', 'D65',], [98, 97, 98.7, 98.5, 98.3,], [3134, 2323, 3232, 3233, 2321])
// this.getList()
this.initWebSocket()
this.SJGInitWebSocket()
@@ -298,7 +355,9 @@ export default {
this.beilv2 = _this.clientWidth / 1920
})()
}
this.getData()
},
// beforeDestroy() {
// //离开页面删除检测器和所有侦听器
// // erd.uninstall(document); //这里用ref是因为vue离开页面后获取不到dom
@@ -448,11 +507,11 @@ export default {
// console.log('22222', this.wsData.data)
if (this.SJGWsData.type === 'order') {
this.orderList = this.SJGWsData.detData.map((ele, index) => {
if (ele.progressRate != 1) {
if (ele.progressRate && ele.progressRate != 1) {
return {
id: ele.id,
name: ele.name,
progressRate: ele.progressRate.toFixed(3)
progressRate: parseFloat((ele.progressRate * 100).toFixed(3))
}
}
});
@@ -464,7 +523,7 @@ export default {
`<span style="color:rgba(255,255,255,0.5)" >${item.name || ''}
</span>`,
`<span style="color:rgba(255,255,255,0.5)">${item.code || ''}</span>`,
`<span style="color:rgba(255,255,255,0.5)">${item.status || ''}</span>`,
`<span style="color:rgba(255,255,255,0.5)"><span style="color:rgba(255,255,255,0.5)"></span> ${item.status || ''}</span>`,
`<span style="color:rgba(255,255,255,0.5)">${item.error || ''}</span>`,
])
this.eqConfig.data = eqArr
@@ -489,122 +548,10 @@ export default {
} else if (this.SJGInitWebSocket === 'inspection') {
}
};
// if (typeof (WebSocket) === 'undefined') {
// alert('您的浏览器不支持WebSocket')
// } else {
// let date = new Date().valueOf()
// // console.log(date);
// console.log(process.env);
// const SJGWsUrl = process.env.VUE_APP_Socket_API + `/websocket/message?userId=SJG${date}`
// this.SJGWebsocket = new WebSocket(SJGWsUrl)
// // 监听 WebSocket 连接
// this.SJGWebsocket.onopen = this.SJGWebsocketOnOpen
// // 监听 WebSocket 错误信息
// this.SJGWebsocket.onerror = this.SJGWebsocketOnError
// // 监听 WebSocket 消息
// this.SJGWebsocket.onmessage = this.SJGWebsocketOnMessage
// // 监听 webSocket 断开信息
// this.SJGWebsocket.onclose = this.SJGWebsocketClose
// }
},
// SJGWebsocketOnOpen() {
// console.log('socket连接成功')
// // console.log(this.SJGWebsocket.onmessage);
// this.SJGWebsocket.onmessage()
// },
// // 连接建立失败重连
// SJGWebsocketOnError(e) {
// // console.log('11111', e)
// this.SJGInitWebSocket()
// },
// 数据接收
// SJGWebsocketOnMessage(e) {
// console.log(1111, e)
// this.SJGWsData = e?.data ? JSON.parse(e?.data) : {}
// // console.log(this.wsData.detData);
// // console.log('22222', this.wsData.data)
// if (this.SJGWsData.type === 'order') {
// this.orderList = this.SJGWsData.detData.map((ele, index) => {
// if (ele.progressRate != 1) {
// return {
// id: ele.id,
// name: ele.name,
// progressRate: ele.progressRate.toFixed(3)
// }
// }
// });
// console.log(this.orderList)
// } else if (this.SJGWsData.type === 'equipment') {
// let eqArr = this.SJGWsData.detData.map((item, index) => [
// `<span style="color:rgba(255,255,255,0.5)" >${index + 1 || ''}
// </span>`,
// `<span style="color:rgba(255,255,255,0.5)" >${item.name || ''}
// </span>`,
// `<span style="color:rgba(255,255,255,0.5)">${item.code || ''}</span>`,
// `<span style="color:rgba(255,255,255,0.5)">${item.status || ''}</span>`,
// `<span style="color:rgba(255,255,255,0.5)">${item.error || ''}</span>`,
// ])
// this.eqConfig.data = eqArr
// this.$refs['eqScrollBoard'].updateRows(eqArr)
// // console.log(SJGWsData.orderList)
// } else if (this.SJGWsData.type === 'productline') {
// console.log('aaaaaaaaaaaaaaaaaaa', this.SJGWsData);
// let nameList = []
// let passRateList = []
// let outputNumList = []
// this.SJGWsData.detData.forEach((ele) => {
// // if (item.id == ele.productionLineId) {
// // if (item.name.substr(0, 1) == "D") {
// // console.log(ele)
// nameList.push(ele.lineName)
// outputNumList.push(ele.outputNum)
// passRateList.push(ele.passRate)
// // }
// // }
// })
// this.$refs.productLineChart.initChart(nameList, passRateList, outputNumList)
// } else if (this.SJGInitWebSocket === 'inspection') {
// }
// },
SJGWebsocketClose(e) {
this.SJGws.ws.onclose = (event) => { console.log(event); }
},
// // 连接建立之后执行send方法发送数据
// websocketOnOpen() {
// console.log('socket连接成功')
// this.websocket.onmessage()
// },
// 连接建立失败重连
// websocketOnError(e) {
// console.log('11111', e)
// this.initWebSocket()
// },
// 数据接收
// websocketOnMessage(e) {
// // console.log(1111, e)
// this.wsData = e?.data ? JSON.parse(e?.data) : {}
// // console.log('22222', this.wsData.data)
// if (this.wsData.type === 'EnergyMonitoring') {
// let EnergyNameList = []
// this.wsData.data.forEach((ele) => {
// EnergyNameList.push(ele.lineName)
// })
// this.EnergyMonitoringNameList = EnergyNameList
// let EnergyDataList = []
// this.wsData.data.forEach((ele) => {
// EnergyDataList.push(ele.useQuantity
// )
// })
// this.EnergyMonitoringList = EnergyDataList
// console.log(EnergyDataList)
// this.$refs.EnergyMonitoringChart.initChart(this.EnergyMonitoringNameList, this.EnergyMonitoringList)
// }
// },
// 数据发送
// websocketSend() {
// this.websocket.send('11111')
// },
// // 关闭
websocketClose(e) {
this.wsHeartbeat.ws.onclose = (event) => { console.log(event); }
},
@@ -613,28 +560,32 @@ export default {
this.beilv2 = this.clientWidth / 1920
},
change() {
console.log(this.isFullScreen);
debugger;
this.isFullScreen = screenfull.isFullscreen
},
init() {
if (screenfull.enabled) {
console.log(screenfull.isEnabled);
if (screenfull.isEnabled) {
debugger;
screenfull.on('change', this.change)
}
},
destroy() {
if (screenfull.enabled) {
if (screenfull.isEnabled) {
screenfull.off('change', this.change)
}
},
changeFullScreen() {
this.containerWidth = 223223223
if (!screenfull.enabled) {
// this.containerWidth = 223223223
if (!screenfull.isEnabled) {
this.$message({
message: 'you browser can not work',
type: 'warning'
})
return false
}
screenfull.toggle(this.$refs.container)
screenfull.toggle()
}
}
}
@@ -654,25 +605,24 @@ export default {
background: url('../../assets/img/OperationalOverview/title.png') no-repeat;
background-size: 100% 100%;
color: #00fff0;
// word-spacing: 8px;
word-spacing: 8px;
letter-spacing: 8px;
text-align: center;
.unit {
position: absolute;
left: 290px;
top: 25px;
letter-spacing: 0px;
color: rgba($color: #ffffff, $alpha: 8);
color: rgba(255, 255, 255, 0.80);
font-size: 20px;
letter-spacing: 0px;
}
.time {
position: absolute;
left: 1360px;
color: rgba(255, 255, 255, 0.80);
top: 25px;
letter-spacing: 0px;
color: rgba($color: #ffffff, $alpha: 8);
font-size: 20px;
letter-spacing: 0px;
}
.title-button {
color: #00fff0;
@@ -785,6 +735,9 @@ export default {
margin-left: 3em;
}
}
.order::-webkit-scrollbar {
display: none
}
</style>
<style scoped>

View File

@@ -1,10 +1,3 @@
/*
* @Author: zhp
* @Date: 2024-01-29 17:05:25
* @LastEditTime: 2024-01-29 17:05:25
* @LastEditors: zhp
* @Description:
*/
/**
* 发起websocket请求函数
* @param {string} url ws连接地址
@@ -49,7 +42,7 @@ export function WsConnect(url, agentData, successCallback, errCallback) {
this.lockReconnect = true;
this.wsCreateHandler && clearTimeout(this.wsCreateHandler);
// 关闭心跳检查
// heartCheck.stop();
heartCheck.stop();
}
};
const initWsEventHandle = () => {
@@ -57,13 +50,13 @@ export function WsConnect(url, agentData, successCallback, errCallback) {
// 连接成功
this.wsObj.onopen = (event) => {
onWsOpen(event);
// heartCheck.start();
heartCheck.start();
};
// 监听服务器端返回的信息
this.wsObj.onmessage = (event) => {
onWsMessage(event);
// heartCheck.start();
heartCheck.start();
};
this.wsObj.onclose = (event) => {
@@ -130,7 +123,7 @@ export function WsConnect(url, agentData, successCallback, errCallback) {
if (this.lockReconnect) {
return;
}
writeToScreen("3秒后重连");
writeToScreen("5秒后重连");
this.lockReconnect = true;
// 没连接上会一直重连,设置延迟避免请求过多
this.wsCreateHandler && clearTimeout(this.wsCreateHandler);
@@ -139,10 +132,40 @@ export function WsConnect(url, agentData, successCallback, errCallback) {
this.createWebSoket();
this.lockReconnect = false;
writeToScreen("重连完成");
}, 3000);
}, 5000);
};
// 心跳检查看看websocket是否还在正常连接中
// 心跳检查看看websocket是否还在正常连接中,不需要服务端返回,单向的
let _this = this
let heartCheck = {
timeout: 55000,
timeoutObj: null,
// 重启
reset() {
clearTimeout(this.timeoutObj);
this.start();
},
// 停止
stop() {
clearTimeout(this.timeoutObj);
},
// 开启定时器
start() {
this.timeoutObj && clearTimeout(this.timeoutObj);
this.timeoutObj = setTimeout(() => {
writeToScreen("心跳检查发送ping到后台");
try {
const datas = { ping: true };
_this.wsObj.send(JSON.stringify(datas));
} catch (err) {
writeToScreen("发送ping异常");
}
}, this.timeout);
},
};
// 心跳检查看看websocket是否还在正常连接中,和服务端通信,双向的)
// let heartCheck = {
// timeout: 15000,
// timeoutObj: null,
@@ -167,7 +190,7 @@ export function WsConnect(url, agentData, successCallback, errCallback) {
// writeToScreen("心跳检查发送ping到后台");
// try {
// const datas = { ping: true };
// this.wsObj.send(JSON.stringify(datas));
// _this.wsObj.send(JSON.stringify(datas));
// } catch (err) {
// writeToScreen("发送ping异常");
// }

View File

@@ -1,174 +0,0 @@
<template>
<div class="app-container">
<doc-alert title="工作流" url="https://doc.iocoder.cn/bpm" />
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="定义编号" align="center" prop="id" width="400" />
<el-table-column label="定义名称" align="center" prop="name" width="100">
<template v-slot="scope">
<el-button type="text" @click="handleBpmnDetail(scope.row)">
<span>{{ scope.row.name }}</span>
</el-button>
</template>
</el-table-column>
<el-table-column label="定义分类" align="center" prop="category" width="100">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.BPM_MODEL_CATEGORY" :value="scope.row.category" />
</template>
</el-table-column>
<el-table-column label="表单信息" align="center" prop="formType" width="200">
<template v-slot="scope">
<el-button v-if="scope.row.formId" type="text" @click="handleFormDetail(scope.row)">
<span>{{ scope.row.formName }}</span>
</el-button>
<el-button v-else-if="scope.row.formCustomCreatePath" type="text" @click="handleFormDetail(scope.row)">
<span>{{ scope.row.formCustomCreatePath }}</span>
</el-button>
<label v-else>暂无表单</label>
</template>
</el-table-column>
<el-table-column label="流程版本" align="center" prop="processDefinition.version" width="80">
<template v-slot="scope">
<el-tag size="medium" v-if="scope.row">v{{ scope.row.version }}</el-tag>
<el-tag size="medium" type="warning" v-else>未部署</el-tag>
</template>
</el-table-column>
<el-table-column label="状态" align="center" prop="version" width="80">
<template v-slot="scope">
<el-tag type="success" v-if="scope.row.suspensionState === 1">激活</el-tag>
<el-tag type="warning" v-if="scope.row.suspensionState === 2">挂起</el-tag>
</template>
</el-table-column>
<el-table-column label="部署时间" align="center" prop="deploymentTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.deploymentTime) }}</span>
</template>
</el-table-column>
<el-table-column label="定义描述" align="center" prop="description" width="300" show-overflow-tooltip />
<el-table-column label="操作" align="center" width="150" fixed="right">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-s-custom" @click="handleAssignRule(scope.row)"
v-hasPermi="['bpm:task-assign-rule:update']">分配规则</el-button>
</template>
</el-table-column>
</el-table>
<!-- 流程表单配置详情 -->
<el-dialog title="表单详情" :visible.sync="detailOpen" width="50%" append-to-body>
<parser :key="new Date().getTime()" :form-conf="detailForm" />
</el-dialog>
<!-- 流程模型图的预览 -->
<el-dialog title="流程图" :visible.sync="showBpmnOpen" width="80%" append-to-body>
<my-process-viewer key="designer" v-model="bpmnXML" v-bind="bpmnControlForm" />
</el-dialog>
<!-- 分页组件 -->
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- ========== 流程任务分配规则 ========== -->
<taskAssignRuleDialog ref="taskAssignRuleDialog" />
</div>
</template>
<script>
import {getProcessDefinitionBpmnXML, getProcessDefinitionPage} from "@/api/bpm/definition";
import {DICT_TYPE, getDictDatas} from "@/utils/dict";
import {decodeFields} from "@/utils/formGenerator";
import Parser from '@/components/parser/Parser'
import taskAssignRuleDialog from "../taskAssignRule/taskAssignRuleDialog";
export default {
name: "BpmProcessDefinition",
components: {
Parser,
taskAssignRuleDialog
},
data() {
return {
// 遮罩层
loading: true,
// 总条数
total: 0,
// 表格数据
list: [],
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10
},
// 流程表单详情
detailOpen: false,
detailForm: {
fields: []
},
// BPMN 数据
showBpmnOpen: false,
bpmnXML: null,
bpmnControlForm: {
prefix: "flowable"
},
// 数据字典
categoryDictDatas: getDictDatas(DICT_TYPE.BPM_MODEL_CATEGORY),
};
},
created() {
const key = this.$route.query && this.$route.query.key
if (key) {
this.queryParams['key'] = key
}
this.getList();
},
methods: {
/** 查询流程定义列表 */
getList() {
this.loading = true;
getProcessDefinitionPage(this.queryParams).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
}
);
},
/** 流程表单的详情按钮操作 */
handleFormDetail(row) {
// 流程表单
if (row.formId) {
// 设置值
this.detailForm = {
...JSON.parse(row.formConf),
fields: decodeFields(row.formFields)
}
// 弹窗打开
this.detailOpen = true
// 业务表单
} else if (row.formCustomCreatePath) {
this.$router.push({ path: row.formCustomCreatePath});
}
},
/** 流程图的详情按钮操作 */
handleBpmnDetail(row) {
getProcessDefinitionBpmnXML(row.id).then(response => {
this.bpmnXML = response.data
// 弹窗打开
this.showBpmnOpen = true
})
},
/** 处理任务分配规则列表的按钮操作 */
handleAssignRule(row) {
this.$refs['taskAssignRuleDialog'].initProcessDefinition(row.id);
},
}
};
</script>
<style lang="scss">
.my-process-designer {
height: calc(100vh - 200px);
}
</style>

View File

@@ -1,567 +0,0 @@
<template>
<div class="container">
<div class="left-board">
<div class="logo-wrapper">
<div class="logo">流程表单</div>
</div>
<el-scrollbar class="left-scrollbar">
<!-- 左边表单项 -->
<div class="components-list">
<div v-for="(item, listIndex) in leftComponents" :key="listIndex">
<div class="components-title">
<svg-icon icon-class="component" />
{{ item.title }}
</div>
<draggable
class="components-draggable"
:list="item.list"
:group="{ name: 'componentsGroup', pull: 'clone', put: false }"
:clone="cloneComponent"
draggable=".components-item"
:sort="false"
@end="onEnd"
>
<div
v-for="(element, index) in item.list"
:key="index"
class="components-item"
@click="addComponent(element)"
>
<div class="components-body">
<svg-icon :icon-class="element.__config__.tagIcon" />
{{ element.__config__.label }}
</div>
</div>
</draggable>
</div>
<!-- 左边动态表单 -->
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="表单名" prop="name">
<el-input v-model="form.name" placeholder="请输入表单名" />
</el-form-item>
<el-form-item label="开启状态" prop="status">
<el-radio-group v-model="form.status">
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.COMMON_STATUS)"
:key="dict.value" :label="parseInt(dict.value)">{{dict.label}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input type="textarea" v-model="form.remark" placeholder="请输入备注" />
</el-form-item>
</el-form>
</div>
</el-scrollbar>
</div>
<div class="center-board">
<div class="action-bar">
<el-button icon="el-icon-check" type="text" @click="save">保存</el-button>
<!-- <el-button icon="el-icon-video-play" type="text" @click="run">-->
<!-- 运行-->
<!-- </el-button>-->
<el-button icon="el-icon-view" type="text" @click="showJson">
查看json
</el-button>
<!-- <el-button icon="el-icon-download" type="text" @click="download">-->
<!-- 导出vue文件-->
<!-- </el-button>-->
<!-- <el-button class="copy-btn-main" icon="el-icon-document-copy" type="text" @click="copy">-->
<!-- 复制代码-->
<!-- </el-button>-->
<el-button class="delete-btn" icon="el-icon-delete" type="text" @click="empty">
清空
</el-button>
</div>
<!-- 中间表单项 -->
<el-scrollbar class="center-scrollbar">
<el-row class="center-board-row" :gutter="formConf.gutter">
<el-form
:size="formConf.size"
:label-position="formConf.labelPosition"
:disabled="formConf.disabled"
:label-width="formConf.labelWidth + 'px'"
>
<draggable class="drawing-board" :list="drawingList" :animation="340" group="componentsGroup">
<draggable-item
v-for="(item, index) in drawingList"
:key="item.renderKey"
:drawing-list="drawingList"
:current-item="item"
:index="index"
:active-id="activeId"
:form-conf="formConf"
@activeItem="activeFormItem"
@copyItem="drawingItemCopy"
@deleteItem="drawingItemDelete"
/>
</draggable>
<div v-show="!drawingList.length" class="empty-info">
从左侧拖入或点选组件进行表单设计
</div>
</el-form>
</el-row>
</el-scrollbar>
</div>
<!-- 右边组件属性/表单属性 -->
<right-panel
:active-data="activeData"
:form-conf="formConf"
:show-field="!!drawingList.length"
@tag-change="tagChange"
@fetch-data="fetchData"
/>
<!-- <form-drawer-->
<!-- :visible.sync="drawerVisible"-->
<!-- :form-data="formData"-->
<!-- size="100%"-->
<!-- :generate-conf="generateConf"-->
<!-- />-->
<json-drawer
size="60%"
:visible.sync="jsonDrawerVisible"
:json-str="JSON.stringify(formData)"
@refresh="refreshJson"
/>
<!-- <code-type-dialog-->
<!-- :visible.sync="dialogVisible"-->
<!-- title="选择生成类型"-->
<!-- :show-file-name="showFileName"-->
<!-- @confirm="generate"-->
<!-- />-->
<!-- <input id="copyNode" type="hidden">-->
</div>
</template>
<script>
import draggable from 'vuedraggable'
import { debounce } from 'throttle-debounce'
import { saveAs } from 'file-saver'
import ClipboardJS from 'clipboard'
import render from '@/components/render/render'
import FormDrawer from '@/views/infra/build/FormDrawer'
import JsonDrawer from '@/views/infra/build/JsonDrawer'
import RightPanel from '@/views/infra/build/RightPanel'
import {
inputComponents, selectComponents, layoutComponents, formConf
} from '@/components/generator/config'
import {beautifierConf, titleCase, deepClone, isObjectObject} from '@/utils'
import {
makeUpHtml, vueTemplate, vueScript, cssStyle
} from '@/components/generator/html'
import { makeUpJs } from '@/components/generator/js'
import { makeUpCss } from '@/components/generator/css'
import drawingDefalut from '@/components/generator/drawingDefalut'
import logo from '@/assets/logo/logo.png'
import CodeTypeDialog from '@/views/infra/build/CodeTypeDialog'
import DraggableItem from '@/views/infra/build/DraggableItem'
import {
getDrawingList, saveDrawingList, getIdGlobal, saveIdGlobal, getFormConf
} from '@/utils/db'
import loadBeautifier from '@/utils/loadBeautifier'
import {CommonStatusEnum} from "@/utils/constants";
import {createForm, getForm, updateForm} from "@/api/bpm/form";
import {decodeFields} from "@/utils/formGenerator";
let beautifier
const emptyActiveData = { style: {}, autosize: {} }
let oldActiveId
let tempActiveData
const drawingListInDB = getDrawingList()
const formConfInDB = getFormConf()
const idGlobal = getIdGlobal()
export default {
components: {
draggable,
render,
FormDrawer,
JsonDrawer,
RightPanel,
CodeTypeDialog,
DraggableItem
},
data() {
return {
logo,
idGlobal,
formConf,
inputComponents,
selectComponents,
layoutComponents,
labelWidth: 100,
// drawingList: drawingDefalut,
drawingData: {}, // 生成后的表单数据
activeId: drawingDefalut[0].__config__.formId,
drawingList: [], // 表单项的数组
// activeId: undefined,
// activeData: {},
drawerVisible: false,
formData: {},
dialogVisible: false,
jsonDrawerVisible: false,
generateConf: null,
showFileName: false,
activeData: drawingDefalut[0], // 右边编辑器激活的表单项
saveDrawingListDebounce: debounce(340, saveDrawingList),
saveIdGlobalDebounce: debounce(340, saveIdGlobal),
leftComponents: [
{
title: '输入型组件',
list: inputComponents
},
{
title: '选择型组件',
list: selectComponents
},
{
title: '布局型组件',
list: layoutComponents
}
],
// 表单参数
form: {
status: CommonStatusEnum.ENABLE,
},
// 表单校验
rules: {
name: [{ required: true, message: "表单名不能为空", trigger: "blur" }],
status: [{ required: true, message: "开启状态不能为空", trigger: "blur" }],
}
}
},
computed: {
},
watch: {
// eslint-disable-next-line func-names
'activeData.__config__.label': function (val, oldVal) {
if (
this.activeData.placeholder === undefined
|| !this.activeData.__config__.tag
|| oldActiveId !== this.activeId
) {
return
}
this.activeData.placeholder = this.activeData.placeholder.replace(oldVal, '') + val
},
activeId: {
handler(val) {
oldActiveId = val
},
immediate: true
},
drawingList: {
handler(val) {
this.saveDrawingListDebounce(val)
if (val.length === 0) this.idGlobal = 100
},
deep: true
},
idGlobal: {
handler(val) {
this.saveIdGlobalDebounce(val)
},
immediate: true
}
},
mounted() {
// 【add by 芋道源码】不读缓存
// if (Array.isArray(drawingListInDB) && drawingListInDB.length > 0) {
// this.drawingList = drawingListInDB
// } else {
// this.drawingList = drawingDefalut
// }
// this.activeFormItem(this.drawingList[0])
// if (formConfInDB) {
// this.formConf = formConfInDB
// }
loadBeautifier(btf => {
beautifier = btf
})
const clipboard = new ClipboardJS('#copyNode', {
text: trigger => {
const codeStr = this.generateCode()
this.$notify({
title: '成功',
message: '代码已复制到剪切板,可粘贴。',
type: 'success'
})
return codeStr
}
})
clipboard.on('error', e => {
this.$message.error('代码复制失败')
})
},
created() {
// 读取表单配置
const formId = this.$route.query && this.$route.query.formId
if (formId) {
getForm(formId).then(response => {
const data = response.data
this.form = {
id: data.id,
name: data.name,
status: data.status,
remark: data.remark
}
this.formConf = JSON.parse(data.conf)
this.drawingList = decodeFields(data.fields)
// 设置激活的表单项
this.activeData = this.drawingList[0]
this.activeId = this.activeData.__config__.formId
// 设置 idGlobal避免重复
this.idGlobal += this.drawingList.length
});
}
},
methods: {
setObjectValueReduce(obj, strKeys, data) {
const arr = strKeys.split('.')
arr.reduce((pre, item, i) => {
if (arr.length === i + 1) {
pre[item] = data
} else if (!isObjectObject(pre[item])) {
pre[item] = {}
}
return pre[item]
}, obj)
},
setRespData(component, resp) {
const { dataPath, renderKey, dataConsumer } = component.__config__
if (!dataPath || !dataConsumer) return
const respData = dataPath.split('.').reduce((pre, item) => pre[item], resp)
// 将请求回来的数据,赋值到指定属性。
// 以el-tabel为例根据Element文档应该将数据赋值给el-tabel的data属性所以dataConsumer的值应为'data';
// 此时赋值代码可写成 component[dataConsumer] = respData
// 但为支持更深层级的赋值dataConsumer的值为'options.data',使用setObjectValueReduce
this.setObjectValueReduce(component, dataConsumer, respData)
const i = this.drawingList.findIndex(item => item.__config__.renderKey === renderKey)
if (i > -1) this.$set(this.drawingList, i, component)
},
fetchData(component) {
const { dataType, method, url } = component.__config__
if (dataType === 'dynamic' && method && url) {
this.setLoading(component, true)
this.$axios({
method,
url
}).then(resp => {
this.setLoading(component, false)
this.setRespData(component, resp.data)
})
}
},
setLoading(component, val) {
const { directives } = component
if (Array.isArray(directives)) {
const t = directives.find(d => d.name === 'loading')
if (t) t.value = val
}
},
activeFormItem(currentItem) {
this.activeData = currentItem
this.activeId = currentItem.__config__.formId
},
onEnd(obj) {
if (obj.from !== obj.to) {
this.fetchData(tempActiveData)
this.activeData = tempActiveData
this.activeId = this.idGlobal
}
},
addComponent(item) {
const clone = this.cloneComponent(item)
this.fetchData(clone)
this.drawingList.push(clone)
this.activeFormItem(clone)
},
cloneComponent(origin) {
const clone = deepClone(origin)
const config = clone.__config__
config.span = this.formConf.span // 生成代码时会根据span做精简判断
this.createIdAndKey(clone)
clone.placeholder !== undefined && (clone.placeholder += config.label)
tempActiveData = clone
return tempActiveData
},
createIdAndKey(item) {
const config = item.__config__
config.formId = ++this.idGlobal
config.renderKey = `${config.formId}${+new Date()}` // 改变renderKey后可以实现强制更新组件
if (config.layout === 'colFormItem') {
item.__vModel__ = `field${this.idGlobal}`
} else if (config.layout === 'rowFormItem') {
config.componentName = `row${this.idGlobal}`
!Array.isArray(config.children) && (config.children = [])
delete config.label // rowFormItem无需配置label属性
}
if (Array.isArray(config.children)) {
config.children = config.children.map(childItem => this.createIdAndKey(childItem))
}
return item
},
// 获得表单数据
AssembleFormData() {
this.formData = {
fields: deepClone(this.drawingList),
...this.formConf
}
},
save() {
// this.AssembleFormData()
// console.log(this.formData)
this.$refs["form"].validate(valid => {
if (!valid) {
return;
}
const form = {
conf: JSON.stringify(this.formConf), // 表单配置
fields: this.encodeFields(), // 表单项的数组
...this.form // 表单名等
}
// 修改的提交
if (this.form.id != null) {
updateForm(form).then(response => {
this.$modal.msgSuccess("修改成功");
this.close()
});
return;
}
// 添加的提交
createForm(form).then(response => {
this.$modal.msgSuccess("新增成功");
this.close()
});
});
},
/** 关闭按钮 */
close() {
this.$tab.closeOpenPage({ path: "/bpm/manager/form" });
},
encodeFields() {
const fields = []
this.drawingList.forEach(item => {
fields.push(JSON.stringify(item))
})
return fields
},
generate(data) {
const func = this[`exec${titleCase(this.operationType)}`]
this.generateConf = data
func && func(data)
},
execRun(data) {
this.AssembleFormData()
this.drawerVisible = true
},
execDownload(data) {
const codeStr = this.generateCode()
const blob = new Blob([codeStr], { type: 'text/plain;charset=utf-8' })
saveAs(blob, data.fileName)
},
execCopy(data) {
document.getElementById('copyNode').click()
},
empty() {
this.$confirm('确定要清空所有组件吗?', '提示', { type: 'warning' }).then(
() => {
this.drawingList = []
this.idGlobal = 100
}
)
},
drawingItemCopy(item, list) {
let clone = deepClone(item)
clone = this.createIdAndKey(clone)
list.push(clone)
this.activeFormItem(clone)
},
drawingItemDelete(index, list) {
list.splice(index, 1)
this.$nextTick(() => {
const len = this.drawingList.length
if (len) {
this.activeFormItem(this.drawingList[len - 1])
}
})
},
generateCode() {
const { type } = this.generateConf
this.AssembleFormData()
const script = vueScript(makeUpJs(this.formData, type))
const html = vueTemplate(makeUpHtml(this.formData, type))
const css = cssStyle(makeUpCss(this.formData))
return beautifier.html(html + script + css, beautifierConf.html)
},
showJson() {
this.AssembleFormData()
this.jsonDrawerVisible = true
},
download() {
this.dialogVisible = true
this.showFileName = true
this.operationType = 'download'
},
run() {
this.dialogVisible = true
this.showFileName = false
this.operationType = 'run'
},
copy() {
this.dialogVisible = true
this.showFileName = false
this.operationType = 'copy'
},
tagChange(newTag) {
newTag = this.cloneComponent(newTag)
const config = newTag.__config__
newTag.__vModel__ = this.activeData.__vModel__
config.formId = this.activeId
config.span = this.activeData.__config__.span
this.activeData.__config__.tag = config.tag
this.activeData.__config__.tagIcon = config.tagIcon
this.activeData.__config__.document = config.document
if (typeof this.activeData.__config__.defaultValue === typeof config.defaultValue) {
config.defaultValue = this.activeData.__config__.defaultValue
}
Object.keys(newTag).forEach(key => {
if (this.activeData[key] !== undefined) {
newTag[key] = this.activeData[key]
}
})
this.activeData = newTag
this.updateDrawingList(newTag, this.drawingList)
},
updateDrawingList(newTag, list) {
const index = list.findIndex(item => item.__config__.formId === this.activeId)
if (index > -1) {
list.splice(index, 1, newTag)
} else {
list.forEach(item => {
if (Array.isArray(item.__config__.children)) this.updateDrawingList(newTag, item.__config__.children)
})
}
},
refreshJson(data) {
this.drawingList = deepClone(data.fields)
delete data.fields
this.formConf = data
}
}
}
</script>
<style lang='scss'>
@import '@/styles/home';
</style>

View File

@@ -1,161 +0,0 @@
<template>
<div class="app-container">
<doc-alert title="工作流" url="https://doc.iocoder.cn/bpm" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="表单名" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入表单名" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
v-hasPermi="['bpm:form:create']">新增</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="编号" align="center" prop="id" />
<el-table-column label="表单名" align="center" prop="name" />
<el-table-column label="开启状态" align="center" prop="status">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status"/>
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleDetail(scope.row)"
v-hasPermi="['bpm:form:query']">详情</el-button>
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['bpm:form:update']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['bpm:form:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!--表单配置详情-->
<el-dialog title="表单详情" :visible.sync="detailOpen" width="50%" append-to-body>
<div class="test-form">
<parser :key="new Date().getTime()" :form-conf="detailForm" />
</div>
</el-dialog>
</div>
</template>
<script>
import {deleteForm, getForm, getFormPage} from "@/api/bpm/form";
import Parser from '@/components/parser/Parser'
import {decodeFields} from "@/utils/formGenerator";
export default {
name: "BpmForm",
components: {
Parser
},
data() {
return {
// 遮罩层
loading: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 工作流的列表
list: [],
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
name: null,
},
// 表单详情
detailOpen: false,
detailForm: {
fields: []
}
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
// 执行查询
getFormPage(this.queryParams).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 详情按钮操作 */
handleDetail(row) {
getForm(row.id).then(response => {
// 设置值
const data = response.data
this.detailForm = {
...JSON.parse(data.conf),
fields: decodeFields(data.fields)
}
// 弹窗打开
this.detailOpen = true
})
},
/** 新增按钮操作 */
handleAdd() {
this.$router.push({
name: "BpmFormEditor"
});
},
/** 修改按钮操作 */
handleUpdate(row) {
this.$router.push({
name: "BpmFormEditor",
query:{
formId: row.id
}
});
},
/** 删除按钮操作 */
handleDelete(row) {
const id = row.id;
this.$modal.confirm('是否确认删除工作表单的编号为"' + id + '"的数据项?').then(function() {
return deleteForm(id);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
}
}
};
</script>

View File

@@ -1,246 +0,0 @@
<template>
<div class="app-container">
<doc-alert title="工作流" url="https://doc.iocoder.cn/bpm" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="组名" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入组名" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable>
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.COMMON_STATUS)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
v-hasPermi="['bpm:user-group:create']">新增</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="编号" align="center" prop="id" />
<el-table-column label="组名" align="center" prop="name" />
<el-table-column label="描述" align="center" prop="description" />
<el-table-column label="成员" align="center">
<template v-slot="scope">
<span v-for="userId in scope.row.memberUserIds">
{{ getUserNickname(userId) }}
</span>
</template>
</el-table-column>
<el-table-column label="状态" align="center" prop="status">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['bpm:user-group:update']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['bpm:user-group:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="组名" prop="name">
<el-input v-model="form.name" placeholder="请输入组名" />
</el-form-item>
<el-form-item label="描述" prop="description">
<el-input v-model="form.description" placeholder="请输入描述" />
</el-form-item>
<el-form-item label="成员" prop="memberUserIds">
<el-select v-model="form.memberUserIds" multiple placeholder="请选择成员">
<el-option v-for="user in users" :key="parseInt(user.id)" :label="user.nickname" :value="parseInt(user.id)"/>
</el-select>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-radio-group v-model="form.status">
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.COMMON_STATUS)"
:key="dict.value" :label="parseInt(dict.value)">{{dict.label}}</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { createUserGroup, updateUserGroup, deleteUserGroup, getUserGroup, getUserGroupPage } from "@/api/bpm/userGroup";
import {CommonStatusEnum} from "@/utils/constants";
import {listSimpleUsers} from "@/api/system/user";
export default {
name: "BpmUserGroup",
components: {
},
data() {
return {
// 遮罩层
loading: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 用户组列表
list: [],
// 用户列表
users: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
name: null,
status: null,
createTime: []
},
// 表单参数
form: {},
// 表单校验
rules: {
name: [{ required: true, message: "组名不能为空", trigger: "blur" }],
description: [{ required: true, message: "描述不能为空", trigger: "blur" }],
memberUserIds: [{ required: true, message: "成员不能为空", trigger: "change" }],
status: [{ required: true, message: "状态不能为空", trigger: "blur" }],
}
};
},
created() {
this.getList();
// 获得用户列表
listSimpleUsers().then(response => {
this.users = response.data;
})
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
// 执行查询
getUserGroupPage(this.queryParams).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 取消按钮 */
cancel() {
this.open = false;
this.reset();
},
/** 表单重置 */
reset() {
this.form = {
id: undefined,
name: undefined,
description: undefined,
memberUserIds: [],
status: CommonStatusEnum.ENABLE,
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加用户组";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const id = row.id;
getUserGroup(id).then(response => {
this.form = response.data;
this.open = true;
this.title = "修改用户组";
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (!valid) {
return;
}
// 修改的提交
if (this.form.id != null) {
updateUserGroup(this.form).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
return;
}
// 添加的提交
createUserGroup(this.form).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
});
},
/** 删除按钮操作 */
handleDelete(row) {
const id = row.id;
this.$modal.confirm('是否确认删除用户组编号为"' + id + '"的数据项?').then(function() {
return deleteUserGroup(id);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
},
getUserNickname(userId) {
for (const user of this.users) {
if (user.id === userId) {
return user.nickname;
}
}
return '未知(' + userId + ')';
},
}
};
</script>

View File

@@ -1,548 +0,0 @@
<template>
<div class="app-container">
<doc-alert title="工作流" url="https://doc.iocoder.cn/bpm" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="流程标识" prop="key">
<el-input v-model="queryParams.key" placeholder="请输入流程标识" clearable style="width: 240px;"
@keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="流程名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入流程名称" clearable style="width: 240px;"
@keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="流程分类" prop="category">
<el-select v-model="queryParams.category" placeholder="流程分类" clearable style="width: 240px">
<el-option v-for="dict in categoryDictDatas" :key="parseInt(dict.value)" :label="dict.label" :value="parseInt(dict.value)"/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
v-hasPermi="['bpm:model:create']">新建流程</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="info" icon="el-icon-upload2" size="mini" @click="handleImport"
v-hasPermi="['bpm:model:import']">导入流程</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="流程标识" align="center" prop="key" />
<el-table-column label="流程名称" align="center" prop="name" width="200">
<template v-slot="scope">
<el-button type="text" @click="handleBpmnDetail(scope.row)">
<span>{{ scope.row.name }}</span>
</el-button>
</template>
</el-table-column>
<el-table-column label="流程分类" align="center" prop="category" width="100">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.BPM_MODEL_CATEGORY" :value="scope.row.category" />
</template>
</el-table-column>
<el-table-column label="表单信息" align="center" prop="formType" width="200">
<template v-slot="scope">
<el-button v-if="scope.row.formId" type="text" @click="handleFormDetail(scope.row)">
<span>{{ scope.row.formName }}</span>
</el-button>
<el-button v-else-if="scope.row.formCustomCreatePath" type="text" @click="handleFormDetail(scope.row)">
<span>{{ scope.row.formCustomCreatePath }}</span>
</el-button>
<label v-else>暂无表单</label>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="最新部署的流程定义" align="center">
<el-table-column label="流程版本" align="center" prop="processDefinition.version" width="80">
<template v-slot="scope">
<el-tag size="medium" v-if="scope.row.processDefinition">v{{ scope.row.processDefinition.version }}</el-tag>
<el-tag size="medium" type="warning" v-else>未部署</el-tag>
</template>
</el-table-column>
<el-table-column label="激活状态" align="center" prop="processDefinition.version" width="80">
<template v-slot="scope">
<el-switch v-if="scope.row.processDefinition" v-model="scope.row.processDefinition.suspensionState"
:active-value="1" :inactive-value="2" @change="handleChangeState(scope.row)" />
</template>
</el-table-column>
<el-table-column label="部署时间" align="center" prop="deploymentTime" width="180">
<template v-slot="scope">
<span v-if="scope.row.processDefinition">{{ parseTime(scope.row.processDefinition.deploymentTime) }}</span>
</template>
</el-table-column>
</el-table-column>
<el-table-column label="操作" align="center" width="450" fixed="right">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['bpm:model:update']">修改流程</el-button>
<el-button size="mini" type="text" icon="el-icon-setting" @click="handleDesign(scope.row)"
v-hasPermi="['bpm:model:update']">设计流程</el-button>
<el-button size="mini" type="text" icon="el-icon-s-custom" @click="handleAssignRule(scope.row)"
v-hasPermi="['bpm:task-assign-rule:query']">分配规则</el-button>
<el-button size="mini" type="text" icon="el-icon-thumb" @click="handleDeploy(scope.row)"
v-hasPermi="['bpm:model:deploy']">发布流程</el-button>
<el-button size="mini" type="text" icon="el-icon-ice-cream-round" @click="handleDefinitionList(scope.row)"
v-hasPermi="['bpm:process-definition:query']">流程定义</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['bpm:model:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- 流程表单配置详情 -->
<el-dialog title="表单详情" :visible.sync="detailOpen" width="50%" append-to-body>
<parser :key="new Date().getTime()" :form-conf="detailForm" />
</el-dialog>
<!-- 流程模型图的预览 -->
<el-dialog title="流程图" :visible.sync="showBpmnOpen" width="80%" append-to-body>
<my-process-viewer key="designer" v-model="bpmnXML" v-bind="bpmnControlForm" />
</el-dialog>
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="110px">
<el-form-item label="流程标识" prop="key">
<el-input v-model="form.key" placeholder="请输入流标标识" style="width: 330px;" :disabled="!!form.id" />
<el-tooltip v-if="!form.id" class="item" effect="light" content="新建后,流程标识不可修改!" placement="top">
<i style="padding-left: 5px;" class="el-icon-question" />
</el-tooltip>
<el-tooltip v-else class="item" effect="light" content="流程标识不可修改!" placement="top">
<i style="padding-left: 5px;" class="el-icon-question" />
</el-tooltip>
</el-form-item>
<el-form-item label="流程名称" prop="name">
<el-input v-model="form.name" placeholder="请输入流程名称" :disabled="!!form.id" clearable />
</el-form-item>
<el-form-item v-if="form.id" label="流程分类" prop="category">
<el-select v-model="form.category" placeholder="请选择流程分类" clearable style="width: 100%">
<el-option v-for="dict in categoryDictDatas" :key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="流程描述" prop="description">
<el-input type="textarea" v-model="form.description" clearable />
</el-form-item>
<div v-if="form.id">
<el-form-item label="表单类型" prop="formType">
<el-radio-group v-model="form.formType">
<el-radio v-for="dict in modelFormTypeDictDatas" :key="parseInt(dict.value)" :label="parseInt(dict.value)">
{{dict.label}}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="form.formType === 10" label="流程表单" prop="formId">
<el-select v-model="form.formId" clearable style="width: 100%">
<el-option v-for="form in forms" :key="form.id" :label="form.name" :value="form.id"/>
</el-select>
</el-form-item>
<el-form-item v-if="form.formType === 20" label="表单提交路由" prop="formCustomCreatePath" >
<el-input v-model="form.formCustomCreatePath" placeholder="请输入表单提交路由" style="width: 330px;" />
<el-tooltip class="item" effect="light" content="自定义表单的提交路径,使用 Vue 的路由地址例如说bpm/oa/leave/create" placement="top">
<i style="padding-left: 5px;" class="el-icon-question" />
</el-tooltip>
</el-form-item>
<el-form-item v-if="form.formType === 20" label="表单查看路由" prop="formCustomViewPath">
<el-input v-model="form.formCustomViewPath" placeholder="请输入表单查看路由" style="width: 330px;" />
<el-tooltip class="item" effect="light" content="自定义表单的查看路径,使用 Vue 的路由地址例如说bpm/oa/leave/view" placement="top">
<i style="padding-left: 5px;" class="el-icon-question" />
</el-tooltip>
</el-form-item>
</div>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
<!-- 用户导入对话框 -->
<el-dialog title="导入流程" :visible.sync="upload.open" width="400px" append-to-body>
<el-upload ref="upload" :limit="1" accept=".bpmn, .xml" :headers="upload.headers" :action="upload.url"
:disabled="upload.isUploading" :on-progress="handleFileUploadProgress" :on-success="handleFileSuccess"
:auto-upload="false" name="bpmnFile" :data="upload.form" drag>
<i class="el-icon-upload"></i>
<div class="el-upload__text">
将文件拖到此处
<em>点击上传</em>
</div>
<div class="el-upload__tip" style="color:red" slot="tip">提示仅允许导入bpmxml格式文件</div>
<div class="el-upload__tip" slot="tip">
<el-form ref="uploadForm" size="mini" label-width="90px" :model="upload.form" :rules="upload.rules" @submit.native.prevent>
<el-form-item label="流程标识" prop="key">
<el-input v-model="upload.form.key" placeholder="请输入流标标识" style="width: 250px;" />
<el-tooltip class="item" effect="light" content="新建后,流程标识不可修改!" placement="top">
<i style="padding-left: 5px;" class="el-icon-question" />
</el-tooltip>
</el-form-item>
<el-form-item label="流程名称" prop="name">
<el-input v-model="upload.form.name" placeholder="请输入流程名称" clearable />
</el-form-item>
<el-form-item label="流程描述" prop="description">
<el-input type="textarea" v-model="upload.form.description" clearable />
</el-form-item>
</el-form>
</div>
</el-upload>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitFileForm"> </el-button>
<el-button @click="uploadClose"> </el-button>
</div>
</el-dialog>
<!-- ========== 流程任务分配规则 ========== -->
<taskAssignRuleDialog ref="taskAssignRuleDialog" />
</div>
</template>
<script>
import {
deleteModel,
deployModel,
getModelPage,
getModel,
updateModelState,
createModel,
updateModel
} from "@/api/bpm/model";
import {DICT_TYPE, getDictDatas} from "@/utils/dict";
import {getForm, getSimpleForms} from "@/api/bpm/form";
import {decodeFields} from "@/utils/formGenerator";
import Parser from '@/components/parser/Parser'
import {getBaseHeader} from "@/utils/request";
import taskAssignRuleDialog from "../taskAssignRule/taskAssignRuleDialog";
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
export default {
name: "BpmModel",
components: {
Parser,
Treeselect,
taskAssignRuleDialog
},
data() {
return {
// 遮罩层
loading: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 表格数据
list: [],
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10
},
// BPMN 数据
showBpmnOpen: false,
bpmnXML: null,
bpmnControlForm: {
prefix: "flowable"
},
// 流程表单详情
detailOpen: false,
detailForm: {
fields: []
},
// 流程表单
title: "",
open: false,
form: {},
// 表单校验
rules: {
key: [{ required: true, message: "流程标识不能为空", trigger: "blur" }],
name: [{ required: true, message: "流程名称不能为空", trigger: "blur" }],
formType: [{ required: true, message: "流程名称不能为空", trigger: "blur" }],
formId: [{ required: true, message: "业务表单不能为空", trigger: "blur" }],
category: [{ required: true, message: "流程分类不能为空", trigger: "blur" }],
formCustomCreatePath: [{ required: true, message: "表单提交路由不能为空", trigger: "blur" }],
formCustomViewPath: [{ required: true, message: "表单查看路由不能为空", trigger: "blur" }],
},
// 流程导入参数
upload: {
// 是否显示弹出层(用户导入)
open: false,
// 是否禁用上传
isUploading: false,
// 设置上传的请求头部
headers: getBaseHeader(),
// 上传的地址
url: process.env.VUE_APP_BASE_API + '/admin-api' + "/bpm/model/import",
// 表单
form: {},
// 校验规则
rules: {
key: [{ required: true, message: "流程标识不能为空", trigger: "blur" }],
name: [{ required: true, message: "流程名称不能为空", trigger: "blur" }],
},
},
// 流程表单的下拉框的数据
forms: [],
// 数据字典
categoryDictDatas: getDictDatas(DICT_TYPE.BPM_MODEL_CATEGORY),
modelFormTypeDictDatas: getDictDatas(DICT_TYPE.BPM_MODEL_FORM_TYPE),
taskAssignRuleDictDatas: getDictDatas(DICT_TYPE.BPM_TASK_ASSIGN_RULE_TYPE),
};
},
created() {
this.getList();
// 获得流程表单的下拉框的数据
getSimpleForms().then(response => {
this.forms = response.data
})
},
methods: {
/** 查询流程模型列表 */
getList() {
this.loading = true;
getModelPage(this.queryParams).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
}
);
},
/** 取消按钮 */
cancel() {
this.open = false;
this.reset();
},
// 表单重置
reset() {
this.form = {
id: undefined,
key: undefined,
name: undefined,
description: undefined,
category: undefined,
formType: undefined,
formId: undefined,
formCustomCreatePath: undefined,
formCustomViewPath: undefined
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.dateRange = [];
this.resetForm("queryForm");
this.handleQuery();
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.title = "新建模型";
this.open = true;
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
this.title = "修改模型";
this.open = true;
// 设置 form
this.form = {
...row
};
// 触发一次校验
// this.$refs["form"].validate();
},
/** 设计按钮操作 */
handleDesign(row) {
this.$router.push({
name: "BpmModelEditor",
query:{
modelId: row.id
}
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (!valid) {
return;
}
// 更新
if (this.form.id) {
updateModel({
...this.form,
formId: this.form.formType === 10 ? this.form.formId : undefined,
formCustomCreatePath: this.form.formType === 20 ? this.form.formCustomCreatePath : undefined,
formCustomViewPath: this.form.formType === 20 ? this.form.formCustomViewPath : undefined,
}).then(response => {
this.$modal.msgSuccess("修改模型成功");
this.open = false;
this.getList();
});
return;
}
// 创建
createModel(this.form).then(response => {
this.open = false;
this.getList();
this.$alert('<strong>新建模型成功!</strong>后续需要执行如下 4 个步骤:' +
'<div>1. 点击【修改流程】按钮,配置流程的分类、表单信息</div>' +
'<div>2. 点击【设计流程】按钮,绘制流程图</div>' +
'<div>3. 点击【分配规则】按钮,设置每个用户任务的审批人</div>' +
'<div>4. 点击【发布流程】按钮,完成流程的最终发布</div>' +
'另外,每次流程修改后,都需要点击【发布流程】按钮,才能正式生效!!!',
'重要提示', {
dangerouslyUseHTMLString: true,
type: 'success'
});
});
});
},
/** 删除按钮操作 */
handleDelete(row) {
const that = this;
this.$modal.confirm('是否删除该流程!!').then(function() {
deleteModel(row.id).then(response => {
that.getList();
that.$modal.msgSuccess("删除成功");
})
}).catch(() => {});
},
/** 部署按钮操作 */
handleDeploy(row) {
const that = this;
this.$modal.confirm('是否部署该流程!!').then(function() {
deployModel(row.id).then(response => {
that.getList();
that.$modal.msgSuccess("部署成功");
})
}).catch(() => {});
},
/** 流程表单的详情按钮操作 */
handleFormDetail(row) {
// 流程表单
if (row.formId) {
getForm(row.formId).then(response => {
// 设置值
const data = response.data
this.detailForm = {
...JSON.parse(data.conf),
fields: decodeFields(data.fields)
}
// 弹窗打开
this.detailOpen = true
})
// 业务表单
} else if (row.formCustomCreatePath) {
this.$router.push({ path: row.formCustomCreatePath});
}
},
/** 流程图的详情按钮操作 */
handleBpmnDetail(row) {
getModel(row.id).then(response => {
this.bpmnXML = response.data.bpmnXml
// 弹窗打开
this.showBpmnOpen = true
})
},
/** 跳转流程定义的列表 */
handleDefinitionList(row) {
this.$router.push({
name: "BpmProcessDefinition",
query:{
key: row.key
}
});
},
/** 更新状态操作 */
handleChangeState(row) {
const id = row.id;
let state = row.processDefinition.suspensionState;
let statusState = state === 1 ? '激活' : '挂起';
this.$modal.confirm('是否确认' + statusState + '流程名字为"' + row.name + '"的数据项?').then(function() {
return updateModelState(id, state);
}).then(() => {
this.getList();
this.$modal.msgSuccess(statusState + "成功");
}).catch(() => {
// 取消后,进行恢复按钮
row.processDefinition.suspensionState = (state === 1 ? 2 : 1);
});
},
/** 导入按钮操作 */
handleImport() {
this.upload.open = true;
},
// 文件上传中处理
handleFileUploadProgress(event, file, fileList) {
this.upload.isUploading = true;
},
// 文件上传成功处理
handleFileSuccess(response, file, fileList) {
if (response.code !== 0) {
this.$modal.msgError(response.msg)
return;
}
// 重置表单
this.uploadClose();
// 提示,并刷新
this.$modal.msgSuccess("导入流程成功!请点击【设计流程】按钮,进行编辑保存后,才可以进行【发布流程】");
this.getList();
},
uploadClose() {
// 关闭弹窗
this.upload.open = false;
// 重置上传状态和文件
this.upload.isUploading = false;
this.$refs.upload.clearFiles();
// 重置表单
this.upload.form = {};
this.resetForm("uploadForm");
},
/** 提交上传文件 */
submitFileForm() {
this.$refs["uploadForm"].validate(valid => {
if (!valid) {
return;
}
this.$refs.upload.submit();
})
},
/** 处理任务分配规则列表的按钮操作 */
handleAssignRule(row) {
this.$refs['taskAssignRuleDialog'].initModel(row.id);
},
}
};
</script>
<style lang="scss">
.my-process-designer {
height: calc(100vh - 200px);
}
</style>

View File

@@ -1,170 +0,0 @@
<template>
<div class="app-container">
<!-- 流程设计器负责绘制流程等 -->
<my-process-designer v-if="xmlString !== undefined" :key="`designer-${reloadIndex}`" v-model="xmlString" v-bind="controlForm"
keyboard ref="processDesigner" @init-finished="initModeler"
@save="save"/>
<!-- 流程属性器负责编辑每个流程节点的属性 -->
<my-properties-panel :key="`penal-${reloadIndex}`" :bpmn-modeler="modeler" :prefix="controlForm.prefix" class="process-panel"
:model="model" />
</div>
</template>
<script>
import translations from "@/components/bpmnProcessDesigner/src/translations";
// 自定义元素选中时的弹出菜单(修改 默认任务 为 用户任务)
import CustomContentPadProvider from "@/components/bpmnProcessDesigner/package/designer/plugins/content-pad";
// 自定义左侧菜单(修改 默认任务 为 用户任务)
import CustomPaletteProvider from "@/components/bpmnProcessDesigner/package/designer/plugins/palette";
// import xmlObj2json from "./utils/xml2json";
import MyProcessPalette from "@/components/bpmnProcessDesigner/package/palette/ProcessPalette";
import {createModel, getModel, updateModel} from "@/api/bpm/model";
// 自定义侧边栏
// import MyProcessPanel from "../package/process-panel/ProcessPanel";
export default {
name: "BpmModelEditor",
components: { MyProcessPalette },
data() {
return {
xmlString: undefined, // BPMN XML
modeler: null,
reloadIndex: 0,
controlDrawerVisible: false,
translationsSelf: translations,
controlForm: {
simulation: true,
labelEditing: false,
labelVisible: false,
prefix: "flowable",
headerButtonSize: "mini",
additionalModel: [CustomContentPadProvider, CustomPaletteProvider]
},
addis: {
CustomContentPadProvider,
CustomPaletteProvider
},
// 流程模型的信息
model: {},
};
},
created() {
// 如果 modelId 非空,说明是修改流程模型
const modelId = this.$route.query && this.$route.query.modelId
if (modelId) {
getModel(modelId).then(response => {
this.xmlString = response.data.bpmnXml
this.model = {
...response.data,
bpmnXml: undefined, // 清空 bpmnXml 属性
}
// this.controlForm.processId = response.data.key
})
}
},
methods: {
initModeler(modeler) {
setTimeout(() => {
this.modeler = modeler;
console.log(modeler);
}, 10);
},
save(bpmnXml) {
const data = {
...this.model,
bpmnXml: bpmnXml, // this.bpmnXml 只是初始化流程图,后续修改无法通过它获得
}
// 修改的提交
if (data.id) {
updateModel(data).then(response => {
this.$modal.msgSuccess("修改成功")
// 跳转回去
this.close()
})
return
}
// 添加的提交
createModel(data).then(response => {
this.$modal.msgSuccess("保存成功")
// 跳转回去
this.close()
})
},
/** 关闭按钮 */
close() {
this.$tab.closeOpenPage({ path: "/bpm/manager/model" });
},
}
};
</script>
<style lang="scss">
//body {
// overflow: hidden;
// margin: 0;
// box-sizing: border-box;
//}
//.app {
// width: 100%;
// height: 100%;
// box-sizing: border-box;
// display: inline-grid;
// grid-template-columns: 100px auto max-content;
//}
.demo-control-bar {
position: fixed;
right: 8px;
bottom: 8px;
z-index: 1;
.open-control-dialog {
width: 48px;
height: 48px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 4px;
font-size: 32px;
background: rgba(64, 158, 255, 1);
color: #ffffff;
cursor: pointer;
}
}
// TODO 芋艿:去掉多余的 faq
//.info-tip {
// position: fixed;
// top: 40px;
// right: 500px;
// z-index: 10;
// color: #999999;
//}
.control-form {
.el-radio {
width: 100%;
line-height: 32px;
}
}
.element-overlays {
box-sizing: border-box;
padding: 8px;
background: rgba(0, 0, 0, 0.6);
border-radius: 4px;
color: #fafafa;
}
.my-process-designer {
height: calc(100vh - 84px);
}
.process-panel__container {
position: absolute;
right: 0;
top: 55px;
height: calc(100vh - 84px);
}
</style>

View File

@@ -1,75 +0,0 @@
<template>
<div class="app-container">
<!-- 对话框(添加 / 修改) -->
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="开始时间" prop="startTime">
<el-date-picker clearable size="small" v-model="form.startTime" type="date" value-format="timestamp" placeholder="选择开始时间" />
</el-form-item>
<el-form-item label="结束时间" prop="endTime">
<el-date-picker clearable size="small" v-model="form.endTime" type="date" value-format="timestamp" placeholder="选择结束时间" />
</el-form-item>
<el-form-item label="请假类型" prop="type">
<el-select v-model="form.type" placeholder="请选择">
<el-option v-for="dict in typeDictData" :key="parseInt(dict.value)" :label="dict.label" :value="parseInt(dict.value)"/>
</el-select>
</el-form-item>
<el-form-item label="原因" prop="reason">
<el-col :span="10">
<el-input type="textarea" :rows="3" v-model="form.reason" placeholder="请输入原因" />
</el-col>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm"> </el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
import { createLeave} from "@/api/bpm/leave"
import { getDictDatas, DICT_TYPE } from '@/utils/dict'
export default {
name: "BpmOALeaveCreate",
components: {
},
data() {
return {
// 表单参数
form: {
startTime: undefined,
endTime: undefined,
type: undefined,
reason: undefined,
},
// 表单校验
rules: {
startTime: [{ required: true, message: "开始时间不能为空", trigger: "blur" }],
endTime: [{ required: true, message: "结束时间不能为空", trigger: "blur" }],
type: [{ required: true, message: "请假类型不能为空", trigger: "change" }],
reason: [{ required: true, message: "请假原因不能为空", trigger: "change" }],
},
typeDictData: getDictDatas(DICT_TYPE.BPM_OA_LEAVE_TYPE),
};
},
created() {
},
methods: {
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (!valid) {
return;
}
// 添加的提交
createLeave(this.form).then(response => {
this.$modal.msgSuccess("发起成功");
this.$tab.closeOpenPage({ path: "/bpm/oa/leave" });
});
});
}
}
};
</script>

View File

@@ -1,59 +0,0 @@
<template>
<div class="app-container">
<!-- 对话框(添加 / 修改) -->
<el-form ref="form" :model="form" label-width="100px">
<el-form-item label="开始时间:" prop="startTime"> {{parseTime(form.startTime, '{y}-{m}-{d}')}} </el-form-item>
<el-form-item label="结束时间:" prop="endTime"> {{parseTime(form.endTime, '{y}-{m}-{d}')}} </el-form-item>
<el-form-item label="请假类型:" prop="type">
<dict-tag :type="DICT_TYPE.BPM_OA_LEAVE_TYPE" :value="form.type"/>
</el-form-item>
<el-form-item label="原因:" prop="reason"> {{ form.reason }}</el-form-item>
</el-form>
</div>
</template>
<script>
import { getLeave} from "@/api/bpm/leave"
import {getDictDatas, DICT_TYPE} from '@/utils/dict'
export default {
name: "BpmOALeaveDetail",
components: {
},
props: {
id: {
type: [String, Number],
default: undefined
},
},
data() {
return {
leaveId: undefined, // 请假编号
// 表单参数
form: {
startTime: undefined,
endTime: undefined,
type: undefined,
reason: undefined,
},
typeDictData: getDictDatas(DICT_TYPE.BPM_OA_LEAVE_TYPE),
};
},
created() {
this.leaveId = this.id || this.$route.query.id;
if (!this.leaveId) {
this.$message.error('未传递 id 参数,无法查看 OA 请假信息');
return;
}
this.getDetail();
},
methods: {
/** 获得请假信息 */
getDetail() {
getLeave(this.leaveId).then(response => {
this.form = response.data;
});
},
}
};
</script>

View File

@@ -1,173 +0,0 @@
<template>
<div class="app-container">
<doc-alert title="工作流" url="https://doc.iocoder.cn/bpm" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="请假类型" prop="type">
<el-select v-model="queryParams.type" placeholder="请选择请假类型" clearable>
<el-option v-for="dict in leaveTypeDictData" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
<el-form-item label="申请时间" prop="createTime">
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
</el-form-item>
<el-form-item label="结果" prop="result">
<el-select v-model="queryParams.result" placeholder="请选择流结果" clearable>
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="原因" prop="reason">
<el-input v-model="queryParams.reason" placeholder="请输入原因" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini"
v-hasPermi="['bpm:oa-leave:create']" @click="handleAdd">发起请假</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="申请编号" align="center" prop="id" />
<el-table-column label="状态" align="center" prop="result">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT" :value="scope.row.result"/>
</template>
</el-table-column>
<el-table-column label="开始时间" align="center" prop="startTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.startTime) }}</span>
</template>
</el-table-column>
<el-table-column label="结束时间" align="center" prop="endTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.endTime) }}</span>
</template>
</el-table-column>
<el-table-column label="请假类型" align="center" prop="type">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.BPM_OA_LEAVE_TYPE" :value="scope.row.type"/>
</template>
</el-table-column>
<el-table-column label="原因" align="center" prop="reason" />
<el-table-column label="申请时间" align="center" prop="applyTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="200">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleCancel(scope.row)"
v-hasPermi="['bpm:oa-leave:create']" v-if="scope.row.result === 1">取消请假</el-button>
<el-button size="mini" type="text" icon="el-icon-view" @click="handleDetail(scope.row)"
v-hasPermi="['bpm:oa-leave:query']">详情</el-button>
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleProcessDetail(scope.row)">审批进度</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
</div>
</template>
<script>
import { getLeavePage } from "@/api/bpm/leave"
import { getDictDatas, DICT_TYPE } from '@/utils/dict'
import {cancelProcessInstance} from "@/api/bpm/processInstance";
export default {
name: "BpmOALeave",
components: {
},
data() {
return {
// 遮罩层
loading: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 请假申请列表
list: [],
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
result: null,
type: null,
reason: null,
createTime: []
},
leaveTypeDictData: getDictDatas(DICT_TYPE.BPM_OA_LEAVE_TYPE),
leaveResultData: getDictDatas(DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT),
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
// 执行查询
getLeavePage(this.queryParams).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 新增按钮操作 */
handleAdd() {
this.$router.push({ name: "BpmOALeaveCreate"});
},
/** 详情按钮操作 */
handleDetail(row) {
this.$router.push({ name: "BpmOALeaveDetail", query: { id: row.id}});
},
/** 查看审批进度的操作 */
handleProcessDetail(row) {
this.$router.push({ name: "BpmProcessInstanceDetail", query: { id: row.processInstanceId}});
},
/** 取消请假 */
handleCancel(row) {
const id = row.processInstanceId;
this.$prompt('请输入取消原因?', "取消流程", {
type: 'warning',
confirmButtonText: "确定",
cancelButtonText: "取消",
inputPattern: /^[\s\S]*.*\S[\s\S]*$/, // 判断非空,且非空格
inputErrorMessage: "取消原因不能为空",
}).then(({ value }) => {
return cancelProcessInstance(id, value);
}).then(() => {
this.getList();
this.$modal.msgSuccess("取消成功");
})
}
}
};
</script>

View File

@@ -1,168 +0,0 @@
<template>
<div class="app-container">
<!-- 第一步通过流程定义的列表选择对应的流程 -->
<div v-if="!selectProcessInstance">
<el-table v-loading="loading" :data="list">
<el-table-column label="流程名称" align="center" prop="name" width="200">
<template v-slot="scope">
<el-button type="text" @click="handleBpmnDetail(scope.row)">
<span>{{ scope.row.name }}</span>
</el-button>
</template>
</el-table-column>
<el-table-column label="流程分类" align="center" prop="category" width="100">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.BPM_MODEL_CATEGORY" :value="scope.row.category" />
</template>
</el-table-column>
<el-table-column label="流程版本" align="center" prop="processDefinition.version" width="80">
<template v-slot="scope">
<el-tag size="medium" v-if="scope.row">v{{ scope.row.version }}</el-tag>
</template>
</el-table-column>
<el-table-column label="流程描述" align="center" prop="description" width="300" show-overflow-tooltip />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button type="text" size="small" icon="el-icon-plus" @click="handleSelect(scope.row)">选择</el-button>
</template>
</el-table-column>
</el-table>
</div>
<!-- 第二步填写表单进行流程的提交 -->
<div v-else>
<el-card class="box-card" >
<div slot="header" class="clearfix">
<span class="el-icon-document">申请信息{{ selectProcessInstance.name }}</span>
<el-button style="float: right;" type="primary" @click="selectProcessInstance = undefined">选择其它流程</el-button>
</div>
<el-col :span="16" :offset="6">
<div>
<parser :key="new Date().getTime()" :form-conf="detailForm" @submit="submitForm" />
</div>
</el-col>
</el-card>
<el-card class="box-card">
<div slot="header" class="clearfix">
<span class="el-icon-picture-outline">流程图</span>
</div>
<my-process-viewer key="designer" v-model="bpmnXML" v-bind="bpmnControlForm" />
</el-card>
</div>
</div>
</template>
<script>
import {getProcessDefinitionBpmnXML, getProcessDefinitionList} from "@/api/bpm/definition";
import {DICT_TYPE, getDictDatas} from "@/utils/dict";
import {decodeFields} from "@/utils/formGenerator";
import Parser from '@/components/parser/Parser'
import {createProcessInstance} from "@/api/bpm/processInstance";
// 流程实例的发起
export default {
name: "ProcessInstanceCreate",
components: {
Parser
},
data() {
return {
// 遮罩层
loading: true,
// 表格数据
list: [],
// 流程表单详情
detailForm: {
fields: []
},
// BPMN 数据
bpmnXML: null,
bpmnControlForm: {
prefix: "flowable"
},
// 流程表单
selectProcessInstance: undefined, // 选择的流程实例
// 数据字典
categoryDictDatas: getDictDatas(DICT_TYPE.BPM_MODEL_CATEGORY),
};
},
created() {
this.getList();
},
methods: {
/** 查询流程定义列表 */
getList() {
this.loading = true;
getProcessDefinitionList({
suspensionState: 1
}).then(response => {
this.list = response.data
this.loading = false
}
);
},
/** 处理选择流程的按钮操作 **/
handleSelect(row) {
// 设置选择的流程
this.selectProcessInstance = row;
// 流程表单
if (row.formId) {
// 设置对应的表单
this.detailForm = {
...JSON.parse(row.formConf),
fields: decodeFields(row.formFields)
}
// 加载流程图
getProcessDefinitionBpmnXML(row.id).then(response => {
this.bpmnXML = response.data
})
} else if (row.formCustomCreatePath) {
this.$router.push({ path: row.formCustomCreatePath});
// 这里暂时无需加载流程图,因为跳出到另外个 Tab
}
},
/** 提交按钮 */
submitForm(params) {
if (!params) {
return;
}
// 设置表单禁用
const conf = params.conf;
conf.disabled = true; // 表单禁用
conf.formBtns = false; // 按钮隐藏
// 提交表单,创建流程
const variables = params.values;
createProcessInstance({
processDefinitionId: this.selectProcessInstance.id,
variables: variables
}).then(response => {
this.$modal.msgSuccess("发起流程成功");
// 关闭当前窗口
this.$tab.closeOpenPage();
this.$router.go(-1);
}).catch(() => {
conf.disabled = false; // 表单开启
conf.formBtns = true; // 按钮展示
})
},
}
};
</script>
<style lang="scss">
.my-process-designer {
height: calc(100vh - 200px);
}
.box-card {
width: 100%;
margin-bottom: 20px;
}
</style>

View File

@@ -1,400 +0,0 @@
<template>
<div class="app-container">
<!-- 审批信息 -->
<el-card class="box-card" v-loading="processInstanceLoading" v-for="(item, index) in runningTasks" :key="index">
<div slot="header" class="clearfix">
<span class="el-icon-picture-outline">审批任务{{ item.name }}</span>
</div>
<el-col :span="16" :offset="6" >
<el-form :ref="'form' + index" :model="auditForms[index]" :rules="auditRule" label-width="100px">
<el-form-item label="流程名" v-if="processInstance && processInstance.name">
{{ processInstance.name }}
</el-form-item>
<el-form-item label="流程发起人" v-if="processInstance && processInstance.startUser">
{{ processInstance.startUser.nickname }}
<el-tag type="info" size="mini">{{ processInstance.startUser.deptName }}</el-tag>
</el-form-item>
<el-form-item label="审批建议" prop="reason">
<el-input type="textarea" v-model="auditForms[index].reason" placeholder="请输入审批建议" />
</el-form-item>
</el-form>
<div style="margin-left: 10%; margin-bottom: 20px; font-size: 14px;">
<el-button icon="el-icon-edit-outline" type="success" size="mini" @click="handleAudit(item, true)">通过</el-button>
<el-button icon="el-icon-circle-close" type="danger" size="mini" @click="handleAudit(item, false)">不通过</el-button>
<el-button icon="el-icon-edit-outline" type="primary" size="mini" @click="handleUpdateAssignee(item)">转办</el-button>
<el-button icon="el-icon-edit-outline" type="primary" size="mini" @click="handleDelegate(item)">委派</el-button>
<el-button icon="el-icon-refresh-left" type="warning" size="mini" @click="handleBack(item)">退回</el-button>
</div>
</el-col>
</el-card>
<!-- 申请信息 -->
<el-card class="box-card" v-loading="processInstanceLoading">
<div slot="header" class="clearfix">
<span class="el-icon-document">申请信息{{ processInstance.name }}</span>
</div>
<el-col v-if="this.processInstance.processDefinition && this.processInstance.processDefinition.formType === 10"
:span="16" :offset="6">
<div >
<parser :key="new Date().getTime()" :form-conf="detailForm" />
</div>
</el-col>
<div v-if="this.processInstance.processDefinition && this.processInstance.processDefinition.formType === 20">
<async-biz-form-component :id="this.processInstance.businessKey"></async-biz-form-component>
</div>
</el-card>
<!-- 审批记录 -->
<el-card class="box-card" v-loading="tasksLoad">
<div slot="header" class="clearfix">
<span class="el-icon-picture-outline">审批记录</span>
</div>
<el-col :span="16" :offset="4" >
<div class="block">
<el-timeline>
<el-timeline-item v-for="(item, index) in tasks" :key="index"
:icon="getTimelineItemIcon(item)" :type="getTimelineItemType(item)">
<p style="font-weight: 700">任务{{ item.name }}</p>
<el-card :body-style="{ padding: '10px' }">
<label v-if="item.assigneeUser" style="font-weight: normal; margin-right: 30px;">
审批人{{ item.assigneeUser.nickname }}
<el-tag type="info" size="mini">{{ item.assigneeUser.deptName }}</el-tag>
</label>
<label style="font-weight: normal" v-if="item.createTime">创建时间</label>
<label style="color:#8a909c; font-weight: normal">{{ parseTime(item.createTime) }}</label>
<label v-if="item.endTime" style="margin-left: 30px;font-weight: normal">审批时间</label>
<label v-if="item.endTime" style="color:#8a909c;font-weight: normal"> {{ parseTime(item.endTime) }}</label>
<label v-if="item.durationInMillis" style="margin-left: 30px;font-weight: normal">耗时</label>
<label v-if="item.durationInMillis" style="color:#8a909c;font-weight: normal"> {{ getDateStar(item.durationInMillis) }} </label>
<p v-if="item.reason">
<el-tag :type="getTimelineItemType(item)">{{ item.reason }}</el-tag>
</p>
</el-card>
</el-timeline-item>
</el-timeline>
</div>
</el-col>
</el-card>
<!-- 高亮流程图 -->
<el-card class="box-card" v-loading="processInstanceLoading">
<div slot="header" class="clearfix">
<span class="el-icon-picture-outline">流程图</span>
</div>
<my-process-viewer key="designer" v-model="bpmnXML" v-bind="bpmnControlForm" :activityData="activityList"
:processInstanceData="processInstance" :taskData="tasks" />
</el-card>
<!-- 对话框(转派审批人) -->
<el-dialog title="转派审批人" :visible.sync="updateAssignee.open" width="500px" append-to-body>
<el-form ref="updateAssigneeForm" :model="updateAssignee.form" :rules="updateAssignee.rules" label-width="110px">
<el-form-item label="新审批人" prop="assigneeUserId">
<el-select v-model="updateAssignee.form.assigneeUserId" clearable style="width: 100%">
<el-option v-for="item in userOptions" :key="item.id" :label="item.nickname" :value="item.id" />
</el-select>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitUpdateAssigneeForm"> </el-button>
<el-button @click="cancelUpdateAssigneeForm"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import {getProcessDefinitionBpmnXML} from "@/api/bpm/definition";
import {DICT_TYPE, getDictDatas} from "@/utils/dict";
import store from "@/store";
import {decodeFields} from "@/utils/formGenerator";
import Parser from '@/components/parser/Parser'
import {getProcessInstance} from "@/api/bpm/processInstance";
import {approveTask, getTaskListByProcessInstanceId, rejectTask, updateTaskAssignee} from "@/api/bpm/task";
import {getDate} from "@/utils/dateUtils";
import {listSimpleUsers} from "@/api/system/user";
import {getActivityList} from "@/api/bpm/activity";
import Vue from "vue";
// 流程实例的详情页,可用于审批
export default {
name: "ProcessInstanceDetail",
components: {
Parser
},
data() {
return {
// 遮罩层
processInstanceLoading: true,
// 流程实例
id: undefined, // 流程实例的编号
processInstance: {},
// 流程表单详情
detailForm: {
fields: []
},
// BPMN 数据
bpmnXML: null,
bpmnControlForm: {
prefix: "flowable"
},
activityList: [],
// 审批记录
tasksLoad: true,
tasks: [],
// 审批表单
runningTasks: [],
auditForms: [],
auditRule: {
reason: [{ required: true, message: "审批建议不能为空", trigger: "blur" }],
},
// 转派审批人
userOptions: [],
updateAssignee: {
open: false,
form: {
assigneeUserId: undefined,
},
rules: {
assigneeUserId: [{ required: true, message: "新审批人不能为空", trigger: "change" }],
}
}
};
},
created() {
this.id = this.$route.query.id;
if (!this.id) {
this.$message.error('未传递 id 参数,无法查看流程信息');
return;
}
this.getDetail();
// 获得用户列表
this.userOptions = [];
listSimpleUsers().then(response => {
this.userOptions.push(...response.data);
});
},
methods: {
/** 获得流程实例 */
getDetail() {
// 获得流程实例相关
this.processInstanceLoading = true;
getProcessInstance(this.id).then(response => {
if (!response.data) {
this.$message.error('查询不到流程信息!');
return;
}
// 设置流程信息
this.processInstance = response.data;
//将业务表单,注册为动态组件
const path = this.processInstance.processDefinition.formCustomViewPath;
Vue.component("async-biz-form-component", function(resolve) {
require([`@/views${path}`], resolve);
});
// 设置表单信息
if (this.processInstance.processDefinition.formType === 10) {
this.detailForm = {
...JSON.parse(this.processInstance.processDefinition.formConf),
disabled: true, // 表单禁用
formBtns: false, // 按钮隐藏
fields: decodeFields(this.processInstance.processDefinition.formFields)
}
// 设置表单的值
this.detailForm.fields.forEach(item => {
const val = this.processInstance.formVariables[item.__vModel__]
if (val) {
item.__config__.defaultValue = val
}
});
}
// 加载流程图
getProcessDefinitionBpmnXML(this.processInstance.processDefinition.id).then(response => {
this.bpmnXML = response.data
});
// 加载活动列表
getActivityList({
processInstanceId: this.processInstance.id
}).then(response => {
this.activityList = response.data;
});
// 取消加载中
this.processInstanceLoading = false;
});
// 获得流程任务列表(审批记录)
this.tasksLoad = true;
this.runningTasks = [];
this.auditForms = [];
getTaskListByProcessInstanceId(this.id).then(response => {
// 审批记录
this.tasks = [];
// 移除已取消的审批
response.data.forEach(task => {
if (task.result !== 4) {
this.tasks.push(task);
}
});
// 排序,将未完成的排在前面,已完成的排在后面;
this.tasks.sort((a, b) => {
// 有已完成的情况,按照完成时间倒序
if (a.endTime && b.endTime) {
return b.endTime - a.endTime;
} else if (a.endTime) {
return 1;
} else if (b.endTime) {
return -1;
// 都是未完成,按照创建时间倒序
} else {
return b.createTime - a.createTime;
}
});
// 需要审核的记录
const userId = store.getters.userId;
this.tasks.forEach(task => {
if (task.result !== 1) { // 只有待处理才需要
return;
}
if (!task.assigneeUser || task.assigneeUser.id !== userId) { // 自己不是处理人
return;
}
this.runningTasks.push({...task});
this.auditForms.push({
reason: ''
})
});
// 取消加载中
this.tasksLoad = false;
});
},
getDateStar(ms) {
return getDate(ms);
},
getTimelineItemIcon(item) {
if (item.result === 1) {
return 'el-icon-time';
}
if (item.result === 2) {
return 'el-icon-check';
}
if (item.result === 3) {
return 'el-icon-close';
}
if (item.result === 4) {
return 'el-icon-remove-outline';
}
return '';
},
getTimelineItemType(item) {
if (item.result === 1) {
return 'primary';
}
if (item.result === 2) {
return 'success';
}
if (item.result === 3) {
return 'danger';
}
if (item.result === 4) {
return 'info';
}
return '';
},
/** 处理审批通过和不通过的操作 */
handleAudit(task, pass) {
const index = this.runningTasks.indexOf(task);
this.$refs['form' + index][0].validate(valid => {
if (!valid) {
return;
}
const data = {
id: task.id,
reason: this.auditForms[index].reason
}
if (pass) {
approveTask(data).then(response => {
this.$modal.msgSuccess("审批通过成功!");
this.getDetail(); // 获得最新详情
});
} else {
rejectTask(data).then(response => {
this.$modal.msgSuccess("审批不通过成功!");
this.getDetail(); // 获得最新详情
});
}
});
},
/** 处理转派审批人 */
handleUpdateAssignee(task) {
// 设置表单
this.resetUpdateAssigneeForm();
this.updateAssignee.form.id = task.id;
// 设置为打开
this.updateAssignee.open = true;
},
/** 提交转派审批人 */
submitUpdateAssigneeForm() {
this.$refs['updateAssigneeForm'].validate(valid => {
if (!valid) {
return;
}
updateTaskAssignee(this.updateAssignee.form).then(response => {
this.$modal.msgSuccess("转派任务成功!");
this.updateAssignee.open = false;
this.getDetail(); // 获得最新详情
});
});
},
/** 取消转派审批人 */
cancelUpdateAssigneeForm() {
this.updateAssignee.open = false;
this.resetUpdateAssigneeForm();
},
/** 重置转派审批人 */
resetUpdateAssigneeForm() {
this.updateAssignee.form = {
id: undefined,
assigneeUserId: undefined,
};
this.resetForm("updateAssigneeForm");
},
/** 处理审批退回的操作 */
handleDelegate(task) {
this.$modal.msgError("暂不支持【委派】功能,可以使用【转派】替代!");
},
/** 处理审批退回的操作 */
handleBack(task) {
this.$modal.msgError("暂不支持【退回】功能!");
// 可参考 http://blog.wya1.com/article/636697030/details/7296
// const data = {
// id: task.id,
// assigneeUserId: 1
// }
// backTask(data).then(response => {
// this.$modal.msgSuccess("回退成功!");
// this.getDetail(); // 获得最新详情
// });
}
}
};
</script>
<style lang="scss">
.my-process-designer {
height: calc(100vh - 200px);
}
.box-card {
width: 100%;
margin-bottom: 20px;
}
</style>

View File

@@ -1,182 +0,0 @@
<template>
<div class="app-container">
<doc-alert title="工作流" url="https://doc.iocoder.cn/bpm" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="流程名" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入流程名" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="所属流程" prop="processDefinitionId">
<el-input v-model="queryParams.processDefinitionId" placeholder="请输入流程定义的编号" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="流程分类" prop="category">
<el-select v-model="queryParams.category" placeholder="请选择流程分类" clearable>
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.BPM_MODEL_CATEGORY)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="提交时间" prop="createTime">
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable>
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="结果" prop="result">
<el-select v-model="queryParams.result" placeholder="请选择流结果" clearable>
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
v-hasPermi="['bpm:process-instance:query']">发起流程</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="编号" align="center" prop="id" width="320" />
<el-table-column label="流程名" align="center" prop="name" />
<el-table-column label="流程分类" align="center" prop="category">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.BPM_MODEL_CATEGORY" :value="scope.row.category" />
</template>
</el-table-column>
<el-table-column label="当前审批任务" align="center" prop="tasks">
<template v-slot="scope">
<el-button v-for="task in scope.row.tasks" :key="task.id" type="text"">
<span>{{ task.name }}</span>
</el-button>
</template>
</el-table-column>
<el-table-column label="状态" align="center" prop="status">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="结果" align="center" prop="result">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT" :value="scope.row.result"/>
</template>
</el-table-column>
<el-table-column label="提交时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="结束时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.endTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button type="text" size="small" icon="el-icon-delete" v-if="scope.row.result === 1"
v-hasPermi="['bpm:process-instance:cancel']" @click="handleCancel(scope.row)">取消</el-button>
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleDetail(scope.row)"
v-hasPermi="['bpm:process-instance:query']">详情</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
</div>
</template>
<script>
import { getMyProcessInstancePage, cancelProcessInstance } from "@/api/bpm/processInstance";
export default {
name: "BpmProcessInstance",
components: {
},
data() {
return {
// 遮罩层
loading: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 工作流的流程实例的拓展列表
list: [],
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
name: null,
processDefinitionId: null,
category: null,
status: null,
result: null,
createTime: []
}
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
// 执行查询
getMyProcessInstancePage(this.queryParams).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 新增按钮操作 **/
handleAdd() {
this.$router.push({ name: "BpmProcessInstanceCreate"})
},
/** 取消按钮操作 */
handleCancel(row) {
const id = row.id;
this.$prompt('请输入取消原因?', "取消流程", {
type: 'warning',
confirmButtonText: "确定",
cancelButtonText: "取消",
inputPattern: /^[\s\S]*.*\S[\s\S]*$/, // 判断非空,且非空格
inputErrorMessage: "取消原因不能为空",
}).then(({ value }) => {
return cancelProcessInstance(id, value);
}).then(() => {
this.getList();
this.$modal.msgSuccess("取消成功");
})
},
/** 处理详情按钮 */
handleDetail(row) {
this.$router.push({ name: "BpmProcessInstanceDetail", query: { id: row.id}});
},
}
};
</script>

View File

@@ -1,120 +0,0 @@
<template>
<div class="app-container">
<doc-alert title="工作流" url="https://doc.iocoder.cn/bpm" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="流程名" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入流程名" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="任务编号" align="center" prop="id" width="320" fixed />
<el-table-column label="任务名称" align="center" prop="name" width="200" />
<el-table-column label="所属流程" align="center" prop="processInstance.name" width="200" />
<el-table-column label="流程发起人" align="center" prop="processInstance.startUserNickname" width="120" />
<el-table-column label="结果" align="center" prop="result">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT" :value="scope.row.result"/>
</template>
</el-table-column>
<el-table-column label="审批意见" align="center" prop="reason" width="200" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="审批时间" align="center" prop="endTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.endTime) }}</span>
</template>
</el-table-column>
<el-table-column label="耗时" align="center" prop="durationInMillis" width="180">
<template v-slot="scope">
<span>{{ getDateStar(scope.row.durationInMillis) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleAudit(scope.row)"
v-hasPermi="['bpm:task:query']">详情</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
</div>
</template>
<script>
import {getDoneTaskPage} from '@/api/bpm/task'
import {getDate} from "@/utils/dateUtils";
export default {
name: "BpmDoneTask",
components: {
},
data() {
return {
// 遮罩层
loading: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 已办任务列表
list: [],
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
name: null,
createTime: []
},
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
getDoneTaskPage(this.queryParams).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
getDateStar(ms) {
return getDate(ms);
},
/** 处理审批按钮 */
handleAudit(row) {
this.$router.push({ name: "BpmProcessInstanceDetail", query: { id: row.processInstance.id}});
},
}
};
</script>

View File

@@ -1,107 +0,0 @@
<template>
<div class="app-container">
<doc-alert title="工作流" url="https://doc.iocoder.cn/bpm" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="流程名" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入流程名" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="任务编号" align="center" prop="id" width="320" />
<el-table-column label="任务名称" align="center" prop="name" />
<el-table-column label="所属流程" align="center" prop="processInstance.name" />
<el-table-column label="流程发起人" align="center" prop="processInstance.startUserNickname" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="状态" align="center" prop="version" width="80">
<template v-slot="scope">
<el-tag type="success" v-if="scope.row.suspensionState === 1">激活</el-tag>
<el-tag type="warning" v-if="scope.row.suspensionState === 2">挂起</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleAudit(scope.row)"
v-hasPermi="['bpm:task:update']">审批</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
</div>
</template>
<script>
import {getTodoTaskPage} from '@/api/bpm/task'
export default {
name: "BpmTodoTask",
components: {
},
data() {
return {
// 遮罩层
loading: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 待办任务列表
list: [],
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
name: null,
createTime: []
},
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
// 处理查询参数
getTodoTaskPage(this.queryParams).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 处理审批按钮 */
handleAudit(row) {
this.$router.push({ name: "BpmProcessInstanceDetail", query: { id: row.processInstance.id}});
},
}
};
</script>

View File

@@ -1,337 +0,0 @@
<template>
<div>
<!-- 列表弹窗 -->
<el-dialog title="任务分配规则" :visible.sync="visible" width="800px" append-to-body>
<el-table v-loading="loading" :data="list">
<el-table-column label="任务名" align="center" prop="taskDefinitionName" width="120" fixed />
<el-table-column label="任务标识" align="center" prop="taskDefinitionKey" width="120" show-tooltip-when-overflow />
<el-table-column label="规则类型" align="center" prop="type" width="120">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.BPM_TASK_ASSIGN_RULE_TYPE" :value="scope.row.type" />
</template>
</el-table-column>
<el-table-column label="规则范围" align="center" prop="options" width="440px">
<template v-slot="scope">
<el-tag size="medium" v-if="scope.row.options" :key="option" v-for="option in scope.row.options">
{{ getAssignRuleOptionName(scope.row.type, option) }}
</el-tag>
</template>
</el-table-column>
<el-table-column v-if="modelId" label="操作" align="center" width="80" fixed="right">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdateTaskAssignRule(scope.row)"
v-hasPermi="['bpm:task-assign-rule:update']">修改</el-button>
</template>
</el-table-column>
</el-table>
</el-dialog>
<!-- 添加/修改弹窗 -->
<el-dialog title="修改任务规则" :visible.sync="open" width="500px" append-to-body>
<el-form ref="taskAssignRuleForm" :model="form" :rules="rules" label-width="110px">
<el-form-item label="任务名称" prop="taskDefinitionName">
<el-input v-model="form.taskDefinitionName" disabled />
</el-form-item>
<el-form-item label="任务标识" prop="taskDefinitionKey">
<el-input v-model="form.taskDefinitionKey" disabled />
</el-form-item>
<el-form-item label="规则类型" prop="type">
<el-select v-model="form.type" clearable style="width: 100%">
<el-option v-for="dict in taskAssignRuleTypeDictDatas" :key="parseInt(dict.value)" :label="dict.label" :value="parseInt(dict.value)"/>
</el-select>
</el-form-item>
<el-form-item v-if="form.type === 10" label="指定角色" prop="roleIds">
<el-select v-model="form.roleIds" multiple clearable style="width: 100%">
<el-option v-for="item in roleOptions" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item v-if="form.type === 20 || form.type === 21" label="指定部门" prop="deptIds">
<treeselect v-model="form.deptIds" :options="deptTreeOptions" multiple flat :defaultExpandLevel="3"
placeholder="请选择指定部门" :normalizer="normalizer"/>
</el-form-item>
<el-form-item v-if="form.type === 22" label="指定岗位" prop="postIds">
<el-select v-model="form.postIds" multiple clearable style="width: 100%">
<el-option v-for="item in postOptions" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item v-if="form.type === 30 || form.type === 31 || form.type === 32" label="指定用户" prop="userIds">
<el-select v-model="form.userIds" multiple clearable style="width: 100%">
<el-option v-for="item in userOptions" :key="item.id" :label="item.nickname" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item v-if="form.type === 40" label="指定用户组" prop="userGroupIds">
<el-select v-model="form.userGroupIds" multiple clearable style="width: 100%">
<el-option v-for="item in userGroupOptions" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item v-if="form.type === 50" label="指定脚本" prop="scripts">
<el-select v-model="form.scripts" multiple clearable style="width: 100%">
<el-option v-for="dict in taskAssignScriptDictDatas" :key="parseInt(dict.value)"
:label="dict.label" :value="parseInt(dict.value)"/>
</el-select>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitAssignRuleForm"> </el-button>
<el-button @click="cancelAssignRuleForm"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import {DICT_TYPE, getDictDatas} from "@/utils/dict";
import {createTaskAssignRule, getTaskAssignRuleList, updateTaskAssignRule} from "@/api/bpm/taskAssignRule";
import {listSimpleRoles} from "@/api/system/role";
import {listSimpleDepts} from "@/api/system/dept";
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
import {listSimplePosts} from "@/api/system/post";
import {listSimpleUsers} from "@/api/system/user";
import {listSimpleUserGroups} from "@/api/bpm/userGroup";
export default {
name: "BpmTaskAssignRule",
components: {
Treeselect
},
data() {
return {
// 如下参数,可传递
modelId: undefined, // 流程模型的编号。如果 modelId 非空,则用于流程模型的查看与配置
processDefinitionId: undefined, // 流程定义的编号。如果 processDefinitionId 非空,则用于流程定义的查看,不支持配置
visible: false,
// 任务分配规则表单
row: undefined, // 选中的流程模型
list: [], // 选中流程模型的任务分配规则们
loading: false, // 加载中
open: false, // 是否打开
form: {}, // 表单
rules: { // 表单校验规则
type: [{ required: true, message: "规则类型不能为空", trigger: "change" }],
roleIds: [{required: true, message: "指定角色不能为空", trigger: "change" }],
deptIds: [{required: true, message: "指定部门不能为空", trigger: "change" }],
postIds: [{required: true, message: "指定岗位不能为空", trigger: "change"}],
userIds: [{required: true, message: "指定用户不能为空", trigger: "change"}],
userGroupIds: [{required: true, message: "指定用户组不能为空", trigger: "change"}],
scripts: [{required: true, message: "指定脚本不能为空", trigger: "change"}],
},
// 各种下拉框
roleOptions: [],
deptOptions: [],
deptTreeOptions: [],
postOptions: [],
userOptions: [],
userGroupOptions: [],
// 数据字典
modelFormTypeDictDatas: getDictDatas(DICT_TYPE.BPM_MODEL_FORM_TYPE),
taskAssignRuleTypeDictDatas: getDictDatas(DICT_TYPE.BPM_TASK_ASSIGN_RULE_TYPE),
taskAssignScriptDictDatas: getDictDatas(DICT_TYPE.BPM_TASK_ASSIGN_SCRIPT),
};
},
methods: {
initModel(modelId) {
this.modelId = modelId;
this.processDefinitionId = undefined;
// 初始化所有下拉框
this.init0();
},
initProcessDefinition(processDefinitionId) {
this.modelId = undefined;
this.processDefinitionId = processDefinitionId;
// 初始化所有下拉框
this.init0();
},
/** 初始化 */
init0() {
// 设置可见
this.visible = true;
// 获得列表
this.getList();
// 获得角色列表
this.roleOptions = [];
listSimpleRoles().then(response => {
this.roleOptions.push(...response.data);
});
// 获得部门列表
this.deptOptions = [];
this.deptTreeOptions = [];
listSimpleDepts().then(response => {
this.deptOptions.push(...response.data);
this.deptTreeOptions.push(...this.handleTree(response.data, "id"));
});
// 获得岗位列表
this.postOptions = [];
listSimplePosts().then(response => {
this.postOptions.push(...response.data);
});
// 获得用户列表
this.userOptions = [];
listSimpleUsers().then(response => {
this.userOptions.push(...response.data);
});
// 获得用户组列表
this.userGroupOptions = [];
listSimpleUserGroups().then(response => {
this.userGroupOptions.push(...response.data);
});
},
/** 获得任务分配规则列表 */
getList() {
this.loading = true;
getTaskAssignRuleList({
modelId: this.modelId,
processDefinitionId: this.processDefinitionId,
}).then(response => {
this.loading = false;
this.list = response.data;
})
},
/** 处理修改任务分配规则的按钮操作 */
handleUpdateTaskAssignRule(row) {
// 先重置标识
this.resetAssignRuleForm();
// 设置表单
this.form = {
...row,
options: [],
roleIds: [],
deptIds: [],
postIds: [],
userIds: [],
userGroupIds: [],
scripts: [],
};
// 将 options 赋值到对应的 roleIds 等选项
if (row.type === 10) {
this.form.roleIds.push(...row.options);
} else if (row.type === 20 || row.type === 21) {
this.form.deptIds.push(...row.options);
} else if (row.type === 22) {
this.form.postIds.push(...row.options);
} else if (row.type === 30 || row.type === 31 || row.type === 32) {
this.form.userIds.push(...row.options);
} else if (row.type === 40) {
this.form.userGroupIds.push(...row.options);
} else if (row.type === 50) {
this.form.scripts.push(...row.options);
}
this.open = true;
},
/** 提交任务分配规则的表单 */
submitAssignRuleForm() {
this.$refs["taskAssignRuleForm"].validate(valid => {
if (valid) {
// 构建表单
let form = {
...this.form,
taskDefinitionName: undefined,
};
// 将 roleIds 等选项赋值到 options 中
if (form.type === 10) {
form.options = form.roleIds;
} else if (form.type === 20 || form.type === 21) {
form.options = form.deptIds;
} else if (form.type === 22) {
form.options = form.postIds;
} else if (form.type === 30 || form.type === 31 || form.type === 32) {
form.options = form.userIds;
} else if (form.type === 40) {
form.options = form.userGroupIds;
} else if (form.type === 50) {
form.options = form.scripts;
}
form.roleIds = undefined;
form.deptIds = undefined;
form.postIds = undefined;
form.userIds = undefined;
form.userGroupIds = undefined;
form.scripts = undefined;
// 新增
if (!form.id) {
form.modelId = this.modelId; // 模型编号
createTaskAssignRule(form).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
// 修改
} else {
form.taskDefinitionKey = undefined; // 无法修改
updateTaskAssignRule(form).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
}
}
});
},
/** 取消任务分配规则的表单 */
cancelAssignRuleForm() {
this.open = false;
this.resetAssignRuleForm();
},
/** 表单重置 */
resetAssignRuleForm() {
this.form = {};
this.resetForm("taskAssignRuleForm");
},
getAssignRuleOptionName(type, option) {
if (type === 10) {
for (const roleOption of this.roleOptions) {
if (roleOption.id === option) {
return roleOption.name;
}
}
} else if (type === 20 || type === 21) {
for (const deptOption of this.deptOptions) {
if (deptOption.id === option) {
return deptOption.name;
}
}
} else if (type === 22) {
for (const postOption of this.postOptions) {
if (postOption.id === option) {
return postOption.name;
}
}
} else if (type === 30 || type === 31 || type === 32) {
for (const userOption of this.userOptions) {
if (userOption.id === option) {
return userOption.nickname;
}
}
} else if (type === 40) {
for (const userGroupOption of this.userGroupOptions) {
if (userGroupOption.id === option) {
return userGroupOption.name;
}
}
} else if (type === 50) {
option = option + ''; // 转换成 string
for (const dictData of this.taskAssignScriptDictDatas) {
if (dictData.value === option) {
return dictData.label;
}
}
}
return '未知(' + option + ')';
},
// 格式化部门的下拉框
normalizer(node) {
return {
id: node.id,
label: node.name,
children: node.children
}
}
}
};
</script>

View File

@@ -2,42 +2,18 @@
<div class="app-container">
<!-- 搜索工作栏 -->
<search-bar
:formConfigs="formConfig"
ref="searchBarForm"
@headBtnClick="buttonClick"
/>
<search-bar :formConfigs="formConfig" ref="searchBarForm" @headBtnClick="buttonClick" />
<!-- 列表 -->
<base-table
:page="queryParams.pageNo"
:limit="queryParams.pageSize"
:table-props="tableProps"
:table-data="list"
:max-height="tableH"
>
<method-btn
v-if="tableBtn.length"
slot="handleBtn"
:width="80"
label="操作"
:method-list="tableBtn"
@clickBtn="handleClick"
/>
<base-table :page="queryParams.pageNo" :limit="queryParams.pageSize" :table-props="tableProps" :table-data="list"
:max-height="tableH">
<method-btn v-if="tableBtn.length" slot="handleBtn" :width="80" label="操作" :method-list="tableBtn"
@clickBtn="handleClick" />
</base-table>
<pagination
:page.sync="queryParams.pageNo"
:limit.sync="queryParams.pageSize"
:total="total"
@pagination="getList"
/>
<pagination :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize" :total="total"
@pagination="getList" />
<!-- 新增 -->
<base-dialog
:dialogTitle="addOrEditTitle"
:dialogVisible="centervisible"
@cancel="handleCancel"
@confirm="handleConfirm"
:before-close="handleCancel"
>
<base-dialog :dialogTitle="addOrEditTitle" :dialogVisible="centervisible" @cancel="handleCancel"
@confirm="handleConfirm" :before-close="handleCancel">
<energy-plc-add ref="energyPlc" @successSubmit="successSubmit" />
</base-dialog>
</div>
@@ -50,7 +26,8 @@ import EnergyPlcAdd from './components/energyPlcAdd.vue'
const tableProps = [
{
prop: 'plcTableName',
label: '关联表名'
label: '关联表名',
showOverflowtooltip: true
},
{
prop: 'code',
@@ -60,7 +37,9 @@ const tableProps = [
},
{
prop: 'name',
label: '标识名'
label: '标识名',
minWidth: 150,
showOverflowtooltip: true
},
{
prop: 'enName',
@@ -109,21 +88,21 @@ export default {
tableBtn: [
this.$auth.hasPermi('base:energy-plc:update')
? {
type: 'edit',
btnName: '编辑'
}
type: 'edit',
btnName: '编辑'
}
: undefined,
this.$auth.hasPermi('base:energy-plc:delete')
? {
type: 'delete',
btnName: '删除'
}
type: 'delete',
btnName: '删除'
}
: undefined
].filter((v) => v),
tableH: this.tableHeight(260),
collectionList: [
{value: 0,label: '否'},
{value: 1,label: '是'}
{ value: 0, label: '否' },
{ value: 1, label: '是' }
],
// 总条数
total: 0,
@@ -167,7 +146,7 @@ export default {
getList() {
getEnergyPlcPage(this.queryParams).then(response => {
let arr = response.data.list || [];
arr&&arr.map(item => {
arr && arr.map(item => {
this.collectionList.map(i => {
if (item.collection === i.value) {
item.collection = i.label
@@ -188,7 +167,7 @@ export default {
this.centervisible = true
break
default:
this.handleDelete(val.data)
this.handleDelete(val.data)
}
},
handleCancel() {
@@ -205,13 +184,13 @@ export default {
},
/** 删除按钮操作 */
handleDelete(row) {
this.$modal.confirm('是否确认删除关联表名为"' + row.name + '"的数据项?').then(function() {
return deleteEnergyPlc(row.id);
}).then(() => {
this.queryParams.pageNo = 1;
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
this.$modal.confirm('是否确认删除关联表名为"' + row.name + '"的数据项?').then(function () {
return deleteEnergyPlc(row.id);
}).then(() => {
this.queryParams.pageNo = 1;
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => { });
}
}
};

View File

@@ -2,47 +2,23 @@
<div class="app-container">
<!-- 搜索工作栏 -->
<search-bar
:formConfigs="formConfig"
ref="searchBarForm"
@headBtnClick="buttonClick"
/>
<search-bar :formConfigs="formConfig" ref="searchBarForm" @headBtnClick="buttonClick" />
<!-- 列表 -->
<base-table
:page="queryParams.pageNo"
:limit="queryParams.pageSize"
:table-props="tableProps"
:table-data="list"
:max-height="tableH"
>
<method-btn
v-if="tableBtn.length"
slot="handleBtn"
:width="160"
label="操作"
:method-list="tableBtn"
@clickBtn="handleClick"
/>
<base-table :page="queryParams.pageNo" :limit="queryParams.pageSize" :table-props="tableProps" :table-data="list"
:max-height="tableH">
<method-btn v-if="tableBtn.length" slot="handleBtn" :width="160" label="操作" :method-list="tableBtn"
@clickBtn="handleClick" />
</base-table>
<pagination
:page.sync="queryParams.pageNo"
:limit.sync="queryParams.pageSize"
:total="total"
@pagination="getList"
/>
<pagination :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize" :total="total"
@pagination="getList" />
<!-- 新增 -->
<base-dialog
:dialogTitle="addOrEditTitle"
:dialogVisible="centervisible"
@cancel="handleCancel"
@confirm="handleConfirm"
:before-close="handleCancel"
width='30%'
>
<base-dialog :dialogTitle="addOrEditTitle" :dialogVisible="centervisible" @cancel="handleCancel"
@confirm="handleConfirm" :before-close="handleCancel" width='30%'>
<energy-plc-connect-add ref="energyPlcConnect" :objList="objList" @successSubmit="successSubmit" />
</base-dialog>
<!-- 参数绑定/查看 -->
<energy-plc-param v-if="paramVisible" ref="plcParam" @closeDrawer="closeDrawer" :energyTypeList="energyTypeList"></energy-plc-param>
<energy-plc-param v-if="paramVisible" ref="plcParam" @closeDrawer="closeDrawer"
:energyTypeList="energyTypeList"></energy-plc-param>
</div>
</template>
@@ -60,20 +36,24 @@ const tableProps = [
},
{
prop: 'objCode',
label: '对象编码'
label: '对象编码',
showOverflowtooltip: true
},
{
prop: 'plcTableName',
prop: 'plcTableName',
label: '关联表名'
},
{
prop: 'plcTableCode',
label: '关联表编码',
minWidth: 150,
showOverflowtooltip: true
},
{
prop: 'cnName',
label: '标识名'
label: '标识名',
minWidth: 150,
showOverflowtooltip: true
},
{
prop: 'varNum',
@@ -113,9 +93,9 @@ export default {
tableBtn: [
this.$auth.hasPermi('base:energy-plc-connect:bind')
? {
type: 'connect',
btnName: '绑定'
}
type: 'connect',
btnName: '绑定'
}
: undefined,
{
type: 'detail',
@@ -123,15 +103,15 @@ export default {
},
this.$auth.hasPermi('base:energy-plc-connect:update')
? {
type: 'edit',
btnName: '编辑'
}
type: 'edit',
btnName: '编辑'
}
: undefined,
this.$auth.hasPermi('base:energy-plc-connect:delete')
? {
type: 'delete',
btnName: '删除'
}
type: 'delete',
btnName: '删除'
}
: undefined
].filter((v) => v),
tableH: this.tableHeight(260),
@@ -230,13 +210,13 @@ export default {
},
/** 删除按钮操作 */
handleDelete(row) {
this.$modal.confirm('是否确认删除对象为"' + row.objName + '"的数据项?').then(function() {
return deleteEnergyPlcConnect(row.id);
}).then(() => {
this.queryParams.pageNo = 1;
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
this.$modal.confirm('是否确认删除对象为"' + row.objName + '"的数据项?').then(function () {
return deleteEnergyPlcConnect(row.id);
}).then(() => {
this.queryParams.pageNo = 1;
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => { });
},
closeDrawer() {
this.getList()

View File

@@ -46,7 +46,8 @@ const tableProps = [
prop: 'tableName',
label: '能源表名',
filter: publicFormatter('table_name'),
minWidth: 110
minWidth: 110,
showOverflowtooltip: true
},
{
prop: 'recordTime',

View File

@@ -20,15 +20,18 @@ import * as XLSX from 'xlsx/xlsx.mjs'
const tableProps = [
{
prop: 'objName',
label: '统计对象'
label: '统计对象',
showOverflowtooltip: true
},
{
prop: 'objCode',
label: '对象编码'
label: '对象编码',
showOverflowtooltip: true
},
{
prop: 'energyTypeName',
label: '能源类型'
label: '能源类型',
showOverflowtooltip: true
},
{
prop: 'startValue',

View File

@@ -2,46 +2,23 @@
<div class="app-container">
<!-- 搜索工作栏 -->
<search-bar
:formConfigs="formConfig"
ref="searchBarForm"
@headBtnClick="buttonClick"
/>
<search-bar :formConfigs="formConfig" ref="searchBarForm" @headBtnClick="buttonClick" />
<!-- 列表 -->
<base-table
:page="queryParams.pageNo"
:limit="queryParams.pageSize"
:table-props="tableProps"
:table-data="list"
:max-height="tableH"
>
<method-btn
v-if="tableBtn.length"
slot="handleBtn"
:width="160"
label="操作"
:method-list="tableBtn"
@clickBtn="handleClick"
/>
<base-table :page="queryParams.pageNo" :limit="queryParams.pageSize" :table-props="tableProps" :table-data="list"
:max-height="tableH">
<method-btn v-if="tableBtn.length" slot="handleBtn" :width="160" label="操作" :method-list="tableBtn"
@clickBtn="handleClick" />
</base-table>
<pagination
:page.sync="queryParams.pageNo"
:limit.sync="queryParams.pageSize"
:total="total"
@pagination="getList"
/>
<pagination :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize" :total="total"
@pagination="getList" />
<!-- 新增 -->
<base-dialog
:dialogTitle="addOrEditTitle"
:dialogVisible="centervisible"
@cancel="handleCancel"
@confirm="handleConfirm"
:before-close="handleCancel"
>
<base-dialog :dialogTitle="addOrEditTitle" :dialogVisible="centervisible" @cancel="handleCancel"
@confirm="handleConfirm" :before-close="handleCancel">
<energy-statistics-add ref="energyStatistics" @successSubmit="successSubmit" />
</base-dialog>
<!-- 参数绑定/查看 -->
<energy-statistics-det ref="plcParam" @closeDrawer="closeDrawer" :energyTypeList="energyTypeList"></energy-statistics-det>
<energy-statistics-det ref="plcParam" @closeDrawer="closeDrawer"
:energyTypeList="energyTypeList"></energy-statistics-det>
</div>
</template>
@@ -63,7 +40,7 @@ const tableProps = [
showOverflowtooltip: true
},
{
prop: 'type',
prop: 'type',
label: '统计类型',
filter: publicFormatter('statistic_type')
},
@@ -81,7 +58,7 @@ const tableProps = [
}
]
export default {
name: "energyStatistics",
name: "EnergyStatistics",
components: { EnergyStatisticsAdd, EnergyStatisticsDet },
data() {
return {
@@ -112,26 +89,26 @@ export default {
tableProps,
tableBtn: [
this.$auth.hasPermi('monitoring:energy-statistics:bind')
? {
? {
type: 'connect',
btnName: '绑定'
}
: undefined,
: undefined,
{
type: 'detail',
btnName: '详情'
},
this.$auth.hasPermi('monitoring:energy-statistics:update')
? {
type: 'edit',
btnName: '编辑'
}
type: 'edit',
btnName: '编辑'
}
: undefined,
this.$auth.hasPermi('monitoring:energy-statistics:delete')
? {
type: 'delete',
btnName: '删除'
}
type: 'delete',
btnName: '删除'
}
: undefined
].filter((v) => v),
tableH: this.tableHeight(260),
@@ -224,13 +201,13 @@ export default {
},
/** 删除按钮操作 */
handleDelete(row) {
this.$modal.confirm('是否确认删除方案名称为"' + row.name + '"的数据项?').then(function() {
return deleteEnergyStatistics(row.id);
}).then(() => {
this.queryParams.pageNo = 1;
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
this.$modal.confirm('是否确认删除方案名称为"' + row.name + '"的数据项?').then(function () {
return deleteEnergyStatistics(row.id);
}).then(() => {
this.queryParams.pageNo = 1;
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => { });
},
closeDrawer() {
this.getList()

View File

@@ -1,7 +1,7 @@
<!--
* @Author: zhp
* @Date: 2023-11-08 14:00:52
* @LastEditTime: 2024-03-25 14:09:33
* @LastEditTime: 2024-03-29 08:46:42
* @LastEditors: zhp
* @Description:
-->
@@ -25,24 +25,24 @@ export default {
},
data() {
return {
// state: false
}
},
computed: {
state() {
return this.injectData.enabled === 1 ? true : false
state: false
}
},
// computed: {
// state() {
// return this.injectData.enabled === 1 ? true : false
// }
// },
mounted() {
// this.mapToState()
this.mapToState()
},
methods: {
// mapToState() {
// if (this.injectData.prop === 'enabled') {
// this.state = this.injectData.enabled === 1 ? true : false
// }
// },
mapToState() {
if (this.injectData.prop === 'enabled') {
this.state = this.injectData.enabled === 1 ? true : false
}
},
changeHandler() {
// let params = {}
let payload = {}

View File

@@ -2,43 +2,18 @@
<div class="app-container">
<!-- 搜索工作栏 -->
<search-bar
:formConfigs="formConfig"
ref="searchBarForm"
@headBtnClick="buttonClick"
/>
<search-bar :formConfigs="formConfig" ref="searchBarForm" @headBtnClick="buttonClick" />
<!-- 列表 -->
<base-table
:page="queryParams.pageNo"
:limit="queryParams.pageSize"
:table-props="tableProps"
:table-data="list"
:max-height="tableH"
>
<method-btn
v-if="tableBtn.length"
slot="handleBtn"
:width="120"
label="操作"
:method-list="tableBtn"
@clickBtn="handleClick"
/>
<base-table :page="queryParams.pageNo" :limit="queryParams.pageSize" :table-props="tableProps" :table-data="list"
:max-height="tableH">
<method-btn v-if="tableBtn.length" slot="handleBtn" :width="120" label="操作" :method-list="tableBtn"
@clickBtn="handleClick" />
</base-table>
<pagination
:page.sync="queryParams.pageNo"
:limit.sync="queryParams.pageSize"
:total="total"
@pagination="getList"
/>
<pagination :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize" :total="total"
@pagination="getList" />
<!-- 新增 -->
<base-dialog
:dialogTitle="addOrEditTitle"
:dialogVisible="centervisible"
@cancel="handleCancel"
@confirm="handleConfirm"
:before-close="handleCancel"
width='50%'
>
<base-dialog :dialogTitle="addOrEditTitle" :dialogVisible="centervisible" @cancel="handleCancel"
@confirm="handleConfirm" :before-close="handleCancel" width='50%'>
<group-class-add ref="classList" @successSubmit="successSubmit" />
</base-dialog>
</div>
@@ -85,7 +60,7 @@ const tableProps = [
}
]
export default {
name: "GroupClass",
name: "GroupClasses",
components: { GroupClassAdd },
data() {
return {
@@ -115,33 +90,33 @@ export default {
],
tableProps,
tableBtn: [
this.$auth.hasPermi('base:group-classes:cancel')
this.$auth.hasPermi('base:group-classes:cancel')
? {
type: 'cancel',
btnName: '作废',
showParam: {
type: '&',
data: [
{
type: 'unequal',
name: 'status',
value: '不可用'
}
]
}
type: 'cancel',
btnName: '作废',
showParam: {
type: '&',
data: [
{
type: 'unequal',
name: 'status',
value: '不可用'
}
]
}
}
: undefined,
this.$auth.hasPermi('base:group-classes:update')
? {
type: 'edit',
btnName: '编辑'
}
type: 'edit',
btnName: '编辑'
}
: undefined,
this.$auth.hasPermi('base:group-classes:delete')
? {
type: 'delete',
btnName: '删除'
}
type: 'delete',
btnName: '删除'
}
: undefined
].filter((v) => v),
tableH: this.tableHeight(260),
@@ -213,7 +188,7 @@ export default {
this.discard(val.data)
break
default:
this.handleDelete(val.data)
this.handleDelete(val.data)
}
},
handleCancel() {
@@ -235,33 +210,33 @@ export default {
obj.endTime = row.endTime
obj.enableTime = row.enableTime
obj.disableTime = Date.parse(new Date())
this.$modal.confirm('是否确认作废班次名称为"' + row.name + '"的数据项?').then(function() {
return updateGroupClasses({ ...obj })
}).then(() => {
this.getList();
this.$modal.msgSuccess("操作成功");
}).catch(() => {});
this.$modal.confirm('是否确认作废班次名称为"' + row.name + '"的数据项?').then(function () {
return updateGroupClasses({ ...obj })
}).then(() => {
this.getList();
this.$modal.msgSuccess("操作成功");
}).catch(() => { });
},
/** 删除按钮操作 */
handleDelete(row) {
console.log(row)
let _this = this
if (row.status === '可用') {// 可用
_this.$modal.confirm('删除的班次"' + row.name + '"可能会影响交接班计划,请点取消再次确认!').then(function() {
return _this.$modal.confirm('是否确认删除班次名称为"' + row.name + '"的数据项?').then(function() {
return deleteGroupClasses(row.id);
}).then(() => {
_this.getList();
_this.$modal.msgSuccess("删除成功");
}).catch(() => {});
})
} else {
_this.$modal.confirm('是否确认删除班次名称为"' + row.name + '"的数据项?').then(function() {
_this.$modal.confirm('删除的班次"' + row.name + '"可能会影响交接班计划,请点取消再次确认!').then(function () {
return _this.$modal.confirm('是否确认删除班次名称为"' + row.name + '"的数据项?').then(function () {
return deleteGroupClasses(row.id);
}).then(() => {
_this.getList();
_this.$modal.msgSuccess("删除成功");
}).catch(() => {});
}).catch(() => { });
})
} else {
_this.$modal.confirm('是否确认删除班次名称为"' + row.name + '"的数据项?').then(function () {
return deleteGroupClasses(row.id);
}).then(() => {
_this.getList();
_this.$modal.msgSuccess("删除成功");
}).catch(() => { });
}
}
}

View File

@@ -279,13 +279,13 @@ export default {
handleJobLog(row) {
if (row.id) {
this.$router.push({
path:"/job/log",
path:"/system/job/log",
query:{
jobId: row.id
}
});
} else {
this.$router.push("/job/log");
this.$router.push("/system/job/log");
}
},
/** 新增按钮操作 */

View File

@@ -1,8 +1,5 @@
<template>
<div class="app-container">
<doc-alert title="定时任务" url="https://doc.iocoder.cn/job/" />
<doc-alert title="异步任务" url="https://doc.iocoder.cn/async-task/" />
<doc-alert title="消息队列" url="https://doc.iocoder.cn/message-queue/" />
<!-- 搜索栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
<el-form-item label="处理器的名字" prop="handlerName">

View File

@@ -1,261 +0,0 @@
<template>
<div class="app-container">
<doc-alert title="功能开启" url="https://doc.iocoder.cn/mall/build/" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="标题" prop="title">
<el-input v-model="queryParams.title" placeholder="请输入标题" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable size="small">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.COMMON_STATUS)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
v-hasPermi="['market:banner:create']">新增
</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="标题" align="center" prop="title"/>
<el-table-column label="缩略图" align="center" prop="picUrl">
<template v-slot="scope">
<img v-if="scope.row.picUrl" :src="scope.row.picUrl" alt="缩略图片" class="img-height"/>
</template>
</el-table-column>
<el-table-column label="跳转链接" align="center" prop="url"/>
<el-table-column label="排序" align="center" prop="sort"/>
<el-table-column label="描述" align="center" prop="memo"/>
<el-table-column label="状态" align="center" prop="status">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status"/>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['market:banner:update']">修改
</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['market:banner:delete']">删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="标题" prop="title">
<el-input v-model="form.title" placeholder="请输入标题"/>
</el-form-item>
<el-form-item label="缩略图" prop="picUrl">
<imageUpload v-model="form.picUrl" :limit="1"/>
</el-form-item>
<el-form-item label="跳转链接" prop="url">
<el-input v-model="form.url" placeholder="请输入跳转链接"/>
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input v-model="form.sort" placeholder="请输入排序"/>
</el-form-item>
<el-form-item label="描述" prop="memo">
<el-input v-model="form.memo" type="textarea" placeholder="请输入描述"/>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-radio-group v-model="form.status">
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.COMMON_STATUS)"
:key="dict.value" :label="parseInt(dict.value)">{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import {
createBanner,
deleteBanner,
getBanner,
getBannerPage,
updateBanner
} from "@/api/mall/market/banner";
import ImageUpload from '@/components/ImageUpload';
export default {
name: "Banner",
components: {
ImageUpload
},
data() {
return {
// 遮罩层
loading: true,
// 导出遮罩层
exportLoading: false,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 品牌列表
list: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
title: null,
status: null,
createTime: []
},
// 商品分类树选项
categoryOptions: [],
// 表单参数
form: {},
// 表单校验
rules: {
title: [{required: true, message: "标题不能不空", trigger: "blur"}],
picUrl: [{required: true, message: "图片地址不能为空", trigger: "blur"}],
url: [{required: true, message: "跳转地址不能为空", trigger: "blur"}],
sort: [{required: true, message: "排序不能为空", trigger: "blur"}],
status: [{required: true, message: "状态不能为空", trigger: "change"}],
}
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
// 执行查询
getBannerPage(this.queryParams).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 取消按钮 */
cancel() {
this.open = false;
this.reset();
},
/** 表单重置 */
reset() {
this.form = {
id: undefined,
title: undefined,
link: undefined,
imgUrl: undefined,
sort: undefined,
memo: undefined,
status: undefined,
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加Banner";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const id = row.id;
getBanner(id).then(response => {
this.form = response.data;
this.open = true;
this.title = "修改Banner";
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (!valid) {
return;
}
// 修改的提交
if (this.form.id != null) {
updateBanner(this.form).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
return;
}
// 添加的提交
createBanner(this.form).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
});
},
/** 删除按钮操作 */
handleDelete(row) {
const id = row.id;
this.$modal.confirm('是否确认删除Banner编号为"' + id + '"的数据项?').then(function () {
return deleteBanner(id);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {
});
}
}
};
</script>
<style scoped lang="scss">
//
.img-height {
height: 150px;
}
</style>

View File

@@ -1,247 +0,0 @@
<template>
<div class="app-container">
<doc-alert title="功能开启" url="https://doc.iocoder.cn/mall/build/" />
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="品牌名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入品牌名称" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable size="small">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.COMMON_STATUS)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
v-hasPermi="['product:brand:create']">新增</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="品牌编号" align="center" prop="id"/>
<el-table-column label="品牌名称" align="center" prop="name"/>
<el-table-column label="品牌图片" align="center" prop="picUrl">
<template v-slot="scope">
<img v-if="scope.row.picUrl" :src="scope.row.picUrl" alt="分类图片" style="height: 100px;" />
</template>
</el-table-column>
<el-table-column label="品牌排序" align="center" prop="sort"/>
<el-table-column label="品牌描述" align="center" prop="description"/>
<el-table-column label="状态" align="center" prop="status">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status"/>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['product:brand:update']">修改
</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['product:brand:delete']">删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="品牌名称" prop="name">
<el-input v-model="form.name" placeholder="请输入品牌名称"/>
</el-form-item>
<el-form-item label="品牌图片" prop="picUrl">
<imageUpload v-model="form.picUrl" :limit="1"/>
</el-form-item>
<el-form-item label="品牌排序" prop="sort">
<el-input v-model="form.sort" placeholder="请输入品牌排序"/>
</el-form-item>
<el-form-item label="品牌描述" prop="description">
<el-input v-model="form.description" type="textarea" placeholder="请输入内容"/>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-radio-group v-model="form.status">
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.COMMON_STATUS)"
:key="dict.value" :label="parseInt(dict.value)">{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import {
createBrand,
deleteBrand,
getBrand,
getBrandPage,
updateBrand
} from "@/api/mall/product/brand";
import ImageUpload from '@/components/ImageUpload';
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
import {CommonStatusEnum} from "@/utils/constants";
export default {
name: "ProductBrand",
components: {
ImageUpload
},
data() {
return {
// 遮罩层
loading: true,
// 导出遮罩层
exportLoading: false,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 品牌列表
list: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
categoryId: null,
name: null,
status: null,
createTime: []
},
// 表单参数
form: {},
// 表单校验
rules: {
name: [{required: true, message: "品牌名称不能为空", trigger: "blur"}],
picUrl: [{required: true, message: "品牌图片不能为空", trigger: "blur"}],
sort: [{required: true, message: "品牌排序不能为空", trigger: "blur"}],
status: [{required: true, message: "状态不能为空", trigger: "change"}],
}
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
// 执行查询
getBrandPage(this.queryParams).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 取消按钮 */
cancel() {
this.open = false;
this.reset();
},
/** 表单重置 */
reset() {
this.form = {
id: undefined,
name: undefined,
picUrl: undefined,
sort: 0,
description: undefined,
status: CommonStatusEnum.ENABLE,
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加品牌";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const id = row.id;
getBrand(id).then(response => {
this.form = response.data;
this.open = true;
this.title = "修改品牌";
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (!valid) {
return;
}
// 修改的提交
if (this.form.id != null) {
updateBrand(this.form).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
return;
}
// 添加的提交
createBrand(this.form).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
});
},
/** 删除按钮操作 */
handleDelete(row) {
const id = row.id;
this.$modal.confirm('是否确认删除品牌编号为"' + id + '"的数据项?').then(function () {
return deleteBrand(id);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {
});
}
}
};
</script>

View File

@@ -1,270 +0,0 @@
<template>
<div class="app-container">
<doc-alert title="功能开启" url="https://doc.iocoder.cn/mall/build/" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="分类名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入分类名称" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
v-hasPermi="['product:category:create']">新增
</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="info" plain icon="el-icon-sort" size="mini" @click="toggleExpandAll">展开/折叠</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-if="refreshTable" v-loading="loading" :data="list" row-key="id" :default-expand-all="isExpandAll"
:tree-props="{children: 'children', hasChildren: 'hasChildren'}">
<el-table-column label="分类名称" prop="name"/>
<el-table-column label="分类图片" align="center" prop="picUrl">
<template v-slot="scope">
<img v-if="scope.row.picUrl" :src="scope.row.picUrl" alt="分类图片" style="height: 100px"/>
</template>
</el-table-column>
<el-table-column label="分类排序" align="center" prop="sort"/>
<el-table-column label="开启状态" align="center" prop="status">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status"/>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['product:category:update']">修改
</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['product:category:delete']">删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="上级分类" prop="parentId">
<Treeselect v-model="form.parentId" :options="parentCategoryOptions" :normalizer="normalizer" :show-count="true"
:defaultExpandLevel="1" placeholder="上级分类"/>
</el-form-item>
<el-form-item label="分类名称" prop="name">
<el-input v-model="form.name" placeholder="请输入分类名称"/>
</el-form-item>
<el-form-item label="分类图片" prop="picUrl">
<ImageUpload v-model="form.picUrl" :limit="1" :is-show-tip="false" />
<div v-if="form.parentId === 0" style="font-size: 10px">推荐 200x100 图片分辨率</div>
<div v-else style="font-size: 10px">推荐 100x100 图片分辨率</div>
</el-form-item>
<el-form-item label="分类排序" prop="sort">
<el-input-number v-model="form.sort" controls-position="right" :min="0" />
</el-form-item>
<el-form-item label="开启状态" prop="status">
<el-radio-group v-model="form.status">
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.COMMON_STATUS)"
:key="dict.value" :label="parseInt(dict.value)">{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="分类描述">
<el-input v-model="form.description" type="textarea" placeholder="请输入分类描述" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import {
createProductCategory,
deleteProductCategory,
getProductCategory,
getProductCategoryList,
updateProductCategory
} from "@/api/mall/product/category";
import Editor from '@/components/Editor';
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
import ImageUpload from '@/components/ImageUpload';
import {CommonStatusEnum} from "@/utils/constants";
export default {
name: "ProductCategory",
components: {
Editor, Treeselect, ImageUpload
},
data() {
return {
// 遮罩层
loading: true,
// 显示搜索条件
showSearch: true,
// 商品分类列表
list: [],
// 商品分类树选项
parentCategoryOptions: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 是否展开,默认全部折叠
isExpandAll: false,
// 重新渲染表格状态
refreshTable: true,
// 查询参数
queryParams: {
name: null,
},
// 表单参数
form: {},
// 表单校验
rules: {
parentId: [{required: true, message: "请选择上级分类", trigger: "blur"}],
name: [{required: true, message: "分类名称不能为空", trigger: "blur"}],
picUrl: [{required: true, message: "分类图片不能为空", trigger: "blur"}],
sort: [{required: true, message: "分类排序不能为空", trigger: "blur"}],
status: [{required: true, message: "开启状态不能为空", trigger: "blur"}],
}
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
// 处理查询参数
let params = {...this.queryParams};
// 执行查询
getProductCategoryList(params).then(response => {
this.list = this.handleTree(response.data, "id", "parentId");
this.loading = false;
// 属性下拉框
this.parentCategoryOptions = [];
const menu = {id: 0, name: '顶级分类', children: []};
menu.children = this.handleTree(response.data, "id", "parentId");
this.parentCategoryOptions.push(menu);
});
},
/** 取消按钮 */
cancel() {
this.open = false;
this.reset();
},
/** 表单重置 */
reset() {
this.form = {
id: undefined,
parentId: undefined,
name: undefined,
pirUrl: undefined,
sort: 0,
description: undefined,
status: CommonStatusEnum.ENABLE,
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 展开/折叠操作 */
toggleExpandAll() {
this.refreshTable = false;
this.isExpandAll = !this.isExpandAll;
this.$nextTick(() => {
this.refreshTable = true;
});
},
/** 转换菜单数据结构 */
normalizer(node) {
if (node.children && !node.children.length) {
delete node.children;
}
return {
id: node.id,
label: node.name,
children: node.children
};
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加商品分类";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const id = row.id;
getProductCategory(id).then(response => {
this.form = response.data;
this.open = true;
this.title = "修改商品分类";
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (!valid) {
return;
}
// 修改的提交
if (this.form.id != null) {
updateProductCategory(this.form).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
return;
}
// 添加的提交
createProductCategory(this.form).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
});
},
/** 删除按钮操作 */
handleDelete(row) {
const id = row.id;
this.$modal.confirm('是否确认删除商品分类编号为"' + id + '"的数据项?').then(function () {
return deleteProductCategory(id);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {
});
}
}
};
</script>

View File

@@ -1,207 +0,0 @@
<template>
<div class="app-container">
<doc-alert title="功能开启" url="https://doc.iocoder.cn/mall/build/" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入名称" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
v-hasPermi="['product:property:create']">新增</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="编号" align="center" prop="id" />
<el-table-column label="名称" align="center" :show-overflow-tooltip="true">
<template v-slot="scope">
<router-link :to="'/property/value/' + scope.row.id" class="link-type">
<span>{{ scope.row.name }}</span>
</router-link>
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['product:property:update']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['product:property:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="名称" prop="name">
<el-input v-model="form.name" placeholder="请输入名称" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" placeholder="备注" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { createProperty, updateProperty, deleteProperty, getProperty, getPropertyPage } from "@/api/mall/product/property";
export default {
name: "ProductProperty",
components: {
},
data() {
return {
// 遮罩层
loading: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 属性项列表
list: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
name: null,
createTime: []
},
// 表单参数
form: {
name:'',
remark:"",
id: null,
},
// 表单校验
rules: {
name: [
{ required: true, message: "名称不能为空", trigger: "blur" }
]
}
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
// 执行查询
getPropertyPage(this.queryParams).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 取消按钮 */
cancel() {
this.open = false;
this.reset();
},
/** 表单重置 */
reset() {
this.form = {
name:'',
remark:"",
id: null,
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加属性项";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const id = row.id;
getProperty(id).then(response => {
this.form = response.data;
this.open = true;
this.title = "修改属性项";
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (!valid) {
return;
}
// 修改的提交
if (this.form.id != null) {
updateProperty(this.form).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
return;
}
// 添加的提交
createProperty(this.form).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
});
},
/** 删除按钮操作 */
handleDelete(row) {
const id = row.id;
this.$modal.confirm('是否确认删除名称为"' + row.name + '"的数据项?').then(function() {
return deleteProperty(id);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
},
}
};
</script>

View File

@@ -1,240 +0,0 @@
<template>
<div class="app-container">
<doc-alert title="功能开启" url="https://doc.iocoder.cn/mall/build/" />
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="属性项" prop="propertyId">
<el-select v-model="queryParams.propertyId">
<el-option v-for="item in propertyOptions" :key="item.id" :label="item.name" :value="item.id"/>
</el-select>
</el-form-item>
<el-form-item label="名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入名称" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
v-hasPermi="['system:dict:create']">新增
</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="dataList">
<el-table-column label="编号" align="center" prop="id"/>
<el-table-column label="名称" align="center" prop="name"/>
<el-table-column label="备注" align="center" prop="remark" :show-overflow-tooltip="true"/>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['system:dict:update']">修改
</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['system:dict:delete']">删除
</el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- 添加或修改参数配置对话框 -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="90px">
<el-form-item label="属性项">
<el-input v-model="form.propertyId" :disabled="true"/>
</el-form-item>
<el-form-item label="名称" prop="name">
<el-input v-model="form.name" placeholder="请输入名称"/>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import {
createPropertyValue,
deletePropertyValue,
getProperty,
getPropertyList,
getPropertyValue,
getPropertyValuePage,
updatePropertyValue
} from '@/api/mall/product/property'
export default {
name: "ProductPropertyValue",
data() {
return {
// 遮罩层
loading: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 字典表格数据
dataList: [],
// 默认字典类型
defaultPropertyId: "",
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 类型数据字典
propertyOptions: [],
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
propertyId: undefined,
name: undefined,
},
// 表单参数
form: {},
// 表单校验
rules: {
name: [
{required: true, message: "名称不能为空", trigger: "blur"}
]
},
};
},
created() {
const propertyId = this.$route.params && this.$route.params.propertyId;
this.getProperty(propertyId);
this.getPropertyList();
},
methods: {
/** 查询字典类型详细 */
getProperty(propertyId) {
getProperty(propertyId).then(response => {
this.queryParams.propertyId = response.data.id;
this.defaultPropertyId = response.data.id;
this.getList();
});
},
/** 查询字典类型列表 */
getPropertyList() {
getPropertyList().then(response => {
this.propertyOptions = response.data
});
},
/** 查询字典数据列表 */
getList() {
this.loading = true;
getPropertyValuePage(this.queryParams).then(response => {
this.dataList = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
// 取消按钮
cancel() {
this.open = false;
this.reset();
},
// 表单重置
reset() {
this.form = {
id: undefined,
propertyId: undefined,
name: undefined,
remark: undefined
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.queryParams.propertyId = this.defaultPropertyId;
this.handleQuery();
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加属性值";
this.form.propertyId = this.queryParams.propertyId;
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const id = row.id;
getPropertyValue(id).then(response => {
this.form = response.data;
this.open = true;
this.title = "修改属性值";
});
},
/** 提交按钮 */
submitForm: function () {
this.$refs["form"].validate(valid => {
if (valid) {
if (this.form.id !== undefined) {
updatePropertyValue(this.form).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
} else {
createPropertyValue(this.form).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
}
}
});
},
/** 删除按钮操作 */
handleDelete(row) {
const ids = row.id;
this.$modal.confirm('是否确认删除字典编码为"' + ids + '"的数据项?').then(function () {
return deletePropertyValue(ids);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {
});
},
/** 导出按钮操作 */
handleExport() {
const queryParams = this.queryParams;
this.$modal.confirm('是否确认导出所有数据项?').then(() => {
this.exportLoading = true;
return exportData(queryParams);
}).then(response => {
this.$download.excel(response, '字典数据.xls');
this.exportLoading = false;
}).catch(() => {
});
}
}
};
</script>

View File

@@ -1,392 +0,0 @@
<template>
<div class="app-container">
<doc-alert title="功能开启" url="https://doc.iocoder.cn/mall/build/" />
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="商品名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入商品名称" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="商品编码" prop="code">
<el-input v-model="queryParams.code" placeholder="请输入商品编码" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="商品分类" prop="categoryIds">
<el-cascader v-model="queryParams.categoryIds" placeholder="请输入商品分类"
:options="categoryList" :props="propName" clearable ref="category"/>
</el-form-item>
<el-form-item label="商品品牌" prop="brandId">
<el-select v-model="queryParams.brandId" placeholder="请输入商品品牌" clearable @keyup.enter.native="handleQuery">
<el-option v-for="item in brandList" :key="item.id" :label="item.name" :value="item.id"/>
</el-select>
</el-form-item>
<!-- TODO 待实现商品类型 -->
<!-- TODO 待实现商品标签 -->
<!-- TODO 待实现营销活动 -->
<!-- TODO 前端优化商品销量商品价格排的整齐一点 -->
<el-form-item label="商品销量">
<el-col :span="7" style="padding-left:0">
<el-form-item prop="salesCountMin">
<el-input v-model="queryParams.salesCountMin" placeholder="最低销量" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
</el-col>
<el-col :span="1">-</el-col>
<el-col :span="7" style="padding-left:0">
<el-form-item prop="salesCountMax">
<el-input v-model="queryParams.salesCountMax" placeholder="最高销量" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
</el-col>
</el-form-item>
<el-form-item label="商品价格" prop="code">
<el-col :span="7" style="padding-left:0">
<el-form-item prop="marketPriceMin">
<el-input v-model="queryParams.marketPriceMin" placeholder="最低价格" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
</el-col>
<el-col :span="1">-</el-col>
<el-col :span="7" style="padding-left:0">
<el-form-item prop="marketPriceMax">
<el-input v-model="queryParams.marketPriceMax" placeholder="最高价格" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
</el-col>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
v-hasPermi="['product:spu:create']">添加商品</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"/>
</el-row>
<el-tabs v-model="activeTabs" type="card" @tab-click="handleClick">
<!-- 全部 -->
<el-tab-pane label="全部" name="all">
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="商品信息" align="center" width="260">
<template v-slot="scope">
<div class="product-info">
<img v-if="scope.row.picUrls" :src="scope.row.picUrls[0]" alt="分类图片" class="img-height" />
<div class="message">{{ scope.row.name }}</div>
</div>
</template>
<!-- TODO 前端优化可以有个 + 点击后展示每个 sku -->
</el-table-column>
<el-table-column label="价格" align="center" prop="marketPrice" :formatter="formatPrice"/>
<el-table-column label="库存" align="center" prop="totalStock"/>
<el-table-column label="销量" align="center" prop="salesCount"/>
<el-table-column label="排序" align="center" prop="sort"/>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="状态" align="center" prop="status">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.PRODUCT_SPU_STATUS" :value="scope.row.status"/>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['product:spu:update']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['product:spu:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<!-- 销售中 -->
<el-tab-pane label="销售中" name="on">
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="商品信息" align="center" width="260">
<template v-slot="scope">
<div class="product-info">
<img v-if="scope.row.picUrls" :src="scope.row.picUrls[0]" alt="分类图片" class="img-height"/>
<div class="message">{{ scope.row.name }}</div>
</div>
</template>
</el-table-column>
<el-table-column label="价格" align="center" prop="marketPrice" :formatter="formatPrice"/>
<el-table-column label="库存" align="center" prop="totalStock"/>
<el-table-column label="销量" align="center" prop="salesCount"/>
<el-table-column label="排序" align="center" prop="sort"/>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="状态" align="center" prop="status">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.PRODUCT_SPU_STATUS" :value="scope.row.status"/>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['product:spu:update']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['product:spu:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<!-- 仓库中 -->
<el-tab-pane label="仓库中" name="off">
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="商品信息" align="center" width="260">
<template v-slot="scope">
<div class="product-info">
<img v-if="scope.row.picUrls" :src="scope.row.picUrls[0]" alt="分类图片" class="img-height"/>
<div class="message">{{ scope.row.name }}</div>
</div>
</template>
</el-table-column>
<el-table-column label="价格" align="center" prop="marketPrice" :formatter="formatPrice"/>
<el-table-column label="库存" align="center" prop="totalStock"/>
<el-table-column label="销量" align="center" prop="salesCount"/>
<el-table-column label="排序" align="center" prop="sort"/>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="状态" align="center" prop="status">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.PRODUCT_SPU_STATUS" :value="scope.row.status"/>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['product:spu:update']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['product:spu:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<!-- 预警中 -->
<el-tab-pane label="预警中" name="remind">
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="商品信息" align="center" width="260">
<template v-slot="scope">
<div class="product-info">
<img v-if="scope.row.picUrls" :src="scope.row.picUrls[0]" alt="分类图片" class="img-height"/>
<div class="message">{{ scope.row.name }}</div>
</div>
</template>
</el-table-column>
<el-table-column label="价格" align="center" prop="marketPrice" :formatter="formatPrice"/>
<el-table-column label="库存" align="center" prop="totalStock"/>
<el-table-column label="销量" align="center" prop="salesCount"/>
<el-table-column label="排序" align="center" prop="sort"/>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="状态" align="center" prop="status">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.PRODUCT_SPU_STATUS" :value="scope.row.status"/>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['product:spu:update']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['product:spu:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
</el-tabs>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
</div>
</template>
<script>
import {deleteSpu, getSpuPage,} from "@/api/mall/product/spu";
import {getProductCategoryList} from "@/api/mall/product/category";
import {getBrandList} from "@/api/mall/product/brand";
import {ProductSpuStatusEnum} from "@/utils/constants";
export default {
name: "ProductSpu",
data() {
return {
activeTabs: "all",
propName: {
checkStrictly: true,
label: "name",
value: "id",
},
brandList: [],
categoryList: [],
// 遮罩层
loading: true,
// 导出遮罩层
exportLoading: false,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 商品spu列表
list: [],
dateRangeCreateTime: [],
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
name: null,
code: null,
categoryIds: [],
categoryId: null,
brandId: null,
status: null,
salesCountMin: null,
salesCountMax: null,
marketPriceMin: null,
marketPriceMax: null,
},
};
},
created() {
this.getList();
this.getListCategory()
this.getListBrand()
},
methods: {
/** 查询分类列表 */
getListCategory() {
// 执行查询
getProductCategoryList().then((response) => {
this.categoryList = this.handleTree(response.data, "id", "parentId");
});
},
/** 查询品牌列表 */
getListBrand() {
// 执行查询
getBrandList().then((response) => {
this.brandList = response.data;
});
},
/** 查询列表 */
getList() {
this.loading = true;
// 处理查询参数
let params = {...this.queryParams};
params.marketPriceMin = this.queryParams.marketPriceMin === null ? null : params.marketPriceMin * 100;
params.marketPriceMax = this.queryParams.marketPriceMax === null ? null : params.marketPriceMax * 100;
params.categoryId = this.queryParams.categoryIds[this.queryParams.categoryIds.length - 1];
this.addBeginAndEndTime(params, this.dateRangeCreateTime, "createTime");
// 执行查询
getSpuPage(params).then((response) => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.dateRangeCreateTime = [];
this.$refs.category.$refs.panel.checkedValue = [];//也可以是指定的值,默认返回值是数组,也可以返回单个值
this.$refs.category.$refs.panel.activePath = [];
this.$refs.category.$refs.panel.syncActivePath();
this.resetForm("queryForm");
this.handleQuery();
},
/** 新增按钮操作 */
handleAdd() {
this.$router.push({ name: 'ProductSpuCreate'})
},
/** 修改按钮操作 */
handleUpdate(row) {
this.$router.push({ name: 'ProductSpuUpdate', params: { spuId: row.id }})
},
/** 删除按钮操作 */
handleDelete(row) {
const id = row.id;
this.$modal
.confirm('是否确认删除商品spu编号为"' + id + '"的数据项?')
.then(function () {
return deleteSpu(id);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {
});
},
formatPrice(row, column, cellValue) {
return '¥' + this.divide(cellValue, 100);
},
// 选中 tab
handleClick(val) {
if (val.name === "all") {
this.queryParams.status = undefined;
this.queryParams.alarmStock = undefined;
} else if (val.name === "on") {
this.queryParams.status = ProductSpuStatusEnum.ENABLE.status;
this.queryParams.alarmStock = undefined;
} else if (val.name === "off") {
this.queryParams.status = ProductSpuStatusEnum.DISABLE.status;
this.queryParams.alarmStock = undefined;
} else if (val.name === "remind") {
this.queryParams.status = undefined;
this.queryParams.alarmStock = true;
}
this.getList();
}
},
};
</script>
<style lang="scss">
.app-container {
.el-tag + .el-tag {
margin-left: 10px;
}
.product-info {
display: flex;
.img-height {
height: 50px;
width: 50px;
}
.message {
margin-left: 10px;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
word-break: break-all;
-webkit-box-orient: vertical;
white-space: normal;
overflow: hidden;
height: 50px;
line-height: 25px;
}
}
}
</style>

View File

@@ -1,580 +0,0 @@
<template>
<div class="container">
<!-- TODO 样式优化表单宽度表单项对齐hr 粗细 -->
<el-tabs v-model="activeName" class="tabs">
<!-- 基础设置 -->
<!-- TODO @luowenfeng基础设置分成基础信息配送信息 -->
<el-tab-pane label="基础设置" name="basic">
<el-form ref="basic" :model="baseForm" :rules="rules" label-width="100px" style="width: 95%">
<el-form-item label="商品名称" prop="name">
<el-input v-model="baseForm.name" placeholder="请输入商品名称" />
</el-form-item>
<el-form-item label="促销语">
<el-input type="textarea" v-model="baseForm.sellPoint" placeholder="请输入促销语"/>
</el-form-item>
<el-form-item label="商品主图" prop="picUrls">
<ImageUpload v-model="baseForm.picUrls" :value="baseForm.picUrls" :limit="10" class="mall-image"/>
</el-form-item>
<el-form-item label="商品视频" prop="videoUrl">
<VideoUpload v-model="baseForm.videoUrl" :value="baseForm.videoUrl"/>
</el-form-item>
<el-form-item label="商品品牌" prop="brandId">
<el-select v-model="baseForm.brandId" placeholder="请选择商品品牌">
<el-option v-for="item in brandList" :key="item.id" :label="item.name" :value="item.id"/>
</el-select>
</el-form-item>
<el-form-item label="商品分类" prop="categoryIds">
<el-cascader v-model="baseForm.categoryIds" placeholder="商品分类" style="width: 100%"
:options="categoryList" :props="propName" clearable/>
</el-form-item>
<el-form-item label="是否上架" prop="status">
<el-radio-group v-model="baseForm.status">
<el-radio :label="1">立即上架</el-radio>
<el-radio :label="0">放入仓库</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
</el-tab-pane>
<!-- 价格库存 -->
<!-- TODO @luowenfengrates=priceStack 会更好哈 -->
<el-tab-pane label="价格库存" name="rates" class="rates">
<el-form ref="rates" :model="ratesForm" :rules="rules">
<el-form-item label="启用多规格">
<el-switch v-model="specSwitch" @change="changeSpecSwitch"/>
</el-form-item>
<!-- 动态添加规格属性 -->
<div v-show="ratesForm.spec === 2">
<div v-for="(specs, index) in dynamicSpec" :key="index" class="dynamic-spec">
<!-- 删除按钮 -->
<el-button type="danger" icon="el-icon-delete" circle class="spec-delete" @click="removeSpec(index)"/>
<div class="spec-header">
规格项
<el-select v-model="specs.specId" filterable placeholder="请选择" @change="changeSpec">
<el-option v-for="item in propertyPageList" :key="item.id" :label="item.name" :value="item.id"/>
</el-select>
</div>
<div class="spec-values">
<template v-for="(v, i) in specs.specValue">
<el-input v-model="v.name" class="spec-value" :key="i" disabled/>
</template>
</div>
</div>
<el-button type="primary" @click="dynamicSpec.push({specValue: []}); ratesForm.rates = []">添加规格项目</el-button>
</div>
<!-- 规格明细 -->
<el-form-item label="规格明细">
<el-table :data="ratesForm.rates" border style="width: 100%" ref="ratesTable">
<template v-if="this.specSwitch">
<el-table-column :key="index" v-for="(item, index) in dynamicSpec.filter(v => v.specName !== undefined)"
:label="item.specName">
<template v-slot="scope">
<el-input v-if="scope.row.spec" v-model="scope.row.spec[index]" disabled/>
</template>
</el-table-column>
</template>
<el-table-column label="规格图片" width="120px" :render-header="addRedStar" key="90">
<template v-slot="scope">
<ImageUpload v-model="scope.row.picUrl" :limit="1" :isShowTip="false" style="width: 100px; height: 50px"/>
</template>
</el-table-column>
<el-table-column label="市场价(元)" :render-header="addRedStar" key="92">
<template v-slot="scope">
<el-form-item :prop="'rates.'+ scope.$index + '.marketPrice'" :rules="[{required: true, trigger: 'change'}]">
<el-input v-model="scope.row.marketPrice"
oninput="value= value.match(/\d+(\.\d{0,2})?/) ? value.match(/\d+(\.\d{0,2})?/)[0] : ''"/>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="销售价(元)" :render-header="addRedStar" key="93">
<template v-slot="scope">
<el-form-item :prop="'rates.'+ scope.$index + '.price'"
:rules="[{required: true, trigger: 'change'}]">
<el-input v-model="scope.row.price"
oninput="value= value.match(/\d+(\.\d{0,2})?/) ? value.match(/\d+(\.\d{0,2})?/)[0] : ''" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="成本价" :render-header="addRedStar" key="94">
<template v-slot="scope">
<el-form-item :prop="'rates.'+ scope.$index + '.costPrice'"
:rules="[{required: true, trigger: 'change'}]">
<el-input v-model="scope.row.costPrice"
oninput="value= value.match(/\d+(\.\d{0,2})?/) ? value.match(/\d+(\.\d{0,2})?/)[0] : ''" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="库存" :render-header="addRedStar" key="95">
<template v-slot="scope">
<el-form-item :prop="'rates.'+ scope.$index + '.stock'" :rules="[{required: true, trigger: 'change'}]">
<el-input v-model="scope.row.stock" oninput="value=value.replace(/^(0+)|[^\d]+/g,'')"></el-input>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="预警库存" key="96">
<template v-slot="scope">
<el-input v-model="scope.row.warnStock" oninput="value=value.replace(/^(0+)|[^\d]+/g,'')"></el-input>
</template>
</el-table-column>
<el-table-column label="体积" key="97">
<template v-slot="scope">
<el-input v-model="scope.row.volume" />
</template>
</el-table-column>
<el-table-column label="重量" key="98">
<template v-slot="scope">
<el-input v-model="scope.row.weight" />
</template>
</el-table-column>
<el-table-column label="条码" key="99">
<template v-slot="scope">
<el-input v-model="scope.row.barCode" />
</template>
</el-table-column>
<template v-if="this.specSwitch">
<el-table-column fixed="right" label="操作" width="50" key="100">
<template v-slot="scope">
<el-button @click="scope.row.status = 1" type="text" size="small"
v-show="scope.row.status === undefined || scope.row.status === 0 ">禁用
</el-button>
<el-button @click="scope.row.status = 0" type="text" size="small" v-show="scope.row.status === 1">
启用
</el-button>
</template>
</el-table-column>
</template>
</el-table>
</el-form-item>
<el-form-item label="虚拟销量" prop="virtualSalesCount">
<el-input v-model="baseForm.virtualSalesCount" placeholder="请输入虚拟销量"
oninput="value=value.replace(/^(0+)|[^\d]+/g,'')"/>
</el-form-item>
</el-form>
</el-tab-pane>
<!-- 商品详情 -->
<el-tab-pane label="商品详情" name="detail">
<el-form ref="detail" :model="baseForm" :rules="rules">
<el-form-item prop="description">
<editor v-model="baseForm.description" :min-height="380"/>
</el-form-item>
</el-form>
</el-tab-pane>
<!-- 销售设置 -->
<el-tab-pane label="高级设置" name="senior">
<el-form ref="senior" :model="baseForm" :rules="rules" label-width="100px" style="width: 95%">
<el-form-item label="排序字段">
<el-input v-model="baseForm.sort" placeholder="请输入排序字段" oninput="value=value.replace(/^(0+)|[^\d]+/g,'')"/>
</el-form-item>
<el-form-item label="是否展示库存" prop="showStock">
<el-radio-group v-model="baseForm.showStock">
<el-radio :label="true"></el-radio>
<el-radio :label="false"></el-radio>
</el-radio-group>
</el-form-item>
</el-form>
</el-tab-pane>
</el-tabs>
<div class="buttons">
<el-button type="info" round @click="cancel">取消</el-button>
<el-button type="success" round @click="submit">确认</el-button>
</div>
</div>
</template>
<script>
import {getBrandList} from "@/api/mall/product/brand";
import {getProductCategoryList} from "@/api/mall/product/category";
import {createSpu, getSpuDetail, updateSpu} from "@/api/mall/product/spu";
import {getPropertyListAndValue,} from "@/api/mall/product/property";
import Editor from "@/components/Editor";
import ImageUpload from "@/components/ImageUpload";
import VideoUpload from "@/components/VideoUpload";
export default {
name: "ProductSave",
components: {
Editor,
ImageUpload,
VideoUpload
},
data() {
return {
specSwitch: false,
activeName: "basic",
propName: {
checkStrictly: true,
label: "name",
value: "id",
},
// 基础设置
baseForm: {
id: null,
name: null,
sellPoint: null,
categoryIds: null,
sort: null,
description: null,
picUrls: null,
videoUrl: null,
status: 0,
virtualSalesCount: 0,
showStock: true,
brandId: null,
},
categoryList: [],
// 价格库存
ratesForm: {
spec: 1,
// 规格明细
rates: [{}]
},
dynamicSpec: [
// {
// specId: 86,
// specName: "颜色",
// specValue:[{
// name: "红色",
// id: 225,
// }]
// },
],
propertyPageList: [],
brandList: [],
specValue: null,
// 表单校验
rules: {
name: [{required: true, message: "商品名称不能为空", trigger: "blur"},],
description: [{required: true, message: "描述不能为空", trigger: "blur"},],
categoryIds: [{required: true, message: "分类id不能为空", trigger: "blur"},],
status: [{required: true, message: "商品状态不能为空", trigger: "blur"}],
brandId: [{required: true, message: "商品品牌不能为空", trigger: "blur"}],
picUrls: [{required: true, message: "商品轮播图地址不能为空", trigger: "blur"}],
},
};
},
created() {
this.getListBrand();
this.getListCategory();
this.getPropertyPageList();
const spuId = this.$route.params && this.$route.params.spuId;
if (spuId != null) {
this.updateType(spuId)
}
},
methods: {
removeSpec(index) {
this.dynamicSpec.splice(index, 1);
this.changeSpecSwitch()
},
// 必选标识
addRedStar(h, {column}) {
return [
h('span', {style: 'color: #F56C6C'}, '*'),
h('span', ' ' + column.label)
];
},
changeSpecSwitch() {
this.specSwitch ? this.ratesForm.spec = 2 : this.ratesForm.spec = 1;
this.$refs.ratesTable.doLayout();
if (this.ratesForm.spec === 1) {
this.ratesForm.rates = [{}]
} else {
this.ratesForm.rates = []
if (this.dynamicSpec.length > 0) {
this.buildRatesFormRates()
}
}
},
// 构建规格明细笛卡尔积
buildRatesFormRates() {
let rates = [];
this.dynamicSpec.map(v => v.specValue.map(m => m.name))
.reduce((last, current) => {
const array = [];
last.forEach(par1 => {
current.forEach(par2 => {
let v
// 当两个对象合并时,需使用[1,2]方式生成数组而当数组和对象合并时需使用concat
if (par1 instanceof Array) {
v = par1.concat(par2)
} else {
v = [par1, par2];
}
array.push(v)
});
});
return array;
})
.forEach(v => {
let spec = v;
// 当v为单个规格项时会变成字符串。造成表格只截取第一个字符串而不是数组的第一个元素
if (typeof v == 'string') {
spec = Array.of(v)
}
rates.push({spec: spec, status: 0, name: Array.of(v).join()})
});
this.ratesForm.rates = rates
},
/** 查询分类 */
getListCategory() {
// 执行查询
getProductCategoryList().then((response) => {
this.categoryList = this.handleTree(response.data, "id", "parentId");
});
},
/** 查询品牌列表 */
getListBrand() {
// 执行查询
getBrandList().then((response) => {
this.brandList = response.data;
});
},
// 取消按钮
cancel() {
var currentView = this.$store.state.tagsView.visitedViews[0]
for (currentView of this.$store.state.tagsView.visitedViews) {
if (currentView.path === this.$route.path) {
break
}
}
this.$store.dispatch('tagsView/delView', currentView)
.then(() => {
this.$router.push("/product/spu")
})
},
submit() {
this.$refs[this.activeName].validate((valid) => {
if (!valid) {
return;
}
let rates = JSON.parse(JSON.stringify(this.ratesForm.rates));
// 价格元转分
rates.forEach(r => {
r.marketPrice = r.marketPrice * 100;
r.price = r.price * 100;
r.costPrice = r.costPrice * 100;
})
// 动态规格调整字段
if (this.specSwitch) {
rates.forEach(r => {
let properties = []
Array.of(r.spec).forEach(s => {
let obj;
if (s instanceof Array) {
obj = s;
} else {
obj = Array.of(s);
}
obj.forEach((v, i) => {
let specValue = this.dynamicSpec[i].specValue.find(o => o.name === v);
let propertie = {};
propertie.propertyId = this.dynamicSpec[i].specId;
propertie.valueId = specValue.id;
properties.push(propertie);
})
})
r.properties = properties;
})
} else {
rates[0].name = this.baseForm.name;
rates[0].status = this.baseForm.status;
}
let form = this.baseForm
if (form.picUrls instanceof Array) {
form.picUrls = form.picUrls.flatMap(m => m.split(','))
} else if (form.picUrls.split(',') instanceof Array) {
form.picUrls = form.picUrls.split(',').flatMap(m => m.split(','))
} else {
form.picUrls = Array.of(form.picUrls)
}
form.skus = rates;
form.specType = this.ratesForm.spec;
let category = form.categoryIds instanceof Array ? form.categoryIds: Array.of(form.categoryIds)
console.log(category)
form.categoryId = category[category.length - 1];
if (form.id == null) {
createSpu(form).then(() => {
this.$modal.msgSuccess("新增成功");
}).then(()=>{
this.cancel();
})
} else {
updateSpu(form).then(() => {
this.$modal.msgSuccess("修改成功");
}).then(()=>{
this.cancel();
})
}
});
},
/** 查询规格 */
getPropertyPageList() {
// 执行查询
getPropertyListAndValue().then((response) => {
this.propertyPageList = response.data;
});
},
// 添加规格项目
changeSpec(val) {
let obj = this.propertyPageList.find(o => o.id === val);
let spec = this.dynamicSpec.find(o => o.specId === val)
spec.specId = obj.id;
spec.specName = obj.name;
spec.specValue = obj.values;
this.buildRatesFormRates();
},
updateType(id) {
getSpuDetail(id).then((response) => {
let data = response.data;
this.baseForm.id = data.id;
this.baseForm.name = data.name;
this.baseForm.sellPoint = data.sellPoint;
this.baseForm.categoryIds = data.categoryId;
this.baseForm.videoUrl = data.videoUrl;
this.baseForm.sort = data.sort;
this.baseForm.description = data.description;
this.baseForm.picUrls = data.picUrls;
this.baseForm.status = data.status;
this.baseForm.virtualSalesCount = data.virtualSalesCount;
this.baseForm.showStock = data.showStock;
this.baseForm.brandId = data.brandId;
this.ratesForm.spec = data.specType;
data.skus.forEach(r => {
r.marketPrice = this.divide(r.marketPrice, 100)
r.price = this.divide(r.price, 100)
r.costPrice = this.divide(r.costPrice, 100)
})
if (this.ratesForm.spec === 2) {
this.specSwitch = true;
data.productPropertyViews.forEach(p => {
let obj = {};
obj.specId = p.propertyId;
obj.specName = p.name;
obj.specValue = p.propertyValues;
this.dynamicSpec.push(obj);
})
data.skus.forEach(s => {
s.spec = [];
s.properties.forEach(sp => {
let spec = data.productPropertyViews.find(o => o.propertyId === sp.propertyId).propertyValues.find(v => v.id === sp.valueId).name;
s.spec.push(spec)
})
})
}
this.ratesForm.rates = data.skus
})
},
},
};
</script>
<style lang="scss">
.container{
padding: 20px;
}
.dynamic-spec {
background-color: #f2f2f2;
width: 85%;
margin: auto;
margin-bottom: 10px;
.spec-header {
padding: 30px;
padding-bottom: 20px;
.spec-name {
display: inline;
input {
width: 30%;
}
}
}
.spec-values {
width: 84%;
padding: 25px;
margin: auto;
padding-top: 5px;
.spec-value {
display: inline-block;
margin-right: 10px;
margin-bottom: 10px;
width: 13%;
}
}
.spec-delete {
float: right;
margin-top: 10px;
margin-right: 10px;
}
}
.tabs {
border-bottom: 2px solid #f2f2f2;
.el-tab-pane {
overflow-y: auto;
}
}
// 库存价格图片样式修改
.rates {
.component-upload-image {
margin: auto;
}
.el-upload--picture-card {
width: 100px;
height: 50px;
line-height: 60px;
margin: auto;
}
.el-upload-list__item {
width: 100px !important;
height: 50px !important;
}
}
.buttons {
margin-top: 20px;
height: 36px;
button {
float: right;
margin-left: 15px;
}
}
.mall-image {
.el-upload--picture-card {
width: 80px;
height: 80px;
line-height: 90px;
}
.el-upload-list__item {
width: 80px;
height: 80px;
}
}
</style>

View File

@@ -1,164 +0,0 @@
<template>
<div class="app-container">
<doc-alert title="功能开启" url="https://doc.iocoder.cn/mall/build/" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="会员昵称" prop="nickname">
<el-input v-model="queryParams.nickname" placeholder="请输入会员昵称" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- Tab 选项真正的内容在 Lab -->
<el-tabs v-model="activeTab" type="card" @tab-click="tabClick" style="margin-top: -40px;">
<el-tab-pane v-for="tab in statusTabs" :key="tab.value" :label="tab.label" :name="tab.value" />
</el-tabs>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="会员信息" align="center" prop="nickname" /> <!-- TODO 芋艿以后支持头像支持跳转 -->
<el-table-column label="优惠劵" align="center" prop="name" />
<el-table-column label="优惠券类型" align="center" prop="discountType">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.PROMOTION_DISCOUNT_TYPE" :value="scope.row.discountType" />
</template>
</el-table-column>
<el-table-column label="领取方式" align="center" prop="takeType">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.PROMOTION_COUPON_TAKE_TYPE" :value="scope.row.takeType" />
</template>
</el-table-column>
<el-table-column label="状态" align="center" prop="status">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.PROMOTION_COUPON_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="领取时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="使用时间" align="center" prop="useTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.useTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['promotion:coupon:delete']">回收</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
</div>
</template>
<script>
import { deleteCoupon, getCouponPage } from "@/api/mall/promotion/coupon";
import { DICT_TYPE, getDictDatas} from "@/utils/dict";
export default {
name: "PromotionCoupon",
components: {
},
data() {
return {
// 遮罩层
loading: true,
// 导出遮罩层
exportLoading: false,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 优惠劵列表
list: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
createTime: [],
status: undefined,
},
// Tab 筛选
activeTab: 'all',
statusTabs: [{
label: '全部',
value: 'all'
}],
};
},
created() {
this.getList();
// 设置 statuses 过滤
for (const dict of getDictDatas(DICT_TYPE.PROMOTION_COUPON_STATUS)) {
this.statusTabs.push({
label: dict.label,
value: dict.value
})
}
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
// 执行查询
getCouponPage(this.queryParams).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 取消按钮 */
cancel() {
this.open = false;
this.reset();
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 删除按钮操作 */
handleDelete(row) {
const id = row.id;
this.$modal.confirm('回收将会收回会员领取的待使用的优惠券,已使用的将无法回收,确定要回收所选优惠券吗?').then(function() {
return deleteCoupon(id);
}).then(() => {
this.getList();
this.$modal.msgSuccess("回收成功");
}).catch(() => {});
},
/** tab 切换 */
tabClick(tab) {
this.queryParams.status = tab.name === 'all' ? undefined : tab.name;
this.getList();
}
}
};
</script>

View File

@@ -1,404 +0,0 @@
<template>
<div class="app-container">
<doc-alert title="功能开启" url="https://doc.iocoder.cn/mall/build/" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="82px">
<el-form-item label="优惠券名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入优惠劵名" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="优惠券类型" prop="discountType">
<el-select v-model="queryParams.discountType" placeholder="请选择优惠券类型" clearable size="small">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.PROMOTION_DISCOUNT_TYPE)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="优惠券状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择优惠券状态" clearable size="small">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.COMMON_STATUS)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
v-hasPermi="['promotion:coupon-template:create']">新增</el-button>
<el-button type="info" plain icon="el-icon-s-operation" size="mini"
@click="() => this.$router.push('/promotion/coupon')"
v-hasPermi="['promotion:coupon:query']">会员优惠劵</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="优惠券名称" align="center" prop="name" />
<el-table-column label="优惠券类型" align="center" prop="discountType">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.PROMOTION_DISCOUNT_TYPE" :value="scope.row.discountType" />
</template>
</el-table-column>
<el-table-column label="优惠金额 / 折扣" align="center" prop="discount" :formatter="discountFormat" />
<el-table-column label="发放数量" align="center" prop="totalCount" />
<el-table-column label="剩余数量" align="center" prop="totalCount" :formatter="row => (row.totalCount - row.takeCount)" />
<el-table-column label="领取上限" align="center" prop="takeLimitCount" :formatter="takeLimitCountFormat" />
<el-table-column label="有效期限" align="center" prop="validityType" width="180" :formatter="validityTypeFormat" />
<el-table-column label="状态" align="center" prop="status">
<template v-slot="scope">
<el-switch v-model="scope.row.status" :active-value="0" :inactive-value="1" @change="handleStatusChange(scope.row)"/>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['promotion:coupon-template:update']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['promotion:coupon-template:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="title" :visible.sync="open" width="600px" v-dialogDrag append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="140px">
<el-form-item label="优惠券名称" prop="name">
<el-input v-model="form.name" placeholder="请输入优惠券名称" />
</el-form-item>
<el-form-item label="优惠券类型" prop="discountType">
<el-radio-group v-model="form.discountType">
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.PROMOTION_DISCOUNT_TYPE)"
:key="dict.value" :label="parseInt(dict.value)">{{dict.label}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="form.discountType === PromotionDiscountTypeEnum.PRICE.type" label="优惠券面额" prop="discountPrice">
<el-input-number v-model="form.discountPrice" placeholder="请输入优惠金额,单位:元"
style="width: 400px" :precision="2" :min="0" />
</el-form-item>
<el-form-item v-if="form.discountType === PromotionDiscountTypeEnum.PERCENT.type" label="优惠券折扣" prop="discountPercent">
<el-input-number v-model="form.discountPercent" placeholder="优惠券折扣不能小于 1 折,且不可大于 9.9 折"
style="width: 400px" :precision="1" :min="1" :max="9.9" />
</el-form-item>
<el-form-item v-if="form.discountType === PromotionDiscountTypeEnum.PERCENT.type" label="最多优惠" prop="discountLimitPrice">
<el-input-number v-model="form.discountLimitPrice" placeholder="请输入最多优惠"
style="width: 400px" :precision="2" :min="0" />
</el-form-item>
<el-form-item label="满多少元可以使用" prop="usePrice">
<el-input-number v-model="form.usePrice" placeholder="无门槛请设为 0"
style="width: 400px" :precision="2" :min="0" />
</el-form-item>
<el-form-item label="领取方式" prop="takeType">
<el-radio-group v-model="form.takeType">
<el-radio :key="1" :label="1">直接领取</el-radio>
<el-radio :key="2" :label="2">指定发放</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="form.takeType === 1" label="发放数量" prop="totalCount">
<el-input-number v-model="form.totalCount" placeholder="发放数量,没有之后不能领取或发放,-1 为不限制"
style="width: 400px" :precision="0" :min="-1" />
</el-form-item>
<el-form-item v-if="form.takeType === 1" label="每人限领个数" prop="takeLimitCount">
<el-input-number v-model="form.takeLimitCount" placeholder="设置为 -1 时,可无限领取"
style="width: 400px" :precision="0" :min="-1" />
</el-form-item>
<el-form-item label="有效期类型" prop="validityType">
<el-radio-group v-model="form.validityType">
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.PROMOTION_COUPON_TEMPLATE_VALIDITY_TYPE)"
:key="dict.value" :label="parseInt(dict.value)">{{dict.label}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="form.validityType === CouponTemplateValidityTypeEnum.DATE.type" label="固定日期" prop="validTimes">
<el-date-picker v-model="form.validTimes" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="datetimerange"
:default-time="['00:00:00', '23:59:59']" />
</el-form-item>
<el-form-item v-if="form.validityType === CouponTemplateValidityTypeEnum.TERM.type" label="领取日期" prop="fixedStartTerm">
<el-input-number v-model="form.fixedStartTerm" placeholder="0 为今天生效"
style="width: 165px" :precision="0" :min="0"/>
<el-input-number v-model="form.fixedEndTerm" placeholder="请输入结束天数"
style="width: 165px" :precision="0" :min="0"/> 天有效
</el-form-item>
<el-form-item label="活动商品" prop="productScope">
<el-radio-group v-model="form.productScope">
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.PROMOTION_PRODUCT_SCOPE)"
:key="dict.value" :label="parseInt(dict.value)">{{dict.label}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="form.productScope === PromotionProductScopeEnum.SPU.scope" prop="productSpuIds">
<el-select v-model="form.productSpuIds" placeholder="请选择活动商品" clearable size="small"
multiple filterable style="width: 400px">
<el-option v-for="item in productSpus" :key="item.id" :label="item.name" :value="item.id">
<span style="float: left">{{ item.name }}</span>
<span style="float: right; color: #8492a6; font-size: 13px">{{ (item.minPrice / 100.0).toFixed(2) }}</span>
</el-option>
</el-select>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import {
createCouponTemplate,
updateCouponTemplate,
deleteCouponTemplate,
getCouponTemplate,
getCouponTemplatePage,
updateCouponTemplateStatus
} from "@/api/mall/promotion/couponTemplate";
import {
CommonStatusEnum,
CouponTemplateValidityTypeEnum,
PromotionDiscountTypeEnum,
PromotionProductScopeEnum
} from "@/utils/constants";
import { getSpuSimpleList } from "@/api/mall/product/spu";
import { parseTime } from "@/utils/ruoyi";
import {changeRoleStatus} from "@/api/system/role";
export default {
name: "PromotionCouponTemplate",
components: {
},
data() {
return {
// 遮罩层
loading: true,
// 导出遮罩层
exportLoading: false,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 优惠劵列表
list: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
name: null,
status: null,
type: null,
createTime: [],
},
// 表单参数
form: {},
// 表单校验
rules: {
name: [{ required: true, message: "优惠券名称不能为空", trigger: "blur" }],
discountType: [{ required: true, message: "优惠券类型不能为空", trigger: "change" }],
discountPrice: [{ required: true, message: "优惠券面额不能为空", trigger: "blur" }],
discountPercent: [{ required: true, message: "优惠券折扣不能为空", trigger: "blur" }],
discountLimitPrice: [{ required: true, message: "最多优惠不能为空", trigger: "blur" }],
usePrice: [{ required: true, message: "满多少元可以使用不能为空", trigger: "blur" }],
takeType: [{ required: true, message: "领取方式不能为空", trigger: "change" }],
totalCount: [{ required: true, message: "发放数量不能为空", trigger: "blur" }],
takeLimitCount: [{ required: true, message: "每人限领个数不能为空", trigger: "blur" }],
validityType: [{ required: true, message: "有效期类型不能为空", trigger: "change" }],
validTimes: [{ required: true, message: "固定日期不能为空", trigger: "change" }],
fixedStartTerm: [{ required: true, message: "开始领取天数不能为空", trigger: "blur" }],
fixedEndTerm: [{ required: true, message: "开始领取天数不能为空", trigger: "blur" }],
productScope: [{ required: true, message: "商品范围不能为空", trigger: "blur" }],
productSpuIds: [{ required: true, message: "商品范围不能为空", trigger: "blur" }],
},
// 商品列表
productSpus: [],
// 如下的变量,主要为了 v-if 判断可以使用到
PromotionProductScopeEnum: PromotionProductScopeEnum,
CouponTemplateValidityTypeEnum: CouponTemplateValidityTypeEnum,
PromotionDiscountTypeEnum: PromotionDiscountTypeEnum,
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
// 执行查询
getCouponTemplatePage(this.queryParams).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
// 查询商品列表
getSpuSimpleList().then(response => {
this.productSpus = response.data
})
},
/** 取消按钮 */
cancel() {
this.open = false;
this.reset();
},
/** 表单重置 */
reset() {
this.form = {
id: undefined,
name: undefined,
discountType: PromotionDiscountTypeEnum.PRICE.type,
discountPrice: undefined,
discountPercent: undefined,
discountLimitPrice: undefined,
usePrice: undefined,
takeType: 1,
totalCount: undefined,
takeLimitCount: undefined,
validityType: CouponTemplateValidityTypeEnum.DATE.type,
validTimes: [],
validStartTime: undefined,
validEndTime: undefined,
fixedStartTerm: undefined,
fixedEndTerm: undefined,
productScope: PromotionProductScopeEnum.ALL.scope,
productSpuIds: [],
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加优惠劵";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const id = row.id;
getCouponTemplate(id).then(response => {
this.form = {
...response.data,
discountPrice: response.data.discountPrice !== undefined ? response.data.discountPrice / 100.0 : undefined,
discountPercent: response.data.discountPercent !== undefined ? response.data.discountPercent / 10.0 : undefined,
discountLimitPrice: response.data.discountLimitPrice !== undefined ? response.data.discountLimitPrice / 100.0 : undefined,
usePrice: response.data.usePrice !== undefined ? response.data.usePrice / 100.0 : undefined,
validTimes: [response.data.validStartTime, response.data.validEndTime]
}
this.open = true;
this.title = "修改优惠劵";
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (!valid) {
return;
}
// 金额相关字段的缩放
let data = {
...this.form,
discountPrice: this.form.discountPrice !== undefined ? this.form.discountPrice * 100 : undefined,
discountPercent: this.form.discountPercent !== undefined ? this.form.discountPercent * 10 : undefined,
discountLimitPrice: this.form.discountLimitPrice !== undefined ? this.form.discountLimitPrice * 100 : undefined,
usePrice: this.form.usePrice !== undefined ? this.form.usePrice * 100 : undefined,
validStartTime: this.form.validTimes && this.form.validTimes.length === 2 ? this.form.validTimes[0] : undefined,
validEndTime: this.form.validTimes && this.form.validTimes.length === 2 ? this.form.validTimes[1] : undefined,
}
// 修改的提交
if (this.form.id != null) {
updateCouponTemplate(data).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
return;
}
// 添加的提交
createCouponTemplate(data).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
});
},
/** 优惠劵模板状态修改 */
handleStatusChange(row) {
// 此时row 已经变成目标状态了,所以可以直接提交请求和提示
let text = row.status === CommonStatusEnum.ENABLE ? "启用" : "停用";
this.$modal.confirm('确认要"' + text + '""' + row.name + '"优惠劵吗?').then(function() {
return updateCouponTemplateStatus(row.id, row.status);
}).then(() => {
this.$modal.msgSuccess(text + "成功");
}).catch(function() {
// 异常时,需要将 row.status 状态重置回之前的
row.status = row.status === CommonStatusEnum.ENABLE ? CommonStatusEnum.DISABLE
: CommonStatusEnum.ENABLE;
});
},
/** 删除按钮操作 */
handleDelete(row) {
const id = row.id;
this.$modal.confirm('是否确认删除优惠劵编号为"' + id + '"的数据项?').then(function() {
return deleteCouponTemplate(id);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
},
// 格式化【优惠金额/折扣】
discountFormat(row, column) {
if (row.discountType === PromotionDiscountTypeEnum.PRICE.type) {
return `${(row.discountPrice / 100.0).toFixed(2)}`;
}
if (row.discountType === PromotionDiscountTypeEnum.PERCENT.type) {
return `${(row.discountPrice / 100.0).toFixed(2)}`;
}
return '未知【' + row.discountType + '】';
},
// 格式化【领取上限】
takeLimitCountFormat(row, column) {
if (row.takeLimitCount === -1) {
return '无领取限制';
}
return `${row.takeLimitCount} 张/人`
},
// 格式化【有效期限】
validityTypeFormat(row, column) {
if (row.validityType === CouponTemplateValidityTypeEnum.DATE.type) {
return `${parseTime(row.validStartTime)}${parseTime(row.validEndTime)}`
}
if (row.validityType === CouponTemplateValidityTypeEnum.TERM.type) {
return `领取后第 ${row.fixedStartTerm} - ${row.fixedEndTerm} 天内可用`
}
return '未知【' + row.validityType + '】';
}
}
};
</script>

View File

@@ -1,383 +0,0 @@
<template>
<div class="app-container">
<doc-alert title="功能开启" url="https://doc.iocoder.cn/mall/build/" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="活动名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入活动名称" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="活动状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择活动状态" clearable size="small">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.PROMOTION_ACTIVITY_STATUS)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
v-hasPermi="['promotion:discount-activity:create']">新增</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="活动名称" align="center" prop="name" />
<el-table-column label="活动时间" align="center" prop="startTime" width="240">
<template v-slot="scope">
<div>开始{{ parseTime(scope.row.startTime) }}</div>
<div>结束{{ parseTime(scope.row.endTime) }}</div>
</template>
</el-table-column>
<el-table-column label="状态" align="center" prop="status">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.PROMOTION_ACTIVITY_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-if="scope.row.status !== PromotionActivityStatusEnum.CLOSE.type"
v-hasPermi="['promotion:discount-activity:update']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleClose(scope.row)"
v-if="scope.row.status !== PromotionActivityStatusEnum.CLOSE.type &&
scope.row.status !== PromotionActivityStatusEnum.END.type"
v-hasPermi="['promotion:discount-activity:close']">关闭</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-if="scope.row.status === PromotionActivityStatusEnum.CLOSE.type"
v-hasPermi="['promotion:discount-activity:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="title" :visible.sync="open" width="1000px" v-dialogDrag append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="活动名称" prop="name">
<el-input v-model="form.name" placeholder="请输入活动名称" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input type="textarea" v-model="form.remark" placeholder="请输入备注" />
</el-form-item>
<el-form-item label="活动时间" prop="startAndEndTime">
<el-date-picker clearable v-model="form.startAndEndTime" type="datetimerange" :default-time="['00:00:00', '23:59:59']"
value-format="timestamp" placeholder="选择开始时间" style="width: 880px" />
</el-form-item>
<el-form-item label="商品选择">
<el-select v-model="form.skuIds" placeholder="请选择活动商品" clearable size="small"
multiple filterable style="width: 880px" @change="changeFormSku">
<el-option v-for="item in productSkus" :key="item.id" :label="item.spuName + ' ' + item.name" :value="item.id">
<span style="float: left">{{ item.spuName }} &nbsp; {{ item.name}}</span>
<span style="float: right; color: #8492a6; font-size: 13px">¥{{ (item.price / 100.0).toFixed(2) }}</span>
</el-option>
</el-select>
<el-table v-loading="loading" :data="form.products">
<el-table-column label="商品名称" align="center" width="200">
<template v-slot="scope">
{{ scope.row.spuName }} &nbsp; {{ scope.row.name}}
</template>
</el-table-column>
<el-table-column label="商品价格" align="center" prop="price">
<template v-slot="scope">
¥{{ (scope.row.price / 100.0).toFixed(2) }}
</template>
</el-table-column>
<el-table-column label="库存" align="center" prop="stock" />
<el-table-column label="优惠类型" align="center" property="discountType">
<template v-slot="scope">
<el-select v-model="scope.row.discountType" placeholder="请选择优惠类型">
<el-option v-for="dict in getDictDatas(DICT_TYPE.PROMOTION_DISCOUNT_TYPE)"
:key="dict.value" :label="dict.label" :value="parseInt(dict.value)"/>
</el-select>
</template>
</el-table-column>
<el-table-column label="优惠" align="center" prop="startTime" width="250">
<template v-slot="scope">
<el-form-item v-if="scope.row.discountType === PromotionDiscountTypeEnum.PRICE.type" prop="discountPrice">
减 <el-input-number v-model="scope.row.discountPrice" placeholder="请输入优惠金额"
style="width: 190px" :precision="2" :min="0" :max="scope.row.price / 100.0 - 0.01" /> 元
</el-form-item>
<el-form-item v-if="scope.row.discountType === PromotionDiscountTypeEnum.PERCENT.type" prop="discountPercent">
打 <el-input-number v-model="scope.row.discountPercent" placeholder="请输入优惠折扣"
style="width: 190px" :precision="1" :min="1" :max="9.9" /> 折
</el-form-item>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-delete" @click="removeFormSku(scope.row.skuId)">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm">确 定</el-button>
<el-button @click="cancel">取 消</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import {
createDiscountActivity,
updateDiscountActivity,
deleteDiscountActivity,
getDiscountActivity,
getDiscountActivityPage,
closeDiscountActivity
} from "@/api/mall/promotion/discountActivity";
import {
PromotionActivityStatusEnum, PromotionDiscountTypeEnum,
PromotionProductScopeEnum
} from "@/utils/constants";
import { getSkuOptionList } from "@/api/mall/product/sku";
import { deepClone } from "@/utils";
export default {
name: "PromotionDiscountActivity",
components: {
},
data() {
return {
// 遮罩层
loading: true,
// 导出遮罩层
exportLoading: false,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 限时折扣活动列表
list: [],
// 弹出层名称
title: "",
// 是否显示弹出层
open: false,
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
name: null,
status: null,
createTime: [],
},
// 表单参数
form: {
skuIds: [], // 选中的 SKU
products: [], // 商品信息
},
// 表单校验
rules: {
name: [{ required: true, message: "活动名称不能为空", trigger: "blur" }],
startAndEndTime: [{ required: true, message: "活动时间不能为空", trigger: "blur" }],
skuIds: [{ required: true, message: "选择商品不能为空", trigger: "blur" }],
},
// 商品 SKU 列表
productSkus: [],
// 如下的变量主要为了 v-if 判断可以使用到
PromotionProductScopeEnum: PromotionProductScopeEnum,
PromotionActivityStatusEnum: PromotionActivityStatusEnum,
PromotionDiscountTypeEnum: PromotionDiscountTypeEnum,
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
// 执行查询
getDiscountActivityPage(this.queryParams).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
// 获得 SKU 商品列表
getSkuOptionList().then(response => {
this.productSkus = response.data;
});
},
/** 取消按钮 */
cancel() {
this.open = false;
this.reset();
},
/** 表单重置 */
reset() {
this.form = {
id: undefined,
name: undefined,
startAndEndTime: undefined,
startTime: undefined,
endTime: undefined,
remark: undefined,
skuIds: [],
products: [],
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加限时折扣活动";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const id = row.id;
getDiscountActivity(id).then(response => {
this.form = response.data;
// 修改数据
this.form.startAndEndTime = [response.data.startTime, response.data.endTime];
this.form.skuIds = response.data.products.map(item => item.skuId);
this.form.products.forEach(product => {
// 获得对应的 SKU 信息
const sku = this.productSkus.find(item => item.id === product.skuId);
if (!sku) {
return;
}
// 设置商品信息
product.name = sku.name;
product.spuName = sku.spuName;
product.price = sku.price;
product.stock = sku.stock;
product.discountPrice = product.discountPrice !== undefined ? product.discountPrice / 100.0 : undefined;
product.discountPercent = product.discountPercent !== undefined ? product.discountPercent / 10.0 : undefined;
});
// 打开弹窗
this.open = true;
this.title = "修改限时折扣活动";
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (!valid) {
return;
}
// 处理数据
const data = deepClone(this.form); // 必须深拷贝,不然后面的 products 操作会有影响
data.startTime = this.form.startAndEndTime[0];
data.endTime = this.form.startAndEndTime[1];
data.products.forEach(product => {
product.discountPrice = product.discountPrice !== undefined ? product.discountPrice * 100 : undefined;
product.discountPercent = product.discountPercent !== undefined ? product.discountPercent * 10 : undefined;
});
if (!valid) {
return;
}
// 修改的提交
if (this.form.id != null) {
updateDiscountActivity(data).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
return;
}
// 添加的提交
createDiscountActivity(data).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
});
},
/** 删除按钮操作 */
handleDelete(row) {
const id = row.id;
this.$modal.confirm('是否确认删除限时折扣活动编号为"' + id + '"的数据项?').then(function() {
return deleteDiscountActivity(id);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
},
/** 关闭按钮操作 */
handleClose(row) {
const id = row.id;
this.$modal.confirm('是否确认关闭限时折扣活动编号为"' + id + '"的数据项?').then(function() {
return closeDiscountActivity(id);
}).then(() => {
this.getList();
this.$modal.msgSuccess("关闭成功");
}).catch(() => {});
},
/** 当 Form 的 SKU 发生变化时 */
changeFormSku(skuIds) {
// 处理【新增】
skuIds.forEach(skuId => {
// 获得对应的 SKU 信息
const sku = this.productSkus.find(item => item.id === skuId);
if (!sku) {
return;
}
// 判断已存在,直接跳过
const product = this.form.products.find(item => item.skuId === skuId);
if (product) {
return;
}
this.form.products.push({
skuId: sku.id,
name: sku.name,
price: sku.price,
stock: sku.stock,
spuId: sku.spuId,
spuName: sku.spuName,
discountType: PromotionDiscountTypeEnum.PRICE.type,
});
});
// 处理【移除】
this.form.products.map((product, index) => {
if (!skuIds.includes(product.skuId)) {
this.form.products.splice(index, 1);
}
});
},
/** 移除 Form 的 SKU */
removeFormSku(skuId) {
this.form.skuIds.map((id, index) => {
if (skuId === id) {
this.form.skuIds.splice(index, 1);
}
});
this.changeFormSku(this.form.skuIds);
},
}
}
</script>

View File

@@ -1,306 +0,0 @@
<template>
<div class="app-container">
<doc-alert title="功能开启" url="https://doc.iocoder.cn/mall/build/" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="活动名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入活动名称" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="活动状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择活动状态" clearable size="small">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.PROMOTION_ACTIVITY_STATUS)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
v-hasPermi="['promotion:reward-activity:create']">新增</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="活动名称" align="center" prop="name" />
<el-table-column label="活动时间" align="center" prop="startTime" width="240">
<template v-slot="scope">
<div>开始{{ parseTime(scope.row.startTime) }}</div>
<div>结束{{ parseTime(scope.row.endTime) }}</div>
</template>
</el-table-column>
<el-table-column label="状态" align="center" prop="status">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.PROMOTION_ACTIVITY_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-if="scope.row.status !== PromotionActivityStatusEnum.CLOSE.type"
v-hasPermi="['promotion:reward-activity:update']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleClose(scope.row)"
v-if="scope.row.status !== PromotionActivityStatusEnum.CLOSE.type &&
scope.row.status !== PromotionActivityStatusEnum.END.type"
v-hasPermi="['promotion:reward-activity:close']">关闭</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-if="scope.row.status === PromotionActivityStatusEnum.CLOSE.type"
v-hasPermi="['promotion:reward-activity:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="title" :visible.sync="open" width="600px" v-dialogDrag append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="活动名称" prop="name">
<el-input v-model="form.name" placeholder="请输入活动名称" />
</el-form-item>
<el-form-item label="活动时间" prop="startAndEndTime">
<el-date-picker clearable v-model="form.startAndEndTime" type="datetimerange" :default-time="['00:00:00', '23:59:59']"
value-format="timestamp" placeholder="选择开始时间" style="width: 480px" />
</el-form-item>
<el-form-item label="条件类型" prop="conditionType">
<el-radio-group v-model="form.conditionType">
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.PROMOTION_CONDITION_TYPE)"
:key="dict.value" :label="parseInt(dict.value)">{{dict.label}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="优惠设置" prop="conditionType">
<!-- TODO 芋艿:待实现! -->
</el-form-item>
<el-form-item label="活动商品" prop="productScope">
<el-radio-group v-model="form.productScope">
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.PROMOTION_PRODUCT_SCOPE)"
:key="dict.value" :label="parseInt(dict.value)">{{dict.label}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="form.productScope === PromotionProductScopeEnum.SPU.scope" prop="productSpuIds">
<el-select v-model="form.productSpuIds" placeholder="请选择活动商品" clearable size="small"
multiple filterable style="width: 400px">
<el-option v-for="item in productSpus" :key="item.id" :label="item.name" :value="item.id">
<span style="float: left">{{ item.name }}</span>
<span style="float: right; color: #8492a6; font-size: 13px">¥{{ (item.minPrice / 100.0).toFixed(2) }}</span>
</el-option>
</el-select>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" placeholder="请输入备注" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm">确 定</el-button>
<el-button @click="cancel">取 消</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import {
createRewardActivity,
updateRewardActivity,
deleteRewardActivity,
getRewardActivity,
getRewardActivityPage,
closeRewardActivity
} from "@/api/mall/promotion/rewardActivity";
import {
PromotionConditionTypeEnum,
PromotionProductScopeEnum,
PromotionActivityStatusEnum
} from "@/utils/constants";
import {getSpuSimpleList} from "@/api/mall/product/spu";
export default {
name: "PromotionRewardActivity",
components: {
},
data() {
return {
// 遮罩层
loading: true,
// 导出遮罩层
exportLoading: false,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 满减送活动列表
list: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
name: null,
status: null,
},
// 表单参数
form: {},
// 表单校验
rules: {
name: [{ required: true, message: "活动名称不能为空", trigger: "blur" }],
startAndEndTime: [{ required: true, message: "活动时间不能为空", trigger: "blur" }],
conditionType: [{ required: true, message: "条件类型不能为空", trigger: "change" }],
productScope: [{ required: true, message: "商品范围不能为空", trigger: "blur" }],
productSpuIds: [{ required: true, message: "商品范围不能为空", trigger: "blur" }],
},
// 商品列表
productSpus: [],
// 如下的变量主要为了 v-if 判断可以使用到
PromotionProductScopeEnum: PromotionProductScopeEnum,
PromotionActivityStatusEnum: PromotionActivityStatusEnum,
};
},
created() {
this.getList();
// 查询商品列表
getSpuSimpleList().then(response => {
this.productSpus = response.data
})
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
// 执行查询
getRewardActivityPage(this.queryParams).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 取消按钮 */
cancel() {
this.open = false;
this.reset();
},
/** 表单重置 */
reset() {
this.form = {
id: undefined,
name: undefined,
startAndEndTime: undefined,
startTime: undefined,
endTime: undefined,
conditionType: PromotionConditionTypeEnum.PRICE.type,
remark: undefined,
productScope: PromotionProductScopeEnum.ALL.scope,
productSpuIds: undefined,
rules: undefined,
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加满减送活动";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const id = row.id;
getRewardActivity(id).then(response => {
this.form = response.data;
this.form.startAndEndTime = [response.data.startTime, response.data.endTime];
this.open = true;
this.title = "修改满减送活动";
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (!valid) {
return;
}
this.form.startTime = this.form.startAndEndTime[0];
this.form.endTime = this.form.startAndEndTime[1];
// TODO 芋艿:临时实现
this.form.rules = [
{
limit: 1,
discountPrice: 10,
freeDelivery: true,
point: 10,
couponIds: [10, 20],
couponCounts: [1, 2]
}, {
limit: 2,
discountPrice: 20,
freeDelivery: false,
point: 20,
couponIds: [30, 40],
couponCounts: [3, 4]
}
];
// 修改的提交
if (this.form.id != null) {
updateRewardActivity(this.form).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
return;
}
// 添加的提交
createRewardActivity(this.form).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
});
},
/** 删除按钮操作 */
handleDelete(row) {
const id = row.id;
this.$modal.confirm('是否确认删除满减送活动编号为"' + id + '"的数据项?').then(function() {
return deleteRewardActivity(id);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
},
/** 关闭按钮操作 */
handleClose(row) {
const id = row.id;
this.$modal.confirm('是否确认关闭满减送活动编号为"' + id + '"的数据项?').then(function() {
return closeRewardActivity(id);
}).then(() => {
this.getList();
this.$modal.msgSuccess("关闭成功");
}).catch(() => {});
}
}
};
</script>

View File

@@ -1,491 +0,0 @@
<template>
<div class="app-container">
<doc-alert title="功能开启" url="https://doc.iocoder.cn/mall/build/" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch"
label-width="68px">
<el-form-item label="活动名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入秒杀活动名称" clearable
@keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="活动状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择活动状态" clearable size="small">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.PROMOTION_ACTIVITY_STATUS)" :key="dict.value"
:label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
<el-form-item label="参与场次" prop="timeId">
<el-select v-model="queryParams.timeId" placeholder="请选择参与场次" clearable size="small">
<el-option v-for="item in seckillTimeList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss"
type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"
:default-time="['00:00:00', '23:59:59']" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
v-hasPermi="['promotion:seckill-activity:create']">新增秒杀活动</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-menu" size="mini" @click="openSeckillTime"
v-hasPermi="['promotion:seckill-activity:create']">管理参与场次</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="活动名称" align="center" prop="name" />
<el-table-column label="活动状态" align="center" prop="status">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.PROMOTION_ACTIVITY_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="参与场次" prop="timeIds" width="250">
<template v-slot="scope">
<span v-for="item in seckillTimeList" :key="item.id"
v-if="scope.row.timeIds.includes(item.id)">
<el-tag style="margin:4px;" size="small">{{ item.name }}</el-tag>
</span>
</template>
</el-table-column>
<el-table-column label="活动开始时间" align="center" prop="startTime" width="190">
<template v-slot="scope">
<span>{{ "开始: " + parseTime(scope.row.startTime) }}</span>
<span>{{ "结束: " + parseTime(scope.row.endTime) }}</span>
</template>
</el-table-column>
<el-table-column label="付款订单数" align="center" prop="orderCount" />
<el-table-column label="付款人数" align="center" prop="userCount" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['promotion:seckill-activity:update']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-close" @click="handleClose(scope.row)"
v-hasPermi="['promotion:seckill-activity:delete']">关闭</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['promotion:seckill-activity:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList" />
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="title" :visible.sync="open" width="1200px" v-dialogDrag append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="活动名称" prop="name">
<el-input v-model="form.name" placeholder="请输入秒杀活动名称" />
</el-form-item>
<el-form-item label="活动时间" prop="startAndEndTime">
<el-date-picker clearable v-model="form.startAndEndTime" type="datetimerange"
value-format="timestamp" range-separator="" start-placeholder="开始日期" end-placeholder="结束日期"
style="width: 1080px" />
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input-number v-model="form.sort" controls-position="right" :min="0" :max="10000">
</el-input-number>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input type="textarea" v-model="form.remark" placeholder="请输入备注" />
</el-form-item>
<el-form-item label="场次选择">
<el-select v-model="form.timeIds" placeholder="请选择参与场次" clearable size="small" multiple filterable
style="width: 880px">
<el-option v-for="item in seckillTimeList" :key="item.id" :label="item.name" :value="item.id">
<span style="float: left">{{ item.name + ': { ' }} {{ item.startTime }} -- {{ item.endTime +
' }'
}}</span>
<span style="float: right; color: #8492a6; font-size: 13px"></span>
</el-option>
</el-select>
</el-form-item>
<el-form-item label="商品选择">
<el-select v-model="form.skuIds" placeholder="请选择活动商品" clearable size="small" multiple filterable
style="width: 880px" @change="changeFormSku">
<el-option v-for="item in productSkus" :key="item.id" :label="item.spuName + ' ' + item.name"
:value="item.id">
<span style="float: left">{{ item.spuName }} &nbsp; {{ item.name }}</span>
<span style="float: right; color: #8492a6; font-size: 13px">{{ (item.price /
100.0).toFixed(2)
}}</span>
</el-option>
</el-select>
<el-row>
<el-button type="primary" size="mini" @click="batchEditProduct('limitBuyCount')">限购</el-button>
<el-button type="primary" size="mini" @click="batchEditProduct('seckillPrice')">秒杀价</el-button>
<el-button type="primary" size="mini" @click="batchEditProduct('seckillStock')">秒杀库存</el-button>
</el-row>
<el-table v-loading="loading" ref="productsTable" :data="form.products">
<el-table-column type="selection" width="55">
</el-table-column>
<el-table-column label="商品名称" align="center" width="200">
<template v-slot="scope">
{{ scope.row.spuName }} &nbsp; {{ scope.row.name }}
</template>
</el-table-column>
<el-table-column label="商品价格" align="center" prop="price">
<template v-slot="scope">
{{ (scope.row.price / 100.0).toFixed(2) }}
</template>
</el-table-column>
<el-table-column label="库存" align="center" prop="productStock" />
<el-table-column label="限购(0为不限购)" align="center" width="150">
<template v-slot="scope">
<el-input-number v-model="scope.row.limitBuyCount" size="mini" :min="0" :max="10000">
</el-input-number>
</template>
</el-table-column>
<el-table-column label="秒杀价(元)" align="center" width="150">
<template v-slot="scope">
<el-input-number v-model="scope.row.seckillPrice" size="mini" :precision="2" :min="0"
:max="10000">
</el-input-number>
</template>
</el-table-column>
<el-table-column label="秒杀库存" align="center" width="150" prop="seckillStock">
<template v-slot="scope">
<el-input-number v-model="scope.row.seckillStock" size="mini" :min="0" :max="10000">
</el-input-number>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-delete"
@click="removeFormSku(scope.row.skuId)">删除
</el-button>
</template>
</el-table-column>
</el-table>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { getSkuOptionList } from "@/api/mall/product/sku";
import { createSeckillActivity, updateSeckillActivity, closeSeckillActivity, deleteSeckillActivity, getSeckillActivity, getSeckillActivityPage, exportSeckillActivityExcel } from "@/api/mall/promotion/seckillActivity";
import { getSeckillTimeList } from "@/api/mall/promotion/seckillTime";
import { deepClone } from "@/utils";
export default {
name: "PromotionSeckillActivity",
components: {
},
data() {
return {
// 遮罩层
loading: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 秒杀活动列表
list: [],
// 秒杀场次列表
seckillTimeList: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
name: null,
status: null,
timeId: null,
createTime: [],
},
// 表单参数
form: {
skuIds: [], // 选中的 SKU
products: [], // 商品信息
timeIds: [], //选中的秒杀场次id
},
// 商品 SKU 列表
productSkus: [],
// 表单校验
rules: {
name: [{ required: true, message: "秒杀活动名称不能为空", trigger: "blur" }],
status: [{ required: true, message: "活动状态不能为空", trigger: "blur" }],
startAndEndTime: [{ required: true, message: "活动时间不能为空", trigger: "blur" }],
sort: [{ required: true, message: "排序不能为空", trigger: "blur" }],
timeIds: [{ required: true, message: "秒杀场次不能为空", trigger: "blur" }],
totalPrice: [{ required: true, message: "订单实付金额,单位:分不能为空", trigger: "blur" }],
}
};
},
created() {
this.getList();
},
watch: {
$route: 'getList'
},
methods: {
/** 查询列表 */
getList() {
// 从秒杀时段跳转过来并鞋带timeId参数进行查询
const timeId = this.$route.params && this.$route.params.timeId;
if (timeId) {
this.queryParams.timeId = timeId
}
this.loading = true;
// 执行查询
getSeckillActivityPage(this.queryParams).then(response => {
console.log(response.data.list, "查询出的值");
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
if (timeId) {
//查询完成后设置为空
this.$route.params.timeId = undefined
}
// 获得 SKU 商品列表
getSkuOptionList().then(response => {
this.productSkus = response.data;
});
// 获取参与场次列表
getSeckillTimeList().then(response => {
this.seckillTimeList = response.data;
});
},
/** 取消按钮 */
cancel() {
this.open = false;
this.reset();
},
/** 表单重置 */
reset() {
this.form = {
id: undefined,
name: undefined,
status: undefined,
remark: undefined,
startTime: undefined,
endTime: undefined,
sort: undefined,
timeIds: [],
totalPrice: undefined,
skuIds: [],
products: [],
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/**打开秒杀场次管理页面 */
openSeckillTime() {
this.$tab.openPage("秒杀场次管理", "/promotion/seckill-time");
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加秒杀活动";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const id = row.id;
getSeckillActivity(id).then(response => {
this.form = response.data;
// 修改数据
this.form.startAndEndTime = [response.data.startTime, response.data.endTime];
this.form.skuIds = response.data.products.map(item => item.skuId);
this.form.products.forEach(product => {
// 获得对应的 SKU 信息
const sku = this.productSkus.find(item => item.id === product.skuId);
if (!sku) {
return;
}
// 设置商品信息
product.name = sku.name;
product.spuName = sku.spuName;
product.price = sku.price;
product.productStock = sku.stock;
this.$set(product, 'seckillStock', product.stock);
product.seckillPrice = product.seckillPrice !== undefined ? product.seckillPrice / 100 : undefined;
});
// 打开弹窗
this.open = true;
this.title = "修改限时折扣活动";
})
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (!valid) {
return;
}
// 处理数据
const data = deepClone(this.form);
data.startTime = this.form.startAndEndTime[0];
data.endTime = this.form.startAndEndTime[1];
data.products.forEach(product => {
product.stock = product.seckillStock;
product.seckillPrice = product.seckillPrice !== undefined ? product.seckillPrice * 100 : undefined;
});
// 修改的提交
if (this.form.id != null) {
updateSeckillActivity(data).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
return;
}
// 添加的提交
createSeckillActivity(data).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
});
},
/** 关闭按钮操作 */
handleClose(row) {
const id = row.id;
this.$modal.confirm('是否确认关闭秒杀活动编号为"' + id + '"的数据项?').then(function () {
return closeSeckillActivity(id);
}).then(() => {
this.getList();
this.$modal.msgSuccess("关闭成功");
}).catch(() => { });
},
/** 删除按钮操作 */
handleDelete(row) {
const id = row.id;
this.$modal.confirm('是否确认删除秒杀活动编号为"' + id + '"的数据项?').then(function () {
return deleteSeckillActivity(id);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => { });
},
/** 批量修改商品秒杀价,秒杀库存,每人限购数量 */
batchEditProduct(editType) {
const selectProducts = this.$refs.productsTable.selection;
if (selectProducts.length === 0) {
this.$modal.msgError("请选择需要修改的商品");
return;
}
let promptTitle = '请输入';
let regularPattern = /^[\s\S]*.*[^\s][\s\S]*$/; // 判断非空,且非空格
//限购数
if (editType === 'limitBuyCount') {
promptTitle = '限购数';
regularPattern = /^[0-9]*$/; //数字
}
//秒杀价
if (editType === 'seckillPrice') {
promptTitle = '秒杀价(元)';
regularPattern = /^[0-9]+(\.[0-9]{1,2})?$/; // 有一位或两位小数的正数
}
//秒杀库存
if (editType === 'seckillStock') {
promptTitle = '秒杀库存';
regularPattern = /^[0-9]*$/; //数字
}
this.$prompt(promptTitle, '提示', {
confirmButtonText: '保存',
cancelButtonText: '取消',
inputPattern: regularPattern,
inputErrorMessage: promptTitle + '格式不正确'
}).then(({ value }) => {
if (editType === 'limitBuyCount') {
selectProducts.forEach((item) => {
item.limitBuyCount = value;
})
}
if (editType === 'seckillPrice') {
selectProducts.forEach((item) => {
item.seckillPrice = value;
})
}
if (editType === 'seckillStock') {
selectProducts.forEach((item) => {
item.seckillStock = value;
})
}
}).catch();
},
/** 当 Form 的 SKU 发生变化时 */
changeFormSku(skuIds) {
// 处理【新增】
skuIds.forEach(skuId => {
// 获得对应的 SKU 信息
const sku = this.productSkus.find(item => item.id === skuId);
if (!sku) {
return;
}
// 判断已存在,直接跳过
const product = this.form.products.find(item => item.skuId === skuId);
if (product) {
return;
}
this.form.products.push({
skuId: sku.id,
name: sku.name,
price: sku.price,
productStock: sku.stock,
spuId: sku.spuId,
spuName: sku.spuName,
limitBuyCount: 1,
seckillStock: sku.stock,
seckillPrice: sku.price,
});
});
// 处理【移除】
this.form.products.map((product, index) => {
if (!skuIds.includes(product.skuId)) {
this.form.products.splice(index, 1);
}
});
},
/** 移除 Form 的 SKU */
removeFormSku(skuId) {
this.form.skuIds.map((id, index) => {
if (skuId === id) {
this.form.skuIds.splice(index, 1);
}
});
this.changeFormSku(this.form.skuIds);
},
}
};
</script>

View File

@@ -1,198 +0,0 @@
<template>
<div class="app-container">
<doc-alert title="功能开启" url="https://doc.iocoder.cn/mall/build/" />
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
v-hasPermi="['promotion:seckill-time:create']">新增秒杀时段</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="秒杀时段名称" align="center" prop="name" />
<el-table-column label="开始时间点" align="center" prop="startTime" width="180">
<template v-slot="scope">
<span>{{ scope.row.startTime }}</span>
</template>
</el-table-column>
<el-table-column label="结束时间点" align="center" prop="endTime" width="180">
<template v-slot="scope">
<span>{{ scope.row.endTime }}</span>
</template>
</el-table-column>
<el-table-column label="秒杀活动数量" align="center" prop="seckillActivityCount" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-view" @click="handleOpenSeckillActivity(scope.row)">
查看秒杀活动</el-button>
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['promotion:seckill-time:update']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['promotion:seckill-time:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="title" :visible.sync="open" width="600px" v-dialogDrag append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="140px">
<el-form-item label="秒杀场次名称" prop="name">
<el-input v-model="form.name" placeholder="请输入秒杀时段名称" clearable />
</el-form-item>
<el-form-item label="秒杀时间段" prop="startAndEndTime">
<el-time-picker is-range v-model="form.startAndEndTime" range-separator="至" start-placeholder="开始时间"
end-placeholder="结束时间" placeholder="选择时间范围" value-format="HH:mm:ss">
</el-time-picker>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { createSeckillTime, updateSeckillTime, deleteSeckillTime, getSeckillTime, getSeckillTimePage, exportSeckillTimeExcel, getSeckillTimeList } from "@/api/mall/promotion/seckillTime";
import router from "@/router";
import { deepClone } from "@/utils";
export default {
name: "PromotionSeckillTime",
components: {
},
data() {
return {
// 遮罩层
loading: true,
// 导出遮罩层
exportLoading: false,
// 显示搜索条件
showSearch: true,
// 总条数
// total: 0,
// 秒杀时段列表
list: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 表单参数
form: {},
// 表单校验
rules: {
name: [{ required: true, message: "秒杀时段名称不能为空", trigger: "blur" }],
startAndEndTime: [{ required: true, message: "秒杀时间段不能为空", trigger: "blur" }],
}
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
// 执行查询
getSeckillTimeList().then(response => {
this.list = response.data;
this.loading = false;
});
},
/** 取消按钮 */
cancel() {
this.open = false;
this.reset();
},
/** 表单重置 */
reset() {
this.form = {
id: undefined,
name: undefined,
startAndEndTime: undefined,
startTime: undefined,
endTime: undefined,
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/**查看当前秒杀时段的秒杀活动 */
handleOpenSeckillActivity(row) {
router.push({ name: 'SeckillActivity', params: { timeId: row.id } })
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加秒杀时段";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const id = row.id;
getSeckillTime(id).then(response => {
response.data.startAndEndTime = [response.data.startTime, response.data.endTime]
this.form = response.data;
this.open = true;
this.title = "修改秒杀时段";
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
console.log(valid, "是否通过");
if (!valid) {
return;
}
// 处理数据
const data = deepClone(this.form);
data.startTime = this.form.startAndEndTime[0];
data.endTime = this.form.startAndEndTime[1];
// 修改的提交
if (this.form.id != null) {
updateSeckillTime(data).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
return;
}
// 添加的提交
createSeckillTime(data).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
});
},
/** 删除按钮操作 */
handleDelete(row) {
const id = row.id;
this.$modal.confirm('是否确认删除秒杀时段编号为"' + id + '"的数据项?').then(function () {
return deleteSeckillTime(id);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => { });
},
}
};
</script>

View File

@@ -1,229 +0,0 @@
<template>
<div class="app-container">
<doc-alert title="功能开启" url="https://doc.iocoder.cn/mall/build/" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="商品名称" prop="spuName">
<el-input v-model="queryParams.spuName" placeholder="请输入商品 SPU 名称" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="退款编号" prop="no">
<el-input v-model="queryParams.no" placeholder="请输入退款编号" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="订单编号" prop="orderNo">
<el-input v-model="queryParams.orderNo" placeholder="请输入订单编号" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="售后状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择售后状态" clearable size="small">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.TRADE_AFTER_SALE_STATUS)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="售后方式" prop="way">
<el-select v-model="queryParams.way" placeholder="请选择售后方式" clearable size="small">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.TRADE_AFTER_SALE_WAY)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="售后类型" prop="type">
<el-select v-model="queryParams.type" placeholder="请选择售后类型" clearable size="small">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.TRADE_AFTER_SALE_TYPE)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"
:picker-options="datePickerOptions" :default-time="['00:00:00', '23:59:59']" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- Tab 选项真正的内容在 Table -->
<el-tabs v-model="activeTab" type="card" @tab-click="tabClick" style="margin-top: -40px;">
<el-tab-pane v-for="tab in statusTabs" :key="tab.value" :label="tab.label" :name="tab.value" />
</el-tabs>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="退款编号" align="center" prop="no" />
<el-table-column label="订单编号" align="center" prop="orderNo" /> <!-- TODO 芋艿未来要加个订单链接 -->
<el-table-column label="商品信息" align="center" prop="spuName" width="auto" min-width="300">
<!-- TODO @小红样式不太对辛苦改改 -->
<!-- <div v-slot="{ row }" class="goods-info">-->
<!-- <img :src="row.picUrl"/>-->
<!-- <span class="ellipsis-2" :title="row.name">{{row.name}}</span>-->
<!-- </div>-->
</el-table-column>
<el-table-column label="订单金额" align="center" prop="refundPrice">
<template v-slot="scope">
<span>{{ (scope.row.refundPrice / 100.0).toFixed(2) }}</span>
</template>
</el-table-column>
<el-table-column label="买家" align="center" prop="user.nickname" /> <!-- TODO 芋艿未来要加个会员链接 -->
<el-table-column label="申请时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="售后状态" align="center">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.TRADE_AFTER_SALE_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="售后方式" align="center">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.TRADE_AFTER_SALE_WAY" :value="scope.row.way" />
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-thumb"
>处理退款</el-button>
<!-- @click="handleUpdate(scope.row)" v-hasPermi="['trade:after-sale:update']"-->
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
</div>
</template>
<script>
import { getAfterSalePage } from "@/api/mall/trade/afterSale";
import { datePickerOptions } from "@/utils/constants";
import { DICT_TYPE, getDictDatas } from "@/utils/dict";
export default {
name: "TradeAfterSale",
components: {
},
data() {
return {
// 遮罩层
loading: true,
// 导出遮罩层
exportLoading: false,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 交易售后列表
list: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
no: null,
status: null,
orderNo: null,
spuName: null,
createTime: [],
way: null,
type: null,
},
// Tab 筛选
activeTab: 'all',
statusTabs: [{
label: '全部',
value: 'all'
}],
// 静态变量
datePickerOptions: datePickerOptions
};
},
created() {
this.getList();
// 设置 statuses 过滤
for (const dict of getDictDatas(DICT_TYPE.TRADE_AFTER_SALE_STATUS)) {
this.statusTabs.push({
label: dict.label,
value: dict.value
})
}
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
// 执行查询
getAfterSalePage(this.queryParams).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.activeTab = this.queryParams.status ? this.queryParams.status : 'all'; // 处理 tab
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.activeTab = 'all'; // 处理 tab
this.handleQuery();
},
/** tab 切换 */
tabClick(tab) {
this.queryParams.status = tab.name === 'all' ? undefined : tab.name;
this.getList();
},
goToDetail (row) {
this.$router.push({ path: '/mall/trade/order/detail', query: { orderNo: row.orderNo }})
}
}
};
</script>
<style lang="scss" scoped>
::v-deep .table-wrapper {
.el-table__row{
.el-table__cell {
border-bottom: none;
.cell{
.el-table {
.el-table__row {
>.el-table__cell {
.goods-info{
display: flex;
img{
margin-right: 10px;
width: 60px;
height: 60px;
border: 1px solid #e2e2e2;
}
}
.ellipsis-2 {
display: -webkit-box;
overflow: hidden;
text-overflow: ellipsis;
white-space: normal;
-webkit-line-clamp: 2; /* 要显示的行数 */
-webkit-box-orient: vertical;
word-break: break-all;
line-height: 22px !important;
max-height: 44px !important;
}
}
}
}
}
}
}
}
</style>

View File

@@ -1,279 +0,0 @@
<template>
<div class="app-container order-detail-page">
<!-- 订单信息 -->
<el-descriptions title="订单信息">
<el-descriptions-item label="订单号">{{ order.no }}</el-descriptions-item>
<el-descriptions-item label="配送方式">物流配送</el-descriptions-item> <!-- TODO 芋艿待实现 -->
<el-descriptions-item label="营销活动">物流配送</el-descriptions-item> <!-- TODO 芋艿待实现 -->
<el-descriptions-item label="订单类型">
<dict-tag :type="DICT_TYPE.TRADE_ORDER_TYPE" :value="order.type" />
</el-descriptions-item>
<el-descriptions-item label="收货人">{{ order.receiverName }}</el-descriptions-item>
<el-descriptions-item label="买家留言">{{ order.userRemark }}</el-descriptions-item>
<el-descriptions-item label="订单来源">
<dict-tag :type="DICT_TYPE.TERMINAL" :value="order.terminal" />
</el-descriptions-item>
<el-descriptions-item label="联系电话">{{ order.receiverMobile }}</el-descriptions-item>
<el-descriptions-item label="商家备注">{{ order.remark }}</el-descriptions-item>
<el-descriptions-item label="支付单号">{{ order.payOrderId }}</el-descriptions-item>
<el-descriptions-item label="付款方式">
<dict-tag :type="DICT_TYPE.PAY_CHANNEL_CODE_TYPE" :value="order.payChannelCode" />
</el-descriptions-item>
<el-descriptions-item label="买家">{{ order.user.nickname }}</el-descriptions-item> <!-- TODO 芋艿待实现跳转会员 -->
<el-descriptions-item label="收货地址">
{{ order.receiverAreaName }} &nbsp; {{ order.receiverDetailAddress }} &nbsp;
<el-link v-clipboard:copy="order.receiverAreaName + ' ' + order.receiverDetailAddress"
v-clipboard:success="clipboardSuccess" icon="el-icon-document-copy" type="primary"/>
</el-descriptions-item>
</el-descriptions>
<!-- 订单状态 -->
<el-descriptions title="订单状态" :column="1">
<el-descriptions-item label="订单状态">
<dict-tag :type="DICT_TYPE.TRADE_ORDER_STATUS" :value="order.status" />
</el-descriptions-item>
<el-descriptions-item label-class-name="no-colon">
<el-button type="primary" size="small">调整价格</el-button> <!-- TODO 芋艿待实现 -->
<el-button type="primary" size="small">备注</el-button> <!-- TODO 芋艿待实现 -->
<el-button type="primary" size="small">发货</el-button> <!-- TODO 芋艿待实现 -->
<el-button type="primary" size="small">关闭订单</el-button> <!-- TODO 芋艿待实现 -->
<el-button type="primary" size="small">修改地址</el-button> <!-- TODO 芋艿待实现 -->
<el-button type="primary" size="small">打印电子面单</el-button> <!-- TODO 芋艿待实现 -->
<el-button type="primary" size="small">打印发货单</el-button> <!-- TODO 芋艿待实现 -->
<el-button type="primary" size="small">确认收货</el-button> <!-- TODO 芋艿待实现 -->
</el-descriptions-item>
<el-descriptions-item label="提醒" label-style="color: red">
买家付款成功后货款将直接进入您的商户号微信支付宝<br />
请及时关注你发出的包裹状态确保可以配送至买家手中 <br />
如果买家表示没收到货或货物有问题请及时联系买家处理友好协商
</el-descriptions-item>
</el-descriptions>
<!-- 物流信息 TODO -->
<!-- 商品信息 -->
<el-descriptions title="商品信息" :column="6">
<el-descriptions-item labelClassName="no-colon">
<el-table :data="order.items" border>
<el-table-column prop="spuName" label="商品" width="700">
<template v-slot="{ row }">
{{row.spuName}}
<el-tag size="medium" v-for="property in row.properties" :key="property.propertyId">
{{property.propertyName}}{{property.valueName}}</el-tag>
</template>
</el-table-column>
<el-table-column prop="originalUnitPrice" label="单价(元)" width="180">
<template v-slot="{ row }">
{{ (row.originalUnitPrice / 100.0).toFixed(2) }}
</template>
</el-table-column>
<el-table-column prop="count" label="数量" width="180"/>
<el-table-column prop="originalPrice" label="小计(元)" width="180">
<template v-slot="{ row }">
{{ (row.originalPrice / 100.0).toFixed(2) }}
</template>
</el-table-column>
<el-table-column prop="afterSaleStatus" label="退款状态">
<template v-slot="{ row }">
<dict-tag :type="DICT_TYPE.TRADE_ORDER_ITEM_AFTER_SALE_STATUS" :value="row.afterSaleStatus" />
</template>
</el-table-column>
</el-table>
</el-descriptions-item>
<el-descriptions-item v-for="(item,index) in 5" label-class-name="no-colon" :key="item" /> <!-- 占位 -->
<el-descriptions-item label="商品总额">{{ (order.originalPrice / 100.0).toFixed(2) }}</el-descriptions-item>
<el-descriptions-item label="运费金额">{{ (order.deliveryPrice / 100.0).toFixed(2) }}</el-descriptions-item>
<el-descriptions-item label="订单调价">{{ (order.adjustPrice / 100.0).toFixed(2) }}</el-descriptions-item>
<el-descriptions-item label="商品优惠" label-style="color: red">
{{ ((order.originalPrice - order.originalPrice) / 100.0).toFixed(2) }}
</el-descriptions-item>
<el-descriptions-item label="订单优惠" label-style="color: red">
{{ (order.discountPrice / 100.0).toFixed(2) }}
</el-descriptions-item>
<el-descriptions-item label="积分抵扣" label-style="color: red">
{{ (order.pointPrice / 100.0).toFixed(2) }}
</el-descriptions-item>
<el-descriptions-item v-for="(item,index) in 5" label-class-name="no-colon" :key="item" /> <!-- 占位 -->
<el-descriptions-item label="应付金额">
{{ (order.payPrice / 100.0).toFixed(2) }}
</el-descriptions-item>
</el-descriptions>
<template v-for="(group, index) in detailGroups">
<el-descriptions v-bind="group.groupProps" :key="`group_${index}`" :title="group.title">
<!-- 订单操作日志 -->
<el-descriptions-item v-if="group.key === 'orderLog'" labelClassName="no-colon">
<el-timeline>
<el-timeline-item
v-for="(activity, index) in detailInfo[group.key]"
:key="index"
:timestamp="activity.timestamp"
>
{{activity.content}}
</el-timeline-item>
</el-timeline>
</el-descriptions-item>
<!-- 物流信息 -->
<el-descriptions-item v-if="group.key === 'expressInfo'" labelClassName="no-colon">
<el-tabs type="card">
<!-- 循环包裹物流信息 -->
<el-tab-pane v-for="(pkgInfo, pInIdx) in detailInfo[group.key]" :key="`pkgInfo_${pInIdx}`" :label="pkgInfo.label">
<!-- 包裹详情 -->
<el-descriptions>
<el-descriptions-item v-for="(pkgChild, pkgCIdx) in group.children" v-bind="pkgChild.childProps" :key="`pkgChild_${pkgCIdx}`" :label="pkgChild.label">
<!-- 包裹商品列表 -->
<template v-if="pkgChild.valueKey === 'goodsList' && pkgInfo[pkgChild.valueKey]">
<div v-for="(goodInfo, goodInfoIdx) in pkgInfo[pkgChild.valueKey]" :key="`goodInfo_${goodInfoIdx}`" style="display: flex;">
<el-image
style="width: 100px;height: 100px;flex: none"
:src="goodInfo.imgUrl">
</el-image>
<el-descriptions :column="1">
<el-descriptions-item labelClassName="no-colon">{{goodInfo.name}}</el-descriptions-item>
<el-descriptions-item label="数量">{{goodInfo.count}}</el-descriptions-item>
</el-descriptions>
</div>
</template>
<!-- 包裹物流详情 -->
<el-timeline v-else-if="pkgChild.valueKey==='wlxq'">
<el-timeline-item
v-for="(activity, index) in pkgInfo[pkgChild.valueKey]"
:key="index"
:timestamp="activity.timestamp"
>
{{activity.content}}
</el-timeline-item>
</el-timeline>
<template v-else>
{{pkgInfo[pkgChild.valueKey]}}
</template>
</el-descriptions-item>
</el-descriptions>
</el-tab-pane>
</el-tabs>
</el-descriptions-item>
</el-descriptions>
</template>
</div>
</template>
<script>
import { getOrderDetail } from "@/api/mall/trade/order";
export default {
name: "TradeOrderDetail",
data () {
return {
detailGroups: [
{
title: '物流信息',
key: 'expressInfo',
children: [
{ label: '发货时间', valueKey: 'fhsj'},
{ label: '物流公司', valueKey: 'wlgs'},
{ label: '运单号', valueKey: 'ydh'},
{ label: '物流状态', valueKey: 'wlzt', childProps: { span: 3 }},
{ label: '物流详情', valueKey: 'wlxq'}
]
},
{
title: '订单操作日志',
key: 'orderLog'
}
],
detailInfo: {
expressInfo: [ // 物流信息
{
label: '包裹1',
name: 'bg1',
fhsj: '2022-11-03 16:50:45',
wlgs: '极兔',
ydh: '2132123',
wlzt: '不支持此快递公司',
wlxq: [
{
content: '正在派送途中,请您准备签收(派件人:王涛,电话:13854563814)',
timestamp: '2018-04-15 15:00:16'
},
{
content: '快件到达 【烟台龙口东江村委营业点】',
timestamp: '2018-04-13 14:54:19'
},
{
content: '快件已发车',
timestamp: '2018-04-11 12:55:52'
},
{
content: '快件已发车',
timestamp: '2018-04-11 12:55:52'
},
{
content: '快件已发车',
timestamp: '2018-04-11 12:55:52'
}
]
}
],
orderLog: [ // 订单操作日志
{
content: '买家【乌鸦】关闭了订单',
timestamp: '2018-04-15 15:00:16'
},
{
content: '买家【乌鸦】下单了',
timestamp: '2018-04-15 15:00:16'
}
],
goodsInfo: [] // 商品详情tableData
},
order: {
items: [],
user: {},
},
}
},
created() {
getOrderDetail(this.$route.query.id).then(res => {
this.order = res.data
})
},
methods: {
clipboardSuccess() {
this.$modal.msgSuccess("复制成功");
}
}
}
</script>
<style lang="scss" scoped>
:deep(.el-descriptions){
&:not(:nth-child(1)) {
margin-top: 20px;
}
.el-descriptions__title{
display: flex;
align-items: center;
&::before{
content: '';
display: inline-block;
margin-right: 10px;
width: 3px;
height: 20px;
background-color: #409EFF;
}
}
.el-descriptions-item__container{
margin: 0 10px;
.no-colon{
margin: 0;
&::after{
content: ''
}
}
}
}
</style>

View File

@@ -1,281 +0,0 @@
<template>
<div class="app-container">
<doc-alert title="功能开启" url="https://doc.iocoder.cn/mall/build/" />
<!-- 搜索工作栏 -->
<!-- TODO: inline 看看是不是需要; v-show= 那块逻辑还是要的 -->
<el-row :gutter="20">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-col :span="6" :xs="24">
<el-form-item label="搜索方式" prop="searchValue">
<el-input v-model="queryParams.searchValue" style="width: 240px">
<el-select v-model="queryParams.searchType" slot="prepend" style="width: 100px">
<el-option v-for="dict in searchTypes" :key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-input>
</el-form-item>
</el-col>
<el-col :span="6" :xs="24">
<el-form-item label="订单类型" prop="type">
<el-select v-model="queryParams.type" clearable style="width: 240px">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.TRADE_ORDER_TYPE)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="6" :xs="24">
<el-form-item label="订单状态" prop="status">
<el-select v-model="queryParams.status" clearable style="width: 240px">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.TRADE_ORDER_STATUS)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="6" :xs="24">
<el-form-item label="订单来源" prop="terminal">
<el-select v-model="queryParams.terminal" clearable style="width: 240px">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.TERMINAL)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="6" :xs="24">
<el-form-item label="支付方式" prop="payChannelCode">
<el-select v-model="queryParams.payChannelCode" clearable style="width: 240px">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.PAY_CHANNEL_CODE_TYPE)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="6" :xs="24">
<el-form-item label="下单时间" prop="createTime">
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"
:picker-options="datePickerOptions" :default-time="['00:00:00', '23:59:59']" />
</el-form-item>
</el-col>
<el-col :span="6" :xs="24" style="line-height: 32px">
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-col>
</el-form>
</el-row>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- tab切换 -->
<!-- TODO @小程看看能不能往上挪 -40px隐藏搜索刷新对齐 -->
<el-tabs v-model="activeTab" type="card" @tab-click="tabClick">
<el-tab-pane v-for="tab in statusTabs" :key="tab.value" :label="tab.label" :name="tab.value">
<!-- 列表 -->
<el-table v-loading="loading" :data="list" :show-header="false" class="order-table">
<el-table-column>
<template v-slot="{ row }">
<el-row type="flex" align="middle">
<el-col :span="5">
订单号{{row.no}}
<el-popover title="支付单号:" :content="row.payOrderId + ''" placement="right" width="200" trigger="click">
<el-button slot="reference" type="text">更多</el-button>
</el-popover>
</el-col>
<el-col :span="5">下单时间{{ parseTime(row.createTime) }}</el-col>
<el-col :span="4">订单来源
<dict-tag :type="DICT_TYPE.TERMINAL" :value="row.terminal" />
</el-col>
<el-col :span="4">支付方式
<dict-tag v-if="row.payChannelCode" :type="DICT_TYPE.PAY_CHANNEL_CODE_TYPE" :value="row.payChannelCode" />
<span v-else>未支付</span>
</el-col>
<el-col :span="6" align="right">
<el-button type="text" @click="goToDetail(row)">详情</el-button>
</el-col>
</el-row>
<!-- 订单下的商品 -->
<el-table :data="row.items" border :show-header="true">
<el-table-column label="商品" prop="goods" header-align="center" width="auto" min-width="300">
<template v-slot="{ row, $index }">
<div class="goods-info">
<img :src="row.picUrl"/>
<span class="ellipsis-2" :title="row.spuName">{{row.spuName}}</span>
<!-- TODO @小程下面是商品属性想当度一行放在商品名下面 -->
<el-tag size="medium" v-for="property in row.properties" :key="property.propertyId">
{{property.propertyName}}{{property.valueName}}</el-tag>
</div>
</template>
</el-table-column>
<el-table-column label="单价(元)/数量" prop="fee" align="center" width="115">
<template v-slot="{ row }">
<div>{{ (row.originalUnitPrice / 100.0).toFixed(2) }}</div>
<div>{{row.count}} </div>
</template>
</el-table-column>
<!-- TODO @小程这里应该是一个订单下多个商品只展示订单上的总金额就是 order.payPrice -->
<el-table-column label="实付金额(元)" prop="amount" align="center" width="100"/>
<!-- TODO @小程这里应该是一个订单下多个商品只展示订单上的收件信息使用 order.receiverXXX 开头的字段 -->
<el-table-column label="买家/收货人" prop="buyer" header-align="center" width="auto" min-width="300">
<template v-slot="{ row }">
<!-- TODO @芋艿以后增加一个会员详情界面 -->
<div>{{row.buyer}}</div>
<div>{{row.receiver}}{{row.tel}}</div>
<div class="ellipsis-2" :title="row.address">{{row.address}}</div>
</template>
</el-table-column>
<!-- TODO @小程这里应该是一个订单下多个商品交易状态是统一的使用 order.status 字段 -->
<el-table-column label="交易状态" prop="status" align="center" width="100"/>
</el-table>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
</el-tabs>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
</div>
</template>
<script>
import { getOrderPage } from "@/api/mall/trade/order";
import { datePickerOptions } from "@/utils/constants";
import { DICT_TYPE, getDictDatas } from "@/utils/dict";
export default {
name: "TradeOrder",
data () {
return {
// 遮罩层
loading: true,
// 导出遮罩层
exportLoading: false,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 交易售后列表
list: [],
queryParams: {
pageNo: 1,
pageSize: 10,
searchType: 'no',
searchValue: '',
type: null,
status: null,
payChannelCode: null,
createTime: [],
},
// Tab 筛选
activeTab: 'all',
statusTabs: [{
label: '全部',
value: 'all'
}],
// 静态变量
datePickerOptions: datePickerOptions,
searchTypes: [
{ label: '订单号', value: 'no' },
{ label: '会员编号', value: 'userId' },
{ label: '会员昵称', value: 'userNickname' },
{ label: '会员手机号', value: 'userMobile' },
{ label: '收货人姓名', value: 'receiverName' },
{ label: '收货人手机号码', value: 'receiverMobile' },
],
}
},
created() {
this.getList();
// 设置 statuses 过滤
for (const dict of getDictDatas(DICT_TYPE.TRADE_ORDER_STATUS)) {
this.statusTabs.push({
label: dict.label,
value: dict.value
})
}
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
// 执行查询
getOrderPage({
...this.queryParams,
searchType: undefined,
searchValue: undefined,
no: this.queryParams.searchType === 'no' ? this.queryParams.searchValue : undefined,
userId: this.queryParams.searchType === 'userId' ? this.queryParams.searchValue : undefined,
userNickname: this.queryParams.searchType === 'userNickname' ? this.queryParams.searchValue : undefined,
userMobile: this.queryParams.searchType === 'userMobile' ? this.queryParams.searchValue : undefined,
receiverName: this.queryParams.searchType === 'receiverName' ? this.queryParams.searchValue : undefined,
receiverMobile: this.queryParams.searchType === 'receiverMobile' ? this.queryParams.searchValue : undefined,
}).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.activeTab = this.queryParams.status ? this.queryParams.status : 'all'; // 处理 tab
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** tab 切换 */
tabClick(tab) {
this.queryParams.status = tab.name === 'all' ? undefined : tab.name;
this.getList();
},
goToDetail (row) {
this.$router.push({ name: 'TradeOrderDetail', query: { id: row.id }})
}
}
}
</script>
<style lang="scss" scoped>
::v-deep .order-table{
border-bottom: none;
&::before{
height: 0;
}
.el-table__row{
.el-table__cell{
border-bottom: none;
.cell{
.el-table {
.el-table__row{
>.el-table__cell{
.goods-info{
display: flex;
img{
margin-right: 10px;
width: 60px;
height: 60px;
border: 1px solid #e2e2e2;
}
}
.ellipsis-2{
display: -webkit-box;
overflow: hidden;
text-overflow: ellipsis;
white-space: normal;
-webkit-line-clamp: 2; /* 要显示的行数 */
-webkit-box-orient: vertical;
word-break: break-all;
line-height: 22px !important;
max-height: 44px !important;
}
}
}
}
}
}
}
}
</style>

View File

@@ -1,290 +0,0 @@
<template>
<div class="app-container">
<doc-alert title="公众号接入" url="https://doc.iocoder.cn/mp/account/" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入名称" clearable
@keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
v-hasPermi="['mp:account:create']">新增
</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="名称" align="center" prop="name"/>
<el-table-column label="微信号" align="center" prop="account" width="180"/>
<el-table-column label="appId" align="center" prop="appId" width="180"/>
<!-- <el-table-column label="appSecret" align="center" prop="appSecret" width="180"/>-->
<!-- <el-table-column label="token" align="center" prop="token"/>-->
<!-- <el-table-column label="消息加解密密钥" align="center" prop="aesKey"/>-->
<el-table-column label="服务器地址(URL)" align="center" prop="appId" width="360">
<template v-slot="scope">
{{ 'http://服务端地址/mp/open/' + scope.row.appId }}
</template>
</el-table-column>
<el-table-column label="二维码" align="center" prop="qrCodeUrl">
<template v-slot="scope">
<img v-if="scope.row.qrCodeUrl" :src="scope.row.qrCodeUrl" alt="二维码" style="height: 100px;" />
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="remark"/>
<!-- <el-table-column label="创建时间" align="center" prop="createTime" width="180">-->
<!-- <template v-slot="scope">-->
<!-- <span>{{ parseTime(scope.row.createTime) }}</span>-->
<!-- </template>-->
<!-- </el-table-column>-->
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['mp:account:update']">修改
</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['mp:account:delete']">删除
</el-button>
<el-button size="mini" type="text" icon="el-icon-refresh" @click="handleGenerateQrCode(scope.row)"
v-hasPermi="['mp:account:qr-code']">生成二维码
</el-button>
<el-button size="mini" type="text" icon="el-icon-share" @click="handleCleanQuota(scope.row)"
v-hasPermi="['mp:account:clear-quota']">清空 API 配额
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="120px">
<el-form-item label="名称" prop="name">
<el-input v-model="form.name" placeholder="请输入名称"/>
</el-form-item>
<el-form-item label="微信号" prop="account">
<span slot="label">
<el-tooltip content="在微信公众平台mp.weixin.qq.com的菜单 [设置与开发 - 公众号设置 - 账号详情] 中能找到「微信号」" placement="top">
<i class="el-icon-question" />
</el-tooltip>
微信号
</span>
<el-input v-model="form.account" placeholder="请输入微信号"/>
</el-form-item>
<el-form-item label="appId" prop="appId">
<span slot="label">
<el-tooltip content="在微信公众平台mp.weixin.qq.com的菜单 [设置与开发 - 公众号设置 - 基本设置] 中能找到「开发者ID(AppID)」" placement="top">
<i class="el-icon-question" />
</el-tooltip>
appId
</span>
<el-input v-model="form.appId" placeholder="请输入公众号 appId"/>
</el-form-item>
<el-form-item label="appSecret" prop="appSecret">
<span slot="label">
<el-tooltip content="在微信公众平台mp.weixin.qq.com的菜单 [设置与开发 - 公众号设置 - 基本设置] 中能找到「开发者密码(AppSecret)」" placement="top">
<i class="el-icon-question" />
</el-tooltip>
appSecret
</span>
<el-input v-model="form.appSecret" placeholder="请输入公众号 appSecret"/>
</el-form-item>
<el-form-item label="token" prop="token">
<el-input v-model="form.token" placeholder="请输入公众号token"/>
</el-form-item>
<el-form-item label="消息加解密密钥" prop="aesKey">
<el-input v-model="form.aesKey" placeholder="请输入消息加解密密钥"/>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input type="textarea" v-model="form.remark" placeholder="请输入备注"/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import {
clearAccountQuota,
createAccount,
deleteAccount,
generateAccountQrCode,
getAccount,
getAccountPage,
updateAccount
} from '@/api/mp/account'
export default {
name: 'MpAccount',
components: {},
data() {
return {
// 遮罩层
loading: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 公众号账号列表
list: [],
// 弹出层标题
title: '',
// 是否显示弹出层
open: false,
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
name: null,
account: null,
appId: null,
},
// 表单参数
form: {},
// 表单校验
rules: {
name: [{required: true, message: '名称不能为空', trigger: 'blur'}],
account: [{required: true, message: '公众号账号不能为空', trigger: 'blur'}],
appId: [{required: true, message: '公众号 appId 不能为空', trigger: 'blur'}],
appSecret: [{required: true, message: '公众号密钥不能为空', trigger: 'blur'}],
token: [{required: true, message: '公众号 token 不能为空', trigger: 'blur'}],
},
}
},
created() {
this.getList()
},
methods: {
/** 查询列表 */
getList() {
this.loading = true
// 处理查询参数
let params = {...this.queryParams}
// 执行查询
getAccountPage(params).then(response => {
this.list = response.data.list
this.total = response.data.total
this.loading = false
})
},
/** 取消按钮 */
cancel() {
this.open = false
this.reset()
},
/** 表单重置 */
reset() {
this.form = {
id: undefined,
name: undefined,
account: undefined,
appId: undefined,
appSecret: undefined,
token: undefined,
aesKey: undefined,
remark: undefined,
}
this.resetForm('form')
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1
this.getList()
},
/** 重置按钮操作 */
resetQuery() {
this.dateRangeCreateTime = []
this.resetForm('queryForm')
this.handleQuery()
},
/** 新增按钮操作 */
handleAdd() {
this.reset()
this.open = true
this.title = '添加公众号账号'
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset()
const id = row.id
getAccount(id).then(response => {
this.form = response.data
this.open = true
this.title = '修改公众号账号'
})
},
/** 提交按钮 */
submitForm() {
this.$refs['form'].validate(valid => {
if (!valid) {
return
}
// 修改的提交
if (this.form.id != null) {
updateAccount(this.form).then(response => {
this.$modal.msgSuccess('修改成功')
this.open = false
this.getList()
})
return
}
// 添加的提交
createAccount(this.form).then(response => {
this.$modal.msgSuccess('新增成功')
this.open = false
this.getList()
})
})
},
/** 删除按钮操作 */
handleDelete(row) {
const id = row.id
this.$modal.confirm('是否确认删除公众号账号编号为"' + row.name + '"的数据项?').then(function () {
return deleteAccount(id)
}).then(() => {
this.getList()
this.$modal.msgSuccess('删除成功')
}).catch(() => {
})
},
/** 生成二维码的按钮操作 */
handleGenerateQrCode(row) {
const id = row.id
this.$modal.confirm('是否确认生成公众号账号编号为"' + row.name + '"的二维码?').then(function () {
return generateAccountQrCode(id)
}).then(() => {
this.getList()
this.$modal.msgSuccess('生成二维码成功')
}).catch(() => {
})
},
/** 清空二维码 API 配额的按钮操作 */
handleCleanQuota(row) {
const id = row.id
this.$modal.confirm('是否确认清空生成公众号账号编号为"' + row.name + '"的 API 配额?').then(function () {
return clearAccountQuota(id)
}).then(() => {
this.$modal.msgSuccess('清空 API 配额成功')
}).catch(() => {
})
},
}
}
</script>

View File

@@ -1,384 +0,0 @@
<!--
MIT License
Copyright (c) 2020 www.joolun.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
芋道源码
移除 avue 框架使用 element-ui 重写
重写代码保持和现有项目保持一致
-->
<template>
<div class="app-container">
<doc-alert title="自动回复" url="https://doc.iocoder.cn/mp/auto-reply/" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="公众号" prop="accountId">
<el-select v-model="queryParams.accountId" placeholder="请选择公众号">
<el-option v-for="item in accounts" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- tab 切换 -->
<el-tabs v-model="type" @tab-click="handleClick">
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
v-hasPermi="['mp:auto-reply:create']" v-if="type !== '1' || list.length <= 0">新增
</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList" />
</el-row>
<!-- tab -->
<el-tab-pane name="1">
<span slot="label"><i class="el-icon-star-off"></i> 关注时回复</span>
</el-tab-pane>
<el-tab-pane name="2">
<span slot="label"><i class="el-icon-chat-line-round"></i> 消息回复</span>
</el-tab-pane>
<el-tab-pane name="3">
<span slot="label"><i class="el-icon-news"></i> 关键词回复</span>
</el-tab-pane>
</el-tabs>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="请求消息类型" align="center" prop="requestMessageType" v-if="type === '2'" />
<el-table-column label="关键词" align="center" prop="requestKeyword" v-if="type === '3'" />
<el-table-column label="匹配类型" align="center" prop="requestMatch" v-if="type === '3'">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.MP_AUTO_REPLY_REQUEST_MATCH" :value="scope.row.requestMatch"/>
</template>
</el-table-column>
<el-table-column label="回复消息类型" align="center">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.MP_MESSAGE_TYPE" :value="scope.row.responseMessageType"/>
</template>
</el-table-column>
<el-table-column label="回复内容" align="center">
<template v-slot="scope">
<div v-if="scope.row.responseMessageType === 'text'">{{ scope.row.responseContent }}</div>
<div v-else-if="scope.row.responseMessageType === 'voice'">
<wx-voice-player :url="scope.row.responseMediaUrl" />
</div>
<div v-else-if="scope.row.responseMessageType === 'image'">
<a target="_blank" :href="scope.row.responseMediaUrl">
<img :src="scope.row.responseMediaUrl" style="width: 100px">
</a>
</div>
<div v-else-if="scope.row.responseMessageType === 'video' || scope.row.responseMessageType === 'shortvideo'">
<wx-video-player :url="scope.row.responseMediaUrl" style="margin-top: 10px" />
</div>
<div v-else-if="scope.row.responseMessageType === 'news'">
<wx-news :articles="scope.row.responseArticles" />
</div>
<div v-else-if="scope.row.responseMessageType === 'music'">
<wx-music :title="scope.row.responseTitle" :description="scope.row.responseDescription"
:thumb-media-url="scope.row.responseThumbMediaUrl"
:music-url="scope.row.responseMusicUrl" :hq-music-url="scope.row.responseHqMusicUrl" />
</div>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['mp:auto-reply:update']">修改
</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['mp:auto-reply:delete']">删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 添加或修改自动回复的对话框 -->
<el-dialog :title="title" :visible.sync="open" width="800px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="消息类型" prop="requestMessageType" v-if="type === '2'">
<el-select v-model="form.requestMessageType" placeholder="请选择">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.MP_MESSAGE_TYPE)"
:key="dict.value" :label="dict.label" :value="dict.value"
v-if="requestMessageTypes.includes(dict.value)"/>
</el-select>
</el-form-item>
<el-form-item label="匹配类型" prop="requestMatch" v-if="type === '3'">
<el-select v-model="form.requestMatch" placeholder="请选择匹配类型" clearable size="small">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.MP_AUTO_REPLY_REQUEST_MATCH)"
:key="dict.value" :label="dict.label" :value="parseInt(dict.value)"/>
</el-select>
</el-form-item>
<el-form-item label="关键词" prop="requestKeyword" v-if="type === '3'">
<el-input v-model="form.requestKeyword" placeholder="请输入内容" clearable />
</el-form-item>
<el-form-item label="回复消息">
<wx-reply-select :objData="objData" v-if="hackResetWxReplySelect" />
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="cancel"> </el-button>
<el-button type="primary" @click="handleSubmit"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import WxVideoPlayer from '@/views/mp/components/wx-video-play/main.vue';
import WxVoicePlayer from '@/views/mp/components/wx-voice-play/main.vue';
import WxMsg from '@/views/mp/components/wx-msg/main.vue';
import WxLocation from '@/views/mp/components/wx-location/main.vue';
import WxMusic from '@/views/mp/components/wx-music/main.vue';
import WxNews from '@/views/mp/components/wx-news/main.vue';
import WxReplySelect from '@/views/mp/components/wx-reply/main.vue'
import { getSimpleAccounts } from "@/api/mp/account";
import { createAutoReply, deleteAutoReply, getAutoReply, getAutoReplyPage, updateAutoReply } from "@/api/mp/autoReply";
export default {
name: 'MpAutoReply',
components: {
WxVideoPlayer,
WxVoicePlayer,
WxMsg,
WxLocation,
WxMusic,
WxNews,
WxReplySelect
},
data() {
return {
// tab 类型1、关注时回复2、消息回复3、关键词回复
type: '3',
// 允许选择的请求消息类型
requestMessageTypes: ['text', 'image', 'voice', 'video', 'shortvideo', 'location', 'link'],
// 遮罩层
loading: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 自动回复列表
list: [],
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
accountId: undefined,
},
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 表单参数
form: {},
// 回复消息
objData: {
type : 'text'
},
// 表单校验
rules: {
requestKeyword: [{ required: true, message: "请求的关键字不能为空", trigger: "blur" }],
requestMatch: [{ required: true, message: "请求的关键字的匹配不能为空", trigger: "blur" }],
},
hackResetWxReplySelect: false, // 重置 WxReplySelect 组件,解决无法清除的问题
// 公众号账号列表
accounts: []
}
},
created() {
getSimpleAccounts().then(response => {
this.accounts = response.data;
// 默认选中第一个
if (this.accounts.length > 0) {
this.queryParams.accountId = this.accounts[0].id;
}
// 加载数据
this.getList();
})
},
methods: {
/** 查询列表 */
getList() {
// 如果没有选中公众号账号,则进行提示。
if (!this.queryParams.accountId) {
this.$message.error('未选中公众号,无法查询自动回复')
return false
}
this.loading = false
// 处理查询参数
let params = {
...this.queryParams,
type: this.type
}
// 执行查询
getAutoReplyPage(params).then(response => {
this.list = response.data.list
this.total = response.data.total
this.loading = false
})
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1
this.getList()
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm('queryForm')
// 默认选中第一个
if (this.accounts.length > 0) {
this.queryParams.accountId = this.accounts[0].id;
}
this.handleQuery()
},
handleClick(tab, event) {
this.type = tab.name
this.handleQuery()
},
/** 新增按钮操作 */
handleAdd(){
this.reset();
this.resetEditor();
// 打开表单,并设置初始化
this.open = true
this.title = '新增自动回复';
this.objData = {
type : 'text',
accountId: this.queryParams.accountId,
}
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
this.resetEditor();
const id = row.id;
getAutoReply(id).then(response => {
// 设置属性
this.form = {...response.data}
this.$delete(this.form, 'responseMessageType');
this.$delete(this.form, 'responseContent');
this.$delete(this.form, 'responseMediaId');
this.$delete(this.form, 'responseMediaUrl');
this.$delete(this.form, 'responseDescription');
this.$delete(this.form, 'responseArticles');
this.objData = {
type: response.data.responseMessageType,
accountId: this.queryParams.accountId,
content: response.data.responseContent,
mediaId: response.data.responseMediaId,
url: response.data.responseMediaUrl,
title: response.data.responseTitle,
description: response.data.responseDescription,
thumbMediaId: response.data.responseThumbMediaId,
thumbMediaUrl: response.data.responseThumbMediaUrl,
articles: response.data.responseArticles,
musicUrl: response.data.responseMusicUrl,
hqMusicUrl: response.data.responseHqMusicUrl,
}
// 打开表单
this.open = true
this.title = '修改自动回复';
})
},
handleSubmit() {
this.$refs["form"].validate(valid => {
if (!valid) {
return;
}
// 处理回复消息
const form = {...this.form};
form.responseMessageType = this.objData.type;
form.responseContent = this.objData.content;
form.responseMediaId = this.objData.mediaId;
form.responseMediaUrl = this.objData.url;
form.responseTitle = this.objData.title;
form.responseDescription = this.objData.description;
form.responseThumbMediaId = this.objData.thumbMediaId;
form.responseThumbMediaUrl = this.objData.thumbMediaUrl;
form.responseArticles = this.objData.articles;
form.responseMusicUrl = this.objData.musicUrl;
form.responseHqMusicUrl = this.objData.hqMusicUrl;
if (this.form.id !== undefined) {
updateAutoReply(form).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
} else {
createAutoReply(form).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
}
});
},
// 表单重置
reset() {
this.form = {
id: undefined,
accountId: this.queryParams.accountId,
type: this.type,
requestKeyword: undefined,
requestMatch: this.type === '3' ? 1 : undefined,
requestMessageType: undefined,
};
this.resetForm("form");
},
// 取消按钮
cancel() {
this.open = false;
this.reset();
},
// 表单 Editor 重置
resetEditor() {
this.hackResetWxReplySelect = false // 销毁组件
this.$nextTick(() => {
this.hackResetWxReplySelect = true // 重建组件
})
},
handleDelete: function(row) {
const ids = row.id;
this.$modal.confirm('是否确认删除此数据?').then(function() {
return deleteAutoReply(ids);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
},
}
}
</script>

View File

@@ -1,230 +0,0 @@
<!--
- Copyright (C) 2018-2019
- All rights reserved, Designed By www.joolun.com
芋道源码
调整 uploadData 属性只需要传入 accountId 即可
-->
<template>
<div id="wxEditor">
<div v-loading="quillUpdateImg" element-loading-text="请稍等,图片上传中">
<!-- 图片上传组件辅助-->
<el-upload class="avatar-uploader" name="file" :action="actionUrl" :headers="headers"
:show-file-list="false" :data="uploadData"
:on-success="uploadSuccess" :on-error="uploadError" :before-upload="beforeUpload">
</el-upload>
<quill-editor class="editor" v-model="content" ref="myQuillEditor" :options="editorOption"
@change="onEditorChange($event)">
</quill-editor>
</div>
</div>
</template>
<script>
// 工具栏配置
const toolbarOptions = [
["bold", "italic", "underline", "strike"], // 加粗 斜体 下划线 删除线
["blockquote", "code-block"], // 引用 代码块
[{ header: 1 }, { header: 2 }], // 1、2 级标题
[{ list: "ordered" }, { list: "bullet" }], // 有序、无序列表
[{ script: "sub" }, { script: "super" }], // 上标/下标
[{ indent: "-1" }, { indent: "+1" }], // 缩进
// [{'direction': 'rtl'}], // 文本方向
[{ size: ["small", false, "large", "huge"] }], // 字体大小
[{ header: [1, 2, 3, 4, 5, 6, false] }], // 标题
[{ color: [] }, { background: [] }], // 字体颜色、字体背景颜色
[{ font: [] }], // 字体种类
[{ align: [] }], // 对齐方式
["clean"], // 清除文本格式
["link", "image", "video"] // 链接、图片、视频
]
import { quillEditor } from "vue-quill-editor"
import "quill/dist/quill.core.css"
import "quill/dist/quill.snow.css"
import "quill/dist/quill.bubble.css"
import { getAccessToken } from "@/utils/auth";
export default {
props: {
/* 公众号账号编号 */
accountId: {
type: Number,
required: true
},
/* 编辑器的内容 */
value: {
type: String
},
/* 图片大小 */
maxSize: {
type: Number,
default: 4000 // kb
}
},
name: 'wxEditor',
components: {
quillEditor
},
data() {
return {
editorType: '1',
content: this.value.replace(/data-src/g, "src"),
quillUpdateImg: false, // 根据图片上传状态来确定是否显示loading动画刚开始是false,不显示
editorOption: {
theme: "snow", // or 'bubble'
placeholder: "请输入文章内容",
modules: {
toolbar: {
container: toolbarOptions,
// container: "#toolbar",
handlers: {
image: function(value) {
if (value) {
// 触发input框选择图片文件
document.querySelector(".avatar-uploader input").click();
} else {
this.quill.format("image", false);
}
},
link: function(value) {
if (value) {
const href = prompt('注意!只支持公众号图文链接');
this.quill.format("link", href);
} else {
this.quill.format("link", false);
}
},
}
}
}
},
actionUrl: process.env.VUE_APP_BASE_API +'/admin-api/mp/material/upload-news-image', // 这里写你要上传的图片服务器地址
headers: { Authorization: "Bearer " + getAccessToken() }, // 设置上传的请求头部
uploadData: {
"type": 'image', // TODO 芋艿:试试要不要换成 thumb
"accountId": this.accountId,
},
}
},
methods: {
onEditorChange(editor) {
//内容改变事件
this.$emit("input", this.content)
},
// 富文本图片上传前
beforeUpload() {
// 显示 loading 动画
this.quillUpdateImg = true
},
// 图片上传成功
// 注意!由于微信公众号的图片有访问限制,所以会显示“此图片来自微信公众号,未经允许不可引用”
uploadSuccess(res, file) {
// res为图片服务器返回的数据
// 获取富文本组件实例
let quill = this.$refs.myQuillEditor.quill
// 如果上传成功
const link = res.data
if (link){
// 获取光标所在位置
let length = quill.getSelection().index;
// 插入图片 res.info为服务器返回的图片地址
quill.insertEmbed(length, 'image', link)
// 调整光标到最后
quill.setSelection(length + 1)
} else {
this.$message.error('图片插入失败')
}
// loading 动画消失
this.quillUpdateImg = false;
},
// 富文本图片上传失败
uploadError() {
// loading 动画消失
this.quillUpdateImg = false;
this.$message.error("图片插入失败");
}
}
}
</script>
<style>
.editor {
line-height: normal !important;
height: 500px;
}
.ql-snow .ql-tooltip[data-mode=link]::before {
content: "请输入链接地址:";
}
.ql-snow .ql-tooltip.ql-editing a.ql-action::after {
border-right: 0;
content: '保存';
padding-right: 0;
}
.ql-snow .ql-tooltip[data-mode=video]::before {
content: "请输入视频地址:";
}
.ql-snow .ql-picker.ql-size .ql-picker-label::before,
.ql-snow .ql-picker.ql-size .ql-picker-item::before {
content: '14px';
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value=small]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=small]::before {
content: '10px';
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value=large]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=large]::before {
content: '18px';
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value=huge]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=huge]::before {
content: '32px';
}
.ql-snow .ql-picker.ql-header .ql-picker-label::before,
.ql-snow .ql-picker.ql-header .ql-picker-item::before {
content: '文本';
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
content: '标题1';
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
content: '标题2';
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
content: '标题3';
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
content: '标题4';
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
content: '标题5';
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before {
content: '标题6';
}
.ql-snow .ql-picker.ql-font .ql-picker-label::before,
.ql-snow .ql-picker.ql-font .ql-picker-item::before {
content: '标准字体';
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=serif]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=serif]::before {
content: '衬线字体';
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=monospace]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=monospace]::before {
content: '等宽字体';
}
</style>

View File

@@ -1,36 +0,0 @@
<!--
微信消息 - 定位
-->
<template>
<div>
<el-link type="primary" target="_blank" :href="'https://map.qq.com/?type=marker&isopeninfowin=1&markertype=1&pointx=' + locationY + '&pointy=' + locationX + '&name=' + label + '&ref=yudao'">
<img :src="'https://apis.map.qq.com/ws/staticmap/v2/?zoom=10&markers=color:blue|label:A|' + locationX + ',' + locationY + '&key=' + qqMapKey + '&size=250*180'">
<p/><i class="el-icon-map-location"></i>{{label}}
</el-link>
</div>
</template>
<script>
export default {
name: "wxLocation",
props: {
locationX: {
required: true,
type: Number
},
locationY: {
required: true,
type: Number
},
label: { // 地名
required: true,
type: String
},
qqMapKey: { // QQ 地图的密钥 https://lbs.qq.com/service/staticV2/staticGuide/staticDoc
required: false,
type: String,
default: 'TVDBZ-TDILD-4ON4B-PFDZA-RNLKH-VVF6E' // 需要自定义
}
}
};
</script>

View File

@@ -1,247 +0,0 @@
<!--
- Copyright (C) 2018-2019
- All rights reserved, Designed By www.joolun.com
芋道源码
移除 avue 组件使用 ElementUI 原生组件
-->
<template>
<!-- 类型图片 -->
<div v-if="objData.type === 'image'">
<div class="waterfall" v-loading="loading">
<div class="waterfall-item" v-for="item in list" :key="item.mediaId">
<img class="material-img" :src="item.url">
<p class="item-name">{{item.name}}</p>
<el-row class="ope-row">
<el-button size="mini" type="success" @click="selectMaterial(item)">选择
<i class="el-icon-circle-check el-icon--right"></i>
</el-button>
</el-row>
</div>
</div>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getMaterialPage"/>
</div>
<!-- 类型语音 -->
<div v-else-if="objData.type === 'voice'">
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="编号" align="center" prop="mediaId" />
<el-table-column label="文件名" align="center" prop="name" />
<el-table-column label="语音" align="center">
<template v-slot="scope">
<wx-voice-player :url="scope.row.url" />
</template>
</el-table-column>
<el-table-column label="上传时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-circle-plus"
@click="selectMaterial(scope.row)">选择</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getPage"/>
</div>
<div v-else-if="objData.type === 'video'">
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="编号" align="center" prop="mediaId" />
<el-table-column label="文件名" align="center" prop="name" />
<el-table-column label="标题" align="center" prop="title" />
<el-table-column label="介绍" align="center" prop="introduction" />
<el-table-column label="视频" align="center">
<template v-slot="scope">
<wx-video-player :url="scope.row.url" />
</template>
</el-table-column>
<el-table-column label="上传时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-circle-plus"
@click="selectMaterial(scope.row)">选择</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getMaterialPage"/>
</div>
<div v-else-if="objData.type === 'news'">
<div class="waterfall" v-loading="loading">
<div class="waterfall-item" v-for="item in list" :key="item.mediaId" v-if="item.content && item.content.newsItem">
<wx-news :articles="item.content.newsItem" />
<el-row class="ope-row">
<el-button size="mini" type="success" @click="selectMaterial(item)">
选择<i class="el-icon-circle-check el-icon--right"></i>
</el-button>
</el-row>
</div>
</div>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getMaterialPage"/>
</div>
</template>
<script>
import WxNews from '@/views/mp/components/wx-news/main.vue';
import WxVoicePlayer from '@/views/mp/components/wx-voice-play/main.vue';
import WxVideoPlayer from '@/views/mp/components/wx-video-play/main.vue';
import { getMaterialPage } from "@/api/mp/material";
import { getFreePublishPage } from "@/api/mp/freePublish";
import { getDraftPage } from "@/api/mp/draft";
export default {
name: "wxMaterialSelect",
components: {
WxNews,
WxVoicePlayer,
WxVideoPlayer
},
props: {
objData: {
type: Object, // type - 类型accountId - 公众号账号编号
required: true
},
newsType:{ // 图文类型1、已发布图文2、草稿箱图文
type: String,
default: "1"
},
},
data() {
return {
// 遮罩层
loading: false,
// 总条数
total: 0,
// 数据列表
list: [],
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
accountId: this.objData.accountId,
},
}
},
created() {
this.getPage()
},
methods:{
selectMaterial(item) {
this.$emit('selectMaterial', item)
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1
this.getPage()
},
getPage() {
this.loading = true
if (this.objData.type === 'news' && this.newsType === '1') { // 【图文】+ 【已发布】
this.getFreePublishPage();
} else if (this.objData.type === 'news' && this.newsType === '2') { // 【图文】+ 【草稿】
this.getDraftPage();
} else { // 【素材】
this.getMaterialPage();
}
},
getMaterialPage() {
getMaterialPage({
...this.queryParams,
type: this.objData.type
}).then(response => {
this.list = response.data.list
this.total = response.data.total
}).finally(() => {
this.loading = false
})
},
getFreePublishPage() {
getFreePublishPage(this.queryParams).then(response => {
// 将 thumbUrl 转成 picUrl保证 wx-news 组件可以预览封面
response.data.list.forEach(item => {
const newsItem = item.content.newsItem;
newsItem.forEach(article => {
article.picUrl = article.thumbUrl;
})
})
this.list = response.data.list
this.total = response.data.total
}).finally(() => {
this.loading = false
})
},
getDraftPage() {
getDraftPage((this.queryParams)).then(response => {
// 将 thumbUrl 转成 picUrl保证 wx-news 组件可以预览封面
response.data.list.forEach(item => {
const newsItem = item.content.newsItem;
newsItem.forEach(article => {
article.picUrl = article.thumbUrl;
})
})
this.list = response.data.list
this.total = response.data.total
}).finally(() => {
this.loading = false
})
}
}
};
</script>
<style lang="scss" scoped>
/*瀑布流样式*/
.waterfall {
width: 100%;
column-gap:10px;
column-count: 5;
margin: 0 auto;
}
.waterfall-item {
padding: 10px;
margin-bottom: 10px;
break-inside: avoid;
border: 1px solid #eaeaea;
}
.material-img {
width: 100%;
}
p {
line-height: 30px;
}
@media (min-width: 992px) and (max-width: 1300px) {
.waterfall {
column-count: 3;
}
p {
color:red;
}
}
@media (min-width: 768px) and (max-width: 991px) {
.waterfall {
column-count: 2;
}
p {
color: orange;
}
}
@media (max-width: 767px) {
.waterfall {
column-count: 1;
}
}
/*瀑布流样式*/
</style>

View File

@@ -1,101 +0,0 @@
.avue-card{
&__item{
margin-bottom: 16px;
border: 1px solid #e8e8e8;
background-color: #fff;
box-sizing: border-box;
color: rgba(0,0,0,.65);
font-size: 14px;
font-variant: tabular-nums;
line-height: 1.5;
list-style: none;
font-feature-settings: "tnum";
cursor: pointer;
height:200px;
&:hover{
border-color: rgba(0,0,0,.09);
box-shadow: 0 2px 8px rgba(0,0,0,.09);
}
&--add{
border:1px dashed #000;
width: 100%;
color: rgba(0,0,0,.45);
background-color: #fff;
border-color: #d9d9d9;
border-radius: 2px;
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
i{
margin-right: 10px;
}
&:hover{
color: #40a9ff;
background-color: #fff;
border-color: #40a9ff;
}
}
}
&__body{
display: flex;
padding: 24px;
}
&__detail{
flex:1
}
&__avatar{
width: 48px;
height: 48px;
border-radius: 48px;
overflow: hidden;
margin-right: 12px;
img{
width: 100%;
height: 100%;
}
}
&__title{
color: rgba(0,0,0,.85);
margin-bottom: 12px;
font-size: 16px;
&:hover{
color:#1890ff;
}
}
&__info{
color: rgba(0,0,0,.45);
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
overflow: hidden;
height: 64px;
}
&__menu{
display: flex;
justify-content:space-around;
height: 50px;
background: #f7f9fa;
color: rgba(0,0,0,.45);
text-align: center;
line-height: 50px;
&:hover{
color:#1890ff;
}
}
}
/** joolun 额外加的 */
.avue-comment__main {
flex: unset!important;
border-radius: 5px!important;
margin: 0 8px!important;
}
.avue-comment__header {
border-top-left-radius: 5px;
border-top-right-radius: 5px;
}
.avue-comment__body {
border-bottom-right-radius: 5px;
border-bottom-left-radius: 5px;
}

View File

@@ -1,88 +0,0 @@
/* 来自 https://github.com/nmxiaowei/avue/blob/master/styles/src/element-ui/comment.scss */
.avue-comment{
margin-bottom: 30px;
display: flex;
align-items: flex-start;
&--reverse{
flex-direction:row-reverse;
.avue-comment__main{
&:before,&:after{
left: auto;
right: -8px;
border-width: 8px 0 8px 8px;
}
&:before{
border-left-color: #dedede;
}
&:after{
border-left-color: #f8f8f8;
margin-right: 1px;
margin-left: auto;
}
}
}
&__avatar{
width: 48px;
height: 48px;
border-radius: 50%;
border: 1px solid transparent;
box-sizing: border-box;
vertical-align: middle;
}
&__header{
padding: 5px 15px;
background: #f8f8f8;
border-bottom: 1px solid #eee;
display: flex;
align-items: center;
justify-content: space-between;
}
&__author{
font-weight: 700;
font-size: 14px;
color: #999;
}
&__main{
flex:1;
margin: 0 20px;
position: relative;
border: 1px solid #dedede;
border-radius: 2px;
&:before,&:after{
position: absolute;
top: 10px;
left: -8px;
right: 100%;
width: 0;
height: 0;
display: block;
content: " ";
border-color: transparent;
border-style: solid solid outset;
border-width: 8px 8px 8px 0;
pointer-events: none;
}
&:before {
border-right-color: #dedede;
z-index: 1;
}
&:after{
border-right-color: #f8f8f8;
margin-left: 1px;
z-index: 2;
}
}
&__body{
padding: 15px;
overflow: hidden;
background: #fff;
font-family: Segoe UI,Lucida Grande,Helvetica,Arial,Microsoft YaHei,FreeSans,Arimo,Droid Sans,wenquanyi micro hei,Hiragino Sans GB,Hiragino Sans GB W3,FontAwesome,sans-serif;color: #333;
font-size: 14px;
}
blockquote{
margin:0;
font-family: Georgia,Times New Roman,Times,Kai,Kaiti SC,KaiTi,BiauKai,FontAwesome,serif;
padding: 1px 0 1px 15px;
border-left: 4px solid #ddd;
}
}

View File

@@ -1,294 +0,0 @@
<!--
- Copyright (C) 2018-2019
- All rights reserved, Designed By www.joolun.com
芋道源码
移除暂时用不到的 websocket
代码优化补充注释提升阅读性
-->
<template>
<div class="msg-main">
<div class="msg-div" :id="'msg-div' + nowStr">
<!-- 加载更多 -->
<div v-loading="loading"></div>
<div v-if="!loading">
<div class="el-table__empty-block" v-if="loadMore" @click="loadingMore"><span class="el-table__empty-text">点击加载更多</span></div>
<div class="el-table__empty-block" v-if="!loadMore"><span class="el-table__empty-text">没有更多了</span></div>
</div>
<!-- 消息列表 -->
<div class="execution" v-for="item in list" :key='item.id'>
<div class="avue-comment" :class="item.sendFrom === 2 ? 'avue-comment--reverse' : ''">
<div class="avatar-div">
<img :src="item.sendFrom === 1 ? user.avatar : mp.avatar" class="avue-comment__avatar">
<div class="avue-comment__author">{{item.sendFrom === 1 ? user.nickname : mp.nickname }}</div>
</div>
<div class="avue-comment__main">
<div class="avue-comment__header">
<div class="avue-comment__create_time">{{ parseTime(item.createTime) }}</div>
</div>
<div class="avue-comment__body" :style="item.sendFrom === 2 ? 'background: #6BED72;' : ''">
<!-- 事件区域 -->
<div v-if="item.type === 'event' && item.event === 'subscribe'">
<el-tag type="success" size="mini">关注</el-tag>
</div>
<div v-else-if="item.type === 'event' && item.event === 'unsubscribe'">
<el-tag type="danger" size="mini">取消关注</el-tag>
</div>
<div v-else-if="item.type === 'event' && item.event === 'CLICK'">
<el-tag size="mini">点击菜单</el-tag>{{ item.eventKey }}
</div>
<div v-else-if="item.type === 'event' && item.event === 'VIEW'">
<el-tag size="mini">点击菜单链接</el-tag>{{ item.eventKey }}
</div>
<div v-else-if="item.type === 'event' && item.event === 'scancode_waitmsg'">
<el-tag size="mini">扫码结果</el-tag>{{ item.eventKey }}
</div>
<div v-else-if="item.type === 'event' && item.event === 'scancode_push'">
<el-tag size="mini">扫码结果</el-tag>{{ item.eventKey }}
</div>
<div v-else-if="item.type === 'event' && item.event === 'pic_sysphoto'">
<el-tag size="mini">系统拍照发图</el-tag>
</div>
<div v-else-if="item.type === 'event' && item.event === 'pic_photo_or_album'">
<el-tag size="mini">拍照或者相册</el-tag>
</div>
<div v-else-if="item.type === 'event' && item.event === 'pic_weixin'">
<el-tag size="mini">微信相册</el-tag>
</div>
<div v-else-if="item.type === 'event' && item.event === 'location_select'">
<el-tag size="mini">选择地理位置</el-tag>
</div>
<div v-else-if="item.type === 'event'">
<el-tag type="danger" size="mini">未知事件类型</el-tag>
</div>
<!-- 消息区域 -->
<div v-else-if="item.type === 'text'">{{ item.content }}</div>
<div v-else-if="item.type === 'voice'">
<wx-voice-player :url="item.mediaUrl" :content="item.recognition" />
</div>
<div v-else-if="item.type === 'image'">
<a target="_blank" :href="item.mediaUrl">
<img :src="item.mediaUrl" style="width: 100px">
</a>
</div>
<div v-else-if="item.type === 'video' || item.type === 'shortvideo'" style="text-align: center">
<wx-video-player :url="item.mediaUrl" />
</div>
<div v-else-if="item.type === 'link'" class="avue-card__detail">
<el-link type="success" :underline="false" target="_blank" :href="item.url">
<div class="avue-card__title"><i class="el-icon-link"></i>{{ item.title }}</div>
</el-link>
<div class="avue-card__info" style="height: unset">{{item.description}}</div>
</div>
<!-- TODO 芋艿待完善 -->
<div v-else-if="item.type === 'location'">
<wx-location :label="item.label" :location-y="item.locationY" :location-x="item.locationX" />
</div>
<div v-else-if="item.type === 'news'" style="width: 300px"> <!-- TODO 芋艿待测试详情页也存在类似的情况 -->
<wx-news :articles="item.articles" />
</div>
<div v-else-if="item.type === 'music'">
<wx-music :title="item.title" :description="item.description" :thumb-media-url="item.thumbMediaUrl"
:music-url="item.musicUrl" :hq-music-url="item.hqMusicUrl" />
</div>
</div>
</div>
</div>
</div>
</div>
<div class="msg-send" v-loading="sendLoading">
<wx-reply-select ref="replySelect" :objData="objData" />
<el-button type="success" size="small" class="send-but" @click="sendMsg">发送(S)</el-button>
</div>
</div>
</template>
<script>
import { getMessagePage, sendMessage } from '@/api/mp/message'
import WxReplySelect from '@/views/mp/components/wx-reply/main.vue'
import WxVideoPlayer from '@/views/mp/components/wx-video-play/main.vue';
import WxVoicePlayer from '@/views/mp/components/wx-voice-play/main.vue';
import WxNews from '@/views/mp/components/wx-news/main.vue';
import WxLocation from '@/views/mp/components/wx-location/main.vue';
import WxMusic from '@/views/mp/components/wx-music/main.vue';
import { getUser } from "@/api/mp/mpuser";
export default {
name: "wxMsg",
components: {
WxReplySelect,
WxVideoPlayer,
WxVoicePlayer,
WxNews,
WxLocation,
WxMusic
},
props: {
userId: {
type: Number,
required: true
},
},
data() {
return {
nowStr: new Date().getTime(), // 当前的时间戳,用于每次消息加载后,回到原位置;具体见 :id="'msg-div' + nowStr" 处
loading: false, // 消息列表是否正在加载中
loadMore: true, // 是否可以加载更多
list: [], // 消息列表
queryParams: {
pageNo: 1, // 当前页数
pageSize: 14, // 每页显示多少条
accountId: undefined,
},
user: { // 由于微信不再提供昵称,直接使用“用户”展示
nickname: '用户',
avatar: require("@/assets/images/profile.jpg"),
accountId: 0, // 公众号账号编号
},
mp: {
nickname: '公众号',
avatar: require("@/assets/images/wechat.png"),
},
// ========= 消息发送 =========
sendLoading: false, // 发送消息是否加载中
objData: { // 微信发送消息
type: 'text',
},
}
},
created() {
// 获得用户信息
getUser(this.userId).then(response => {
this.user.nickname = response.data.nickname && response.data.nickname.length > 0 ?
response.data.nickname : this.user.nickname;
this.user.avatar = response.data.avatar && this.user.avatar.length > 0 ?
response.data.avatar : this.user.avatar;
this.user.accountId = response.data.accountId;
// 设置公众号账号编号
this.queryParams.accountId = response.data.accountId;
this.objData.accountId = response.data.accountId;
// 加载消息
console.log(this.queryParams)
this.refreshChange()
})
},
methods:{
sendMsg(){
if (!this.objData) {
return;
}
// 公众号限制:客服消息,公众号只允许发送一条
if (this.objData.type === 'news'
&& this.objData.articles.length > 1) {
this.objData.articles = [this.objData.articles[0]]
this.$message({
showClose: true,
message: '图文消息条数限制在 1 条以内,已默认发送第一条',
type: 'success'
})
}
// 执行发送
this.sendLoading = true
sendMessage(Object.assign({
userId: this.userId
}, {
...this.objData
})).then(response => {
this.sendLoading = false
// 添加到消息列表,并滚动
this.list = [...this.list , ...[response.data] ]
this.scrollToBottom()
// 重置 objData 状态
this.$refs['replySelect'].deleteObj(); // 重置,避免 tab 的数据未清理
}).catch(() => {
this.sendLoading = false
})
},
loadingMore() {
this.queryParams.pageNo++
this.getPage(this.queryParams)
},
getPage(page, params) {
this.loading = true
getMessagePage(Object.assign({
pageNo: page.pageNo,
pageSize: page.pageSize,
userId: this.userId,
accountId: page.accountId,
}, params)).then(response => {
// 计算当前的滚动高度
const msgDiv = document.getElementById('msg-div' + this.nowStr);
let scrollHeight = 0
if(msgDiv){
scrollHeight = msgDiv.scrollHeight
}
// 处理数据
const data = response.data.list.reverse();
this.list = [...data, ...this.list]
this.loading = false
if (data.length < this.queryParams.pageSize || data.length === 0){
this.loadMore = false
}
this.queryParams.pageNo = page.pageNo
this.queryParams.pageSize = page.pageSize
// 滚动到原来的位置
if(this.queryParams.pageNo === 1) { // 定位到消息底部
this.scrollToBottom()
} else if (data.length !== 0) { // 定位滚动条
this.$nextTick(() => {
if (scrollHeight !== 0){
msgDiv.scrollTop = document.getElementById('msg-div'+this.nowStr).scrollHeight - scrollHeight - 100
}
})
}
})
},
/**
* 刷新回调
*/
refreshChange() {
this.getPage(this.queryParams)
},
/** 定位到消息底部 */
scrollToBottom: function () {
this.$nextTick(() => {
let div = document.getElementById('msg-div' + this.nowStr)
div.scrollTop = div.scrollHeight
})
},
}
};
</script>
<style lang="scss" scoped>
/* 因为 joolun 实现依赖 avue 组件,该页面使用了 comment.scss、card.scc */
@import './comment.scss';
@import './card.scss';
.msg-main {
margin-top: -30px;
padding: 10px;
}
.msg-div {
height: 50vh;
overflow: auto;
background-color: #eaeaea;
margin-left: 10px;
margin-right: 10px;
}
.msg-send {
padding: 10px;
}
.avatar-div {
text-align: center;
width: 80px;
}
.send-but {
float: right;
margin-top: 8px!important;
}
</style>

View File

@@ -1,52 +0,0 @@
<!--
微信消息 - 音乐
-->
<template>
<div>
<el-link type="success" :underline="false" target="_blank" :href="hqMusicUrl ? hqMusicUrl : musicUrl">
<div class="avue-card__body" style="padding:10px;background-color: #fff;border-radius: 5px">
<div class="avue-card__avatar">
<img :src="thumbMediaUrl" alt=""/>
</div>
<div class="avue-card__detail">
<div class="avue-card__title" style="margin-bottom:unset">{{ title }}</div>
<div class="avue-card__info" style="height: unset">{{ description }}</div>
</div>
</div>
</el-link>
</div>
</template>
<script>
export default {
name: "wxMusic",
props: {
title: {
required: false,
type: String
},
description: {
required: false,
type: String
},
musicUrl: {
required: false,
type: String
},
hqMusicUrl: {
required: false,
type: String
},
thumbMediaUrl: {
required: true,
type: String
},
}
};
</script>
<style lang="scss" scoped>
/* 因为 joolun 实现依赖 avue 组件,该页面使用了 card.scc */
@import '../wx-msg/card.scss';
</style>

View File

@@ -1,104 +0,0 @@
<!--
- Copyright (C) 2018-2019
- All rights reserved, Designed By www.joolun.com
微信消息 - 图文
芋道源码
代码优化补充注释提升阅读性
-->
<template>
<div class="news-home">
<div v-for="(article, index) in articles" :key="index" class="news-div">
<!-- 头条 -->
<a target="_blank" :href="article.url" v-if="index === 0">
<div class="news-main">
<div class="news-content">
<img class="material-img" :src="article.picUrl" width="280px" height="120px"/>
<div class="news-content-title">
<span>{{article.title}}</span>
</div>
</div>
</div>
</a>
<!-- 二条/三条等等 -->
<a target="_blank" :href="article.url" v-else>
<div class="news-main-item">
<div class="news-content-item">
<div class="news-content-item-title">{{article.title}}</div>
<div class="news-content-item-img">
<img class="material-img" :src="article.picUrl" height="100%"/>
</div>
</div>
</div>
</a>
</div>
</div>
</template>
<script>
export default {
name: "wxNews",
props: {
articles: {
type: Array // title - 标题description - 描述picUrl - 图片连接url - 跳转链接
}
},
// created() {
// console.log(this.articles)
// },
};
</script>
<style lang="scss" scoped>
.news-home {
background-color: #FFFFFF;
width: 100%;
margin: auto;
}
.news-main {
width: 100%;
margin: auto;
}
.news-content {
background-color: #acadae;
width: 100%;
position: relative;
}
.news-content-title {
display: inline-block;
font-size: 12px;
color: #FFFFFF;
position: absolute;
left: 0;
bottom: 0;
background-color: black;
width: 98%;
padding: 1%;
opacity: 0.65;
white-space: normal;
box-sizing: unset!important
}
.news-main-item {
background-color: #FFFFFF;
padding: 5px 0;
border-top: 1px solid #eaeaea;
}
.news-content-item {
position: relative;
}
.news-content-item-title {
display: inline-block;
font-size: 10px;
width: 70%;
margin-left: 1%;
white-space: normal
}
.news-content-item-img {
display: inline-block;
width: 25%;
background-color: #acadae;
margin-right: 1%;
}
.material-img {
width: 100%;
}
</style>

View File

@@ -1,547 +0,0 @@
<!--
- Copyright (C) 2018-2019
- All rights reserved, Designed By www.joolun.com
芋道源码
移除多余的 rep 为前缀的变量 message 消息更简单
代码优化补充注释提升阅读性
优化消息的临时缓存策略发送消息时只清理被发送消息的 tab不会强制切回到 text 输入
支持发送视频消息时支持新建视频
-->
<template>
<el-tabs type="border-card" v-model="objData.type" @tab-click="handleClick">
<!-- 类型 1文本 -->
<el-tab-pane name="text">
<span slot="label"><i class="el-icon-document"></i> 文本</span>
<el-input type="textarea" :rows="5" placeholder="请输入内容" v-model="objData.content" @input="inputContent" />
</el-tab-pane>
<!-- 类型 2图片 -->
<el-tab-pane name="image">
<span slot="label"><i class="el-icon-picture"></i> 图片</span>
<el-row>
<!-- 情况一已经选择好素材或者上传好图片 -->
<div class="select-item" v-if="objData.url">
<img class="material-img" :src="objData.url">
<p class="item-name" v-if="objData.name">{{objData.name}}</p>
<el-row class="ope-row">
<el-button type="danger" icon="el-icon-delete" circle @click="deleteObj"></el-button>
</el-row>
</div>
<!-- 情况二未做完上述操作 -->
<div v-else>
<el-row style="text-align: center">
<!-- 选择素材 -->
<el-col :span="12" class="col-select">
<el-button type="success" @click="openMaterial">
素材库选择<i class="el-icon-circle-check el-icon--right"></i>
</el-button>
<el-dialog title="选择图片" :visible.sync="dialogImageVisible" width="90%" append-to-body>
<wx-material-select :obj-data="objData" @selectMaterial="selectMaterial" />
</el-dialog>
</el-col>
<!-- 文件上传 -->
<el-col :span="12" class="col-add">
<el-upload :action="actionUrl" :headers="headers" multiple :limit="1" :file-list="fileList" :data="uploadData"
:before-upload="beforeImageUpload" :on-success="handleUploadSuccess">
<el-button type="primary">上传图片</el-button>
<div slot="tip" class="el-upload__tip">支持 bmp/png/jpeg/jpg/gif 格式大小不超过 2M</div>
</el-upload>
</el-col>
</el-row>
</div>
</el-row>
</el-tab-pane>
<!-- 类型 3语音 -->
<el-tab-pane name="voice">
<span slot="label"><i class="el-icon-phone"></i> 语音</span>
<el-row>
<div class="select-item2" v-if="objData.url">
<p class="item-name">{{objData.name}}</p>
<div class="item-infos">
<wx-voice-player :url="objData.url" />
</div>
<el-row class="ope-row">
<el-button type="danger" icon="el-icon-delete" circle @click="deleteObj"></el-button>
</el-row>
</div>
<div v-else>
<el-row style="text-align: center">
<!-- 选择素材 -->
<el-col :span="12" class="col-select">
<el-button type="success" @click="openMaterial">
素材库选择<i class="el-icon-circle-check el-icon--right"></i>
</el-button>
<el-dialog title="选择语音" :visible.sync="dialogVoiceVisible" width="90%" append-to-body>
<WxMaterialSelect :objData="objData" @selectMaterial="selectMaterial"></WxMaterialSelect>
</el-dialog>
</el-col>
<!-- 文件上传 -->
<el-col :span="12" class="col-add">
<el-upload :action="actionUrl" :headers="headers" multiple :limit="1" :file-list="fileList" :data="uploadData"
:before-upload="beforeVoiceUpload" :on-success="handleUploadSuccess">
<el-button type="primary">点击上传</el-button>
<div slot="tip" class="el-upload__tip">格式支持 mp3/wma/wav/amr文件大小不超过 2M播放长度不超过 60s</div>
</el-upload>
</el-col>
</el-row>
</div>
</el-row>
</el-tab-pane>
<!-- 类型 4视频 -->
<el-tab-pane name="video">
<span slot="label"><i class="el-icon-share"></i> 视频</span>
<el-row>
<el-input v-model="objData.title" placeholder="请输入标题" @input="inputContent" />
<div style="margin: 20px 0;"></div>
<el-input v-model="objData.description" placeholder="请输入描述" @input="inputContent" />
<div style="margin: 20px 0;"></div>
<div style="text-align: center;">
<wx-video-player v-if="objData.url" :url="objData.url" />
</div>
<div style="margin: 20px 0;"></div>
<el-row style="text-align: center">
<!-- 选择素材 -->
<el-col :span="12">
<el-button type="success" @click="openMaterial">
素材库选择<i class="el-icon-circle-check el-icon--right"></i>
</el-button>
<el-dialog title="选择视频" :visible.sync="dialogVideoVisible" width="90%" append-to-body>
<wx-material-select :objData="objData" @selectMaterial="selectMaterial" />
</el-dialog>
</el-col>
<!-- 文件上传 -->
<el-col :span="12">
<el-upload :action="actionUrl" :headers="headers" multiple :limit="1" :file-list="fileList" :data="uploadData"
:before-upload="beforeVideoUpload" :on-success="handleUploadSuccess">
<el-button type="primary">新建视频<i class="el-icon-upload el-icon--right"></i></el-button>
</el-upload>
</el-col>
</el-row>
</el-row>
</el-tab-pane>
<!-- 类型 5图文 -->
<el-tab-pane name="news">
<span slot="label"><i class="el-icon-news"></i> 图文</span>
<el-row>
<div class="select-item" v-if="objData.articles">
<wx-news :articles="objData.articles" />
<el-row class="ope-row">
<el-button type="danger" icon="el-icon-delete" circle @click="deleteObj" />
</el-row>
</div>
<!-- 选择素材 -->
<div v-if="!objData.content">
<el-row style="text-align: center">
<el-col :span="24">
<el-button type="success" @click="openMaterial">{{newsType === '1' ? '选择已发布图文' : '选择草稿箱图文'}}<i class="el-icon-circle-check el-icon--right"></i></el-button>
</el-col>
</el-row>
</div>
<el-dialog title="选择图文" :visible.sync="dialogNewsVisible" width="90%" append-to-body>
<wx-material-select :objData="objData" @selectMaterial="selectMaterial" :newsType="newsType" />
</el-dialog>
</el-row>
</el-tab-pane>
<!-- 类型 6音乐 -->
<el-tab-pane name="music">
<span slot="label"><i class="el-icon-service"></i> 音乐</span>
<el-row>
<el-col :span="6">
<div class="thumb-div">
<img style="width: 100px" v-if="objData.thumbMediaUrl" :src="objData.thumbMediaUrl">
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
<div class="thumb-but">
<el-upload :action="actionUrl" :headers="headers" multiple :limit="1" :file-list="fileList" :data="uploadData"
:before-upload="beforeThumbImageUpload" :on-success="handleUploadSuccess">
<el-button slot="trigger" size="mini" type="text">本地上传</el-button>
<el-button size="mini" type="text" @click="openMaterial" style="margin-left: 5px">素材库选择</el-button>
</el-upload>
</div>
</div>
<el-dialog title="选择图片" :visible.sync="dialogThumbVisible" width="80%" append-to-body>
<wx-material-select :objData="{type:'image', accountId: objData.accountId}" @selectMaterial="selectMaterial" />
</el-dialog>
</el-col>
<el-col :span="18">
<el-input v-model="objData.title" placeholder="请输入标题" @input="inputContent" />
<div style="margin: 20px 0;"></div>
<el-input v-model="objData.description" placeholder="请输入描述" @input="inputContent" />
</el-col>
</el-row>
<div style="margin: 20px 0;"></div>
<el-input v-model="objData.musicUrl" placeholder="请输入音乐链接" @input="inputContent" />
<div style="margin: 20px 0;"></div>
<el-input v-model="objData.hqMusicUrl" placeholder="请输入高质量音乐链接" @input="inputContent" />
</el-tab-pane>
</el-tabs>
</template>
<script>
import WxNews from '@/views/mp/components/wx-news/main.vue'
import WxMaterialSelect from '@/views/mp/components/wx-material-select/main.vue'
import WxVoicePlayer from '@/views/mp/components/wx-voice-play/main.vue';
import WxVideoPlayer from '@/views/mp/components/wx-video-play/main.vue';
import { getAccessToken } from '@/utils/auth'
export default {
name: "wxReplySelect",
components: {
WxNews,
WxMaterialSelect,
WxVoicePlayer,
WxVideoPlayer
},
props: {
objData: { // 消息对象。
type: Object, // 设置为 Object 的原因,方便属性的传递
required: true,
},
newsType:{ // 图文类型1、已发布图文2、草稿箱图文
type: String,
default: "1"
},
},
data() {
return {
tempPlayerObj: {
type: '2'
},
tempObj: new Map().set( // 临时缓存,切换消息类型的 tab 的时候,可以保存对应的数据;
this.objData.type, // 消息类型
Object.assign({}, this.objData)), // 消息内容
// ========== 素材选择的弹窗,是否可见 ==========
dialogNewsVisible: false, // 图文
dialogImageVisible: false, // 图片
dialogVoiceVisible: false, // 语音
dialogVideoVisible: false, // 视频
dialogThumbVisible: false, // 缩略图
// ========== 文件上传(图片、语音、视频) ==========
fileList: [], // 文件列表
uploadData: {
"accountId": undefined,
"type": this.objData.type,
"title":'',
"introduction":''
},
actionUrl: process.env.VUE_APP_BASE_API + '/admin-api/mp/material/upload-temporary',
headers: { Authorization: "Bearer " + getAccessToken() }, // 设置上传的请求头部
}
},
methods:{
beforeThumbImageUpload(file){
const isType = file.type === 'image/jpeg'
|| file.type === 'image/png'
|| file.type === 'image/gif'
|| file.type === 'image/bmp'
|| file.type === 'image/jpg';
if (!isType) {
this.$message.error('上传图片格式不对!');
return false;
}
const isLt = file.size / 1024 / 1024 < 2;
if (!isLt) {
this.$message.error('上传图片大小不能超过 2M!');
return false;
}
this.uploadData.accountId = this.objData.accountId;
return true;
},
beforeVoiceUpload(file){
// 校验格式
const isType = file.type === 'audio/mp3'
|| file.type === 'audio/mpeg'
|| file.type === 'audio/wma'
|| file.type === 'audio/wav'
|| file.type === 'audio/amr';
if (!isType) {
this.$message.error('上传语音格式不对!' + file.type);
return false;
}
// 校验大小
const isLt = file.size / 1024 / 1024 < 2;
if (!isLt) {
this.$message.error('上传语音大小不能超过 2M!');
return false;
}
this.uploadData.accountId = this.objData.accountId;
return true;
},
beforeImageUpload(file) {
// 校验格式
const isType = file.type === 'image/jpeg'
|| file.type === 'image/png'
|| file.type === 'image/gif'
|| file.type === 'image/bmp'
|| file.type === 'image/jpg';
if (!isType) {
this.$message.error('上传图片格式不对!');
return false;
}
// 校验大小
const isLt = file.size / 1024 / 1024 < 2;
if (!isLt) {
this.$message.error('上传图片大小不能超过 2M!');
return false;
}
this.uploadData.accountId = this.objData.accountId;
return true;
},
beforeVideoUpload(file){
// 校验格式
const isType = file.type === 'video/mp4';
if (!isType) {
this.$message.error('上传视频格式不对!');
return false;
}
// 校验大小
const isLt = file.size / 1024 / 1024 < 10;
if (!isLt) {
this.$message.error('上传视频大小不能超过 10M!');
return false;
}
this.uploadData.accountId = this.objData.accountId;
return true;
},
handleUploadSuccess(response, file, fileList) {
if (response.code !== 0) {
this.$message.error('上传出错:' + response.msg)
return false;
}
// 清空上传时的各种数据
this.fileList = []
this.uploadData.title = ''
this.uploadData.introduction = ''
// 上传好的文件,本质是个素材,所以可以进行选中
let item = response.data
this.selectMaterial(item)
},
/**
* 切换消息类型的 tab
*
* @param tab tab
*/
handleClick(tab) {
// 设置后续文件上传的文件类型
this.uploadData.type = this.objData.type;
if (this.uploadData.type === 'music') { // 【音乐】上传的是缩略图
this.uploadData.type = 'thumb';
}
// 从 tempObj 临时缓存中,获取对应的数据,并设置回 objData
let tempObjItem = this.tempObj.get(this.objData.type)
if (tempObjItem) {
this.objData.content = tempObjItem.content ? tempObjItem.content : null
this.objData.mediaId = tempObjItem.mediaId ? tempObjItem.mediaId : null
this.objData.url = tempObjItem.url ? tempObjItem.url : null
this.objData.name = tempObjItem.url ? tempObjItem.name : null
this.objData.title = tempObjItem.title ? tempObjItem.title : null
this.objData.description = tempObjItem.description ? tempObjItem.description : null
return;
}
// 如果获取不到,需要把 objData 复原
// 必须使用 $set 赋值,不然 input 无法输入内容
this.$set(this.objData, 'content', '');
this.$delete(this.objData, 'mediaId');
this.$delete(this.objData, 'url');
this.$set(this.objData, 'title', '');
this.$set(this.objData, 'description', '');
},
/**
* 选择素材,将设置设置到 objData 变量
*
* @param item 素材
*/
selectMaterial(item) {
// 选择好素材,所以隐藏弹窗
this.closeMaterial();
// 创建 tempObjItem 对象,并设置对应的值
let tempObjItem = {}
tempObjItem.type = this.objData.type;
if (this.objData.type === 'news') {
tempObjItem.articles = item.content.newsItem
this.objData.articles = item.content.newsItem
} else if (this.objData.type === 'music') { // 音乐需要特殊处理,因为选择的是图片的缩略图
tempObjItem.thumbMediaId = item.mediaId
this.objData.thumbMediaId = item.mediaId
tempObjItem.thumbMediaUrl = item.url
this.objData.thumbMediaUrl = item.url
// title、introduction、musicUrl、hqMusicUrl从 objData 到 tempObjItem避免上传素材后被覆盖掉
tempObjItem.title = this.objData.title || ''
tempObjItem.introduction = this.objData.introduction || ''
tempObjItem.musicUrl = this.objData.musicUrl || ''
tempObjItem.hqMusicUrl = this.objData.hqMusicUrl || ''
} else if (this.objData.type === 'image'
|| this.objData.type === 'voice') {
tempObjItem.mediaId = item.mediaId
this.objData.mediaId = item.mediaId
tempObjItem.url = item.url;
this.objData.url = item.url;
tempObjItem.name = item.name
this.objData.name = item.name
} else if (this.objData.type === 'video') {
tempObjItem.mediaId = item.mediaId
this.objData.mediaId = item.mediaId
tempObjItem.url = item.url;
this.objData.url = item.url;
tempObjItem.name = item.name
this.objData.name = item.name
// title、introduction从 item 到 tempObjItem因为素材里有 title、introduction
if (item.title) {
this.objData.title = item.title || ''
tempObjItem.title = item.title || ''
}
if (item.introduction) {
this.objData.description = item.introduction || '' // 消息使用的是 description素材使用的是 introduction所以转换下
tempObjItem.description = item.introduction || ''
}
} else if (this.objData.type === 'text') {
this.objData.content = item.content || ''
}
// 最终设置到临时缓存
this.tempObj.set(this.objData.type, tempObjItem)
},
openMaterial() {
if (this.objData.type === 'news') {
this.dialogNewsVisible = true
} else if(this.objData.type === 'image') {
this.dialogImageVisible = true
} else if(this.objData.type === 'voice') {
this.dialogVoiceVisible = true
} else if(this.objData.type === 'video') {
this.dialogVideoVisible = true
} else if(this.objData.type === 'music') {
this.dialogThumbVisible = true
}
},
closeMaterial() {
this.dialogNewsVisible = false
this.dialogImageVisible = false
this.dialogVoiceVisible = false
this.dialogVideoVisible = false
this.dialogThumbVisible = false
},
deleteObj() {
if (this.objData.type === 'news') {
this.$delete(this.objData, 'articles');
} else if(this.objData.type === 'image') {
this.objData.mediaId = null
this.$delete(this.objData, 'url');
this.objData.name = null
} else if(this.objData.type === 'voice') {
this.objData.mediaId = null
this.$delete(this.objData, 'url');
this.objData.name = null
} else if(this.objData.type === 'video') {
this.objData.mediaId = null
this.$delete(this.objData, 'url');
this.objData.name = null
this.objData.title = null
this.objData.description = null
} else if(this.objData.type === 'music') {
this.objData.thumbMediaId = null
this.objData.thumbMediaUrl = null
this.objData.title = null
this.objData.description = null
this.objData.musicUrl = null
this.objData.hqMusicUrl = null
} else if(this.objData.type === 'text') {
this.objData.content = null
}
// 覆盖缓存
this.tempObj.set(this.objData.type, Object.assign({}, this.objData));
},
/**
* 输入时,缓存每次 objData 到 tempObj 中
*
* why不确定为什么 v-model="objData.content" 不能自动缓存,所以通过这样的方式
*/
inputContent(str) {
// 覆盖缓存
this.tempObj.set(this.objData.type, Object.assign({}, this.objData));
}
}
};
</script>
<style lang="scss" scoped>
.public-account-management{
.el-input{
width: 70%;
margin-right: 2%;
}
}
.pagination{
text-align: right;
margin-right: 25px;
}
.select-item{
width: 280px;
padding: 10px;
margin: 0 auto 10px auto;
border: 1px solid #eaeaea;
}
.select-item2{
padding: 10px;
margin: 0 auto 10px auto;
border: 1px solid #eaeaea;
}
.ope-row{
padding-top: 10px;
text-align: center;
}
.item-name{
font-size: 12px;
overflow: hidden;
text-overflow:ellipsis;
white-space: nowrap;
text-align: center;
}
.el-form-item__content{
line-height:unset!important;
}
.col-select{
border: 1px solid rgb(234, 234, 234);
padding: 50px 0px;
height: 160px;
width: 49.5%;
}
.col-select2{
border: 1px solid rgb(234, 234, 234);
padding: 50px 0px;
height: 160px;
}
.col-add{
border: 1px solid rgb(234, 234, 234);
padding: 50px 0px;
height: 160px;
width: 49.5%;
float: right
}
.avatar-uploader-icon {
border: 1px solid #d9d9d9;
font-size: 28px;
color: #8c939d;
width: 100px!important;
height: 100px!important;
line-height: 100px!important;
text-align: center;
}
.material-img {
width: 100%;
}
.thumb-div{
display: inline-block;
text-align: center;
}
.item-infos{
width: 30%;
margin: auto
}
</style>

View File

@@ -1,91 +0,0 @@
<!--
- Copyright (C) 2018-2019
- All rights reserved, Designed By www.joolun.com
微信消息 - 视频
芋道源码
bug 修复
1joolun 的做法使用 mediaId 从微信公众号下载对应的 mp4 素材从而播放内容
存在的问题mediaId 有效期是 3 超过时间后无法播放
2重构后的做法后端接收到微信公众号的视频消息后将视频消息的 media_id 的文件内容保存到文件服务器中这样前端可以直接使用 URL 播放
体验优化弹窗关闭后自动暂停视频的播放
-->
<template>
<div>
<!-- 提示 -->
<div @click="playVideo()">
<i class="el-icon-video-play" style="font-size: 40px!important;" ></i>
<p>点击播放视频</p>
</div>
<!-- 弹窗播放 -->
<el-dialog title="视频播放" :visible.sync="dialogVideo" width="40%" append-to-body @close="closeDialog">
<video-player v-if="playerOptions.sources[0].src" class="video-player vjs-custom-skin" ref="videoPlayer"
:playsinline="true" :options="playerOptions"
@play="onPlayerPlay($event)" @pause="onPlayerPause($event)">
</video-player>
</el-dialog>
</div>
</template>
<script>
// 引入 videoPlayer 相关组件。教程https://juejin.cn/post/6923056942281654285
import { videoPlayer } from 'vue-video-player'
require('video.js/dist/video-js.css')
require('vue-video-player/src/custom-theme.css')
export default {
name: "wxVideoPlayer",
props: {
url: { // 视频地址例如说https://www.iocoder.cn/xxx.mp4
type: String,
required: true
},
},
components: {
videoPlayer
},
data() {
return {
dialogVideo:false,
playerOptions: {
playbackRates: [0.5, 1.0, 1.5, 2.0], // 播放速度
autoplay: false, // 如果 true,浏览器准备好时开始回放。
muted: false, // 默认情况下将会消除任何音频。
loop: false, // 导致视频一结束就重新开始。
preload: 'auto', // 建议浏览器在 <video> 加载元素后是否应该开始下载视频数据。auto 浏览器选择最佳行为,立即开始加载视频(如果浏览器支持)
language: 'zh-CN',
aspectRatio: '16:9', // 将播放器置于流畅模式,并在计算播放器的动态大小时使用该值。值应该代表一个比例 - 用冒号分隔的两个数字(例如"16:9"或"4:3"
fluid: true, // 当true时Video.js player 将拥有流体大小。换句话说,它将按比例缩放以适应其容器。
sources: [{
type: "video/mp4",
src: "" // 你的视频地址(必填)【重要】
}],
poster: "", // 你的封面地址
width: document.documentElement.clientWidth,
notSupportedMessage: '此视频暂无法播放,请稍后再试', //允许覆盖 Video.js 无法播放媒体源时显示的默认信息。
controlBar: {
timeDivider: true,
durationDisplay: true,
remainingTimeDisplay: false,
fullscreenToggle: true //全屏按钮
}
}
}
},
methods: {
playVideo(){
this.dialogVideo = true
// 设置地址
this.$set(this.playerOptions.sources[0], 'src', this.url)
},
closeDialog(){
// 暂停播放
this.$refs.videoPlayer.player.pause()
},
onPlayerPlay(player) {
},
onPlayerPause(player) {
},
}
};
</script>

View File

@@ -1,98 +0,0 @@
<!--
- Copyright (C) 2018-2019
- All rights reserved, Designed By www.joolun.com
微信消息 - 语音
芋道源码
bug 修复
1joolun 的做法使用 mediaId 从微信公众号下载对应的 mp4 素材从而播放内容
存在的问题mediaId 有效期是 3 超过时间后无法播放
2重构后的做法后端接收到微信公众号的视频消息后将视频消息的 media_id 的文件内容保存到文件服务器中这样前端可以直接使用 URL 播放
代码优化 props 中的 objData 调成为 data 中对应的属性并补充相关注释
-->
<template>
<div class="wx-voice-div" @click="playVoice">
<i :class="playing !== true ? 'el-icon-video-play': 'el-icon-video-pause'">
<span class="amr-duration" v-if="duration">{{ duration }} </span>
</i>
<div v-if="content">
<el-tag type="success" size="mini">语音识别</el-tag>
{{ content }}
</div>
</div>
</template>
<script>
// 因为微信语音是 amr 格式,所以需要用到 amr 解码器https://www.npmjs.com/package/benz-amr-recorder
const BenzAMRRecorder = require('benz-amr-recorder')
export default {
name: "wxVoicePlayer",
props: {
url: { // 语音地址例如说https://www.iocoder.cn/xxx.amr
type: String,
required: true
},
content: { // 语音文本
type: String,
required: false
}
},
data() {
return {
amr: undefined, // BenzAMRRecorder 对象
playing: false, // 是否在播放中
duration: undefined, // 播放时长
}
},
methods:{
playVoice() {
debugger
// 情况一:未初始化,则创建 BenzAMRRecorder
if (this.amr === undefined){
this.amrInit();
return;
}
if (this.amr.isPlaying()) {
this.amrStop();
} else {
this.amrPlay();
}
},
amrInit() {
const amr = new BenzAMRRecorder();
this.amr = amr;
// 设置播放
const that = this
amr.initWithUrl(this.url).then(function() {
that.amrPlay()
that.duration = amr.getDuration();
})
// 监听暂停
amr.onEnded(function() {
that.playing = false;
})
},
amrPlay() {
this.playing = true;
this.amr.play()
},
amrStop() {
this.playing = false;
this.amr.stop()
},
}
};
</script>
<style lang="scss" scoped>
.wx-voice-div {
padding: 5px;
background-color: #eaeaea;
border-radius: 10px;
}
.amr-duration {
font-size: 11px;
margin-left: 5px;
}
</style>

View File

@@ -1,700 +0,0 @@
<!--
MIT License
Copyright (c) 2020 www.joolun.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
芋道源码
优化代码和项目的代码保持一致
清理冗余代码保证代码整洁
增加注释提升可读性
-->
<template>
<div class="app-container">
<doc-alert title="公众号图文" url="https://doc.iocoder.cn/mp/article/" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="公众号" prop="accountId">
<el-select v-model="queryParams.accountId" placeholder="请选择公众号">
<el-option v-for="item in accounts" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
v-hasPermi="['mp:draft:create']">新增
</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<div class="waterfall" v-loading="loading">
<div v-if="item.content && item.content.newsItem" class="waterfall-item" v-for="item in list"
:key='item.articleId'>
<wx-news :articles="item.content.newsItem" />
<!-- 操作按钮 -->
<el-row class="ope-row">
<el-button type="success" circle @click="handlePublish(item)" v-hasPermi="['mp:free-publish:submit']">发布</el-button>
<el-button type="primary" icon="el-icon-edit" circle @click="handleUpdate(item)" v-hasPermi="['mp:draft:update']" />
<el-button type="danger" icon="el-icon-delete" circle @click="handleDelete(item)" v-hasPermi="['mp:draft:delete']" />
</el-row>
</div>
</div>
<!-- 分页记录 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- 添加或修改草稿对话框 -->
<el-dialog :title="operateMaterial === 'add' ? '新建图文' : '修改图文'"
append-to-body width="80%" top="20px" :visible.sync="dialogNewsVisible"
:before-close="dialogNewsClose" :close-on-click-modal="false">
<div class="left">
<div class="select-item">
<div v-for="(news, index) in articlesAdd" :key='news.id'>
<div class="news-main father" v-if="index === 0" :class="{'activeAddNews': isActiveAddNews === index}"
@click="activeNews(index)">
<div class="news-content">
<img class="material-img" v-if="news.thumbUrl" :src="news.thumbUrl"/>
<div class="news-content-title">{{news.title}}</div>
</div>
<div class="child" v-if="articlesAdd.length>1">
<el-button type="mini" icon="el-icon-sort-down" @click="downNews(index)">下移</el-button>
<el-button v-if="operateMaterial === 'add'" type="mini" icon="el-icon-delete"
@click="minusNews(index)">删除
</el-button>
</div>
</div>
<div class="news-main-item father" v-if="index>0" :class="{'activeAddNews': isActiveAddNews === index}"
@click="activeNews(index)">
<div class="news-content-item">
<div class="news-content-item-title ">{{news.title}}</div>
<div class="news-content-item-img">
<img class="material-img" v-if="news.thumbUrl" :src="news.thumbUrl" height="100%"/>
</div>
</div>
<div class="child">
<el-button v-if="articlesAdd.length > index+1" type="mini" icon="el-icon-sort-down"
@click="downNews(index)">下移
</el-button>
<el-button type="mini" icon="el-icon-sort-up" @click="upNews(index)">上移</el-button>
<el-button v-if="operateMaterial=== 'add'" type="mini" icon="el-icon-delete"
@click="minusNews(index)">删除
</el-button>
</div>
</div>
</div>
<div class="news-main-plus" @click="plusNews" v-if="articlesAdd.length<8 && operateMaterial==='add'">
<i class="el-icon-circle-plus icon-plus"></i>
</div>
</div>
</div>
<div class="right" v-loading="addMaterialLoading" v-if="articlesAdd.length > 0">
<br /> <br /> <br /> <br />
<!-- 标题作者原文地址 -->
<el-input v-model="articlesAdd[isActiveAddNews].title" placeholder="请输入标题(必填)" />
<el-input v-model="articlesAdd[isActiveAddNews].author" placeholder="请输入作者" style="margin-top: 5px;" />
<el-input v-model="articlesAdd[isActiveAddNews].contentSourceUrl" placeholder="请输入原文地址" style="margin-top: 5px;" />
<!-- 封面和摘要 -->
<div class="input-tt">封面和摘要</div>
<div>
<div class="thumb-div">
<img class="material-img" v-if="articlesAdd[isActiveAddNews].thumbUrl"
:src="articlesAdd[isActiveAddNews].thumbUrl" :class="isActiveAddNews === 0 ? 'avatar':'avatar1'">
<i v-else class="el-icon-plus avatar-uploader-icon"
:class="isActiveAddNews === 0 ? 'avatar':'avatar1'"></i>
<div class="thumb-but">
<el-upload :action="actionUrl" :headers="headers" multiple :limit="1" :file-list="fileList" :data="uploadData"
:before-upload="beforeThumbImageUpload" :on-success="handleUploadSuccess">
<el-button slot="trigger" size="mini" type="primary">本地上传</el-button>
<el-button size="mini" type="primary" @click="openMaterial" style="margin-left: 5px">素材库选择</el-button>
<div slot="tip" class="el-upload__tip">支持 bmp/png/jpeg/jpg/gif 格式大小不超过 2M</div>
</el-upload>
</div>
<el-dialog title="选择图片" :visible.sync="dialogImageVisible" width="80%" append-to-body>
<wx-material-select ref="materialSelect" :objData="{type: 'image', accountId: this.queryParams.accountId}"
@selectMaterial="selectMaterial" />
</el-dialog>
</div>
<el-input :rows="8" type="textarea" v-model="articlesAdd[isActiveAddNews].digest" placeholder="请输入摘要"
class="digest" maxlength="120" style="float: right" />
</div>
<!--富文本编辑器组件-->
<el-row>
<wx-editor v-model="articlesAdd[isActiveAddNews].content" :account-id="this.uploadData.accountId"
v-if="hackResetEditor"/>
</el-row>
</div>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogNewsVisible = false"> </el-button>
<el-button type="primary" @click="submitForm"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import WxEditor from '@/views/mp/components/wx-editor/WxEditor.vue';
import WxNews from '@/views/mp/components/wx-news/main.vue';
import WxMaterialSelect from '@/views/mp/components/wx-material-select/main.vue'
import { getAccessToken } from '@/utils/auth'
import {createDraft, deleteDraft, getDraftPage, updateDraft} from "@/api/mp/draft";
import { getSimpleAccounts } from "@/api/mp/account";
import {deleteFreePublish, submitFreePublish} from "@/api/mp/freePublish";
export default {
name: 'MpDraft',
components: {
WxEditor,
WxNews,
WxMaterialSelect
},
data() {
return {
// 遮罩层
loading: false,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 数据列表
list: [],
queryParams: {
pageNo: 1,
pageSize: 10,
accountId: undefined,
},
// ========== 文件上传 ==========
actionUrl: process.env.VUE_APP_BASE_API + "/admin-api/mp/material/upload-permanent", // 上传永久素材的地址
headers: { Authorization: "Bearer " + getAccessToken() }, // 设置上传的请求头部
fileList: [],
uploadData: {
"type": 'image',
// "accountId": 1,
},
// ========== 草稿新建 or 修改 ==========
dialogNewsVisible: false,
addMaterialLoading: false, // 添加草稿的 loading 标识
articlesAdd: [],
isActiveAddNews: 0,
dialogImageVisible: false,
operateMaterial: 'add',
articlesMediaId: '',
hackResetEditor: false,
// 公众号账号列表
accounts: [],
}
},
created() {
getSimpleAccounts().then(response => {
this.accounts = response.data;
// 默认选中第一个
if (this.accounts.length > 0) {
this.setAccountId(this.accounts[0].id);
}
// 加载数据
this.getList();
})
},
methods: {
// ======================== 列表查询 ========================
/** 设置账号编号 */
setAccountId(accountId) {
this.queryParams.accountId = accountId;
this.uploadData.accountId = accountId;
},
/** 查询列表 */
getList() {
// 如果没有选中公众号账号,则进行提示。
if (!this.queryParams.accountId) {
this.$message.error('未选中公众号,无法查询草稿箱')
return false
}
this.loading = true
getDraftPage(this.queryParams).then(response => {
// 将 thumbUrl 转成 picUrl保证 wx-news 组件可以预览封面
response.data.list.forEach(item => {
const newsItem = item.content.newsItem;
newsItem.forEach(article => {
article.picUrl = article.thumbUrl;
})
})
this.list = response.data.list
this.total = response.data.total
}).finally(() => {
this.loading = false
})
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1
// 默认选中第一个
if (this.queryParams.accountId) {
this.setAccountId(this.queryParams.accountId)
}
this.getList()
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm('queryForm')
// 默认选中第一个
if (this.accounts.length > 0) {
this.setAccountId(this.accounts[0].id)
}
this.handleQuery()
},
// ======================== 新增/修改草稿 ========================
/** 新增按钮操作 */
handleAdd() {
this.resetEditor();
this.reset();
// 打开表单,并设置初始化
this.operateMaterial = 'add'
this.dialogNewsVisible = true
},
/** 更新按钮操作 */
handleUpdate(item){
this.resetEditor();
this.reset();
this.articlesMediaId = item.mediaId
this.articlesAdd = JSON.parse(JSON.stringify(item.content.newsItem))
// 打开表单,并设置初始化
this.operateMaterial = 'edit'
this.dialogNewsVisible = true
},
/** 提交按钮 */
submitForm() {
this.addMaterialLoading = true
if (this.operateMaterial === 'add') {
createDraft(this.queryParams.accountId, this.articlesAdd).then(response => {
this.$modal.msgSuccess("新增成功");
this.dialogNewsVisible = false;
this.getList()
}).finally(() => {
this.addMaterialLoading = false
})
} else {
updateDraft(this.queryParams.accountId, this.articlesMediaId, this.articlesAdd).then(response => {
this.$modal.msgSuccess("更新成功");
this.dialogNewsVisible = false;
this.getList()
}).finally(() => {
this.addMaterialLoading = false
})
}
},
// 关闭弹窗
dialogNewsClose(done) {
this.$modal.confirm('修改内容可能还未保存,确定关闭吗?').then(() => {
this.reset()
this.resetEditor()
done()
}).catch(() => {})
},
// 表单重置
reset() {
this.isActiveAddNews = 0
this.articlesAdd = [this.buildEmptyArticle()]
},
// 表单 Editor 重置
resetEditor() {
this.hackResetEditor = false // 销毁组件
this.$nextTick(() => {
this.hackResetEditor = true // 重建组件
})
},
// 将图文向下移动
downNews(index) {
let temp = this.articlesAdd[index]
this.articlesAdd[index] = this.articlesAdd[index+1]
this.articlesAdd[index + 1] = temp
this.isActiveAddNews = index + 1
},
// 将图文向上移动
upNews(index) {
let temp = this.articlesAdd[index]
this.articlesAdd[index] = this.articlesAdd[index - 1]
this.articlesAdd[index - 1] = temp
this.isActiveAddNews = index - 1
},
// 选中指定 index 的图文
activeNews(index) {
this.resetEditor();
this.isActiveAddNews = index
},
// 删除指定 index 的图文
minusNews(index) {
this.$modal.confirm('确定删除该图文吗?').then(() => {
this.articlesAdd.splice(index,1);
if (this.isActiveAddNews === index) {
this.isActiveAddNews = 0
}
}).catch(() => {})
},
// 添加一个图文
plusNews() {
this.articlesAdd.push(this.buildEmptyArticle())
this.isActiveAddNews = this.articlesAdd.length - 1
},
// 创建空的 article
buildEmptyArticle() {
return {
"title": '',
"thumbMediaId": '',
"author": '',
"digest": '',
"showCoverPic": '',
"content": '',
"contentSourceUrl": '',
"needOpenComment":'',
"onlyFansCanComment":'',
"thumbUrl":''
}
},
// ======================== 文件上传 ========================
beforeThumbImageUpload(file) {
this.addMaterialLoading = true
const isType = file.type === 'image/jpeg'
|| file.type === 'image/png'
|| file.type === 'image/gif'
|| file.type === 'image/bmp'
|| file.type === 'image/jpg';
if (!isType) {
this.$message.error('上传图片格式不对!')
this.addMaterialLoading = false
return false;
}
const isLt = file.size / 1024 / 1024 < 2
if (!isLt) {
this.$message.error('上传图片大小不能超过 2M!')
this.addMaterialLoading = false
return false;
}
// 校验通过
return true;
},
handleUploadSuccess(response, file, fileList) {
this.addMaterialLoading = false // 关闭 loading
if (response.code !== 0) {
this.$message.error('上传出错:' + response.msg)
return false;
}
// 重置上传文件的表单
this.fileList = []
// 设置草稿的封面字段
this.articlesAdd[this.isActiveAddNews].thumbMediaId = response.data.mediaId
this.articlesAdd[this.isActiveAddNews].thumbUrl = response.data.url
},
// 选择 or 上传完素材,设置回草稿
selectMaterial(item) {
this.dialogImageVisible = false
this.articlesAdd[this.isActiveAddNews].thumbMediaId = item.mediaId
this.articlesAdd[this.isActiveAddNews].thumbUrl = item.url
},
// 打开素材选择
openMaterial() {
this.dialogImageVisible = true
try {
this.$refs['materialSelect'].queryParams.accountId = this.queryParams.accountId // 强制设置下 accountId避免二次查询不对
this.$refs['materialSelect'].handleQuery(); // 刷新列表,失败也无所谓
} catch (e) {}
},
// ======================== 草稿箱发布 ========================
handlePublish(item) {
const accountId = this.queryParams.accountId;
const mediaId = item.mediaId;
const content = '你正在通过发布的方式发表内容。 发布不占用群发次数,一天可多次发布。已发布内容不会推送给用户,也不会展示在公众号主页中。 发布后,你可以前往发表记录获取链接,也可以将发布内容添加到自定义菜单、自动回复、话题和页面模板中。';
this.$modal.confirm(content).then(function() {
return submitFreePublish(accountId, mediaId);
}).then(() => {
this.getList();
this.$modal.msgSuccess("发布成功");
}).catch(() => {});
},
handleDelete(item) {
const accountId = this.queryParams.accountId;
const mediaId = item.mediaId;
this.$modal.confirm('此操作将永久删除该草稿, 是否继续?').then(function() {
return deleteDraft(accountId, mediaId);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
},
}
}
</script>
<style lang="scss" scoped>
.pagination {
float: right;
margin-right: 25px;
}
.add_but {
padding: 10px;
}
.ope-row {
margin-top: 5px;
text-align: center;
border-top: 1px solid #eaeaea;
padding-top: 5px;
}
.item-name {
font-size: 12px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
text-align: center;
}
.el-upload__tip {
margin-left: 5px;
}
/*新增图文*/
.left {
display: inline-block;
width: 35%;
vertical-align: top;
margin-top: 200px;
}
.right {
display: inline-block;
width: 60%;
margin-top: -40px;
}
.avatar-uploader {
width: 20%;
display: inline-block;
}
.avatar-uploader .el-upload {
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
text-align: unset !important;
}
.avatar-uploader .el-upload:hover {
border-color: #165dff;
}
.avatar-uploader-icon {
border: 1px solid #d9d9d9;
font-size: 28px;
color: #8c939d;
width: 120px;
height: 120px;
line-height: 120px;
text-align: center;
}
.avatar {
width: 230px;
height: 120px;
}
.avatar1 {
width: 120px;
height: 120px;
}
.digest {
width: 60%;
display: inline-block;
vertical-align: top;
}
/*新增图文*/
/*瀑布流样式*/
.waterfall {
width: 100%;
column-gap: 10px;
column-count: 5;
margin: 0 auto;
}
.waterfall-item {
padding: 10px;
margin-bottom: 10px;
break-inside: avoid;
border: 1px solid #eaeaea;
}
p {
line-height: 30px;
}
@media (min-width: 992px) and (max-width: 1300px) {
.waterfall {
column-count: 3;
}
p {
color: red;
}
}
@media (min-width: 768px) and (max-width: 991px) {
.waterfall {
column-count: 2;
}
p {
color: orange;
}
}
@media (max-width: 767px) {
.waterfall {
column-count: 1;
}
}
/*瀑布流样式*/
.news-main {
background-color: #FFFFFF;
width: 100%;
margin: auto;
height: 120px;
}
.news-content {
background-color: #acadae;
width: 100%;
height: 120px;
position: relative;
}
.news-content-title {
display: inline-block;
font-size: 15px;
color: #FFFFFF;
position: absolute;
left: 0px;
bottom: 0px;
background-color: black;
width: 98%;
padding: 1%;
opacity: 0.65;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
height: 25px;
}
.news-main-item {
background-color: #FFFFFF;
padding: 5px 0px;
border-top: 1px solid #eaeaea;
width: 100%;
margin: auto;
}
.news-content-item {
position: relative;
margin-left: -3px
}
.news-content-item-title {
display: inline-block;
font-size: 12px;
width: 70%;
}
.news-content-item-img {
display: inline-block;
width: 25%;
background-color: #acadae
}
.input-tt {
padding: 5px;
}
.activeAddNews {
border: 5px solid #2bb673;
}
.news-main-plus {
width: 280px;
text-align: center;
margin: auto;
height: 50px;
}
.icon-plus {
margin: 10px;
font-size: 25px;
}
.select-item {
width: 60%;
padding: 10px;
margin: 0 auto 10px auto;
border: 1px solid #eaeaea;
}
.father .child {
display: none;
text-align: center;
position: relative;
bottom: 25px;
}
.father:hover .child {
display: block;
}
.thumb-div {
display: inline-block;
width: 30%;
text-align: center;
}
.thumb-but {
margin: 5px;
}
.material-img {
width: 100%;
height: 100%;
}
</style>

View File

@@ -1,395 +0,0 @@
<!--
MIT License
Copyright (c) 2020 www.joolun.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
芋道源码
优化代码和项目的代码保持一致
-->
<template>
<div class="app-container">
<doc-alert title="公众号图文" url="https://doc.iocoder.cn/mp/article/" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="公众号" prop="accountId">
<el-select v-model="queryParams.accountId" placeholder="请选择公众号">
<el-option v-for="item in accounts" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 列表 -->
<div class="waterfall" v-loading="loading">
<div v-if="item.content && item.content.newsItem" class="waterfall-item" v-for="item in list"
:key='item.articleId'>
<wx-news :articles="item.content.newsItem" />
<!-- 操作 -->
<el-row class="ope-row">
<el-button type="danger" icon="el-icon-delete" circle @click="handleDelete(item)"
v-hasPermi="['mp:free-publish:delete']" />
</el-row>
</div>
</div>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
</div>
</template>
<script>
import { getFreePublishPage, deleteFreePublish } from "@/api/mp/freePublish";
import { getSimpleAccounts } from "@/api/mp/account";
import WxNews from '@/views/mp/components/wx-news/main.vue';
export default {
name: 'MpFreePublish',
components: {
WxNews
},
data() {
return {
// 遮罩层
loading: false,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 已发表列表
list: [],
// 查询参数
queryParams: {
total: 0, // 总页数
currentPage: 1, // 当前页数
queryParamsSize: 10 // 每页显示多少条
},
// 公众号账号列表
accounts: [],
}
},
created() {
getSimpleAccounts().then(response => {
this.accounts = response.data;
// 默认选中第一个
if (this.accounts.length > 0) {
this.queryParams.accountId = this.accounts[0].id;
}
// 加载数据
this.getList();
})
},
methods: {
/** 查询列表 */
getList() {
// 如果没有选中公众号账号,则进行提示。
if (!this.queryParams.accountId) {
this.$message.error('未选中公众号,无法查询已发表图文')
return false
}
this.loading = true;
getFreePublishPage(this.queryParams).then(response => {
// 将 thumbUrl 转成 picUrl保证 wx-news 组件可以预览封面
response.data.list.forEach(item => {
const newsItem = item.content.newsItem;
newsItem.forEach(article => {
article.picUrl = article.thumbUrl;
})
})
this.list = response.data.list
this.total = response.data.total
}).finally(() => {
this.loading = false
})
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
// 默认选中第一个
if (this.accounts.length > 0) {
this.queryParams.accountId = this.accounts[0].id;
}
this.handleQuery();
},
/** 删除按钮操作 */
handleDelete(item){
const articleId = item.articleId;
const accountId = this.queryParams.accountId;
this.$modal.confirm('删除后用户将无法访问此页面,确定删除?').then(function() {
return deleteFreePublish(accountId, articleId);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
},
}
}
</script>
<style lang="scss" scoped>
.pagination {
float: right;
margin-right: 25px;
}
.add_but {
padding: 10px;
}
.ope-row {
margin-top: 5px;
text-align: center;
border-top: 1px solid #eaeaea;
padding-top: 5px;
}
.item-name {
font-size: 12px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
text-align: center;
}
.el-upload__tip {
margin-left: 5px;
}
/*新增图文*/
.left {
display: inline-block;
width: 35%;
vertical-align: top;
margin-top: 200px;
}
.right {
display: inline-block;
width: 60%;
margin-top: -40px;
}
.avatar-uploader {
width: 20%;
display: inline-block;
}
.avatar-uploader .el-upload {
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
text-align: unset !important;
}
.avatar-uploader .el-upload:hover {
border-color: #165dff;
}
.avatar-uploader-icon {
border: 1px solid #d9d9d9;
font-size: 28px;
color: #8c939d;
width: 120px;
height: 120px;
line-height: 120px;
text-align: center;
}
.avatar {
width: 230px;
height: 120px;
}
.avatar1 {
width: 120px;
height: 120px;
}
.digest {
width: 60%;
display: inline-block;
vertical-align: top;
}
/*新增图文*/
/*瀑布流样式*/
.waterfall {
width: 100%;
column-gap: 10px;
column-count: 5;
margin: 0 auto;
}
.waterfall-item {
padding: 10px;
margin-bottom: 10px;
break-inside: avoid;
border: 1px solid #eaeaea;
}
p {
line-height: 30px;
}
@media (min-width: 992px) and (max-width: 1300px) {
.waterfall {
column-count: 3;
}
p {
color: red;
}
}
@media (min-width: 768px) and (max-width: 991px) {
.waterfall {
column-count: 2;
}
p {
color: orange;
}
}
@media (max-width: 767px) {
.waterfall {
column-count: 1;
}
}
/*瀑布流样式*/
.news-main {
background-color: #FFFFFF;
width: 100%;
margin: auto;
height: 120px;
}
.news-content {
background-color: #acadae;
width: 100%;
height: 120px;
position: relative;
}
.news-content-title {
display: inline-block;
font-size: 15px;
color: #FFFFFF;
position: absolute;
left: 0px;
bottom: 0px;
background-color: black;
width: 98%;
padding: 1%;
opacity: 0.65;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
height: 25px;
}
.news-main-item {
background-color: #FFFFFF;
padding: 5px 0px;
border-top: 1px solid #eaeaea;
width: 100%;
margin: auto;
}
.news-content-item {
position: relative;
margin-left: -3px
}
.news-content-item-title {
display: inline-block;
font-size: 12px;
width: 70%;
}
.news-content-item-img {
display: inline-block;
width: 25%;
background-color: #acadae
}
.input-tt {
padding: 5px;
}
.activeAddNews {
border: 5px solid #2bb673;
}
.news-main-plus {
width: 280px;
text-align: center;
margin: auto;
height: 50px;
}
.icon-plus {
margin: 10px;
font-size: 25px;
}
.select-item {
width: 60%;
padding: 10px;
margin: 0 auto 10px auto;
border: 1px solid #eaeaea;
}
.father .child {
display: none;
text-align: center;
position: relative;
bottom: 25px;
}
.father:hover .child {
display: block;
}
.thumb-div {
display: inline-block;
width: 30%;
text-align: center;
}
.thumb-but {
margin: 5px;
}
.material-img {
width: 100%;
height: 100%;
}
</style>

View File

@@ -1,440 +0,0 @@
<!--
MIT License
Copyright (c) 2020 www.joolun.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
芋道源码
清理冗余 css 内容清理冗余 data 变量
美化样式支持播放提升使用体验
优化代码特别是方法名和变量提升可读性
-->
<template>
<div class="app-container">
<doc-alert title="公众号素材" url="https://doc.iocoder.cn/mp/material/" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="公众号" prop="accountId">
<el-select v-model="queryParams.accountId" placeholder="请选择公众号">
<el-option v-for="item in accounts" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-tabs v-model="type" @tab-click="handleClick">
<!-- tab 1图片 -->
<el-tab-pane name="image">
<span slot="label"><i class="el-icon-picture"></i> 图片</span>
<div class="add_but" v-hasPermi="['mp:material:upload-permanent']">
<el-upload :action="actionUrl" :headers="headers" multiple :limit="1" :file-list="fileList" :data="uploadData"
:before-upload="beforeImageUpload" :on-success="handleUploadSuccess">
<el-button size="mini" type="primary">点击上传</el-button>
<sapn slot="tip" class="el-upload__tip" style="margin-left: 5px">支持 bmp/png/jpeg/jpg/gif 格式大小不超过 2M</sapn>
</el-upload>
</div>
<div class="waterfall" v-loading="loading">
<div class="waterfall-item" v-for="item in list" :key='item.id'>
<a target="_blank" :href="item.url">
<img class="material-img" :src="item.url">
<div class="item-name">{{item.name}}</div>
</a>
<el-row class="ope-row">
<el-button type="danger" icon="el-icon-delete" circle @click="handleDelete(item)"
v-hasPermi="['mp:material:delete']"/>
</el-row>
</div>
</div>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
</el-tab-pane>
<!-- tab 2语音 -->
<el-tab-pane name="voice">
<span slot="label"><i class="el-icon-microphone"></i> 语音</span>
<div class="add_but" v-hasPermi="['mp:material:upload-permanent']">
<el-upload :action="actionUrl" :headers="headers" multiple :limit="1" :file-list="fileList" :data="uploadData"
:on-success="handleUploadSuccess" :before-upload="beforeVoiceUpload">
<el-button size="mini" type="primary">点击上传</el-button>
<span slot="tip" class="el-upload__tip" style="margin-left: 5px">格式支持 mp3/wma/wav/amr文件大小不超过 2M播放长度不超过 60s</span>
</el-upload>
</div>
<el-table :data="list" stripe border v-loading="loading" style="margin-top: 10px;">
<el-table-column label="编号" align="center" prop="mediaId" />
<el-table-column label="文件名" align="center" prop="name" />
<el-table-column label="语音" align="center">
<template v-slot="scope">
<wx-voice-player :url="scope.row.url" />
</template>
</el-table-column>
<el-table-column label="上传时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button type="text" icon="el-icon-download" size="small" plain @click="handleDownload(scope.row)">下载</el-button>
<el-button type="text" icon="el-icon-delete" size="small" plain @click="handleDelete(scope.row)"
v-hasPermi="['mp:material:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
</el-tab-pane>
<!-- tab 3视频 -->
<el-tab-pane name="video">
<span slot="label"><i class="el-icon-video-play"></i> 视频</span>
<div class="add_but" v-hasPermi="['mp:material:upload-permanent']">
<el-button size="mini" type="primary" @click="handleAddVideo">新建视频</el-button>
</div>
<!-- 新建视频的弹窗 -->
<el-dialog title="新建视频" :visible.sync="dialogVideoVisible" append-to-body width="600px"
v-loading="addMaterialLoading">
<el-upload :action="actionUrl" :headers="headers" multiple :limit="1" :file-list="fileList" :data="uploadData"
:before-upload="beforeVideoUpload" :on-success="handleUploadSuccess"
ref="uploadVideo" :auto-upload="false">
<el-button slot="trigger" size="mini" type="primary">选择视频</el-button>
<span class="el-upload__tip" style="margin-left: 10px;">格式支持 MP4文件大小不超过 10MB</span>
</el-upload>
<el-form :model="uploadData" :rules="uploadRules" ref="uploadForm" label-width="80px">
<el-row>
<el-form-item label="标题" prop="title">
<el-input v-model="uploadData.title" placeholder="标题将展示在相关播放页面,建议填写清晰、准确、生动的标题" />
</el-form-item>
</el-row>
<el-row>
<el-form-item label="描述" prop="introduction">
<el-input :rows="3" type="textarea" v-model="uploadData.introduction"
placeholder="介绍语将展示在相关播放页面,建议填写简洁明确、有信息量的内容" />
</el-form-item>
</el-row>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="cancelVideo"> </el-button>
<el-button type="primary" @click="submitVideo"> </el-button>
</div>
</el-dialog>
<el-table :data="list" stripe border v-loading="loading" style="margin-top: 10px;">
<el-table-column label="编号" align="center" prop="mediaId" />
<el-table-column label="文件名" align="center" prop="name" />
<el-table-column label="标题" align="center" prop="title" />
<el-table-column label="介绍" align="center" prop="introduction" />
<el-table-column label="视频" align="center">
<template v-slot="scope">
<wx-video-player :url="scope.row.url" />
</template>
</el-table-column>
<el-table-column label="上传时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button type="text" icon="el-icon-download" size="small" plain @click="handleDownload(scope.row)">下载</el-button>
<el-button type="text" icon="el-icon-delete" size="small" plain @click="handleDelete(scope.row)"
v-hasPermi="['mp:material:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
import WxVoicePlayer from '@/views/mp/components/wx-voice-play/main.vue';
import WxVideoPlayer from '@/views/mp/components/wx-video-play/main.vue';
import { getSimpleAccounts } from "@/api/mp/account";
import { getMaterialPage, deletePermanentMaterial } from "@/api/mp/material";
import { getAccessToken } from '@/utils/auth'
export default {
name: 'MpMaterial',
components: {
WxVoicePlayer,
WxVideoPlayer
},
data() {
return {
type: 'image',
// 遮罩层
loading: false,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 数据列表
list: [],
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
accountId: undefined,
permanent: true,
},
actionUrl: process.env.VUE_APP_BASE_API + '/admin-api/mp/material/upload-permanent',
headers: { Authorization: "Bearer " + getAccessToken() }, // 设置上传的请求头部
fileList:[],
uploadData: {
"type": 'image',
"title":'',
"introduction":''
},
// === 视频上传,独有变量 ===
dialogVideoVisible: false,
addMaterialLoading: false,
uploadRules: { // 视频上传的校验规则
title: [{ required: true, message: '请输入标题', trigger: 'blur' }],
introduction: [{ required: true, message: '请输入描述', trigger: 'blur' }],
},
// 公众号账号列表
accounts: [],
}
},
created() {
getSimpleAccounts().then(response => {
this.accounts = response.data;
// 默认选中第一个
if (this.accounts.length > 0) {
this.setAccountId(this.accounts[0].id);
}
// 加载数据
this.getList();
})
},
methods: {
// ======================== 列表查询 ========================
/** 设置账号编号 */
setAccountId(accountId) {
this.queryParams.accountId = accountId;
this.uploadData.accountId = accountId;
},
/** 查询列表 */
getList() {
// 如果没有选中公众号账号,则进行提示。
if (!this.queryParams.accountId) {
this.$message.error('未选中公众号,无法查询草稿箱')
return false
}
this.loading = true
getMaterialPage({
...this.queryParams,
type: this.type
}).then(response => {
this.list = response.data.list
this.total = response.data.total
}).finally(() => {
this.loading = false
})
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1
// 默认选中第一个
if (this.queryParams.accountId) {
this.setAccountId(this.queryParams.accountId)
}
this.getList()
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm('queryForm')
// 默认选中第一个
if (this.accounts.length > 0) {
this.setAccountId(this.accounts[0].id)
}
this.handleQuery()
},
handleClick(tab, event) {
// 设置 type
this.uploadData.type = tab.name
// 从第一页开始查询
this.handleQuery();
},
// ======================== 文件上传 ========================
beforeImageUpload(file) {
const isType = file.type === 'image/jpeg'
|| file.type === 'image/png'
|| file.type === 'image/gif'
|| file.type === 'image/bmp'
|| file.type === 'image/jpg';
if (!isType) {
this.$message.error('上传图片格式不对!')
return false;
}
const isLt = file.size / 1024 / 1024 < 2
if (!isLt) {
this.$message.error('上传图片大小不能超过 2M!')
return false;
}
this.loading = true
return true;
},
beforeVoiceUpload(file){
const isType = file.type === 'audio/mp3'
|| file.type === 'audio/wma'
|| file.type === 'audio/wav'
|| file.type === 'audio/amr';
const isLt = file.size / 1024 / 1024 < 2
if (!isType) {
this.$message.error('上传语音格式不对!')
return false;
}
if (!isLt) {
this.$message.error('上传语音大小不能超过 2M!')
return false;
}
this.loading = true
return true;
},
beforeVideoUpload(file){
const isType = file.type === 'video/mp4'
if (!isType) {
this.$message.error('上传视频格式不对!')
return false;
}
const isLt = file.size / 1024 / 1024 < 10
if (!isLt) {
this.$message.error('上传视频大小不能超过 10M!')
return false
}
this.addMaterialLoading = true
return true
},
handleUploadSuccess(response, file, fileList) {
this.loading = false
this.addMaterialLoading = false
if (response.code !== 0) {
this.$message.error('上传出错:' + response.msg)
return false;
}
// 清空上传时的各种数据
this.dialogVideoVisible = false
this.fileList = []
this.uploadData.title = ''
this.uploadData.introduction = ''
// 加载数据
this.getList()
},
// 下载文件
handleDownload(row) {
window.open(row.url,'_blank')
},
// 提交 video 新建的表单
submitVideo() {
this.$refs['uploadForm'].validate((valid) => {
if (!valid) {
return false;
}
this.$refs.uploadVideo.submit()
})
},
handleAddVideo() {
this.resetVideo();
this.dialogVideoVisible = true
},
/** 取消按钮 */
cancelVideo() {
this.dialogVideoVisible = false;
this.resetVideo();
},
/** 表单重置 */
resetVideo() {
this.fileList = []
this.uploadData.title = ''
this.uploadData.introduction = ''
this.resetForm("uploadForm");
},
// ======================== 其它操作 ========================
handleDelete(item) {
const id = item.id
this.$modal.confirm('此操作将永久删除该文件, 是否继续?').then(function() {
return deletePermanentMaterial(id);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
},
}
}
</script>
<style lang="scss" scoped>
/*瀑布流样式*/
.waterfall {
width: 100%;
column-gap:10px;
column-count: 5;
margin-top: 10px; /* 芋道源码:增加 10px避免顶着上面 */
}
.waterfall-item {
padding: 10px;
margin-bottom: 10px;
break-inside: avoid;
border: 1px solid #eaeaea;
}
.material-img {
width: 100%;
}
p {
line-height: 30px;
}
@media (min-width: 992px) and (max-width: 1300px) {
.waterfall {
column-count: 3;
}
p {
color:red;
}
}
@media (min-width: 768px) and (max-width: 991px) {
.waterfall {
column-count: 2;
}
p {
color: orange;
}
}
@media (max-width: 767px) {
.waterfall {
column-count: 1;
}
}
/*瀑布流样式*/
</style>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -1,714 +0,0 @@
<!--
MIT License
Copyright (c) 2020 www.joolun.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
芋道源码
less 切到 scss减少对 less less-loader 的依赖
-->
<template>
<div class="app-container">
<doc-alert title="公众号菜单" url="https://doc.iocoder.cn/mp/menu/" />
<!-- 搜索工作栏 -->
<el-form ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="公众号" prop="accountId">
<el-select v-model="accountId" placeholder="请选择公众号">
<el-option v-for="item in accounts" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<div class="public-account-management clearfix" v-loading="loading">
<!--左边配置菜单-->
<div class="left">
<div class="weixin-hd">
<div class="weixin-title">{{ name }}</div>
</div>
<div class="weixin-menu menu_main clearfix">
<div class="menu_bottom" v-for="(item, i) of menuList" :key="i" >
<!-- 一级菜单 -->
<div @click="menuClick(i, item)" class="menu_item el-icon-s-fold" :class="{'active': isActive === i}">{{item.name}}</div>
<!-- 以下为二级菜单-->
<div class="submenu" v-if="isSubMenuFlag === i">
<div class="subtitle menu_bottom" v-if="item.children" v-for="(subItem, k) in item.children" :key="k">
<div class="menu_subItem" :class="{'active': isSubMenuActive === i + '' + k}" @click="subMenuClick(subItem, i, k)">
{{subItem.name}}
</div>
</div>
<!-- 二级菜单加号 当长度 小于 5 才显示二级菜单的加号 -->
<div class="menu_bottom menu_addicon" v-if="!item.children || item.children.length < 5" @click="addSubMenu(i,item)">
<i class="el-icon-plus" />
</div>
</div>
</div>
<!-- 一级菜单加号 -->
<div class="menu_bottom menu_addicon" v-if="this.menuList.length < 3" @click="addMenu"><i class="el-icon-plus"></i></div>
</div>
<div class="save_div">
<el-button class="save_btn" type="success" size="small" @click="handleSave" v-hasPermi="['mp:menu:save']">保存并发布菜单</el-button>
<el-button class="save_btn" type="danger" size="small" @click="handleDelete" v-hasPermi="['mp:menu:delete']">清空菜单</el-button>
</div>
</div>
<!--右边配置-->
<div v-if="showRightFlag" class="right">
<div class="configure_page">
<div class="delete_btn">
<el-button size="mini" type="danger" icon="el-icon-delete" @click="deleteMenu(tempObj)">删除当前菜单</el-button>
</div>
<div>
<span>菜单名称</span>
<el-input class="input_width" v-model="tempObj.name" placeholder="请输入菜单名称" :maxlength="nameMaxLength" clearable />
</div>
<div v-if="showConfigureContent">
<div class="menu_content">
<span>菜单标识</span>
<el-input class="input_width" v-model="tempObj.menuKey" placeholder="请输入菜单 KEY" clearable />
</div>
<div class="menu_content">
<span>菜单内容</span>
<el-select v-model="tempObj.type" clearable placeholder="请选择" class="menu_option">
<el-option v-for="item in menuOptions" :label="item.label" :value="item.value" :key="item.value" />
</el-select>
</div>
<div class="configur_content" v-if="tempObj.type === 'view'">
<span>跳转链接</span>
<el-input class="input_width" v-model="tempObj.url" placeholder="请输入链接" clearable />
</div>
<div class="configur_content" v-if="tempObj.type === 'miniprogram'">
<div class="applet">
<span>小程序的 appid </span>
<el-input class="input_width" v-model="tempObj.miniProgramAppId" placeholder="请输入小程序的appid" clearable />
</div>
<div class="applet">
<span>小程序的页面路径</span>
<el-input class="input_width" v-model="tempObj.miniProgramPagePath"
placeholder="请输入小程序的页面路径pages/index" clearable />
</div>
<div class="applet">
<span>小程序的备用网页</span>
<el-input class="input_width" v-model="tempObj.url" placeholder="不支持小程序的老版本客户端将打开本网页" clearable />
</div>
<p class="blue">tips:需要和公众号进行关联才可以把小程序绑定带微信菜单上哟</p>
</div>
<div class="configur_content" v-if="tempObj.type === 'article_view_limited'">
<el-row>
<div class="select-item" v-if="tempObj && tempObj.replyArticles">
<wx-news :articles="tempObj.replyArticles" />
<el-row class="ope-row">
<el-button type="danger" icon="el-icon-delete" circle @click="deleteMaterial" />
</el-row>
</div>
<div v-else>
<el-row>
<el-col :span="24" style="text-align: center">
<el-button type="success" @click="openMaterial">
素材库选择<i class="el-icon-circle-check el-icon--right"></i>
</el-button>
</el-col>
</el-row>
</div>
<el-dialog title="选择图文" :visible.sync="dialogNewsVisible" width="90%">
<wx-material-select :objData="{type: 'news', accountId: this.accountId}" @selectMaterial="selectMaterial" />
</el-dialog>
</el-row>
</div>
<div class="configur_content" v-if="tempObj.type === 'click' || tempObj.type === 'scancode_waitmsg'">
<wx-reply-select :objData="tempObj.reply" v-if="hackResetWxReplySelect" />
</div>
</div>
</div>
</div>
<!-- 一进页面就显示的默认页面当点击左边按钮的时候就不显示了-->
<div v-else class="right">
<p>请选择菜单配置</p>
</div>
</div>
</div>
</template>
<script>
import WxReplySelect from '@/views/mp/components/wx-reply/main.vue'
import WxNews from '@/views/mp/components/wx-news/main.vue';
import WxMaterialSelect from '@/views/mp/components/wx-material-select/main.vue'
import { deleteMenu, getMenuList, saveMenu } from "@/api/mp/menu";
import { getSimpleAccounts } from "@/api/mp/account";
export default {
name: 'MpMenu',
components: {
WxReplySelect,
WxNews,
WxMaterialSelect
},
data(){
return {
// ======================== 列表查询 ========================
// 遮罩层
loading: true,
// 显示搜索条件
showSearch: true,
// 查询参数
accountId: undefined,
name:'', // 公众号名
menuList: {
children: [],
},
// ======================== 菜单操作 ========================
isActive: -1,// 一级菜单点中样式
isSubMenuActive: -1, // 一级菜单点中样式
isSubMenuFlag: -1, // 二级菜单显示标志
// ======================== 菜单编辑 ========================
showRightFlag: false, // 右边配置显示默认详情还是配置详情
nameMaxLength: 0, // 菜单名称最大长度1 级是 4 字符2 级是 7 字符;
showConfigureContent: true, // 是否展示配置内容;如果有子菜单,就不显示配置内容
hackResetWxReplySelect: false, // 重置 WxReplySelect 组件
tempObj: {}, // 右边临时变量,作为中间值牵引关系
tempSelfObj: { // 一些临时值放在这里进行判断,如果放在 tempObj由于引用关系menu 也会多了多余的参数
},
dialogNewsVisible: false, // 跳转图文时的素材选择弹窗
menuOptions: [{
value: 'view',
label: '跳转网页'
}, {
value: 'miniprogram',
label: '跳转小程序'
}, {
value: 'click',
label: '点击回复'
}, {
value: 'article_view_limited',
label: '跳转图文消息'
}, {
value: 'scancode_push',
label: '扫码直接返回结果'
}, {
value: 'scancode_waitmsg',
label: '扫码回复'
}, {
value: 'pic_sysphoto',
label: '系统拍照发图'
}, {
value: 'pic_photo_or_album',
label: '拍照或者相册'
}, {
value: 'pic_weixin',
label: '微信相册'
}, {
value: 'location_select',
label: '选择地理位置'
}],
// 公众号账号列表
accounts: [],
}
},
created() {
getSimpleAccounts().then(response => {
this.accounts = response.data;
// 默认选中第一个
if (this.accounts.length > 0) {
this.setAccountId(this.accounts[0].id);
}
// 加载数据
this.getList();
})
},
methods: {
// ======================== 列表查询 ========================
/** 设置账号编号 */
setAccountId(accountId) {
this.accountId = accountId;
this.name = this.accounts.find(item => item.id === accountId)?.name;
},
getList() {
this.loading = false;
getMenuList(this.accountId).then(response => {
response.data = this.convertMenuList(response.data);
this.menuList = this.handleTree(response.data, "id");
}).finally(() => {
this.loading = false;
})
},
/** 搜索按钮操作 */
handleQuery() {
this.resetForm();
// 默认选中第一个
if (this.accountId) {
this.setAccountId(this.accountId)
}
this.getList()
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm();
// 默认选中第一个
if (this.accounts.length > 0) {
this.setAccountId(this.accounts[0].id)
}
this.handleQuery()
},
// 将后端返回的 menuList转换成前端的 menuList
convertMenuList(list) {
const menuList = [];
list.forEach(item => {
const menu = {
...item,
};
if (item.type === 'click' || item.type === 'scancode_waitmsg') {
this.$delete(menu, 'replyMessageType');
this.$delete(menu, 'replyContent');
this.$delete(menu, 'replyMediaId');
this.$delete(menu, 'replyMediaUrl');
this.$delete(menu, 'replyDescription');
this.$delete(menu, 'replyArticles');
menu.reply = {
type: item.replyMessageType,
accountId: item.accountId,
content: item.replyContent,
mediaId: item.replyMediaId,
url: item.replyMediaUrl,
title: item.replyTitle,
description: item.replyDescription,
thumbMediaId: item.replyThumbMediaId,
thumbMediaUrl: item.replyThumbMediaUrl,
articles: item.replyArticles,
musicUrl: item.replyMusicUrl,
hqMusicUrl: item.replyHqMusicUrl,
}
}
menuList.push(menu);
});
return menuList;
},
// 重置表单,清空表单数据
resetForm() {
// 菜单操作
this.isActive = -1;
this.isSubMenuActive = -1;
this.isSubMenuFlag = -1;
// 菜单编辑
this.showRightFlag = false;
this.nameMaxLength = 0;
this.showConfigureContent = 0;
this.hackResetWxReplySelect = true;
this.hackResetWxReplySelect = false;
this.tempObj = {};
this.tempSelfObj = {};
this.dialogNewsVisible = false;
},
// ======================== 菜单操作 ========================
// 一级菜单点击事件
menuClick(i, item) {
// 右侧的表单相关
this.resetEditor();
this.showRightFlag = true; // 右边菜单
this.tempObj = item; // 这个如果放在顶部flag 会没有。因为重新赋值了。
this.tempSelfObj.grand = "1"; // 表示一级菜单
this.tempSelfObj.index = i; // 表示一级菜单索引
this.nameMaxLength = 4
this.showConfigureContent = !(item.children && item.children.length > 0); // 有子菜单,就不显示配置内容
// 左侧的选中
this.isActive = i; // 一级菜单选中样式
this.isSubMenuFlag = i; // 二级菜单显示标志
this.isSubMenuActive = -1; // 二级菜单去除选中样式
},
// 二级菜单点击事件
subMenuClick(subItem, index, k) {
// 右侧的表单相关
this.resetEditor();
this.showRightFlag = true; // 右边菜单
this.tempObj = subItem; // 将点击的数据放到临时变量,对象有引用作用
this.tempSelfObj.grand = "2"; // 表示二级菜单
this.tempSelfObj.index = index; // 表示一级菜单索引
this.tempSelfObj.secondIndex = k; // 表示二级菜单索引
this.nameMaxLength = 7
this.showConfigureContent = true;
// 左侧的选中
this.isActive = -1; // 一级菜单去除样式
this.isSubMenuActive = index + "" + k; // 二级菜单选中样式
},
// 添加横向一级菜单
addMenu() {
const menuKeyLength = this.menuList.length;
const addButton = {
name: "菜单名称",
children: [],
reply: { // 用于存储回复内容
'type': 'text',
'accountId': this.accountId // 保证组件里,可以使用到对应的公众号
}
}
this.$set(this.menuList, menuKeyLength, addButton)
this.menuClick(this.menuKeyLength - 1, addButton)
},
// 添加横向二级菜单item 表示要操作的父菜单
addSubMenu(i, item) {
// 清空父菜单的属性,因为它只需要 name 属性即可
if (!item.children || item.children.length <= 0) {
this.$set( item, 'children',[])
this.$delete( item, 'type')
this.$delete( item, 'menuKey')
this.$delete( item, 'miniProgramAppId')
this.$delete( item, 'miniProgramPagePath')
this.$delete( item, 'url')
this.$delete( item, 'reply')
this.$delete( item, 'articleId')
this.$delete( item, 'replyArticles')
// 关闭配置面板
this.showConfigureContent = false
}
let subMenuKeyLength = item.children.length; // 获取二级菜单key长度
let addButton = {
name: "子菜单名称",
reply: { // 用于存储回复内容
'type': 'text',
'accountId': this.accountId // 保证组件里,可以使用到对应的公众号
}
}
this.$set(item.children, subMenuKeyLength, addButton);
this.subMenuClick(item.children[subMenuKeyLength], i, subMenuKeyLength)
},
// 删除当前菜单
deleteMenu(item) {
this.$modal.confirm('确定要删除吗?').then(() => {
// 删除数据
if (this.tempSelfObj.grand === "1") { // 一级菜单的删除方法
this.menuList.splice(this.tempSelfObj.index, 1);
} else if (this.tempSelfObj.grand === "2") { // 二级菜单的删除方法
this.menuList[this.tempSelfObj.index].children.splice(this.tempSelfObj.secondIndex, 1);
}
// 提示
this.$modal.msgSuccess("删除成功");
// 处理菜单的选中
this.tempObj = {};
this.showRightFlag = false;
this.isActive = -1;
this.isSubMenuActive = -1;
}).catch(() => {});
},
// ======================== 菜单编辑 ========================
handleSave() {
this.$modal.confirm('确定要保证并发布该菜单吗?').then(() => {
this.loading = true
return saveMenu(this.accountId, this.convertMenuFormList());
}).then(() => {
this.getList();
this.$modal.msgSuccess("发布成功");
}).finally(() => {
this.loading = false
});
},
// 表单 Editor 重置
resetEditor() {
this.hackResetWxReplySelect = false // 销毁组件
this.$nextTick(() => {
this.hackResetWxReplySelect = true // 重建组件
})
},
handleDelete() {
this.$modal.confirm('确定要清空所有菜单吗?').then(() => {
this.loading = true
return deleteMenu(this.accountId);
}).then(() => {
this.handleQuery();
this.$modal.msgSuccess("清空成功");
}).catch(() => {}).finally(() => {
this.loading = false
});
},
// 将前端的 menuList转换成后端接收的 menuList
convertMenuFormList() {
const menuList = [];
this.menuList.forEach(item => {
let menu = this.convertMenuForm(item);
menuList.push(menu);
// 处理子菜单
if (!item.children || item.children.length <= 0) {
return;
}
menu.children = [];
item.children.forEach(subItem => {
menu.children.push(this.convertMenuForm(subItem))
})
})
return menuList;
},
// 将前端的 menu转换成后端接收的 menu
convertMenuForm(menu) {
let result = {
...menu,
children: undefined, // 不处理子节点
reply: undefined, // 稍后复制
}
if (menu.type === 'click' || menu.type === 'scancode_waitmsg') {
result.replyMessageType = menu.reply.type;
result.replyContent = menu.reply.content;
result.replyMediaId = menu.reply.mediaId;
result.replyMediaUrl = menu.reply.url;
result.replyTitle = menu.reply.title;
result.replyDescription = menu.reply.description;
result.replyThumbMediaId = menu.reply.thumbMediaId;
result.replyThumbMediaUrl = menu.reply.thumbMediaUrl;
result.replyArticles = menu.reply.articles;
result.replyMusicUrl = menu.reply.musicUrl;
result.replyHqMusicUrl = menu.reply.hqMusicUrl;
}
return result;
},
// ======================== 菜单编辑(素材选择) ========================
openMaterial() {
this.dialogNewsVisible = true
},
selectMaterial(item) {
const articleId = item.articleId;
const articles = item.content.newsItem;
// 提示,针对多图文
if (articles.length > 1) {
this.$alert('您选择的是多图文,将默认跳转第一篇', '提示', {
confirmButtonText: '确定'
})
}
this.dialogNewsVisible = false
// 设置菜单的回复
this.tempObj.articleId = articleId;
this.tempObj.replyArticles = [];
articles.forEach(article => {
this.tempObj.replyArticles.push({
title: article.title,
description: article.digest,
picUrl: article.picUrl,
url: article.url,
})
})
},
deleteMaterial() {
this.$delete(this.tempObj,'articleId')
this.$delete(this.tempObj,'replyArticles')
},
},
}
</script>
<!--本组件样式-->
<style lang="scss" scoped="scoped">
/* 公共颜色变量 */
.clearfix{*zoom:1;}
.clearfix::after{content: "";display: table; clear: both;}
div{
text-align: left;
}
.weixin-hd{
color: #fff;
text-align: center;
position: relative;
bottom: 426px;
left:0px;
width: 300px;
height:64px;
background: transparent url("assets/menu_head.png") no-repeat 0 0;
background-position: 0 0;
background-size: 100%
}
.weixin-title{
color:#fff;
font-size:14px;
width:100%;
text-align: center;
position:absolute;
top: 33px;
left: 0px;
}
.weixin-menu{
background: transparent url("assets/menu_foot.png") no-repeat 0 0;
padding-left: 43px;
font-size: 12px
}
.menu_option{
width: 40%!important;
}
.public-account-management{
min-width: 1200px;
width: 1200px;
margin: 0 auto;
.left{
float: left;
display: inline-block;
width: 350px;
height: 715px;
background: url("assets/iphone_backImg.png") no-repeat;
background-size: 100% auto;
padding: 518px 25px 88px;
position: relative;
box-sizing: border-box;
/*第一级菜单*/
.menu_main{
.menu_bottom{
position: relative;
float: left;
display: inline-block;
box-sizing: border-box;
width: 85.5px;
text-align: center;
border: 1px solid #ebedee;
background-color: #fff;
cursor: pointer;
&.menu_addicon{
height: 46px;
line-height: 46px;
}
.menu_item{
height: 44px;
line-height: 44px;
text-align: center;
box-sizing: border-box;
width: 100%;
&.active{
border: 1px solid #2bb673;
}
}
.menu_subItem{
height: 44px;
line-height: 44px;
text-align: center;
box-sizing: border-box;
&.active{
border: 1px solid #2bb673;
}
}
}
i{
color:#2bb673;
}
/*第二级菜单*/
.submenu{
position: absolute;
width: 85.5px;
bottom: 45px;
.subtitle{
background-color: #fff;
box-sizing: border-box;
}
}
}
.save_div{
margin-top: 15px;
text-align: center;
.save_btn{
bottom: 20px;
left: 100px;
}
}
}
/*右边菜单内容*/
.right {
float: left;
width: 63%;
background-color: #e8e7e7;
padding: 20px;
margin-left: 20px;
-webkit-box-sizing: border-box;
box-sizing: border-box;
.configure_page {
.delete_btn {
text-align: right;
margin-bottom: 15px;
}
.menu_content {
margin-top: 20px;
}
.configur_content {
margin-top: 20px;
background-color: #fff;
padding: 20px 10px;
border-radius: 5px
}
.blue {
color:#29b6f6;
margin-top: 10px;
}
.applet{
margin-bottom: 20px;
span{
width: 20%;
}
}
.input_width {
width: 40%;
}
.material{
.input_width{
width: 30%;
}
.el-textarea{
width: 80%
}
}
}
}
.el-input {
width: 70%;
margin-right: 2%;
}
}
</style>
<!--素材样式-->
<style lang="scss" scoped>
.pagination {
text-align: right;
margin-right: 25px;
}
.select-item {
width: 280px;
padding: 10px;
margin: 0 auto 10px auto;
border: 1px solid #eaeaea;
}
.select-item2 {
padding: 10px;
margin: 0 auto 10px auto;
border: 1px solid #eaeaea;
}
.ope-row {
padding-top: 10px;
text-align: center;
}
.item-name {
font-size: 12px;
overflow: hidden;
text-overflow:ellipsis;
white-space: nowrap;
text-align: center;
}
</style>

View File

@@ -1,238 +0,0 @@
<template>
<div class="app-container">
<doc-alert title="公众号消息" url="https://doc.iocoder.cn/mp/message/" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="公众号" prop="accountId">
<el-select v-model="queryParams.accountId" placeholder="请选择公众号">
<el-option v-for="item in accounts" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<!-- TODO 等待处理 -->
<el-form-item label="消息类型" prop="type">
<el-select v-model="queryParams.type" placeholder="请选择消息类型" clearable size="small">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.MP_MESSAGE_TYPE)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="用户标识" prop="openid">
<el-input v-model="queryParams.openid" placeholder="请输入用户标识" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="发送时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="消息类型" align="center" prop="type" width="80"/>
<el-table-column label="发送方" align="center" prop="sendFrom" width="80">
<template v-slot="scope">
<el-tag v-if="scope.row.sendFrom === 1" type="success">粉丝</el-tag>
<el-tag v-else type="info">公众号</el-tag>
</template>
</el-table-column>
<el-table-column label="用户标识" align="center" prop="openid" width="300" />
<el-table-column label="内容" prop="content">
<template v-slot="scope">
<!-- 事件区域 -->
<div v-if="scope.row.type === 'event' && scope.row.event === 'subscribe'">
<el-tag type="success" size="mini">关注</el-tag>
</div>
<div v-else-if="scope.row.type === 'event' && scope.row.event === 'unsubscribe'">
<el-tag type="danger" size="mini">取消关注</el-tag>
</div>
<div v-else-if="scope.row.type === 'event' && scope.row.event === 'CLICK'">
<el-tag size="mini">点击菜单</el-tag>{{ scope.row.eventKey }}
</div>
<div v-else-if="scope.row.type === 'event' && scope.row.event === 'VIEW'">
<el-tag size="mini">点击菜单链接</el-tag>{{ scope.row.eventKey }}
</div>
<div v-else-if="scope.row.type === 'event' && scope.row.event === 'scancode_waitmsg'">
<el-tag size="mini">扫码结果</el-tag>{{ scope.row.eventKey }}
</div>
<div v-else-if="scope.row.type === 'event' && scope.row.event === 'scancode_push'">
<el-tag size="mini">扫码结果</el-tag>{{ scope.row.eventKey }}
</div>
<div v-else-if="scope.row.type === 'event' && scope.row.event === 'pic_sysphoto'">
<el-tag size="mini">系统拍照发图</el-tag>
</div>
<div v-else-if="scope.row.type === 'event' && scope.row.event === 'pic_photo_or_album'">
<el-tag size="mini">拍照或者相册</el-tag>
</div>
<div v-else-if="scope.row.type === 'event' && scope.row.event === 'pic_weixin'">
<el-tag size="mini">微信相册</el-tag>
</div>
<div v-else-if="scope.row.type === 'event' && scope.row.event === 'location_select'">
<el-tag size="mini">选择地理位置</el-tag>
</div>
<div v-else-if="scope.row.type === 'event'">
<el-tag type="danger" size="mini">未知事件类型</el-tag>
</div>
<!-- 消息区域 -->
<div v-else-if="scope.row.type === 'text'">{{ scope.row.content }}</div>
<div v-else-if="scope.row.type === 'voice'">
<wx-voice-player :url="scope.row.mediaUrl" :content="scope.row.recognition" />
</div>
<div v-else-if="scope.row.type === 'image'">
<a target="_blank" :href="scope.row.mediaUrl">
<img :src="scope.row.mediaUrl" style="width: 100px">
</a>
</div>
<div v-else-if="scope.row.type === 'video' || scope.row.type === 'shortvideo'">
<wx-video-player :url="scope.row.mediaUrl" style="margin-top: 10px" />
</div>
<div v-else-if="scope.row.type === 'link'">
<el-tag size="mini">链接</el-tag>
<a :href="scope.row.url" target="_blank">{{scope.row.title}}</a>
</div>
<div v-else-if="scope.row.type === 'location'">
<wx-location :label="scope.row.label" :location-y="scope.row.locationY" :location-x="scope.row.locationX" />
</div>
<div v-else-if="scope.row.type === 'music'">
<wx-music :title="scope.row.title" :description="scope.row.description" :thumb-media-url="scope.row.thumbMediaUrl"
:music-url="scope.row.musicUrl" :hq-music-url="scope.row.hqMusicUrl" />
</div>
<div v-else-if="scope.row.type === 'news'">
<wx-news :articles="scope.row.articles" />
</div>
<div v-else>
<el-tag type="danger" size="mini">未知消息类型</el-tag>
</div>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleSend(scope.row)"
v-hasPermi="['mp:message:send']">消息
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- 发送消息的弹窗 -->
<el-dialog title="粉丝消息列表" :visible.sync="open" width="50%">
<wx-msg :user-id="userId" v-if="open" />
</el-dialog>
</div>
</template>
<script>
import WxVideoPlayer from '@/views/mp/components/wx-video-play/main.vue';
import WxVoicePlayer from '@/views/mp/components/wx-voice-play/main.vue';
import WxMsg from '@/views/mp/components/wx-msg/main.vue';
import WxLocation from '@/views/mp/components/wx-location/main.vue';
import WxMusic from '@/views/mp/components/wx-music/main.vue';
import WxNews from '@/views/mp/components/wx-news/main.vue';
import { getMessagePage } from "@/api/mp/message";
import { getSimpleAccounts } from "@/api/mp/account";
export default {
name: "MpMessage",
components: {
WxVideoPlayer,
WxVoicePlayer,
WxMsg,
WxLocation,
WxMusic,
WxNews
},
data() {
return {
// 遮罩层
loading: true,
// 导出遮罩层
exportLoading: false,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 粉丝消息列表
list: [],
// 是否显示弹出层
open: false,
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
openid: null,
accountId: null,
type: null,
createTime: []
},
// 操作的用户编号
userId: 0,
// 公众号账号列表
accounts: []
};
},
created() {
getSimpleAccounts().then(response => {
this.accounts = response.data;
// 默认选中第一个
if (this.accounts.length > 0) {
this.queryParams.accountId = this.accounts[0].id;
}
// 加载数据
this.getList();
})
},
methods: {
/** 查询列表 */
getList() {
// 如果没有选中公众号账号,则进行提示。
if (!this.queryParams.accountId) {
this.$message.error('未选中公众号,无法查询消息')
return false
}
this.loading = true;
// 执行查询
getMessagePage(this.queryParams).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
// 默认选中第一个
if (this.accounts.length > 0) {
this.queryParams.accountId = this.accounts[0].id;
}
this.handleQuery();
},
handleSend(row) {
this.userId = row.userId;
this.open = true;
},
}
};
</script>

View File

@@ -1,364 +0,0 @@
<template>
<div class="app-container">
<doc-alert title="公众号统计" url="https://doc.iocoder.cn/mp/statistics/" />
<!-- 搜索工作栏 -->
<el-form ref="queryForm" size="small" :inline="true" label-width="68px">
<el-form-item label="公众号" prop="accountId">
<el-select v-model="accountId" @change="getSummary">
<el-option v-for="item in accounts" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="时间范围" prop="date">
<el-date-picker v-model="date" style="width: 260px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"
:picker-options="datePickerOptions" :default-time="['00:00:00', '23:59:59']"
@change="getSummary">
</el-date-picker>
</el-form-item>
</el-form>
<!-- 图表 -->
<el-row>
<el-col :span="12" class="card-box">
<el-card>
<div slot="header">
<span>用户增减数据</span>
</div>
<div class="el-table el-table--enable-row-hover el-table--medium">
<div ref="userSummaryChart" style="height: 420px" />
</div>
</el-card>
</el-col>
<el-col :span="12" class="card-box">
<el-card>
<div slot="header">
<span>累计用户数据</span>
</div>
<div class="el-table el-table--enable-row-hover el-table--medium">
<div ref="userCumulateChart" style="height: 420px" />
</div>
</el-card>
</el-col>
<el-col :span="12" class="card-box">
<el-card>
<div slot="header">
<span>消息概况数据</span>
</div>
<div class="el-table el-table--enable-row-hover el-table--medium">
<div ref="upstreamMessageChart" style="height: 420px" />
</div>
</el-card>
</el-col>
<el-col :span="12" class="card-box">
<el-card>
<div slot="header">
<span>接口分析数据</span>
</div>
<div class="el-table el-table--enable-row-hover el-table--medium">
<div ref="interfaceSummaryChart" style="height: 420px" />
</div>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script>
// 引入基本模板
import * as echarts from 'echarts'
// 引入柱状图组件
require('echarts/lib/chart/bar')
// 引入柱拆线组件
require('echarts/lib/chart/line')
// 引入提示框和title组件
require('echarts/lib/component/tooltip')
require('echarts/lib/component/title')
require('echarts/lib/component/legend')
import { getInterfaceSummary, getUserSummary, getUserCumulate, getUpstreamMessage} from '@/api/mp/statistics'
import { datePickerOptions } from "@/utils/constants";
import {addTime, beginOfDay, betweenDay, endOfDay, formatDate} from "@/utils/dateUtils";
import { getSimpleAccounts } from "@/api/mp/account";
export default {
name: 'MpStatistics',
data() {
return {
date : [beginOfDay(new Date(new Date().getTime() - 3600 * 1000 * 24 * 7)), // -7 天
endOfDay(new Date(new Date().getTime() - 3600 * 1000 * 24))], // -1 天
accountId: undefined,
accounts: [],
xAxisDate: [], // X 轴的日期范围
userSummaryOption: { // 用户增减数据
color: ['#67C23A', '#e5323e'],
legend: {
data: ['新增用户','取消关注的用户']
},
tooltip: {},
xAxis: {
data: [] // X 轴的日期范围
},
yAxis: {
minInterval: 1
},
series: [{
name: '新增用户',
type: 'bar',
label: {
normal: {
show: true
}
},
barGap: 0,
data: [] // 新增用户的数据
}, {
name: '取消关注的用户',
type: 'bar',
label: {
normal: {
show: true
}
},
data: [] // 取消关注的用户的数据
}]
},
userCumulateOption: { // 累计用户数据
legend: {
data: ['累计用户量']
},
xAxis: {
type: 'category',
data: []
},
yAxis: {
minInterval: 1
},
series: [{
name:'累计用户量',
data: [], // 累计用户量的数据
type: 'line',
smooth: true,
label: {
normal: {
show: true
}
}
}]
},
upstreamMessageOption: { // 消息发送概况数据
color: ['#67C23A', '#e5323e'],
legend: {
data: ['用户发送人数', '用户发送条数']
},
tooltip: {},
xAxis: {
data: [] // X 轴的日期范围
},
yAxis: {
minInterval: 1
},
series: [{
name: '用户发送人数',
type: 'line',
smooth: true,
label: {
normal: {
show: true
}
},
data: [] // 用户发送人数的数据
}, {
name: '用户发送条数',
type: 'line',
smooth: true,
label: {
normal: {
show: true
}
},
data: [] // 用户发送条数的数据
}]
},
interfaceSummaryOption: { // 接口分析况数据
color: ['#67C23A', '#e5323e', '#E6A23C', '#409EFF'],
legend: {
data: ['被动回复用户消息的次数','失败次数', '最大耗时','总耗时']
},
tooltip: {},
xAxis: {
data: [] // X 轴的日期范围
},
yAxis: {},
series: [{
name: '被动回复用户消息的次数',
type: 'bar',
label: {
normal: {
show: true
}
},
barGap: 0,
data: [] // 被动回复用户消息的次数的数据
}, {
name: '失败次数',
type: 'bar',
label: {
normal: {
show: true
}
},
data: [] // 失败次数的数据
}, {
name: '最大耗时',
type: 'bar',
label: {
normal: {
show: true
}
},
data: [] // 最大耗时的数据
}, {
name: '总耗时',
type: 'bar',
label: {
normal: {
show: true
}
},
data: [] // 总耗时的数据
}]
},
// 静态变量
datePickerOptions: datePickerOptions,
}
},
created() {
getSimpleAccounts().then(response => {
this.accounts = response.data;
// 默认选中第一个
if (this.accounts.length > 0) {
this.accountId = this.accounts[0].id;
}
// 加载数据
this.getSummary();
})
},
methods: {
getSummary() {
// 如果没有选中公众号账号,则进行提示。
if (!this.accountId) {
this.$message.error('未选中公众号,无法统计数据')
return false
}
// 必须选择 7 天内,因为公众号有时间跨度限制为 7
if (betweenDay(this.date[0], this.date[1]) >= 7) {
this.$message.error('时间间隔 7 天以内,请重新选择')
return false
}
this.xAxisDate = []
const days = betweenDay(this.date[0], this.date[1]) // 相差天数
for(let i = 0; i <= days; i++){
this.xAxisDate.push(formatDate(addTime(this.date[0], 3600 * 1000 * 24 * i), 'yyyy-MM-dd'));
}
// 初始化图表
this.initUserSummaryChart();
this.initUserCumulateChart();
this.initUpstreamMessageChart();
this.interfaceSummaryChart();
},
initUserSummaryChart() {
this.userSummaryOption.xAxis.data = [];
this.userSummaryOption.series[0].data = [];
this.userSummaryOption.series[1].data = [];
getUserSummary({
accountId: this.accountId,
date: [formatDate(this.date[0], 'yyyy-MM-dd HH:mm:ss'), formatDate(this.date[1], 'yyyy-MM-dd HH:mm:ss'),]
}).then(response => {
this.userSummaryOption.xAxis.data = this.xAxisDate;
// 处理数据
this.xAxisDate.forEach((date, index) => {
response.data.forEach((item) => {
// 匹配日期
const refDate = formatDate(new Date(item.refDate), 'yyyy-MM-dd');
if (refDate.indexOf(date) === -1) {
return;
}
// 设置数据到对应的位置
this.userSummaryOption.series[0].data[index] = item.newUser;
this.userSummaryOption.series[1].data[index] = item.cancelUser;
})
})
// 绘制图表
const userSummaryChart = echarts.init(this.$refs.userSummaryChart);
userSummaryChart.setOption(this.userSummaryOption)
}).catch(() => {})
},
initUserCumulateChart() {
this.userCumulateOption.xAxis.data = [];
this.userCumulateOption.series[0].data = [];
// 发起请求
getUserCumulate({
accountId: this.accountId,
date: [formatDate(this.date[0], 'yyyy-MM-dd HH:mm:ss'), formatDate(this.date[1], 'yyyy-MM-dd HH:mm:ss'),]
}).then(response => {
this.userCumulateOption.xAxis.data = this.xAxisDate;
// 处理数据
response.data.forEach((item, index) => {
this.userCumulateOption.series[0].data[index] = item.cumulateUser;
})
// 绘制图表
const userCumulateChart = echarts.init(this.$refs.userCumulateChart);
userCumulateChart.setOption(this.userCumulateOption)
}).catch(() => {})
},
initUpstreamMessageChart() {
this.upstreamMessageOption.xAxis.data = [];
this.upstreamMessageOption.series[0].data = [];
this.upstreamMessageOption.series[1].data = [];
// 发起请求
getUpstreamMessage({
accountId: this.accountId,
date: [formatDate(this.date[0], 'yyyy-MM-dd HH:mm:ss'), formatDate(this.date[1], 'yyyy-MM-dd HH:mm:ss'),]
}).then(response => {
this.upstreamMessageOption.xAxis.data = this.xAxisDate;
// 处理数据
response.data.forEach((item, index) => {
this.upstreamMessageOption.series[0].data[index] = item.messageUser;
this.upstreamMessageOption.series[1].data[index] = item.messageCount;
})
// 绘制图表
const upstreamMessageChart = echarts.init(this.$refs.upstreamMessageChart);
upstreamMessageChart.setOption(this.upstreamMessageOption);
}).catch(() => {})
},
interfaceSummaryChart() {
this.interfaceSummaryOption.xAxis.data = [];
this.interfaceSummaryOption.series[0].data = [];
this.interfaceSummaryOption.series[1].data = [];
this.interfaceSummaryOption.series[2].data = [];
this.interfaceSummaryOption.series[3].data = [];
// 发起请求
getInterfaceSummary({
accountId: this.accountId,
date: [formatDate(this.date[0], 'yyyy-MM-dd HH:mm:ss'), formatDate(this.date[1], 'yyyy-MM-dd HH:mm:ss'),]
}).then(response => {
this.interfaceSummaryOption.xAxis.data = this.xAxisDate;
// 处理数据
response.data.forEach((item, index) => {
this.interfaceSummaryOption.series[0].data[index] = item.callbackCount;
this.interfaceSummaryOption.series[1].data[index] = item.failCount;
this.interfaceSummaryOption.series[2].data[index] = item.maxTimeCost;
this.interfaceSummaryOption.series[3].data[index] = item.totalTimeCost;
})
// 绘制图表
const interfaceSummaryChart = echarts.init(this.$refs.interfaceSummaryChart);
interfaceSummaryChart.setOption(this.interfaceSummaryOption);
}).catch(() => {})
}
}
}
</script>

View File

@@ -1,243 +0,0 @@
<template>
<div class="app-container">
<doc-alert title="公众号标签" url="https://doc.iocoder.cn/mp/tag/" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="公众号" prop="accountId">
<el-select v-model="queryParams.accountId" placeholder="请选择公众号">
<el-option v-for="item in accounts" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="标签名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入标签名称" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
v-hasPermi="['mp:tag:create']">新增
</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="info" plain icon="el-icon-refresh" size="mini" @click="handleSync"
v-hasPermi="['mp:tag:sync']">同步
</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="编号" align="center" prop="id"/>
<el-table-column label="标签名称" align="center" prop="name"/>
<el-table-column label="粉丝数" align="center" prop="count"/>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['mp:tag:update']">修改
</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['mp:tag:delete']">删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="标签名称" prop="name">
<el-input v-model="form.name" placeholder="请输入标签名称"/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import {
createTag,
updateTag,
deleteTag,
getTag,
getTagPage,
syncTag,
} from '@/api/mp/tag'
import { getSimpleAccounts} from '@/api/mp/account'
export default {
name: 'MpTag',
components: {},
data() {
return {
// 遮罩层
loading: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 公众号标签列表
list: [],
// 弹出层标题
title: '',
// 是否显示弹出层
open: false,
// 查询参数
queryParams: {
accountId: null,
name: null,
},
// 表单参数
form: {
accountId: undefined,
name: undefined,
},
// 表单校验
rules: {
name: [{ required: true, message: '请输入标签名称', trigger: 'blur' }]
},
// 公众号账号列表
accounts: []
}
},
created() {
getSimpleAccounts().then(response => {
this.accounts = response.data;
// 默认选中第一个
if (this.accounts.length > 0) {
this.queryParams.accountId = this.accounts[0].id;
}
// 加载数据
this.getList();
})
},
methods: {
/** 查询列表 */
getList() {
// 如果没有选中公众号账号,则进行提示。
if (!this.queryParams.accountId) {
this.$message.error('未选中公众号,无法查询标签')
return false
}
this.loading = false
// 处理查询参数
let params = {...this.queryParams}
// 执行查询
getTagPage(params).then(response => {
this.list = response.data.list
this.total = response.data.total
this.loading = false
})
},
/** 取消按钮 */
cancel() {
this.open = false
this.reset()
},
/** 表单重置 */
reset() {
this.form = {
accountId: undefined,
name: undefined,
}
this.resetForm('form')
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1
this.getList()
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm('queryForm')
// 默认选中第一个
if (this.accounts.length > 0) {
this.queryParams.accountId = this.accounts[0].id;
}
this.handleQuery()
},
/** 新增按钮操作 */
handleAdd() {
this.reset()
this.open = true
this.title = '添加公众号标签'
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset()
const id = row.id
getTag(id).then(response => {
this.form = response.data
this.open = true
this.title = '修改公众号标签'
})
},
/** 提交按钮 */
submitForm() {
this.$refs['form'].validate(valid => {
if (!valid) {
return
}
this.form.accountId = this.queryParams.accountId;
// 修改的提交
if (this.form.id != null) {
updateTag(this.form).then(response => {
this.$modal.msgSuccess('修改成功')
this.open = false
this.getList()
})
return
}
// 添加的提交
createTag(this.form).then(response => {
this.$modal.msgSuccess('新增成功')
this.open = false
this.getList()
})
})
},
/** 删除按钮操作 */
handleDelete(row) {
const id = row.id
this.$modal.confirm('是否确认删除公众号标签编号为"' + id + '"的数据项?').then(function () {
return deleteTag(id)
}).then(() => {
this.getList()
this.$modal.msgSuccess('删除成功')
}).catch(() => {
})
},
/** 同步标签 */
handleSync() {
const accountId = this.queryParams.accountId
this.$modal.confirm('是否确认同步标签?').then(function () {
return syncTag(accountId)
}).then(() => {
this.$modal.msgSuccess('同步标签成功')
}).catch(() => {
})
},
}
}
</script>

View File

@@ -1,236 +0,0 @@
<template>
<div class="app-container">
<doc-alert title="公众号粉丝" url="https://doc.iocoder.cn/mp/user/" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="公众号" prop="accountId">
<el-select v-model="queryParams.accountId" placeholder="请选择公众号">
<el-option v-for="item in accounts" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="用户标识" prop="openid">
<el-input v-model="queryParams.openid" placeholder="请输入用户标识" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="昵称" prop="nickname">
<el-input v-model="queryParams.nickname" placeholder="请输入昵称" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="info" plain icon="el-icon-refresh" size="mini" @click="handleSync"
v-hasPermi="['mp:user:sync']">同步
</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="编号" align="center" prop="id" />
<el-table-column label="用户标识" align="center" prop="openid" width="260" />
<el-table-column label="昵称" align="center" prop="nickname" />
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="标签" align="center" prop="tagIds" width="200">
<template v-slot="scope">
<span v-for="(tagId, index) in scope.row.tagIds" :key="index">
<el-tag>{{ tags.find(tag => tag.tagId === tagId)?.name }} </el-tag>&nbsp;
</span>
</template>
</el-table-column>
<el-table-column label="订阅状态" align="center" prop="subscribeStatus">
<template v-slot="scope">
<el-tag v-if="scope.row.subscribeStatus === 0" type="success">已订阅</el-tag>
<el-tag v-else type="danger">未订阅</el-tag>
</template>
</el-table-column>
<el-table-column label="订阅时间" align="center" prop="subscribeTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.subscribeTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['mp:user:update']">修改</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="昵称" prop="nickname">
<el-input v-model="form.nickname" placeholder="请输入昵称" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" placeholder="请输入备注" />
</el-form-item>
<el-form-item label="标签" prop="tagIds">
<el-select v-model="form.tagIds" multiple clearable placeholder="请选择标签">
<el-option v-for="item in tags" :key="parseInt(item.tagId)" :label="item.name" :value="parseInt(item.tagId)" />
</el-select>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { updateUser, getUser, getUserPage, syncUser } from "@/api/mp/mpuser";
import { getSimpleAccounts } from "@/api/mp/account";
import { getSimpleTags } from "@/api/mp/tag";
export default {
name: "MpUser",
components: {
},
data() {
return {
// 遮罩层
loading: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 微信公众号粉丝列表
list: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
accountId: null,
openid: null,
nickname: null,
},
// 表单参数
form: {},
// 表单校验
rules: {},
// 公众号账号列表
accounts: [],
// 公众号标签列表
tags: [],
};
},
created() {
getSimpleAccounts().then(response => {
this.accounts = response.data;
// 默认选中第一个
if (this.accounts.length > 0) {
this.queryParams.accountId = this.accounts[0].id;
}
// 加载数据
this.getList();
})
// 加载标签
getSimpleTags().then(response => {
this.tags = response.data;
})
},
methods: {
/** 查询列表 */
getList() {
// 如果没有选中公众号账号,则进行提示。
if (!this.queryParams.accountId) {
this.$message.error('未选中公众号,无法查询用户')
return false
}
this.loading = true;
// 处理查询参数
let params = {...this.queryParams};
// 执行查询
getUserPage(params).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 取消按钮 */
cancel() {
this.open = false;
this.reset();
},
/** 表单重置 */
reset() {
this.form = {
id: undefined,
nickname: undefined,
remark: undefined,
tagIds: [],
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
// 默认选中第一个
if (this.accounts.length > 0) {
this.queryParams.accountId = this.accounts[0].id;
}
this.handleQuery();
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const id = row.id;
getUser(id).then(response => {
this.form = response.data;
this.open = true;
this.title = "修改公众号粉丝";
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (!valid) {
return;
}
// 修改的提交
if (this.form.id != null) {
updateUser(this.form).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
}
});
},
/** 同步标签 */
handleSync() {
const accountId = this.queryParams.accountId
this.$modal.confirm('是否确认同步粉丝?').then(function () {
return syncUser(accountId)
}).then(() => {
this.$modal.msgSuccess('开始从微信公众号同步粉丝信息,同步需要一段时间,建议稍后再查询')
}).catch(() => {
})
},
}
};
</script>

View File

@@ -21,7 +21,7 @@
</el-col>
<el-col :span='4'>
<div class="blodTip">产品名称</div>
<div class="lightTip">{{ orderMsg.productName }}</div>
<div class="lightTip" :title='orderMsg.productName'>{{ orderMsg.productName }}</div>
</el-col>
<el-col :span='4'>
<div class="blodTip">产品规格</div>
@@ -282,6 +282,9 @@ export default {
font-weight: 400;
color: rgba(102, 102, 102, 0.75);
margin-bottom: 12px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}

View File

@@ -1,38 +1,21 @@
<template>
<div class="app-container orderMonitoring">
<!-- 搜索工作栏 -->
<search-bar
:formConfigs="formConfig"
ref="searchBarForm"
@headBtnClick="buttonClick"
/>
<search-bar :formConfigs="formConfig" ref="searchBarForm" @headBtnClick="buttonClick" />
<el-tabs v-model="activeName" @tab-click="toggleTab">
<el-tab-pane label="数据列表" name="dataList"></el-tab-pane>
<el-tab-pane label="环形图" name="barChart"></el-tab-pane>
</el-tabs>
<!-- -->
<div v-if="activeName === 'dataList'">
<base-table
:page="1"
:limit="1000000000000"
:table-props="tableProps"
:table-data="list"
:max-height="tableH"
row-key="id"
:tree-props="{children: 'orderMonitorVOS', hasChildren: 'hasChildren'}"
>
<method-btn
v-if="tableBtn.length"
slot="handleBtn"
:width="100"
label="操作"
:method-list="tableBtn"
@clickBtn="handleClick"
/>
<base-table :page="1" :limit="1000000000000" :table-props="tableProps" :table-data="list" :max-height="tableH"
row-key="id" :tree-props="{ children: 'orderMonitorVOS', hasChildren: 'hasChildren' }">
<method-btn v-if="tableBtn.length" slot="handleBtn" :width="100" label="操作" :method-list="tableBtn"
@clickBtn="handleClick" />
</base-table>
</div>
<!-- -->
<monitoring-ring-charts ref='monitoringRingCharts' v-else :chart-list='chartList'/>
<monitoring-ring-charts ref='monitoringRingCharts' v-else :chart-list='chartList' />
<!-- <pagination
:page.sync="queryParams.pageNo"
:limit.sync="queryParams.pageSize"
@@ -168,65 +151,65 @@ export default {
},
tableProps,
list: [],
tableH: this.tableHeight(305),
tableH: this.tableHeight(260),
total: 0,
tableBtn: [
this.$auth.hasPermi('base:order-completion-monitoring:orderDet')
this.$auth.hasPermi('base:order-completion-monitoring:orderDet')
? {
type: 'orderDetail',
btnName: '详情',
showTip: '订单/工单详情',
showParam: {
type: '&',
data: [
{
type: 'more',
name: 'orderNum',
value: 1
}
]
}
type: 'orderDetail',
btnName: '详情',
showTip: '订单/工单详情',
showParam: {
type: '&',
data: [
{
type: 'more',
name: 'orderNum',
value: 1
}
]
}
}
: undefined,
this.$auth.hasPermi('base:order-completion-monitoring:qualityDet')
this.$auth.hasPermi('base:order-completion-monitoring:qualityDet')
? {
type: 'qualityDetail',
btnName: '质量',
showTip: '质量详情',
showParam: {
type: '&',
data: [
{
type: 'more',
name: 'orderNum',
value: 1
},
{
type: 'unequal',
name: 'woIdString',
value: ''
}
]
}
type: 'qualityDetail',
btnName: '质量',
showTip: '质量详情',
showParam: {
type: '&',
data: [
{
type: 'more',
name: 'orderNum',
value: 1
},
{
type: 'unequal',
name: 'woIdString',
value: ''
}
]
}
}
: undefined
// this.$auth.hasPermi('base:order-completion-monitoring:sendOut')
// ? {
// type: 'sendOutDetail',
// btnName: '发货',
// showTip: '发货详情',
// showParam: {
// type: '&',
// data: [
// {
// type: 'more',
// name: 'workOrderNum',
// value: 1
// }
// ]
// }
// }
// : undefined
// this.$auth.hasPermi('base:order-completion-monitoring:sendOut')
// ? {
// type: 'sendOutDetail',
// btnName: '发货',
// showTip: '发货详情',
// showParam: {
// type: '&',
// data: [
// {
// type: 'more',
// name: 'workOrderNum',
// value: 1
// }
// ]
// }
// }
// : undefined
].filter((v) => v),
chartList: []
}
@@ -234,7 +217,7 @@ export default {
components: { MonitoringRingCharts },
mounted() {
window.addEventListener('resize', () => {
this.tableH = this.tableHeight(305)
this.tableH = this.tableHeight(260)
})
let start = moment().subtract(30, 'days').format('yyyy-MM-DD')
let end = moment().format('yyyy-MM-DD')
@@ -245,10 +228,10 @@ export default {
},
methods: {
getPage() {
orderGroupMonitor({...this.queryParams}).then(res => {
orderGroupMonitor({ ...this.queryParams }).then(res => {
let arr = res.data || []
if (arr.length > 0) {
let color = ['#7164FF','#288AFF','#63BDFF','#8EF0AB','#FFCE6A']
let color = ['#7164FF', '#288AFF', '#63BDFF', '#8EF0AB', '#FFCE6A']
let arr2 = []
arr.map(item => {
item.orderType = '集团订单'//前端写死订单类型
@@ -261,11 +244,11 @@ export default {
obj.num = item.planQuantity || 0
let sunNum = 0
if (item.orderMonitorVOS && item.orderMonitorVOS.length > 0) {
for(let i = 0; i < item.orderMonitorVOS.length; i++) {
for (let i = 0; i < item.orderMonitorVOS.length; i++) {
item.orderMonitorVOS[i].orderType = '厂务订单'//前端写死订单类型
item.orderMonitorVOS[i].id = item.orderMonitorVOS[i].orderid
item.orderMonitorVOS[i].orderNum = item.orderMonitorVOS[i].workOrderNum
woIdString+=item.orderMonitorVOS[i].woIdString?item.orderMonitorVOS[i].woIdString:''
woIdString += item.orderMonitorVOS[i].woIdString ? item.orderMonitorVOS[i].woIdString : ''
//==============图数据
let subObj = {}
subObj.value = item.orderMonitorVOS[i].actualquantity
@@ -273,9 +256,9 @@ export default {
if (i < 5) {
subObj.color = color[i]
} else {
subObj.color = color[i%5]
subObj.color = color[i % 5]
}
sunNum+=(item.orderMonitorVOS[i].actualquantity || 0)
sunNum += (item.orderMonitorVOS[i].actualquantity || 0)
arr3.push(subObj)
}
}
@@ -295,7 +278,7 @@ export default {
this.$refs.monitoringRingCharts.initChart()
})
}
}else{
} else {
// 显示无数据的图片
this.chartList = []
}
@@ -320,24 +303,24 @@ export default {
case 'orderDetail':
if (val.data.orderType === '集团订单') {
this.$router.push({// 去厂务订单详情
path: '/order/base/order-manage/order-detail-data?orderIdString='+ val.data.orderIds.join(',')
path: '/order/base/order-manage/order-detail-data?orderIdString=' + val.data.orderIds.join(',')
})
}else{
} else {
this.$router.push({// 去工单详情
path: '/core/core-work-order-detail?woIdString='+val.data.woIdString
path: '/core/core-work-order-detail?woIdString=' + val.data.woIdString
})
}
break
case 'qualityDetail':
this.$router.push({
path: '/quality/base/quality-inspection-data/detection-information/statistical-data?woIdString='+val.data.woIdString
path: '/quality/base/quality-inspection-data/detection-information/statistical-data?woIdString=' + val.data.woIdString
})
break
default:
// this.$router.push({
// path: '/delivery/delivery-log?orderId='+encodeURI(val.data.name)
// })
}
// this.$router.push({
// path: '/delivery/delivery-log?orderId='+encodeURI(val.data.name)
// })
}
},
toggleTab() {
if (this.activeName === 'barChart' && this.chartList.length > 0) {
@@ -346,7 +329,7 @@ export default {
})
}
}
}
}
}
</script>
<style lang='scss'>
@@ -360,31 +343,39 @@ export default {
height: 2px;
background-color: #e4e7ed;
}
.el-tabs__nav-wrap::after {
width: 0;
}
.el-tabs__item {
padding: 0 10px;
}
.el-tabs__item:hover {
color: rgba(0, 0, 0, 0.85);
}
.el-tabs__item.is-active {
color: rgba(0, 0, 0, 0.85);
}
.el-tabs__item {
color: rgba(0, 0, 0, 0.45);
}
.searchBarBox {
margin-bottom: 0;
}
.boxTitle {
display: inline-block;
font-size: 16px;
font-weight: 400;
color: #000000;
margin:0 10px 16px 0;
margin: 0 10px 16px 0;
}
.blueTitle {
content: '';
display: inline-block;

View File

@@ -1,358 +0,0 @@
<template>
<div>
<el-dialog :visible.sync="transferParam.aliPayOpen" :title="title" @closed="close" append-to-body width="800px">
<el-form ref="aliPayForm" :model="form" :rules="rules" size="medium" label-width="100px"
v-loading="transferParam.loading">
<el-form-item label-width="180px" label="渠道费率" prop="feeRate">
<el-input v-model="form.feeRate" placeholder="请输入渠道费率" clearable :style="{width: '100%'}">
<template slot="append">%</template>
</el-input>
</el-form-item>
<el-form-item label-width="180px" label="开放平台APPID" prop="aliPayConfig.appId">
<el-input v-model="form.aliPayConfig.appId" placeholder="请输入开放平台APPID" clearable :style="{width: '100%'}">
</el-input>
</el-form-item>
<el-form-item label-width="180px" label="渠道状态" prop="status">
<el-radio-group v-model="form.status" size="medium">
<el-radio v-for="dict in statusDictDatas" :key="parseInt(dict.value)" :label="parseInt(dict.value)">
{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label-width="180px" label="网关地址" prop="aliPayConfig.serverUrl">
<el-radio-group v-model="form.aliPayConfig.serverUrl" size="medium">
<el-radio v-for="dict in aliPayServerDatas" :key="dict.value" :label="dict.value">
{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label-width="180px" label="算法类型" prop="aliPayConfig.signType">
<el-radio-group v-model="form.aliPayConfig.signType" size="medium">
<el-radio v-for="dict in aliPaySignTypeDatas" :key="dict.value" :label="dict.value">
{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label-width="180px" label="公钥类型" prop="aliPayConfig.mode">
<el-radio-group v-model="form.aliPayConfig.mode" size="medium">
<el-radio v-for="dict in aliPayModeDatas" :key="parseInt(dict.value)" :label="parseInt(dict.value)">
{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<div v-if="form.aliPayConfig.mode === 1">
<el-form-item label-width="180px" label="商户私钥" prop="aliPayConfig.privateKey">
<el-input type="textarea" :autosize="{minRows: 8, maxRows: 8}" v-model="form.aliPayConfig.privateKey"
placeholder="请输入商户私钥" clearable :style="{width: '100%'}">
</el-input>
</el-form-item>
<el-form-item label-width="180px" label="支付宝公钥字符串" prop="aliPayConfig.alipayPublicKey">
<el-input
type="textarea"
:autosize="{minRows: 8, maxRows: 8}"
v-model="form.aliPayConfig.alipayPublicKey"
placeholder="请输入支付宝公钥字符串" clearable
:style="{width: '100%'}">
</el-input>
</el-form-item>
</div>
<div v-if="form.aliPayConfig.mode === 2">
<el-form-item label-width="180px" label="商户公钥应用证书" prop="aliPayConfig.appCertContent">
<el-input v-model="form.aliPayConfig.appCertContent" type="textarea"
placeholder="请上传商户公钥应用证书"
readonly :autosize="{minRows: 8, maxRows: 8}" :style="{width: '100%'}"></el-input>
</el-form-item>
<el-form-item label-width="180px" label="">
<el-upload
action=""
ref="privateKeyContentFile"
:limit="1"
:accept="fileAccept"
:http-request="appCertUpload"
:before-upload="fileBeforeUpload">
<el-button size="small" type="primary" icon="el-icon-upload">点击上传</el-button>
</el-upload>
</el-form-item>
<el-form-item label-width="180px" label="支付宝公钥证书" prop="aliPayConfig.alipayPublicCertContent">
<el-input v-model="form.aliPayConfig.alipayPublicCertContent" type="textarea"
placeholder="请上传支付宝公钥证书"
readonly :autosize="{minRows: 8, maxRows: 8}" :style="{width: '100%'}"></el-input>
</el-form-item>
<el-form-item label-width="180px" label="">
<el-upload
ref="privateCertContentFile"
action=""
:limit="1"
:accept="fileAccept"
:before-upload="fileBeforeUpload"
:http-request="alipayPublicCertUpload">
<el-button size="small" type="primary" icon="el-icon-upload">点击上传</el-button>
</el-upload>
</el-form-item>
<el-form-item label-width="180px" label="根证书" prop="aliPayConfig.rootCertContent">
<el-input
v-model="form.aliPayConfig.rootCertContent"
type="textarea"
placeholder="请上传根证书"
readonly :autosize="{minRows: 8, maxRows: 8}"
:style="{width: '100%'}">
</el-input>
</el-form-item>
<el-form-item label-width="180px" label="">
<el-upload
ref="privateCertContentFile"
:limit="1"
:accept="fileAccept"
action=""
:before-upload="fileBeforeUpload"
:http-request="rootCertUpload">
<el-button size="small" type="primary" icon="el-icon-upload">点击上传</el-button>
</el-upload>
</el-form-item>
</div>
<el-form-item label-width="180px" label="备注" prop="remark">
<el-input v-model="form.remark" :style="{width: '100%'}"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="close">取消</el-button>
<el-button type="primary" @click="handleConfirm">确定</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import {DICT_TYPE, getDictDatas} from "@/utils/dict";
import {createChannel, getChannel, updateChannel} from "@/api/pay/channel";
const defaultForm = {
code: '',
status: null,
remark: '',
feeRate: null,
appId: '',
merchantId: null,
aliPayConfig: {
appId: '',
serverUrl: null,
signType: '',
mode: null,
privateKey: '',
alipayPublicKey: '',
appCertContent: '',
alipayPublicCertContent: '',
rootCertContent: ''
}
};
export default {
name: "aliPayChannelForm",
components: {},
props: {
// 传输的参数
transferParam: {
// 加载动画
"loading": false,
// 是否修改
"edit": false,
// 是否显示
"aliPayOpen": false,
// 应用ID
"appId": null,
// 渠道编码
"payCode": null,
// 商户对象
"payMerchant": {
// 编号
"id": null,
// 名称
"name": null
},
}
},
data() {
return {
title:'',
form: JSON.parse(JSON.stringify(defaultForm)),
rules: {
feeRate: [{
required: true,
message: '请输入渠道费率',
trigger: 'blur'
}],
'aliPayConfig.appId': [{
required: true,
message: '请输入开放平台上创建的应用的 ID',
trigger: 'blur'
}],
status: [{
required: true,
message: '渠道状态不能为空',
trigger: 'blur'
}],
'aliPayConfig.serverUrl': [{
required: true,
message: '请传入网关地址',
trigger: 'blur'
}],
'aliPayConfig.signType': [{
required: true,
message: '请传入签名算法类型',
trigger: 'blur'
}],
'aliPayConfig.mode': [{
required: true,
message: '公钥类型不能为空',
trigger: 'blur'
}],
'aliPayConfig.privateKey': [{
required: true,
message: '请输入商户私钥',
trigger: 'blur'
}],
'aliPayConfig.alipayPublicKey': [{
required: true,
message: '请输入支付宝公钥字符串',
trigger: 'blur'
}],
'aliPayConfig.appCertContent': [{
required: true,
message: '请上传商户公钥应用证书',
trigger: 'blur'
}],
'aliPayConfig.alipayPublicCertContent': [{
required: true,
message: '请上传支付宝公钥证书',
trigger: 'blur'
}],
'aliPayConfig.rootCertContent': [{
required: true,
message: '请上传指定根证书',
trigger: 'blur'
}],
},
fileAccept: ".crt",
// 渠道状态 数据字典
statusDictDatas: getDictDatas(DICT_TYPE.COMMON_STATUS),
// 支付宝加密方式
aliPaySignTypeDatas: getDictDatas(DICT_TYPE.PAY_CHANNEL_ALIPAY_SIGN_TYPE),
// 版本状态 数据字典
aliPayModeDatas: getDictDatas(DICT_TYPE.PAY_CHANNEL_ALIPAY_MODE),
// 支付宝网关地址
aliPayServerDatas: getDictDatas(DICT_TYPE.PAY_CHANNEL_ALIPAY_SERVER_TYPE),
}
},
watch: {
transferParam: {
deep: true, // 深度监听
handler(newVal) {
if (newVal.aliPayOpen) {
this.form.code = newVal.payCode;
this.form.appId = newVal.appId;
this.form.merchantId = newVal.payMerchant.id;
// 只有在初次进来为编辑 并且为加载中的时候才回去请求数据
if (newVal.edit === true && newVal.loading) {
this.title = "编辑支付渠道";
this.init();
} else {
this.title = "创建支付渠道";
}
}
}
}
},
methods: {
init() {
getChannel(this.transferParam.payMerchant.id, this.transferParam.appId, this.transferParam.payCode)
.then(response => {
this.form.id = response.data.id;
this.form.feeRate = response.data.feeRate;
this.form.status = response.data.status;
this.form.remark = response.data.remark;
let config = JSON.parse(response.data.config);
this.form.aliPayConfig.appId = config.appId;
this.form.aliPayConfig.serverUrl = config.serverUrl;
this.form.aliPayConfig.signType = config.signType;
this.form.aliPayConfig.mode = config.mode;
this.form.aliPayConfig.privateKey = config.privateKey;
this.form.aliPayConfig.alipayPublicKey = config.alipayPublicKey;
this.form.aliPayConfig.appCertContent = config.appCertContent;
this.form.aliPayConfig.alipayPublicCertContent = config.alipayPublicCertContent;
this.form.aliPayConfig.rootCertContent = config.rootCertContent;
this.transferParam.loading = false;
})
},
close() {
this.transferParam.aliPayOpen = false;
this.form = JSON.parse(JSON.stringify(defaultForm));
},
handleConfirm() {
this.$refs['aliPayForm'].validate(valid => {
if (!valid) {
return
}
let data = this.form;
data.config = JSON.stringify(this.form.aliPayConfig);
if (this.transferParam.edit) {
updateChannel(data).then(response => {
if (response.code === 0) {
this.$modal.msgSuccess("修改成功");
this.close();
}
})
} else {
createChannel(data).then(response => {
if (response.code === 0) {
this.$modal.msgSuccess("新增成功");
this.$parent.refreshTable();
this.close();
}
});
}
});
},
fileBeforeUpload(file) {
let format = '.' + file.name.split(".")[1];
if (format !== this.fileAccept) {
this.$message.error('请上传指定格式"' + this.fileAccept + '"文件');
return false;
}
let isRightSize = file.size / 1024 / 1024 < 2
if (!isRightSize) {
this.$message.error('文件大小超过 2MB')
}
return isRightSize
},
appCertUpload(event) {
const readFile = new FileReader()
readFile.onload = (e) => {
this.form.aliPayConfig.appCertContent = e.target.result
}
readFile.readAsText(event.file);
},
alipayPublicCertUpload(event) {
const readFile = new FileReader()
readFile.onload = (e) => {
this.form.aliPayConfig.alipayPublicCertContent = e.target.result
}
readFile.readAsText(event.file);
},
rootCertUpload(event) {
const readFile = new FileReader()
readFile.onload = (e) => {
this.form.aliPayConfig.rootCertContent = e.target.result
}
readFile.readAsText(event.file);
},
}
}
</script>
<style scoped>
</style>

View File

@@ -1,299 +0,0 @@
<template>
<div>
<el-dialog :visible.sync="transferParam.wechatOpen" :title="title" @close="close" append-to-body width="800px">
<el-form ref="wechatJsApiForm" :model="form" :rules="rules" size="medium" label-width="100px"
v-loading="transferParam.loading">
<el-form-item label-width="180px" label="渠道费率" prop="feeRate">
<el-input v-model="form.feeRate" placeholder="请输入渠道费率" clearable :style="{width: '100%'}">
<template slot="append">%</template>
</el-input>
</el-form-item>
<el-form-item label-width="180px" label="公众号APPID" prop="weChatConfig.appId">
<el-input v-model="form.weChatConfig.appId" placeholder="请输入公众号APPID" clearable :style="{width: '100%'}">
</el-input>
</el-form-item>
<el-form-item label-width="180px" label="商户号" prop="weChatConfig.mchId">
<el-input v-model="form.weChatConfig.mchId" :style="{width: '100%'}"></el-input>
</el-form-item>
<el-form-item label-width="180px" label="渠道状态" prop="status">
<el-radio-group v-model="form.status" size="medium">
<el-radio v-for="dict in statusDictDatas" :key="parseInt(dict.value)" :label="parseInt(dict.value)">
{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label-width="180px" label="API 版本" prop="weChatConfig.apiVersion">
<el-radio-group v-model="form.weChatConfig.apiVersion" size="medium">
<el-radio v-for="dict in versionDictDatas" :key="dict.value" :label="dict.value">
{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label-width="180px" label="商户密钥" prop="weChatConfig.mchKey"
v-if="form.weChatConfig.apiVersion === 'v2'">
<el-input v-model="form.weChatConfig.mchKey" placeholder="请输入商户密钥" clearable
:style="{width: '100%'}" type="textarea" :autosize="{minRows: 8, maxRows: 8}"></el-input>
</el-form-item>
<div v-if="form.weChatConfig.apiVersion === 'v3'">
<el-form-item label-width="180px" label="API V3密钥" prop="weChatConfig.apiV3Key">
<el-input v-model="form.weChatConfig.apiV3Key" placeholder="请输入API V3密钥" clearable
:style="{width: '100%'}" type="textarea" :autosize="{minRows: 8, maxRows: 8}"></el-input>
</el-form-item>
<el-form-item label-width="180px" label="apiclient_key.perm证书" prop="weChatConfig.privateKeyContent">
<el-input v-model="form.weChatConfig.privateKeyContent" type="textarea"
placeholder="请上传apiclient_key.perm证书"
readonly :autosize="{minRows: 8, maxRows: 8}" :style="{width: '100%'}"></el-input>
</el-form-item>
<el-form-item label-width="180px" label="" prop="privateKeyContentFile">
<el-upload ref="privateKeyContentFile"
:limit="1"
:accept="fileAccept"
:headers="header"
action=""
:before-upload="pemFileBeforeUpload"
:http-request="privateKeyUpload"
>
<el-button size="small" type="primary" icon="el-icon-upload">点击上传</el-button>
</el-upload>
</el-form-item>
<el-form-item label-width="180px" label="apiclient_cert.perm证书" prop="weChatConfig.privateCertContent">
<el-input v-model="form.weChatConfig.privateCertContent" type="textarea"
placeholder="请上传apiclient_cert.perm证书"
readonly :autosize="{minRows: 8, maxRows: 8}" :style="{width: '100%'}"></el-input>
</el-form-item>
<el-form-item label-width="180px" label="" prop="privateCertContentFile">
<el-upload ref="privateCertContentFile"
:limit="1"
:accept="fileAccept"
:headers="header"
action=""
:before-upload="pemFileBeforeUpload"
:http-request="privateCertUpload"
>
<el-button size="small" type="primary" icon="el-icon-upload">点击上传</el-button>
</el-upload>
</el-form-item>
</div>
<el-form-item label-width="180px" label="备注" prop="remark">
<el-input v-model="form.remark" :style="{width: '100%'}"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="close">取消</el-button>
<el-button type="primary" @click="handleConfirm">确定</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import {DICT_TYPE, getDictDatas} from "@/utils/dict";
import {createChannel, getChannel, updateChannel} from "@/api/pay/channel";
const defaultForm = {
code: '',
status: null,
remark: '',
feeRate: null,
appId: '',
merchantId: null,
weChatConfig: {
appId: '',
mchId: '',
apiVersion: '',
mchKey: '',
privateKeyContent: '',
privateCertContent: '',
apiV3Key:'',
}
}
export default {
name: "wechatChannelForm",
components: {},
props: {
// 传输的参数
transferParam: {
// 加载动画
"loading": false,
// 是否修改
"edit": false,
// 是否显示
"wechatOpen": false,
// 应用ID
"appId": null,
// 渠道编码
"payCode": null,
// 商户对象
"payMerchant": {
// 编号
"id": null,
// 名称
"name": null
},
}
},
data() {
return {
title:'',
form: JSON.parse(JSON.stringify(defaultForm)),
rules: {
feeRate: [{
required: true,
message: '请输入渠道费率',
trigger: 'blur'
}],
'weChatConfig.mchId': [{
required: true,
message: '请传入商户号',
trigger: 'blur'
}],
'weChatConfig.appId': [{
required: true,
message: '请输入公众号APPID',
trigger: 'blur'
}],
status: [{
required: true,
message: '渠道状态不能为空',
trigger: 'blur'
}],
'weChatConfig.apiVersion': [{
required: true,
message: 'API版本不能为空',
trigger: 'blur'
}],
'weChatConfig.mchKey': [{
required: true,
message: '请输入商户密钥',
trigger: 'blur'
}],
'weChatConfig.privateKeyContent': [{
required: true,
message: '请上传apiclient_key.perm证书',
trigger: 'blur'
}],
'weChatConfig.privateCertContent': [{
required: true,
message: '请上传apiclient_cert.perm证书',
trigger: 'blur'
}],
'weChatConfig.apiV3Key': [{
required: true,
message: '请上传apiV3密钥值',
trigger: 'blur'
}],
},
// 文件上传的header
header: {
"Authorization": null
},
fileAccept: ".pem",
// 渠道状态 数据字典
statusDictDatas: getDictDatas(DICT_TYPE.COMMON_STATUS),
versionDictDatas: getDictDatas(DICT_TYPE.PAY_CHANNEL_WECHAT_VERSION),
}
},
watch: {
transferParam: {
deep: true, // 深度监听
handler(newVal) {
if (newVal.wechatOpen) {
this.form.code = newVal.payCode;
this.form.appId = newVal.appId;
this.form.merchantId = newVal.payMerchant.id;
// 只有在初次进来为编辑 并且为加载中的时候才回去请求数据
if (newVal.edit && newVal.loading) {
this.title = "编辑支付渠道";
this.init();
} else {
this.title = "创建支付渠道";
}
}
}
}
},
methods: {
init() {
getChannel(this.transferParam.payMerchant.id, this.transferParam.appId, this.transferParam.payCode)
.then(response => {
this.form.id = response.data.id;
this.form.feeRate = response.data.feeRate;
this.form.appId = response.data.appId;
this.form.status = response.data.status;
this.form.remark = response.data.remark;
let config = JSON.parse(response.data.config);
this.form.weChatConfig.appId = config.appId;
this.form.weChatConfig.apiVersion = config.apiVersion;
this.form.weChatConfig.mchId = config.mchId;
this.form.weChatConfig.mchKey = config.mchKey;
this.form.weChatConfig.privateKeyContent = config.privateKeyContent;
this.form.weChatConfig.privateCertContent = config.privateCertContent;
this.form.weChatConfig.apiV3Key = config.apiV3Key;
this.transferParam.loading = false;
})
},
close() {
this.transferParam.wechatOpen = false;
this.form = JSON.parse(JSON.stringify(defaultForm));
},
handleConfirm() {
this.$refs['wechatJsApiForm'].validate(valid => {
if (!valid) {
return
}
let data = this.form;
data.config = JSON.stringify(this.form.weChatConfig);
if (this.transferParam.edit) {
updateChannel(data).then(response => {
if (response.code === 0) {
this.$modal.msgSuccess("修改成功");
this.close();
}
})
} else {
createChannel(data).then(response => {
if (response.code === 0) {
this.$modal.msgSuccess("新增成功");
this.$parent.refreshTable();
this.close();
}
});
}
});
},
pemFileBeforeUpload(file) {
let format = '.' + file.name.split(".")[1];
if (format !== this.fileAccept) {
this.$message.error('请上传指定格式"' + this.fileAccept + '"文件');
return false;
}
let isRightSize = file.size / 1024 / 1024 < 2
if (!isRightSize) {
this.$message.error('文件大小超过 2MB')
}
return isRightSize
},
privateKeyUpload(event) {
const readFile = new FileReader()
readFile.onload = (e) => {
this.form.weChatConfig.privateKeyContent = e.target.result
}
readFile.readAsText(event.file);
},
privateCertUpload(event) {
const readFile = new FileReader()
readFile.onload = (e) => {
this.form.weChatConfig.privateCertContent = e.target.result
}
readFile.readAsText(event.file);
}
}
}
</script>
<style scoped>
</style>

View File

@@ -1,484 +0,0 @@
<template>
<div class="app-container">
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="应用名" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入应用名" clearable
@keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="商户名称" prop="merchantName">
<el-input v-model="queryParams.merchantName" placeholder="请输入商户名称" clearable
@keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="开启状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择开启状态" clearable>
<el-option v-for="dict in statusDictDatas" :key="parseInt(dict.value)" :label="dict.label"
:value="parseInt(dict.value)"/>
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
v-hasPermi="['pay:app:create']">新增
</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport"
v-hasPermi="['pay:app:export']">导出
</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="应用编号" align="center" prop="id"/>
<el-table-column label="应用名" align="center" prop="name"/>
<el-table-column label="开启状态" align="center" prop="status">
<template v-slot="scope">
<el-switch v-model="scope.row.status" :active-value="0" :inactive-value="1"
@change="handleStatusChange(scope.row)"/>
</template>
</el-table-column>
<el-table-column label="商户名称" align="center" prop="payMerchant.name"/>
<el-table-column label="支付宝配置" align="center">
<el-table-column :label="payChannelEnum.ALIPAY_APP.name" align="center">
<template v-slot="scope">
<el-button type="success" icon="el-icon-check" circle
v-if="judgeChannelExist(scope.row.channelCodes,payChannelEnum.ALIPAY_APP.code)"
@click="handleUpdateChannel(scope.row,payChannelEnum.ALIPAY_APP.code,payType.ALIPAY)">
</el-button>
<el-button v-else
type="danger" icon="el-icon-close" circle
@click="handleCreateChannel(scope.row,payChannelEnum.ALIPAY_APP.code,payType.ALIPAY)">
</el-button>
</template>
</el-table-column>
<el-table-column :label="payChannelEnum.ALIPAY_PC.name" align="center">
<template v-slot="scope">
<el-button type="success" icon="el-icon-check" circle
v-if="judgeChannelExist(scope.row.channelCodes,payChannelEnum.ALIPAY_PC.code)"
@click="handleUpdateChannel(scope.row,payChannelEnum.ALIPAY_PC.code,payType.ALIPAY)">
</el-button>
<el-button v-else
type="danger" icon="el-icon-close" circle
@click="handleCreateChannel(scope.row,payChannelEnum.ALIPAY_PC.code,payType.ALIPAY)">
</el-button>
</template>
</el-table-column>
<el-table-column :label="payChannelEnum.ALIPAY_WAP.name" align="center">
<template v-slot="scope">
<el-button type="success" icon="el-icon-check" circle
v-if="judgeChannelExist(scope.row.channelCodes,payChannelEnum.ALIPAY_WAP.code)"
@click="handleUpdateChannel(scope.row,payChannelEnum.ALIPAY_WAP.code,payType.ALIPAY)">
</el-button>
<el-button v-else
type="danger" icon="el-icon-close" circle
@click="handleCreateChannel(scope.row,payChannelEnum.ALIPAY_WAP.code,payType.ALIPAY)">
</el-button>
</template>
</el-table-column>
<el-table-column :label="payChannelEnum.ALIPAY_QR.name" align="center">
<template v-slot="scope">
<el-button type="success" icon="el-icon-check" circle
v-if="judgeChannelExist(scope.row.channelCodes,payChannelEnum.ALIPAY_QR.code)"
@click="handleUpdateChannel(scope.row,payChannelEnum.ALIPAY_QR.code,payType.ALIPAY)">
</el-button>
<el-button v-else
type="danger" icon="el-icon-close" circle
@click="handleCreateChannel(scope.row,payChannelEnum.ALIPAY_QR.code,payType.ALIPAY)">
</el-button>
</template>
</el-table-column>
<el-table-column :label="payChannelEnum.ALIPAY_BAR.name" align="center">
<template v-slot="scope">
<el-button type="success" icon="el-icon-check" circle
v-if="judgeChannelExist(scope.row.channelCodes,payChannelEnum.ALIPAY_BAR.code)"
@click="handleUpdateChannel(scope.row,payChannelEnum.ALIPAY_BAR.code,payType.ALIPAY)">
</el-button>
<el-button v-else
type="danger" icon="el-icon-close" circle
@click="handleCreateChannel(scope.row,payChannelEnum.ALIPAY_BAR.code,payType.ALIPAY)">
</el-button>
</template>
</el-table-column>
</el-table-column>
<el-table-column label="微信配置" align="center">
<el-table-column :label="payChannelEnum.WX_LITE.name" align="center">
<template v-slot="scope">
<el-button type="success" icon="el-icon-check" circle
v-if="judgeChannelExist(scope.row.channelCodes,payChannelEnum.WX_LITE.code)"
@click="handleUpdateChannel(scope.row,payChannelEnum.WX_LITE.code,payType.WECHAT)">
</el-button>
<el-button v-else
type="danger" icon="el-icon-close" circle
@click="handleCreateChannel(scope.row,payChannelEnum.WX_LITE.code,payType.WECHAT)">
</el-button>
</template>
</el-table-column>
<el-table-column :label="payChannelEnum.WX_PUB.name" align="center">
<template v-slot="scope">
<el-button type="success" icon="el-icon-check" circle
v-if="judgeChannelExist(scope.row.channelCodes,payChannelEnum.WX_PUB.code)"
@click="handleUpdateChannel(scope.row,payChannelEnum.WX_PUB.code,payType.WECHAT)">
</el-button>
<el-button v-else
type="danger" icon="el-icon-close" circle
@click="handleCreateChannel(scope.row,payChannelEnum.WX_PUB.code,payType.WECHAT)">
</el-button>
</template>
</el-table-column>
<el-table-column :label="payChannelEnum.WX_APP.name" align="center">
<template v-slot="scope">
<el-button type="success" icon="el-icon-check" circle
v-if="judgeChannelExist(scope.row.channelCodes,payChannelEnum.WX_APP.code)"
@click="handleUpdateChannel(scope.row,payChannelEnum.WX_APP.code,payType.WECHAT)">
</el-button>
<el-button v-else
type="danger" icon="el-icon-close" circle
@click="handleCreateChannel(scope.row,payChannelEnum.WX_APP.code,payType.WECHAT)">
</el-button>
</template>
</el-table-column>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['pay:app:update']">修改
</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['pay:app:delete']">删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="title" :visible.sync="open" width="800px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="160px">
<el-form-item label="应用名" prop="name">
<el-input v-model="form.name" placeholder="请输入应用名"/>
</el-form-item>
<el-form-item label="所属商户" prop="merchantId">
<el-select
v-model="form.merchantId"
filterable
remote
reserve-keyword
placeholder="请选择所属商户"
:remote-method="handleGetMerchantListByName"
:loading="loading">
<el-option
v-for="item in merchantList"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="开启状态" prop="status">
<el-radio-group v-model="form.status">
<el-radio v-for="dict in statusDictDatas" :key="parseInt(dict.value)" :label="parseInt(dict.value)">
{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="支付结果的回调地址" prop="payNotifyUrl">
<el-input v-model="form.payNotifyUrl" placeholder="请输入支付结果的回调地址"/>
</el-form-item>
<el-form-item label="退款结果的回调地址" prop="refundNotifyUrl">
<el-input v-model="form.refundNotifyUrl" placeholder="请输入退款结果的回调地址"/>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" placeholder="请输入备注"/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
<wechat-channel-form :transferParam="channelParam"></wechat-channel-form>
<ali-pay-channel-form :transferParam="channelParam"></ali-pay-channel-form>
</div>
</template>
<script>
import {createApp, updateApp, changeAppStatus, deleteApp, getApp, getAppPage, exportAppExcel} from "@/api/pay/app";
import {DICT_TYPE, getDictDatas} from "@/utils/dict";
import {PayType, PayChannelEnum, CommonStatusEnum} from "@/utils/constants";
import {getMerchantListByName} from "@/api/pay/merchant";
import wechatChannelForm from "@/views/pay/app/components/wechatChannelForm";
import aliPayChannelForm from "@/views/pay/app/components/aliPayChannelForm";
export default {
name: "PayApp",
components: {
"wechatChannelForm": wechatChannelForm,
"aliPayChannelForm": aliPayChannelForm
},
data() {
return {
// 遮罩层
loading: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 支付应用信息列表
list: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
name: null,
status: null,
remark: null,
payNotifyUrl: null,
refundNotifyUrl: null,
merchantName: null,
createTime: []
},
// 表单参数
form: {},
// 表单校验
rules: {
name: [{required: true, message: "应用名不能为空", trigger: "blur"}],
status: [{required: true, message: "开启状态不能为空", trigger: "blur"}],
payNotifyUrl: [{required: true, message: "支付结果的回调地址不能为空", trigger: "blur"}],
refundNotifyUrl: [{required: true, message: "退款结果的回调地址不能为空", trigger: "blur"}],
merchantId: [{required: true, message: "商户编号不能为空", trigger: "blur"}],
},
// 数据字典
statusDictDatas: getDictDatas(DICT_TYPE.COMMON_STATUS),
sysCommonStatusEnum: CommonStatusEnum,
// 支付渠道枚举
payChannelEnum: PayChannelEnum,
// 支付类型
payType: PayType,
// 商户列表
merchantList: [],
// 是否显示支付窗口
payOpen: false,
// 微信组件传参参数
channelParam: {
// 是否修改
"edit": false,
// 微信是否显示
"wechatOpen": false,
// 支付宝是否显示
"aliPayOpen": false,
// 应用ID
"appId": null,
// 渠道编码
"payCode": null,
// 商户对象
"payMerchant": {
// 编号
"id": null,
// 名称
"name": null
},
}
};
},
created() {
this.getList();
this.handleGetMerchantListByName(null);
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
// 执行查询
getAppPage(this.queryParams).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 取消按钮 */
cancel() {
this.open = false;
this.reset();
},
/** 表单重置 */
reset() {
this.form = {
id: undefined,
name: undefined,
status: undefined,
remark: undefined,
payNotifyUrl: undefined,
refundNotifyUrl: undefined,
merchantId: undefined,
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加支付应用信息";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const id = row.id;
getApp(id).then(response => {
this.form = response.data;
this.open = true;
this.title = "修改支付应用信息";
});
},
// 用户状态修改
handleStatusChange(row) {
let text = row.status === CommonStatusEnum.ENABLE ? "启用" : "停用";
this.$modal.confirm('确认要"' + text + '""' + row.name + '"应用吗?').then(function () {
return changeAppStatus(row.id, row.status);
}).then(() => {
this.$modal.msgSuccess(text + "成功");
}).catch(function () {
row.status = row.status === CommonStatusEnum.ENABLE ? CommonStatusEnum.DISABLE
: CommonStatusEnum.ENABLE;
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (!valid) {
return;
}
// 修改的提交
if (this.form.id != null) {
updateApp(this.form).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
return;
}
// 添加的提交
createApp(this.form).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
});
},
/** 删除按钮操作 */
handleDelete(row) {
this.$modal.confirm('是否确认删除支付应用信息编号为"' + row.id + '"的数据项?').then(function () {
return deleteApp(row.id);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {
this.$message({
type: 'info',
message: '已取消删除'
});
});
},
/** 导出按钮操作 */
handleExport() {
// 处理查询参数
let params = {...this.queryParams};
params.pageNo = undefined;
params.pageSize = undefined;
// 执行导出
this.$modal.confirm('是否确认导出所有支付应用信息数据项?').then(function () {
return exportAppExcel(params);
}).then(response => {
this.$download.excel(response, '支付应用信息.xls');
}).catch(() => {});
},
/**
* 根据商户名称模糊匹配商户信息
* @param name 商户名称
*/
handleGetMerchantListByName(name) {
getMerchantListByName(name).then(response => {
this.merchantList = response.data;
});
},
/**
* 修改支付渠道信息
*/
handleUpdateChannel(row, payCode, type) {
this.settingChannelParam(row, payCode, type)
this.channelParam.edit = true;
this.channelParam.loading = true;
},
/**
* 新增支付渠道信息
*/
handleCreateChannel(row, payCode, type) {
this.settingChannelParam(row, payCode, type)
this.channelParam.edit = false;
this.channelParam.loading = false;
},
/**
* 设置支付渠道信息
*/
settingChannelParam(row, payCode, type) {
if (type === PayType.WECHAT) {
this.channelParam.wechatOpen = true;
this.channelParam.aliPayOpen = false;
}
if (type === PayType.ALIPAY) {
this.channelParam.aliPayOpen = true;
this.channelParam.wechatOpen = false;
}
this.channelParam.edit = false;
this.channelParam.loading = false;
this.channelParam.appId = row.id;
this.channelParam.payCode = payCode;
this.channelParam.payMerchant = row.payMerchant;
},
/**
* 根据渠道编码判断渠道列表中是否存在
* @param channels 渠道列表
* @param channelCode 渠道编码
*/
judgeChannelExist(channels, channelCode) {
return channels.indexOf(channelCode) !== -1;
},
refreshTable() {
this.getList();
}
}
};
</script>

View File

@@ -1,214 +0,0 @@
<template>
<div class="app-container">
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd">发起订单</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="订单编号" align="center" prop="id" />
<el-table-column label="用户编号" align="center" prop="userId" />
<el-table-column label="商品名字" align="center" prop="spuName" />
<el-table-column label="支付价格" align="center" prop="price">
<template v-slot="scope">
<span>{{ (scope.row.price / 100.0).toFixed(2) }}</span>
</template>
</el-table-column>
<el-table-column label="退款金额" align="center" prop="refundPrice">
<template v-slot="scope">
<span>{{ (scope.row.refundPrice / 100.0).toFixed(2) }}</span>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="支付单号" align="center" prop="payOrderId" />
<el-table-column label="是否支付" align="center" prop="payed">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.payed" />
</template>
</el-table-column>
<el-table-column label="支付时间" align="center" prop="payTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.payTime) }}</span>
</template>
</el-table-column>
<el-table-column label="退款时间" align="center" prop="refundTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.refundTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handlePay(scope.row)"
v-if="!scope.row.payed">前往支付</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleRefund(scope.row)"
v-if="scope.row.payed && !scope.row.payRefundId">发起退款</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="title" :visible.sync="open" width="500px" v-dialogDrag append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="商品" prop="spuId">
<el-select v-model="form.spuId" placeholder="请输入下单商品" clearable size="small" style="width: 380px" >
<el-option v-for="item in spus" :key="item.id" :label="item.name" :value="item.id">
<span style="float: left">{{ item.name}}</span>
<span style="float: right; color: #8492a6; font-size: 13px">{{ (item.price / 100.0).toFixed(2) }}</span>
</el-option>
</el-select>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import {createDemoOrder, getDemoOrderPage, refundDemoOrder} from "@/api/pay/demo";
import {deleteMerchant} from "@/api/pay/merchant";
export default {
name: "PayDemoOrder",
components: {
},
data() {
return {
// 遮罩层
loading: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 示例订单列表
list: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
},
// 表单参数
form: {},
// 表单校验
rules: {
spuId: [{ required: true, message: "商品编号不能为空", trigger: "blur" }],
},
// 商品数组
spus: [{
id: 1,
name: '华为手机',
price: 1,
}, {
id: 2,
name: '小米电视',
price: 10,
}, {
id: 3,
name: '苹果手表',
price: 100,
}, {
id: 4,
name: '华硕笔记本',
price: 1000,
}, {
id: 5,
name: '蔚来汽车',
price: 200000,
}]
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
// 执行查询
getDemoOrderPage(this.queryParams).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 取消按钮 */
cancel() {
this.open = false;
this.reset();
},
/** 表单重置 */
reset() {
this.form = {
spuId: undefined,
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "发起订单";
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (!valid) {
return;
}
// 添加的提交
createDemoOrder(this.form).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
});
},
/** 支付按钮操作 */
handlePay(row) {
this.$router.push({
name: 'PayOrderSubmit',
query:{
id: row.payOrderId
}
})
},
/** 退款按钮操作 */
handleRefund(row) {
const id = row.id;
this.$modal.confirm('是否确认退款编号为"' + id + '"的示例订单?').then(function() {
return refundDemoOrder(id);
}).then(() => {
this.getList();
this.$modal.msgSuccess("发起退款成功!");
}).catch(() => {});
}
}
};
</script>

View File

@@ -1,282 +0,0 @@
<template>
<div class="app-container">
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="商户号" prop="no">
<el-input v-model="queryParams.no" placeholder="请输入商户号" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="商户全称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入商户全称" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="商户简称" prop="shortName">
<el-input v-model="queryParams.shortName" placeholder="请输入商户简称" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="开启状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择开启状态" clearable>
<el-option v-for="dict in statusDictDatas" :key="parseInt(dict.value)" :label="dict.label" :value="parseInt(dict.value)"/>
</el-select>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="queryParams.remark" placeholder="请输入备注" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
v-hasPermi="['pay:merchant:create']">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="el-icon-download" size="mini" :loading="exportLoading" @click="handleExport"
v-hasPermi="['pay:merchant:export']">导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="商户编号" align="center" prop="id" />
<el-table-column label="商户号" align="center" prop="no" />
<el-table-column label="商户全称" align="center" prop="name" />
<el-table-column label="商户简称" align="center" prop="shortName" />
<el-table-column label="开启状态" align="center" prop="status" >
<template v-slot="scope">
<el-switch v-model="scope.row.status" :active-value="0" :inactive-value="1" @change="handleStatusChange(scope.row)" />
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['pay:merchant:update']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['pay:merchant:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<!-- <el-form-item label="商户号" prop="no">-->
<!-- <el-input v-model="form.no" placeholder="请输入商户号" />-->
<!-- </el-form-item>-->
<el-form-item label="商户全称" prop="name">
<el-input v-model="form.name" placeholder="请输入商户全称" />
</el-form-item>
<el-form-item label="商户简称" prop="shortName">
<el-input v-model="form.shortName" placeholder="请输入商户简称" />
</el-form-item>
<el-form-item label="开启状态" prop="status">
<el-radio-group v-model="form.status">
<el-radio v-for="dict in statusDictDatas" :key="parseInt(dict.value)" :label="parseInt(dict.value)">
{{dict.label}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" placeholder="请输入备注" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import {
createMerchant,
updateMerchant,
changeMerchantStatus,
deleteMerchant,
getMerchant,
getMerchantPage,
exportMerchantExcel
} from "@/api/pay/merchant";
import {DICT_TYPE, getDictDatas} from "@/utils/dict";
import {CommonStatusEnum} from "@/utils/constants";
export default {
name: "PayMerchant",
components: {
},
data() {
return {
// 遮罩层
loading: true,
// 导出遮罩层
exportLoading: false,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 支付商户信息列表
list: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
no: null,
name: null,
shortName: null,
status: null,
remark: null,
createTime: []
},
// 表单参数
form: {},
// 表单校验
rules: {
no: [{ required: true, message: "商户号不能为空", trigger: "blur" }],
name: [{ required: true, message: "商户全称不能为空", trigger: "blur" }],
shortName: [{ required: true, message: "商户简称不能为空", trigger: "blur" }],
status: [{ required: true, message: "开启状态不能为空", trigger: "blur" }],
},
// 商户状态数据字典
statusDictDatas: getDictDatas(DICT_TYPE.COMMON_STATUS)
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
// 执行查询
getMerchantPage(this.queryParams).then(response => {
console.log(response.data);
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 取消按钮 */
cancel() {
this.open = false;
this.reset();
},
/** 表单重置 */
reset() {
this.form = {
id: undefined,
no: undefined,
name: undefined,
shortName: undefined,
status: undefined,
remark: undefined,
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加支付商户信息";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
getMerchant(row.id).then(response => {
this.form = response.data;
this.open = true;
this.title = "修改支付商户信息";
});
},
// 用户状态修改
handleStatusChange(row) {
let text = row.status === CommonStatusEnum.ENABLE ? "启用" : "停用";
this.$modal.confirm('确认要"' + text + '""' + row.name + '"商户吗?').then(function() {
return changeMerchantStatus(row.id, row.status);
}).then(() => {
this.$modal.msgSuccess(text + "成功");
}).catch(function() {
row.status = row.status === CommonStatusEnum.ENABLE ? CommonStatusEnum.DISABLE
: CommonStatusEnum.ENABLE;
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (!valid) {
return;
}
// 修改的提交
if (this.form.id != null) {
updateMerchant(this.form).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
return;
}
// 添加的提交
createMerchant(this.form).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
});
},
/** 删除按钮操作 */
handleDelete(row) {
const id = row.id;
this.$modal.confirm('是否确认删除支付商户信息编号为"' + id + '"的数据项?').then(function() {
return deleteMerchant(id);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
},
/** 导出按钮操作 */
handleExport() {
// 处理查询参数
let params = {...this.queryParams};
params.pageNo = undefined;
params.pageSize = undefined;
// 执行导出
this.$modal.confirm('是否确认导出所有支付商户信息数据项?').then(() => {
this.exportLoading = true;
return exportMerchantExcel(params);
}).then(response => {
this.$download.excel(response, '支付商户信息.xls');
this.exportLoading = false;
}).catch(() => {});
}
}
};
</script>

View File

@@ -1,439 +0,0 @@
<template>
<div class="app-container">
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
<el-form-item label="所属商户" prop="merchantId">
<el-select v-model="queryParams.merchantId" clearable @clear="()=>{queryParams.merchantId = null}"
filterable remote reserve-keyword placeholder="请选择所属商户" @change="handleGetAppListByMerchantId"
:remote-method="handleGetMerchantListByName" :loading="merchantLoading">
<el-option v-for="item in merchantList" :key="item.id" :label="item.name" :value="item.id"/>
</el-select>
</el-form-item>
<el-form-item label="应用编号" prop="appId">
<el-select clearable v-model="queryParams.appId" filterable placeholder="请选择应用信息">
<el-option v-for="item in appList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="渠道编码" prop="channelCode">
<el-select v-model="queryParams.channelCode" placeholder="请输入渠道编码" clearable
@clear="()=>{queryParams.channelCode = null}">
<el-option v-for="dict in payChannelCodeDictDatum" :key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="商户订单编号" prop="merchantOrderId">
<el-input v-model="queryParams.merchantOrderId" placeholder="请输入商户订单编号" clearable
@keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="渠道订单号" prop="channelOrderNo">
<el-input v-model="queryParams.channelOrderNo" placeholder="请输入渠道订单号" clearable
@keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="支付状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择支付状态" clearable size="small">
<el-option v-for="dict in payOrderDictDatum" :key="parseInt(dict.value)"
:label="dict.label" :value="parseInt(dict.value)"/>
</el-select>
</el-form-item>
<el-form-item label="退款状态" prop="refundStatus">
<el-select v-model="queryParams.refundStatus" placeholder="请选择退款状态" clearable>
<el-option v-for="dict in payOrderRefundDictDatum" :key="parseInt(dict.value)"
:label="dict.label" :value="parseInt(dict.value)"/>
</el-select>
</el-form-item>
<el-form-item label="回调商户状态" prop="notifyStatus">
<el-select v-model="queryParams.notifyStatus" placeholder="请选择订单回调商户状态" clearable>
<el-option v-for="dict in payOrderNotifyDictDatum" :key="parseInt(dict.value)"
:label="dict.label" :value="parseInt(dict.value)"/>
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport"
v-hasPermi="['pay:order:export']">导出
</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="订单编号" align="center" prop="id" width="80"/>
<!-- <el-table-column label="商户名称" align="center" prop="merchantName" width="120"/>-->
<!-- <el-table-column label="应用名称" align="center" prop="appName" width="120"/>-->
<el-table-column label="支付渠道" align="center" width="130">
<template v-slot="scope">
<el-popover trigger="hover" placement="top">
<p>商户名称: {{ scope.row.merchantName }}</p>
<p>应用名称: {{ scope.row.appName }}</p>
<p>渠道名称: {{ scope.row.channelCodeName }}</p>
<div slot="reference" class="name-wrapper">
{{ scope.row.channelCodeName }}
</div>
</el-popover>
</template>
</el-table-column>
<el-table-column label="支付订单" align="left" width="280">
<template v-slot="scope">
<p class="order-font"><el-tag size="mini">商户</el-tag> {{scope.row.merchantOrderId}}</p>
<p class="order-font"><el-tag size="mini" type="warning">支付</el-tag> {{scope.row.channelOrderNo}}</p>
</template>
</el-table-column>
<!-- <el-table-column label="商户订单编号" align="center" prop="merchantOrderId" width="140"/>-->
<!-- <el-table-column label="渠道订单号" align="center" prop="channelOrderNo" width="140"/>-->
<el-table-column label="商品标题" align="center" prop="subject" width="180" :show-overflow-tooltip="true"/>
<el-table-column label="支付金额" align="center" prop="amount" width="100">
<template v-slot="scope">
{{ parseFloat(scope.row.amount / 100).toFixed(2) }}
</template>
</el-table-column>
<el-table-column label="手续金额" align="center" prop="channelFeeAmount" width="100">
<template v-slot="scope">
{{ parseFloat(scope.row.channelFeeAmount / 100).toFixed(2) }}
</template>
</el-table-column>
<el-table-column label="退款金额" align="center" prop="refundAmount" width="100">
<template v-slot="scope">
{{ parseFloat(scope.row.refundAmount / 100).toFixed(2) }}
</template>
</el-table-column>
<el-table-column label="支付状态" align="center" prop="status">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.PAY_ORDER_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
<!-- <el-table-column label="退款状态" align="center" prop="refundStatus">-->
<!-- <template v-slot="scope">-->
<!-- <span>{{ getDictDataLabel(DICT_TYPE.PAY_ORDER_REFUND_STATUS, scope.row.refundStatus) }}</span>-->
<!-- </template>-->
<!-- </el-table-column>-->
<el-table-column label="回调状态" align="center" prop="notifyStatus" width="100">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.PAY_ORDER_NOTIFY_STATUS" :value="scope.row.notifyStatus" />
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="100">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="支付时间" align="center" prop="successTime" width="100">
<template v-slot="scope">
<span>{{ parseTime(scope.row.successTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-search" @click="handleQueryDetails(scope.row)"
v-hasPermi="['pay:order:query']">查看详情
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- 对话框(添加 / 修改) -->
<el-dialog title="订单详情" :visible.sync="open" width="50%">
<el-descriptions :column="2" label-class-name="desc-label">
<el-descriptions-item label="商户名称">{{ orderDetail.merchantName }}</el-descriptions-item>
<el-descriptions-item label="应用名称">{{ orderDetail.appName }}</el-descriptions-item>
<el-descriptions-item label="商品名称">{{ orderDetail.subject }}</el-descriptions-item>
</el-descriptions>
<el-divider></el-divider>
<el-descriptions :column="2" label-class-name="desc-label">
<el-descriptions-item label="商户订单号">
<el-tag size="small">{{ orderDetail.merchantOrderId }}</el-tag>
</el-descriptions-item>
<el-descriptions-item label="渠道订单号">
<el-tag class="tag-purple" size="small">{{ orderDetail.channelOrderNo }}</el-tag>
</el-descriptions-item>
<el-descriptions-item label="支付订单号">
<el-tag v-if="orderDetail.payOrderExtension.no !== ''" class="tag-pink" size="small">
{{ orderDetail.payOrderExtension.no }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="金额">
<el-tag type="success" size="small">{{ parseFloat(orderDetail.amount / 100, 2) }}</el-tag>
</el-descriptions-item>
<el-descriptions-item label="手续费">
<el-tag type="warning" size="small">{{ parseFloat(orderDetail.channelFeeAmount / 100, 2) }}</el-tag>
</el-descriptions-item>
<el-descriptions-item label="手续费比例">
{{ parseFloat(orderDetail.channelFeeRate / 100, 2) }}%
</el-descriptions-item>
<el-descriptions-item label="支付状态">
<dict-tag :type="DICT_TYPE.PAY_ORDER_STATUS" :value="orderDetail.status" />
</el-descriptions-item>
<el-descriptions-item label="回调状态">
<dict-tag :type="DICT_TYPE.PAY_ORDER_NOTIFY_STATUS" :value="orderDetail.notifyStatus" />
</el-descriptions-item>
<el-descriptions-item label="回调地址">{{ orderDetail.notifyUrl }}</el-descriptions-item>
<el-descriptions-item label="创建时间">{{ parseTime(orderDetail.createTime) }}</el-descriptions-item>
<el-descriptions-item label="支付时间">{{ parseTime(orderDetail.successTime) }}</el-descriptions-item>
<el-descriptions-item label="失效时间">{{ parseTime(orderDetail.expireTime) }}</el-descriptions-item>
<el-descriptions-item label="通知时间">{{ parseTime(orderDetail.notifyTime) }}</el-descriptions-item>
</el-descriptions>
<el-divider></el-divider>
<el-descriptions :column="2" label-class-name="desc-label">
<el-descriptions-item label="支付渠道">{{ orderDetail.channelCodeName }}</el-descriptions-item>
<el-descriptions-item label="支付IP">{{ orderDetail.userIp }}</el-descriptions-item>
<el-descriptions-item label="退款状态">
<dict-tag :type="DICT_TYPE.PAY_ORDER_REFUND_STATUS" :value="orderDetail.refundStatus" />
</el-descriptions-item>
<el-descriptions-item label="退款次数">{{ orderDetail.refundTimes }}</el-descriptions-item>
<el-descriptions-item label="退款金额">
<el-tag type="warning">
{{ parseFloat(orderDetail.refundAmount / 100, 2) }}
</el-tag>
</el-descriptions-item>
</el-descriptions>
<el-divider></el-divider>
<el-descriptions :column="1" label-class-name="desc-label" direction="vertical" border>
<el-descriptions-item label="商品描述">
{{ orderDetail.body }}
</el-descriptions-item>
<el-descriptions-item label="支付通道异步回调内容">
{{ orderDetail.payOrderExtension.channelNotifyData }}
</el-descriptions-item>
</el-descriptions>
</el-dialog>
</div>
</template>
<script>
import { getOrderDetail, getOrderPage, exportOrderExcel} from "@/api/pay/order";
import {getMerchantListByName} from "@/api/pay/merchant";
import {getAppListByMerchantId} from "@/api/pay/app";
import {DICT_TYPE, getDictDatas} from "@/utils/dict";
import { getNowDateTime} from "@/utils/ruoyi";
const defaultOrderDetail = {
merchantName: '',
appName: '',
channelCodeName: '',
subject: '',
merchantOrderId: null,
channelOrderNo: '',
body: '',
amount: null,
channelFeeRate: null,
channelFeeAmount: null,
userIp: '',
status: null,
notifyUrl: '',
notifyStatus: null,
refundStatus: null,
refundTimes: '',
refundAmount: null,
createTime: '',
successTime: '',
notifyTime: '',
expireTime: '',
payOrderExtension: {
channelNotifyData: '',
no: ''
}
};
export default {
name: "PayOrder",
components: {},
data() {
return {
// 遮罩层
loading: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 支付订单列表
list: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
merchantId: null,
appId: null,
channelId: null,
channelCode: null,
merchantOrderId: null,
subject: null,
body: null,
notifyUrl: null,
notifyStatus: null,
amount: null,
channelFeeRate: null,
channelFeeAmount: null,
status: null,
userIp: null,
successExtensionId: null,
refundStatus: null,
refundTimes: null,
refundAmount: null,
channelUserId: null,
channelOrderNo: null,
expireTime: [],
successTime: [],
notifyTime: [],
createTime: []
},
// 商户加载遮罩层
merchantLoading: false,
// 商户列表集合
merchantList: null,
// 支付应用列表集合
appList: null,
// 支付渠道编码字典数据集合
payChannelCodeDictDatum: getDictDatas(DICT_TYPE.PAY_CHANNEL_CODE_TYPE),
// 订单回调商户状态字典数据集合
payOrderNotifyDictDatum: getDictDatas(DICT_TYPE.PAY_ORDER_NOTIFY_STATUS),
// 订单状态字典数据集合
payOrderDictDatum: getDictDatas(DICT_TYPE.PAY_ORDER_STATUS),
// 订单退款状态字典数据集合
payOrderRefundDictDatum: getDictDatas(DICT_TYPE.PAY_ORDER_REFUND_STATUS),
orderDetail: JSON.parse(JSON.stringify(defaultOrderDetail)),
};
},
created() {
// 初始化默认时间
this.initTime();
this.getList();
this.handleGetMerchantListByName(null);
},
methods: {
initTime(){
this.queryParams.createTime = [getNowDateTime("00:00:00"), getNowDateTime("23:59:59")];
},
/** 查询列表 */
getList() {
// 判断选择的日期是否超过了一个月
let oneMonthTime = 31 * 24 * 3600 * 1000;
if (this.queryParams.createTime == null){
this.initTime();
} else {
let minDateTime = new Date(this.queryParams.createTime[0]).getTime();
let maxDateTime = new Date(this.queryParams.createTime[1]).getTime()
if (maxDateTime - minDateTime > oneMonthTime) {
this.$message.error('时间范围最大为 31 天!');
return false;
}
}
this.loading = true;
// 执行查询
getOrderPage(this.queryParams).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 取消按钮 */
cancel() {
this.open = false;
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.initTime();
this.handleQuery();
},
/**
* 查看订单详情
*/
handleQueryDetails(row) {
this.orderDetail = JSON.parse(JSON.stringify(defaultOrderDetail));
getOrderDetail(row.id).then(response => {
this.orderDetail = response.data;
if (response.data.payOrderExtension === null) {
this.orderDetail.payOrderExtension = Object.assign(defaultOrderDetail.payOrderExtension, {});
}
this.open = true;
});
},
/** 导出按钮操作 */
handleExport() {
// 处理查询参数
let params = {...this.queryParams};
params.pageNo = undefined;
params.pageSize = undefined;
// 执行导出
this.$modal.confirm('是否确认导出所有支付订单数据项?').then(function () {
return exportOrderExcel(params);
}).then(response => {
this.$download.excel(response, '支付订单.xls');
}).catch(() => {});
},
/**
* 根据商户名称模糊匹配商户信息
* @param name 商户名称
*/
handleGetMerchantListByName(name) {
getMerchantListByName(name).then(response => {
this.merchantList = response.data;
this.merchantLoading = false;
});
},
/**
* 根据商户 ID 查询支付应用信息
*/
handleGetAppListByMerchantId() {
this.queryParams.appId = null;
getAppListByMerchantId(this.queryParams.merchantId).then(response => {
this.appList = response.data;
});
}
}
};
</script>
<style>
.desc-label {
font-weight: bold;
}
.tag-purple {
color: #722ed1;
background: #f9f0ff;
border-color: #d3adf7;
}
.tag-cyan {
color: #13c2c2;
background: #e6fffb;
border-color: #87e8de;
}
.tag-pink {
color: #eb2f96;
background: #fff0f6;
border-color: #ffadd2;
}
.order-font{
font-size: 12px;
padding: 2px 0;
}
</style>

View File

@@ -1,397 +0,0 @@
<template>
<div class="app-container">
<!-- 支付信息 -->
<el-card v-loading="loading">
<el-descriptions title="支付信息" :column="3" border>
<el-descriptions-item label="支付单号">{{ payOrder.id }}</el-descriptions-item>
<el-descriptions-item label="商品标题">{{ payOrder.subject }}</el-descriptions-item>
<el-descriptions-item label="商品内容">{{ payOrder.body }}</el-descriptions-item>
<el-descriptions-item label="支付金额">{{ (payOrder.amount / 100.0).toFixed(2) }}</el-descriptions-item>
<el-descriptions-item label="创建时间">{{ parseTime(payOrder.createTime) }}</el-descriptions-item>
<el-descriptions-item label="过期时间">{{ parseTime(payOrder.expireTime) }}</el-descriptions-item>
</el-descriptions>
</el-card>
<!-- 支付选择框 -->
<el-card style="margin-top: 10px" v-loading="submitLoading" element-loading-text="提交支付中...">
<!-- 支付宝 -->
<el-descriptions title="选择支付宝支付">
</el-descriptions>
<div class="pay-channel-container">
<div class="box" v-for="channel in aliPayChannels" :key="channel.code" @click="submit(channel.code)">
<img :src="icons[channel.code]">
<div class="title">{{ channel.name }}</div>
</div>
</div>
<!-- 微信支付 -->
<el-descriptions title="选择微信支付" style="margin-top: 20px;" />
<div class="pay-channel-container">
<div class="box" v-for="channel in wxPayChannels" :key="channel.code">
<img :src="icons[channel.code]">
<div class="title">{{ channel.name }}</div>
</div>
</div>
<!-- 其它支付 -->
<el-descriptions title="选择其它支付" style="margin-top: 20px;" />
<div class="pay-channel-container">
<div class="box" v-for="channel in otherPayChannels" :key="channel.code">
<img :src="icons[channel.code]">
<div class="title">{{ channel.name }}</div>
</div>
</div>
</el-card>
<!-- 展示形式二维码 URL -->
<el-dialog :title="qrCode.title" :visible.sync="qrCode.visible" width="350px" append-to-body
:close-on-press-escape="false">
<qrcode-vue :value="qrCode.url" size="310" level="L" />
</el-dialog>
<!-- 展示形式IFrame -->
<el-dialog :title="iframe.title" :visible.sync="iframe.visible" width="800px" height="800px" append-to-body
:close-on-press-escape="false">
<iframe :src="iframe.url" width="100%" />
</el-dialog>
<!-- 展示形式Form -->
<div ref="formRef" v-html="form.value" />
<!-- 展示形式BarCode 条形码 -->
<el-dialog :title="barCode.title" :visible.sync="barCode.visible" width="500px" append-to-body
:close-on-press-escape="false">
<el-form ref="form" label-width="80px">
<el-row>
<el-col :span="24">
<el-form-item label="条形码" prop="name">
<el-input v-model="barCode.value" placeholder="请输入条形码" required />
</el-form-item>
</el-col>
<el-col :span="24">
<div style="text-align: right">
或使用
<el-link type="danger" target="_blank"
href="https://baike.baidu.com/item/条码支付/10711903">(扫码枪/扫码盒)</el-link>
扫码
</div>
</el-col>
</el-row>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submit0(barCode.channelCode)"
:disabled="barCode.value.length === 0">确认支付</el-button>
<el-button @click="barCode.visible = false"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import QrcodeVue from 'qrcode.vue'
import { DICT_TYPE, getDictDatas } from "@/utils/dict";
import { getOrder, submitOrder } from '@/api/pay/order';
import {PayChannelEnum, PayDisplayModeEnum, PayOrderStatusEnum} from "@/utils/constants";
export default {
name: "PayOrderSubmit",
components: {
QrcodeVue,
},
data() {
return {
id: undefined, // 请假编号
loading: false, // 支付信息的 loading
payOrder: {}, // 支付信息
aliPayChannels: [], // 阿里支付的渠道
wxPayChannels: [], // 微信支付的渠道
otherPayChannels: [], // 其它的支付渠道
icons: {
alipay_qr: require("@/assets/images/pay/icon/alipay_qr.svg"),
alipay_app: require("@/assets/images/pay/icon/alipay_app.svg"),
alipay_wap: require("@/assets/images/pay/icon/alipay_wap.svg"),
alipay_pc: require("@/assets/images/pay/icon/alipay_pc.svg"),
alipay_bar: require("@/assets/images/pay/icon/alipay_bar.svg"),
wx_app: require("@/assets/images/pay/icon/wx_app.svg"),
wx_lite: require("@/assets/images/pay/icon/wx_lite.svg"),
wx_pub: require("@/assets/images/pay/icon/wx_pub.svg"),
mock: require("@/assets/images/pay/icon/mock.svg"),
},
submitLoading: false, // 提交支付的 loading
interval: undefined, // 定时任务,轮询是否完成支付
qrCode: { // 展示形式:二维码
url: '',
title: '',
visible: false,
},
iframe: { // 展示形式iframe
url: '',
title: '',
visible: false
},
form: { // 展示形式form
html: '',
},
barCode: { // 展示形式:条形码
channelCode: '',
value: '',
title: '',
visible: false,
},
};
},
created() {
this.id = this.$route.query.id;
this.getDetail();
this.initPayChannels();
},
methods: {
/** 初始化支付渠道 */
initPayChannels() {
// 微信支付
for (const dict of getDictDatas(DICT_TYPE.PAY_CHANNEL_CODE_TYPE)) {
const payChannel = {
name: dict.label,
code: dict.value
}
if (dict.value.indexOf('wx_') === 0) {
this.wxPayChannels.push(payChannel);
} else if (dict.value.indexOf('alipay_') === 0) {
this.aliPayChannels.push(payChannel);
} else {
this.otherPayChannels.push(payChannel);
}
}
},
/** 获得支付信息 */
getDetail() {
// 1.1 未传递订单编号
if (!this.id) {
this.$message.error('未传递支付单号,无法查看对应的支付信息');
this.goBackToList();
return;
}
getOrder(this.id).then(response => {
// 1.2 无法查询到支付信息
if (!response.data) {
this.$message.error('支付订单不存在,请检查!');
this.goBackToList();
return;
}
// 1.3 订单已支付
if (response.data.status !== PayOrderStatusEnum.WAITING.status) {
this.$message.error('支付订单不处于待支付状态,请检查!');
this.goBackToList();
return;
}
// 2. 可以展示
this.payOrder = response.data;
});
},
/** 提交支付 */
submit(channelCode) {
// 条形码支付,需要特殊处理
if (channelCode === PayChannelEnum.ALIPAY_BAR.code) {
this.barCode = {
channelCode: channelCode,
value: '',
title: '“支付宝”条码支付',
visible: true
}
return;
}
// 默认的提交处理
this.submit0(channelCode)
},
submit0(channelCode) {
this.submitLoading = true
submitOrder({
id: this.id,
channelCode: channelCode,
...this.buildSubmitParam(channelCode)
}).then(response => {
const data = response.data
if (data.displayMode === PayDisplayModeEnum.IFRAME.mode) {
this.displayIFrame(channelCode, data)
} else if (data.displayMode === PayDisplayModeEnum.URL.mode) {
this.displayUrl(channelCode, data)
} else if (data.displayMode === PayDisplayModeEnum.FORM.mode) {
this.displayForm(channelCode, data)
} else if (data.displayMode === PayDisplayModeEnum.QR_CODE.mode) {
this.displayQrCode(channelCode, data)
}
// 打开轮询任务
this.createQueryInterval()
}).catch(() => {
this.submitLoading = false
});
},
/** 构建提交支付的额外参数 */
buildSubmitParam(channelCode) {
// ① 支付宝 PC 支付时,有多种展示形态
if (channelCode === PayChannelEnum.ALIPAY_PC.code) {
// 情况【前置模式】:将二维码前置到商户的订单确认页的模式。需要商户在自己的页面中以 iframe 方式请求支付宝页面。具体支持的枚举值有以下几种:
// 0订单码-简约前置模式,对应 iframe 宽度不能小于 600px高度不能小于 300px
// return {
// "channelExtras": {
// "qr_pay_mode": "0"
// }
// }
// 1订单码-前置模式对应iframe 宽度不能小于 300px高度不能小于 600px
// return {
// "channelExtras": {
// "qr_pay_mode": "1"
// }
// }
// 3订单码-迷你前置模式,对应 iframe 宽度不能小于 75px高度不能小于 75px
// return {
// "channelExtras": {
// "qr_pay_mode": "3"
// }
// }
// 4订单码-可定义宽度的嵌入式二维码,商户可根据需要设定二维码的大小
// return {
// "channelExtras": {
// "qr_pay_mode": "4"
// }
// }
// 情况【跳转模式】:跳转模式下,用户的扫码界面是由支付宝生成的,不在商户的域名下。支持传入的枚举值有
return {
"channelExtras": {
"qr_pay_mode": "2"
}
}
// 情况【表单模式】:直接提交当前页面到支付宝
// return {
// displayMode: PayDisplayModeEnum.FORM.mode
// }
}
// ② 支付宝 Wap 支付时,引导手机扫码支付
if (channelCode === PayChannelEnum.ALIPAY_WAP.code) {
return {
displayMode: PayDisplayModeEnum.QR_CODE.mode
}
}
// ③ 支付宝 BarCode 支付时,需要传递 authCode 条形码
if (channelCode === PayChannelEnum.ALIPAY_BAR.code) {
return {
"channelExtras": {
"auth_code": this.barCode.value
}
}
}
return {}
},
/** 提交支付后IFrame 内置 URL 的展示形式 */
displayIFrame(channelCode, data) {
// TODO 芋艿:目前有点奇怪,支付宝总是会显示“支付环境存在风险”
this.iframe = {
title: '支付窗口',
url: data.displayContent,
visible: true
}
this.submitLoading = false
},
/** 提交支付后URL 的展示形式 */
displayUrl(channelCode, data) {
window.open(data.displayContent)
this.submitLoading = false
},
/** 提交支付后Form 的展示形式 */
displayForm(channelCode, data) {
// 渲染支付页面
this.form = {
value: data.displayContent
}
// 防抖避免重复支付
this.$nextTick(() => {
// 提交支付表单
this.$refs.formRef.children[0].submit();
setTimeout(() => {
this.submitLoading = false
}, 1000);
});
},
/** 提交支付后(支付宝扫码支付) */
displayQrCode(channelCode, data) {
let title = '请使用手机浏览器“扫一扫”';
if (channelCode === PayChannelEnum.ALIPAY_WAP.code) {
// 考虑到 WAP 测试,所以引导手机浏览器搞
} else if (channelCode.indexOf('alipay_') === 0) {
title = '请使用支付宝“扫一扫”扫码支付';
} else if (channelCode.indexOf('wx_') === 0) {
title = '请使用微信“扫一扫”扫码支付';
}
this.qrCode = {
title: title,
url: data.displayContent,
visible: true
}
this.submitLoading = false
},
/** 轮询查询任务 */
createQueryInterval() {
if (this.interval) {
return
}
this.interval = setInterval(() => {
getOrder(this.id).then(response => {
// 已支付
if (response.data.status === PayOrderStatusEnum.SUCCESS.status) {
this.clearQueryInterval();
this.$message.success('支付成功!');
this.goBackToList();
}
// 已取消
if (response.data.status === PayOrderStatusEnum.CLOSED.status) {
this.clearQueryInterval();
this.$message.error('支付已关闭!');
this.goBackToList();
}
})
}, 1000 * 2)
},
/** 清空查询任务 */
clearQueryInterval() {
// 清空各种弹窗
this.qrCode = {
title: '',
url: '',
visible: false
}
// 清空任务
clearInterval(this.interval)
this.interval = undefined
},
/** 回到列表 **/
goBackToList() {
this.$tab.closePage();
this.$router.go(-1);
}
}
};
</script>
<style lang="scss" scoped>
.pay-channel-container {
display: flex;
margin-top: -10px;
.box {
width: 130px;
border: 1px solid #e6ebf5;
cursor: pointer;
text-align: center;
padding-top: 10px;
padding-bottom: 5px;
margin-right: 10px;
img {
width: 40px;
height: 40px;
}
.title {
padding-top: 5px
}
}
}
</style>

View File

@@ -1,486 +0,0 @@
<template>
<div class="app-container">
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
<el-form-item label="所属商户" prop="merchantId">
<el-select v-model="queryParams.merchantId" clearable @clear="()=>{queryParams.merchantId = null}"
filterable remote reserve-keyword placeholder="请选择所属商户" @change="handleGetAppListByMerchantId"
:remote-method="handleGetMerchantListByName" :loading="merchantLoading">
<el-option v-for="item in merchantList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="应用编号" prop="appId">
<el-select clearable v-model="queryParams.appId" filterable placeholder="请选择应用信息">
<el-option v-for="item in appList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="渠道编码" prop="channelCode">
<el-select v-model="queryParams.channelCode" placeholder="请输入渠道编码" clearable
@clear="()=>{queryParams.channelCode = null}">
<el-option v-for="dict in payChannelCodeDictDatum" :key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="退款类型" prop="type">
<el-select v-model="queryParams.type" placeholder="请选择退款类型" clearable>
<el-option v-for="dict in payRefundOrderTypeDictDatum" :key="parseInt(dict.value)"
:label="dict.label" :value="parseInt(dict.value)"/>
</el-select>
</el-form-item>
<el-form-item label="商户退款订单号" prop="merchantRefundNo">
<el-input v-model="queryParams.merchantRefundNo" placeholder="请输入商户退款订单号" clearable
@keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="退款状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择退款状态" clearable>
<el-option v-for="dict in payRefundOrderDictDatum" :key="parseInt(dict.value)"
:label="dict.label" :value="parseInt(dict.value)"/>
</el-select>
</el-form-item>
<el-form-item label="退款回调状态" prop="notifyStatus">
<el-select v-model="queryParams.notifyStatus" placeholder="请选择通知商户退款结果的回调状态" clearable>
<el-option v-for="dict in payOrderNotifyDictDatum" :key="parseInt(dict.value)"
:label="dict.label" :value="parseInt(dict.value)"/>
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport"
v-hasPermi="['pay:refund:export']">导出
</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="编号" align="center" prop="id"/>
<!-- <el-table-column label="商户名称" align="center" prop="merchantName" width="120"/>-->
<!-- <el-table-column label="应用名称" align="center" prop="appName" width="120"/>-->
<el-table-column label="支付渠道" align="center" width="130">
<template v-slot="scope">
<el-popover trigger="hover" placement="top">
<p>商户名称: {{ scope.row.merchantName }}</p>
<p>应用名称: {{ scope.row.appName }}</p>
<p>渠道名称: {{ scope.row.channelCodeName }}</p>
<div slot="reference" class="name-wrapper">
{{ scope.row.channelCodeName }}
</div>
</el-popover>
</template>
</el-table-column>
<!-- <el-table-column label="交易订单号" align="center" prop="tradeNo" width="140"/>-->
<!-- <el-table-column label="商户订单编号" align="center" prop="merchantOrderId" width="140"/>-->
<el-table-column label="商户订单号" align="left" width="230">
<template v-slot="scope">
<p class="order-font">
<el-tag size="mini">退款</el-tag>
{{ scope.row.merchantRefundNo }}
</p>
<p class="order-font">
<el-tag type="success">交易</el-tag>
{{ scope.row.merchantOrderId }}
</p>
</template>
</el-table-column>
<el-table-column label="支付订单号" align="center" prop="merchantRefundNo" width="250">
<template v-slot="scope">
<p class="order-font">
<el-tag size="mini">交易</el-tag>
{{ scope.row.tradeNo }}
</p>
<p class="order-font">
<el-tag size="mini" type="warning">渠道</el-tag>
{{ scope.row.channelOrderNo }}
</p>
</template>
</el-table-column>
<el-table-column label="支付金额(元)" align="center" prop="payAmount" width="100">
<template v-slot="scope" class="">
{{ parseFloat(scope.row.payAmount / 100).toFixed(2) }}
</template>
</el-table-column>
<el-table-column label="退款金额(元)" align="center" prop="refundAmount" width="100">
<template v-slot="scope">
{{ parseFloat(scope.row.refundAmount / 100).toFixed(2) }}
</template>
</el-table-column>
<el-table-column label="退款类型" align="center" prop="type" width="80">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.PAY_REFUND_ORDER_TYPE" :value="scope.row.type" />
</template>
</el-table-column>
<el-table-column label="退款状态" align="center" prop="status">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.PAY_REFUND_ORDER_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="回调状态" align="center" prop="notifyStatus">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.PAY_ORDER_NOTIFY_STATUS" :value="scope.row.notifyStatus" />
</template>
</el-table-column>
<el-table-column label="退款原因" align="center" prop="reason" width="140" :show-overflow-tooltip="true"/>
<el-table-column label="创建时间" align="center" prop="createTime" width="100">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="退款成功时间" align="center" prop="successTime" width="100">
<template v-slot="scope">
<span>{{ parseTime(scope.row.successTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-search" @click="handleQueryDetails(scope.row)"
v-hasPermi="['pay:order:query']">查看详情
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- 对话框(添加 / 修改) -->
<el-dialog title="退款订单详情" :visible.sync="open" width="700px" append-to-body>
<el-descriptions :column="2" label-class-name="desc-label">
<el-descriptions-item label="商户名称">{{ refundDetail.merchantName }}</el-descriptions-item>
<el-descriptions-item label="应用名称">{{ refundDetail.appName }}</el-descriptions-item>
<el-descriptions-item label="商品名称">{{ refundDetail.subject }}</el-descriptions-item>
</el-descriptions>
<el-divider></el-divider>
<el-descriptions :column="2" label-class-name="desc-label">
<el-descriptions-item label="商户退款单号">
<el-tag size="mini">{{ refundDetail.merchantRefundNo }}</el-tag>
</el-descriptions-item>
<el-descriptions-item label="商户订单号">{{ refundDetail.merchantOrderId }}</el-descriptions-item>
<el-descriptions-item label="交易订单号">{{ refundDetail.tradeNo }}</el-descriptions-item>
</el-descriptions>
<el-divider></el-divider>
<el-descriptions :column="2" label-class-name="desc-label">
<el-descriptions-item label="支付金额">
{{ parseFloat(refundDetail.payAmount / 100).toFixed(2) }}
</el-descriptions-item>
<el-descriptions-item label="退款金额" size="mini">
<el-tag class="tag-purple" size="mini">{{ parseFloat(refundDetail.refundAmount / 100).toFixed(2) }}</el-tag>
</el-descriptions-item>
<el-descriptions-item label="退款类型">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.PAY_REFUND_ORDER_TYPE" :value="refundDetail.type" />
</template>
</el-descriptions-item>
<el-descriptions-item label="退款状态">
<dict-tag :type="DICT_TYPE.PAY_REFUND_ORDER_STATUS" :value="refundDetail.status" />
</el-descriptions-item>
<el-descriptions-item label="创建时间">{{ parseTime(refundDetail.createTime) }}</el-descriptions-item>
<el-descriptions-item label="退款成功时间">{{ parseTime(refundDetail.successTime) }}</el-descriptions-item>
<el-descriptions-item label="退款失效时间">{{ parseTime(refundDetail.expireTime) }}</el-descriptions-item>
<el-descriptions-item label="更新时间">{{ parseTime(refundDetail.updateTime) }}</el-descriptions-item>
</el-descriptions>
<el-divider></el-divider>
<el-descriptions :column="2" label-class-name="desc-label">
<el-descriptions-item label="支付渠道">
{{ refundDetail.channelCodeName }}
</el-descriptions-item>
<el-descriptions-item label="支付IP" size="mini">
{{refundDetail.userIp}}
</el-descriptions-item>
<el-descriptions-item label="回调地址">{{ refundDetail.notifyUrl }}</el-descriptions-item>
<el-descriptions-item label="回调状态">
<dict-tag :type="DICT_TYPE.PAY_ORDER_NOTIFY_STATUS" :value="refundDetail.notifyStatus" />
</el-descriptions-item>
<el-descriptions-item label="回调时间">{{ parseTime(refundDetail.notifyTime) }}</el-descriptions-item>
</el-descriptions>
<el-divider></el-divider>
<el-descriptions :column="2" label-class-name="desc-label">
<el-descriptions-item label="渠道订单号">{{ refundDetail.channelOrderNo }}</el-descriptions-item>
<el-descriptions-item label="渠道退款单号">{{ refundDetail.channelRefundNo }}</el-descriptions-item>
<el-descriptions-item label="渠道错误码">{{refundDetail.channelErrorCode}}</el-descriptions-item>
<el-descriptions-item label="渠道错误码描述">{{refundDetail.channelErrorMsg}}</el-descriptions-item>
</el-descriptions>
<br>
<el-descriptions :column="1" label-class-name="desc-label" direction="vertical" border>
<el-descriptions-item label="渠道额外参数">{{ refundDetail.channelExtras }}</el-descriptions-item>
<el-descriptions-item label="退款原因">{{ refundDetail.reason }}</el-descriptions-item>
</el-descriptions>
</el-dialog>
</div>
</template>
<script>
import {getRefundPage, exportRefundExcel, getRefund} from "@/api/pay/refund";
import {getMerchantListByName} from "@/api/pay/merchant";
import {getAppListByMerchantId} from "@/api/pay/app";
import {DICT_TYPE, getDictDatas} from "@/utils/dict";
import {
PayOrderRefundStatusEnum,
PayRefundStatusEnum
} from "@/utils/constants";
import {getNowDateTime} from "@/utils/ruoyi";
const defaultRefundDetail = {
id: null,
appId: null,
appName: '',
channelCode: '',
channelCodeName: '',
channelErrorCode: '',
channelErrorMsg: '',
channelExtras: '',
channelId: null,
channelOrderNo: '',
channelRefundNo: '',
createTime: null,
expireTime: null,
merchantId: null,
merchantName: '',
merchantOrderId: '',
merchantRefundNo: '',
notifyStatus: null,
notifyTime: null,
notifyUrl: '',
orderId: null,
payAmount: null,
reason: '',
refundAmount: null,
status: null,
subject: '',
successTime: null,
tradeNo: '',
type: null,
userIp: ''
}
export default {
name: "PayRefund",
components: {},
data() {
return {
// 遮罩层
loading: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 退款订单列表
list: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
merchantId: null,
appId: null,
channelId: null,
channelCode: null,
orderId: null,
tradeNo: null,
merchantOrderId: null,
merchantRefundNo: null,
notifyUrl: null,
notifyStatus: null,
status: null,
type: null,
payAmount: null,
refundAmount: null,
reason: null,
userIp: null,
channelOrderNo: null,
channelRefundNo: null,
channelErrorCode: null,
channelErrorMsg: null,
channelExtras: null,
expireTime: [],
successTime: [],
notifyTime: [],
createTime: []
},
// 商户加载遮罩层
merchantLoading: false,
// 商户列表集合
merchantList: null,
// 支付应用列表集合
appList: null,
// 支付渠道编码字典数据集合
payChannelCodeDictDatum: getDictDatas(DICT_TYPE.PAY_CHANNEL_CODE_TYPE),
// 订单退款状态字典数据集合
payRefundOrderDictDatum: getDictDatas(DICT_TYPE.PAY_REFUND_ORDER_STATUS),
// 退款订单类别字典数据集合
payRefundOrderTypeDictDatum: getDictDatas(DICT_TYPE.PAY_REFUND_ORDER_TYPE),
// 订单回调商户状态字典数据集合
payOrderNotifyDictDatum: getDictDatas(DICT_TYPE.PAY_ORDER_NOTIFY_STATUS),
// el-tag订单退款状态type值
refundStatusType: '',
// 退款订单详情
refundDetail: JSON.parse(JSON.stringify(defaultRefundDetail)),
};
},
created() {
this.initTime();
this.getList();
this.handleGetMerchantListByName(null);
},
methods: {
initTime(){
this.queryParams.createTime = [getNowDateTime("00:00:00"), getNowDateTime("23:59:59")];
},
/** 查询列表 */
getList() {
// 判断选择的日期是否超过了一个月
let oneMonthTime = 31 * 24 * 3600 * 1000;
if (this.queryParams.createTime == null){
this.initTime();
} else {
let minDateTime = new Date(this.queryParams.createTime[0]).getTime();
let maxDateTime = new Date(this.queryParams.createTime[1]).getTime()
if (maxDateTime - minDateTime > oneMonthTime) {
this.$message.error('时间范围最大为 31 天!');
return false;
}
}
this.loading = true;
// 执行查询
getRefundPage(this.queryParams).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 取消按钮 */
cancel() {
this.open = false;
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 导出按钮操作 */
handleExport() {
// 处理查询参数
let params = {...this.queryParams};
params.pageNo = undefined;
params.pageSize = undefined;
// 执行导出
this.$modal.confirm('是否确认导出所有退款订单数据项?').then(function () {
return exportRefundExcel(params);
}).then(response => {
this.$download.excel(response, '退款订单.xls');
}).catch(() => {});
},
/**
* 根据商户名称模糊匹配商户信息
* @param name 商户名称
*/
handleGetMerchantListByName(name) {
getMerchantListByName(name).then(response => {
this.merchantList = response.data;
this.merchantLoading = false;
});
},
/**
* 根据商户 ID 查询支付应用信息
*/
handleGetAppListByMerchantId() {
this.queryParams.appId = null;
getAppListByMerchantId(this.queryParams.merchantId).then(response => {
this.appList = response.data;
});
},
/**
* 根据退款类别得到样式名称
* @param refundType 退款类别
*/
findByRefundTypeGetStyle(refundType) {
switch (refundType) {
case PayOrderRefundStatusEnum.NO.status:
return "success";
case PayOrderRefundStatusEnum.SOME.status:
return "warning";
case PayOrderRefundStatusEnum.ALL.status:
return "danger";
}
},
/**
* 根据退款状态得到样式名称
* @param refundStatus 退款状态
*/
findByRefundStatusGetStyle(refundStatus) {
switch (refundStatus) {
case PayRefundStatusEnum.CREATE.status:
return "info";
case PayRefundStatusEnum.SUCCESS.status:
return "success";
case PayRefundStatusEnum.FAILURE.status:
case PayRefundStatusEnum.CLOSE.status:
return "danger";
case PayRefundStatusEnum.PROCESSING_NOTIFY.status:
case PayRefundStatusEnum.PROCESSING_QUERY.status:
case PayRefundStatusEnum.UNKNOWN_RETRY.status:
case PayRefundStatusEnum.UNKNOWN_QUERY.status:
return "warning";
}
},
/**
* 查看订单详情
*/
handleQueryDetails(row) {
this.refundDetail = JSON.parse(JSON.stringify(defaultRefundDetail));
getRefund(row.id).then(response => {
this.refundDetail = response.data;
this.open = true;
});
},
}
};
</script>
<style>
.desc-label {
font-weight: bold;
}
.tag-purple {
color: #722ed1;
background: #f9f0ff;
border-color: #d3adf7;
}
.tag-cyan {
color: #13c2c2;
background: #e6fffb;
border-color: #87e8de;
}
.tag-pink {
color: #eb2f96;
background: #fff0f6;
border-color: #ffadd2;
}
.order-font {
font-size: 12px;
padding: 2px 0;
}
</style>

View File

@@ -1,7 +1,7 @@
<!--
* @Author: zhp
* @Date: 2024-01-24 15:15:24
* @LastEditTime: 2024-03-26 09:34:12
* @LastEditTime: 2024-03-27 09:27:23
* @LastEditors: zhp
* @Description:
-->
@@ -16,9 +16,9 @@
</el-date-picker>
</el-form-item>
<el-form-item>
<el-button v-if="this.$auth.hasPermi('report:glass-month:query')" type="primary" size="small"
<el-button v-if="this.$auth.hasPermi('base:report-auto-original-glass:query')" type="primary" size="small"
@click="getDataList">查询</el-button>
<el-button v-if="this.$auth.hasPermi('report:glass-month:export')" type="primary" size="small" plain
<el-button v-if="this.$auth.hasPermi('report:glass-day:export')" type="primary" size="small" plain
@click="handleExport">导出</el-button>
</el-form-item>
</el-form>
@@ -187,8 +187,8 @@ export default {
this.endTimeStamp = this.format(val.setHours(7, 0, 0)) //+ ' 00:00:00' //new Date(this.startTimeStamp + ' 00:00:00').getTime() / 1000
this.startTimeStamp = this.format(val.setHours(7, 0, 1) - 24 * 60 * 60 * 1000) //+ ' 23:59:59' //new Date(this.endTimeStamp + ' 23:59:59').getTime() / 1000
// console.log(this.listQuery.reportTime);
this.listQuery.reportTime[1] = this.format(val.setHours(7, 0, 1)) //+ ' 00:00:00' //new Date(this.startTimeStamp + ' 00:00:00').getTime() / 1000
this.listQuery.reportTime[0] = this.format(val.setHours(7, 0, 0) + 24 * 60 * 60 * 1000) //+ ' 23:59:59' //new Date(this.endTimeStamp + ' 23:59:59').getTime() / 1000
this.listQuery.reportTime[0] = this.format(val.setHours(7, 0, 1)) //+ ' 00:00:00' //new Date(this.startTimeStamp + ' 00:00:00').getTime() / 1000
this.listQuery.reportTime[1] = this.format(val.setHours(7, 0, 0) + 24 * 60 * 60 * 1000) //+ ' 23:59:59' //new Date(this.endTimeStamp + ' 23:59:59').getTime() / 1000
console.log(this.listQuery.reportTime);
} else {
this.listQuery.reportTime = []

View File

@@ -1,14 +1,14 @@
<!--
* @Author: Do not edit
* @Date: 2023-12-13 14:10:04
* @LastEditTime: 2024-03-22 09:45:23
* @LastEditTime: 2024-03-29 09:43:52
* @LastEditors: zhp
* @Description:
-->
<template>
<div>
<el-row style="float: right; margin-bottom: 5px">
<el-button v-if="!edit" size="small" @click="edit = true">编辑</el-button>
<el-button v-if="!edit" :disabled="noData" size="small" @click="edit = true">编辑</el-button>
<el-button v-if="edit" size="small" @click="handleReturn()">返回</el-button>
<el-button v-if="edit" size="small" @click="updateData">保存</el-button>
</el-row>
@@ -20,7 +20,7 @@
<el-table-column v-for="(y, index2) in it.children" :key="index2" :prop="y.prop" :label="y.label">
<template slot-scope="scope">
<span v-if="!edit">{{ scope.row[y.prop] }}</span>
<el-input @change="handleChange" :disabled="y.prop == 'dailyOutputTrend' || y.prop === 'originalGlassStatisticsTrend'
<el-input type="number" @change="handleChange" :disabled="y.prop == 'dailyOutputTrend' || y.prop === 'originalGlassStatisticsTrend'
|| y.prop === 'actualProductTrend' || y.prop === 'originalGlassPassTrend' || y.prop === 'originalGlassPassNow' || y.prop === 'originalGlassPassHis'
" v-else v-model="scope.row[y.prop]"></el-input>
</template>
@@ -158,9 +158,19 @@ const cols = [
cols,
remark:null,
edit: false,
noData:false,
};
},
watch: {
watch: {
data: {
handler(newv, oldv) {
if (newv.length != 0) {
this.noData = false
} else {
this.noData = true
}
}
},
time: {
immediate: true,
handler(newv, oldv) {
@@ -206,12 +216,10 @@ const cols = [
},
methods: {
handleReturn() {
// this.disabled = true
this.edit = false
console.log(this.$parent.getDataList());
},
handleChange(e) {
// console.log(q)
console.log(e);
},
updateData() {
let obj = {}

View File

@@ -7,7 +7,7 @@
</el-date-picker>
</el-form-item>
<el-form-item>
<el-button v-if="this.$auth.hasPermi('report:glass-month:query')" type="primary" size="small"
<el-button v-if="this.$auth.hasPermi('base:report-auto-original-glass:query')" type="primary" size="small"
@click="getDataList">查询</el-button>
<el-button v-if="this.$auth.hasPermi('report:glass-month:export')" type="primary" size="small" plain
@click="handleExport">导出</el-button>

View File

@@ -14,7 +14,7 @@
</el-date-picker>
</el-form-item>
<el-form-item>
<el-button v-if="this.$auth.hasPermi('report:glass-weekly:query')" type="primary" size="small"
<el-button v-if="this.$auth.hasPermi('base:report-auto-original-glass:query')" type="primary" size="small"
@click="getDataList">查询</el-button>
<el-button v-if="this.$auth.hasPermi('report:glass-weekly:export')" type="primary" size="small" plain
@click="handleExport">导出</el-button>

View File

@@ -9,7 +9,7 @@
</el-date-picker>
</el-form-item>
<el-form-item>
<el-button v-if="this.$auth.hasPermi('report:glass-year:query')" type="primary" size="small"
<el-button v-if="this.$auth.hasPermi('base:report-auto-original-glass:query')" type="primary" size="small"
@click="getDataList">查询</el-button>
<el-button v-if="this.$auth.hasPermi('report:glass-year:export')" type="primary" size="small" plain
@click="handleExport">导出</el-button>

View File

@@ -1,7 +1,7 @@
<!--
* @Author: zhp
* @Date: 2023-12-12 13:45:25
* @LastEditTime: 2024-03-26 09:41:07
* @LastEditTime: 2024-03-29 09:48:24
* @LastEditors: zhp
* @Description:
-->
@@ -17,7 +17,7 @@
@click="handleExport">导出</el-button>
</el-form>
<el-row style="float: right; margin-bottom: 5px">
<el-button v-if="!isSave" size="small" @click="editDataList()">编辑</el-button>
<el-button v-if="!isSave" :disabled="noData" size="small" @click="editDataList()">编辑</el-button>
<el-button v-if="isSave" size="small" @click="handleReturn()">返回</el-button>
<el-button size="small" v-if="isSave" @click="saveDataList()">保存</el-button>
</el-row>
@@ -28,59 +28,61 @@
<el-table-column :label="'许昌安彩日成品生产汇总' + '(' + timeTips + ')'" align="center">
<el-table-column prop="lineName" label="生产线" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.lineName" :disabled="disabled"></el-input>
<el-input v-if="!disabled" type="number" v-model="scope.row.lineName" :disabled="disabled"></el-input>
<span v-else>{{ scope.row.lineName }} </span>
</template>
</el-table-column>
<el-table-column label="投入数㎡" align="center">
<el-table-column prop="inputNow" label="今日" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.inputNow" :disabled="disabled"></el-input>
<el-input v-if="!disabled" type="number" v-model="scope.row.inputNow" :disabled="disabled"></el-input>
<span v-else>{{ scope.row.inputNow }} </span>
</template>
</el-table-column>
<el-table-column prop="inputHis" label="昨日" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.inputHis" :disabled="disabled"></el-input>
<el-input v-if="!disabled" type="number" v-model="scope.row.inputHis" :disabled="disabled"></el-input>
<span v-else>{{ scope.row.inputHis }} </span>
</template>
</el-table-column>
<el-table-column prop="inputTrend" label="增减" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.inputTrend" :disabled="!disabled"></el-input>
<span v-else>{{ scope.row.inputTrend ? parseFloat((scope.row.inputTrend * 100).toFixed(2)) + '%' :
null}} </span>
<el-input v-if="!disabled" type="number" v-model="scope.row.inputTrend" :disabled="!disabled"></el-input>
<span v-else>{{ scope.row.inputTrend && scope.row.inputTrend != 0 ? parseFloat((scope.row.inputTrend *
100).toFixed(2)) + '%' : scope.row.inputTrend == 0 ? 0 : null }} </span>
</template>
</el-table-column>
</el-table-column>
<el-table-column label="完成良品产量" align="center">
<el-table-column prop="goodProductNow" label="今日" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.goodProductNow" :disabled="disabled">
<el-input v-if="!disabled" type="number" v-model="scope.row.goodProductNow" :disabled="disabled">
</el-input>
<span v-else>{{ scope.row.goodProductNow }} </span>
</template>
</el-table-column>
<el-table-column prop="goodProductHis" label="昨日" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.goodProductHis" :disabled="disabled">
<el-input v-if="!disabled" type="number" v-model="scope.row.goodProductHis" :disabled="disabled">
</el-input>
<span v-else>{{ scope.row.goodProductHis }} </span>
</template>
</el-table-column>
<el-table-column prop="goodProductTrend" label="增减" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.goodProductTrend" :disabled="!disabled">
<el-input v-if="!disabled" type="number" v-model="scope.row.goodProductTrend" :disabled="!disabled">
</el-input>
<span v-else>{{ scope.row.goodProductTrend ? parseFloat((scope.row.goodProductTrend * 100).toFixed(2)) +
'%' : null }} </span>
<span v-else>{{ scope.row.goodProductTrend && scope.row.goodProductTrend != 0 ?
parseFloat((scope.row.goodProductTrend * 100).toFixed(2)) + '%' : scope.row.goodProductTrend == 0 ? 0 :
null
}} </span>
</template>
</el-table-column>
</el-table-column>
<el-table-column label="原片漏检率" align="center">
<el-table-column prop="missCheckNow" label="今日" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.missCheckNow" :disabled="disabled"></el-input>
<el-input v-if="!disabled" type="number" v-model="scope.row.missCheckNow" :disabled="disabled"></el-input>
<span v-else>{{ scope.row.missCheckNow ? parseFloat((scope.row.missCheckNow *
100).toFixed(2)) +
'%' : null }}
@@ -89,7 +91,7 @@
</el-table-column>
<el-table-column prop="missCheckHis" label="昨日" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.missCheckHis" :disabled="disabled"></el-input>
<el-input v-if="!disabled" type="number" v-model="scope.row.missCheckHis" :disabled="disabled"></el-input>
<span v-else>{{ scope.row.missCheckHis ? parseFloat((scope.row.missCheckHis *
100).toFixed(2)) +
'%' : null }}
@@ -98,19 +100,18 @@
</el-table-column>
<el-table-column prop="missCheckTrend" label="增减" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.missCheckTrend" :disabled="!disabled">
<el-input v-if="!disabled" type="number" v-model="scope.row.missCheckTrend" :disabled="!disabled">
</el-input>
<span v-else>{{ scope.row.missCheckTrend ? parseFloat((scope.row.missCheckTrend *
100).toFixed(2)) +
'%' : null }}
</span>
<span v-else>{{ scope.row.missCheckTrend && scope.row.missCheckTrend != 0 ?
parseFloat((scope.row.missCheckTrend * 100).toFixed(2)) + '%' : scope.row.missCheckTrend == 0 ? 0 : null
}} </span>
</template>
</el-table-column>
</el-table-column>
<el-table-column label="综合良品率" align="center">
<el-table-column prop="goodProductPassNow" label="今日" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.goodProductPassNow" :disabled="!disabled">
<el-input v-if="!disabled" type="number" v-model="scope.row.goodProductPassNow" :disabled="!disabled">
</el-input>
<span v-else>{{ scope.row.goodProductPassNow ? parseFloat((scope.row.goodProductPassNow *
100).toFixed(2)) +
@@ -120,7 +121,7 @@
</el-table-column>
<el-table-column prop="goodProductPassHis" label="昨日" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.goodProductPassHis" :disabled="!disabled">
<el-input v-if="!disabled" type="number" v-model="scope.row.goodProductPassHis" :disabled="!disabled">
</el-input>
<span v-else>{{ scope.row.goodProductPassHis ? parseFloat((scope.row.goodProductPassHis *
100).toFixed(2)) +
@@ -130,12 +131,13 @@
</el-table-column>
<el-table-column prop="goodProductPassTrend" label="增减" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.goodProductPassTrend" :disabled="!disabled">
<el-input v-if="!disabled" type="number" v-model="scope.row.goodProductPassTrend" :disabled="!disabled">
</el-input>
<span v-else>{{ scope.row.goodProductPassTrend ? parseFloat((scope.row.goodProductPassTrend *
100).toFixed(2)) +
'%' : null }}
</span>
<span v-else>{{ scope.row.goodProductPassTrend && scope.row.goodProductPassTrend != 0 ?
parseFloat((scope.row.goodProductPassTrend * 100).toFixed(2)) + '%' : scope.row.goodProductPassTrend ==
0 ? 0 :
null
}} </span>
</template>
</el-table-column>
</el-table-column>
@@ -284,6 +286,7 @@ export default {
urlOptions: {
exportURL: exportProductExcel
},
noData:false,
isSave:false,
disabled: true,
sumArr: [],
@@ -456,6 +459,8 @@ export default {
handleReturn() {
this.disabled = true
this.isSave = false
this.getDataList()
},
format(shijianchuo) {
//shijianchuo是整数否则要parseInt转换
@@ -629,6 +634,11 @@ export default {
params: this.listQuery
})
console.log(res)
if (res.data.list.length == 0) {
this.noData = true
} else {
this.noData = false
}
// let sum = undefined
// res.data.list.forEach((ele, index) => {
// if (ele.det === false) {

View File

@@ -1,7 +1,7 @@
<!--
* @Author: zhp
* @Date: 2023-12-12 13:45:25
* @LastEditTime: 2024-03-26 09:41:33
* @LastEditTime: 2024-03-29 09:48:32
* @LastEditors: zhp
* @Description:
-->
@@ -17,7 +17,7 @@
@click="handleExport">导出</el-button>
</el-form>
<el-row style="float: right; margin-bottom: 5px">
<el-button v-if="!isSave" size="small" @click="editDataList()">编辑</el-button>
<el-button v-if="!isSave" :disabled="noData" size="small" @click="editDataList()">编辑</el-button>
<el-button v-if="isSave" size="small" @click="handleReturn()">返回</el-button>
<el-button size="small" v-if="isSave" @click="saveDataList()">保存</el-button>
</el-row>
@@ -28,114 +28,122 @@
<el-table-column :label="'许昌安彩月成品生产汇总' + '(' + timeTips + ')'" align="center">
<el-table-column prop="lineId" label="生产线" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.lineName" :disabled="disabled"></el-input>
<el-input v-if="!disabled" type="number" v-model="scope.row.lineName" :disabled="disabled"></el-input>
<span v-else>{{ scope.row.lineName }} </span>
</template>
</el-table-column>
<el-table-column label="投入数㎡" align="center">
<el-table-column prop="inputNow" label="本月" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.inputNow" :disabled="disabled"></el-input>
<el-input v-if="!disabled" type="number" v-model="scope.row.inputNow" :disabled="disabled"></el-input>
<span v-else>{{ scope.row.inputNow }} </span>
</template>
</el-table-column>
<el-table-column prop="inputHis" label="上月" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.inputHis" :disabled="disabled"></el-input>
<el-input v-if="!disabled" type="number" v-model="scope.row.inputHis" :disabled="disabled"></el-input>
<span v-else>{{ scope.row.inputHis }} </span>
</template>
</el-table-column>
<el-table-column prop="inputTrend" label="增减" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.inputTrend" :disabled="!disabled"></el-input>
<span v-else>{{ scope.row.inputTrend ? parseFloat((scope.row.inputTrend *
100).toFixed(2)) + '%' : undefined }} </span>
<el-input v-if="!disabled" type="number" v-model="scope.row.inputTrend" :disabled="!disabled"></el-input>
<span v-else>{{ scope.row.inputTrend && scope.row.inputTrend != 0 ?
parseFloat((scope.row.inputTrend * 100).toFixed(2)) + '%' : scope.row.inputTrend == 0 ? 0 :
null
}} </span>
</template>
</el-table-column>
</el-table-column>
<el-table-column label="完成良品产量" align="center">
<el-table-column prop="goodProductNow" label="本月" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.goodProductNow" :disabled="disabled">
<el-input v-if="!disabled" type="number" v-model="scope.row.goodProductNow" :disabled="disabled">
</el-input>
<span v-else>{{ scope.row.goodProductNow }} </span>
</template>
</el-table-column>
<el-table-column prop="goodProductHis" label="上月" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.goodProductHis" :disabled="disabled">
<el-input v-if="!disabled" type="number" v-model="scope.row.goodProductHis" :disabled="disabled">
</el-input>
<span v-else>{{ scope.row.goodProductHis }} </span>
</template>
</el-table-column>
<el-table-column prop="goodProductTrend" label="增减" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.goodProductTrend" :disabled="!disabled">
<el-input v-if="!disabled" type="number" v-model="scope.row.goodProductTrend" :disabled="!disabled">
</el-input>
<span v-else>{{ scope.row.goodProductTrend ? parseFloat((scope.row.goodProductTrend *
100).toFixed(2)) + '%' : undefined }} </span>
<span v-else>{{ scope.row.goodProductTrend && scope.row.goodProductTrend != 0 ?
parseFloat((scope.row.goodProductTrend * 100).toFixed(2)) + '%' : scope.row.goodProductTrend == 0 ? 0 :
null
}} </span>
</template>
</el-table-column>
</el-table-column>
<el-table-column label="原片漏检率" align="center">
<el-table-column prop="missCheckNow" label="本月" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.missCheckNow" :disabled="disabled"></el-input>
<el-input v-if="!disabled" type="number" v-model="scope.row.missCheckNow" :disabled="disabled"></el-input>
<span v-else>{{ scope.row.missCheckNow ? parseFloat((scope.row.missCheckNow *
100).toFixed(2)) +
'%' : undefined }}
100).toFixed(2)) +
'%' : null }}
</span>
</template>
</el-table-column>
<el-table-column prop="missCheckHis" label="上月" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.missCheckHis" :disabled="disabled"></el-input>
<el-input v-if="!disabled" type="number" v-model="scope.row.missCheckHis" :disabled="disabled"></el-input>
<span v-else>{{ scope.row.missCheckHis ? parseFloat((scope.row.missCheckHis *
100).toFixed(2)) +
'%' : undefined }}
100).toFixed(2)) +
'%' : null }}
</span>
</template>
</el-table-column>
<el-table-column prop="missCheckTrend" label="增减" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.missCheckTrend" :disabled="!disabled">
<el-input v-if="!disabled" type="number" v-model="scope.row.missCheckTrend" :disabled="!disabled">
</el-input>
<span v-else>{{ scope.row.missCheckTrend ? parseFloat((scope.row.missCheckTrend *
100).toFixed(2)) +
'%' : undefined }}
</span>
<span v-else>{{ scope.row.missCheckTrend && scope.row.missCheckTrend != 0 ?
parseFloat((scope.row.missCheckTrend * 100).toFixed(2)) + '%' : scope.row.missCheckTrend == 0 ?
0 :
null
}} </span>
</template>
</el-table-column>
</el-table-column>
<el-table-column label="综合良品率" align="center">
<el-table-column prop="goodProductPassNow" label="本月" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.goodProductPassNow" :disabled="!disabled">
<el-input v-if="!disabled" type="number" v-model="scope.row.goodProductPassNow" :disabled="!disabled">
</el-input>
<span v-else>{{ scope.row.goodProductPassNow ? parseFloat((scope.row.goodProductPassNow *
100).toFixed(2)) +
'%' : undefined }}
100).toFixed(2)) +
'%' : null }}
</span>
</template>
</el-table-column>
<el-table-column prop="goodProductPassHis" label="上月" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.goodProductPassHis" :disabled="!disabled">
<el-input v-if="!disabled" type="number" v-model="scope.row.goodProductPassHis" :disabled="!disabled">
</el-input>
<span v-else>{{ scope.row.goodProductPassHis ? parseFloat((scope.row.goodProductPassHis *
100).toFixed(2)) +
'%' : undefined }}
100).toFixed(2)) +
'%' : null }}
</span>
</template>
</el-table-column>
<el-table-column prop="goodProductPassTrend" label="增减" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.goodProductPassTrend" :disabled="!disabled">
<el-input v-if="!disabled" type="number" v-model="scope.row.goodProductPassTrend" :disabled="!disabled">
</el-input>
<span v-else>{{ scope.row.goodProductPassTrend ? parseFloat((scope.row.goodProductPassTrend *
100).toFixed(2)) +
'%' : undefined }}
</span>
<span v-else>{{ scope.row.goodProductPassTrend && scope.row.goodProductPassTrend != 0 ?
parseFloat((scope.row.goodProductPassTrend * 100).toFixed(2)) + '%' : scope.row.goodProductPassTrend ==
0 ?
0 :
null
}} </span>
</template>
</el-table-column>
</el-table-column>
@@ -288,6 +296,7 @@ export default {
isSave:false,
disabled: true,
sumArr: [],
noData:false,
timeTips:'',
searchBarFormConfig: [
// {
@@ -666,6 +675,11 @@ export default {
params: this.dataForm
})
console.log(res)
if (res.data.list.length == 0) {
this.noData = true
} else {
this.noData = false
}
// let sum = undefined
// res.data.list.forEach((ele, index) => {
// if (ele.det === false) {

View File

@@ -1,7 +1,7 @@
<!--
* @Author: zhp
* @Date: 2023-12-12 13:45:25
* @LastEditTime: 2024-03-18 15:26:52
* @LastEditTime: 2024-03-29 08:51:46
* @LastEditors: zhp
* @Description:
-->
@@ -25,7 +25,7 @@
background: '#F2F4F9',
color: '#606266'
}">
<el-table-column :label="'许昌安彩成品发货及库存日报' + '(' + timeTips + ')'" align="center">
<el-table-column :label="'许昌安彩成品发货及库存日报' + '(' + timeTips + ')'" align="center">
<el-table-column prop="glassType" label="品种" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.glassType" :disabled="disabled"></el-input>
@@ -39,7 +39,7 @@
</template>
</el-table-column>
<el-table-column label="规格" align="center">
<el-table-column label="成品" align="center">
<el-table-column width="180" label="成品" align="center">
<!-- <el-table-column :show-header="false"> -->
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.productionGlassSize" :disabled="disabled"></el-input>
@@ -59,7 +59,7 @@
</template>
</el-table-column> -->
</el-table-column>
<el-table-column prop="originalGlassSize" label="原片" align="center">
<el-table-column width="180" prop="originalGlassSize" label="原片" align="center">
<!-- <el-table-column prop="originalGlassSize"> -->
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.originalGlassSize" :disabled="disabled">

View File

@@ -1,7 +1,7 @@
<!--
* @Author: zhp
* @Date: 2023-12-12 13:45:25
* @LastEditTime: 2024-03-26 09:41:51
* @LastEditTime: 2024-03-29 09:48:41
* @LastEditors: zhp
* @Description:
-->
@@ -20,7 +20,7 @@
@click="handleExport">导出</el-button>
</el-form>
<el-row style="float: right; margin-bottom: 5px">
<el-button v-if="!isSave" size="small" @click="editDataList()">编辑</el-button>
<el-button v-if="!isSave" :disabled="noData" size="small" @click="editDataList()">编辑</el-button>
<el-button v-if="isSave" size="small" @click="handleReturn()">返回</el-button>
<el-button size="small" v-if="isSave" @click="saveDataList()">保存</el-button>
</el-row>
@@ -31,114 +31,126 @@
<el-table-column :label="'许昌安彩周成品生产汇总' + '(' + timeTips + ')'" align="center">
<el-table-column prop="lineId" label="生产线" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.lineName" :disabled="disabled"></el-input>
<el-input v-if="!disabled" type="number" v-model="scope.row.lineName" :disabled="disabled"></el-input>
<span v-else>{{ scope.row.lineName }} </span>
</template>
</el-table-column>
<el-table-column label="投入数㎡" align="center">
<el-table-column prop="inputNow" label="本周" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.inputNow" :disabled="disabled"></el-input>
<el-input v-if="!disabled" type="number" v-model="scope.row.inputNow" :disabled="disabled"></el-input>
<span v-else>{{ scope.row.inputNow }} </span>
</template>
</el-table-column>
<el-table-column prop="inputHis" label="上周" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.inputHis" :disabled="disabled"></el-input>
<el-input v-if="!disabled" type="number" v-model="scope.row.inputHis" :disabled="disabled"></el-input>
<span v-else>{{ scope.row.inputHis }} </span>
</template>
</el-table-column>
<el-table-column prop="inputTrend" label="增减" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.inputTrend" :disabled="!disabled"></el-input>
<span v-else>{{ scope.row.inputTrend ? parseFloat((scope.row.inputTrend *
100).toFixed(2)) + '%' : undefined }} </span>
<el-input v-if="!disabled" type="number" v-model="scope.row.inputTrend" :disabled="!disabled"></el-input>
<span v-else>{{ scope.row.inputTrend && scope.row.inputTrend != 0 ?
parseFloat((scope.row.inputTrend * 100).toFixed(2)) + '%' : scope.row.inputTrend ==
0 ?
0 :
null
}} </span>
</template>
</el-table-column>
</el-table-column>
<el-table-column label="完成良品产量" align="center">
<el-table-column prop="goodProductNow" label="本周" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.goodProductNow" :disabled="disabled">
<el-input v-if="!disabled" type="number" v-model="scope.row.goodProductNow" :disabled="disabled">
</el-input>
<span v-else>{{ scope.row.goodProductNow }} </span>
</template>
</el-table-column>
<el-table-column prop="goodProductHis" label="上周" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.goodProductHis" :disabled="disabled">
<el-input v-if="!disabled" type="number" v-model="scope.row.goodProductHis" :disabled="disabled">
</el-input>
<span v-else>{{ scope.row.goodProductHis }} </span>
</template>
</el-table-column>
<el-table-column prop="goodProductTrend" label="增减" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.goodProductTrend" :disabled="!disabled">
<el-input v-if="!disabled" type="number" v-model="scope.row.goodProductTrend" :disabled="!disabled">
</el-input>
<span v-else>{{ scope.row.goodProductTrend ? parseFloat((scope.row.goodProductTrend *
100).toFixed(2)) + '%' : undefined }} </span>
<span v-else>{{ scope.row.goodProductTrend && scope.row.goodProductTrend != 0 ?
parseFloat((scope.row.goodProductTrend * 100).toFixed(2)) + '%' : scope.row.goodProductTrend ==
0 ?
0 :
null
}} </span>
</template>
</el-table-column>
</el-table-column>
<el-table-column label="原片漏检率" align="center">
<el-table-column prop="missCheckNow" label="本周" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.missCheckNow" :disabled="disabled"></el-input>
<el-input v-if="!disabled" type="number" v-model="scope.row.missCheckNow" :disabled="disabled"></el-input>
<span v-else>{{ scope.row.missCheckNow ? parseFloat((scope.row.missCheckNow *
100).toFixed(2)) +
'%' : undefined }}
100).toFixed(2)) +
'%' : null }}
</span>
</template>
</el-table-column>
<el-table-column prop="missCheckHis" label="上周" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.missCheckHis" :disabled="disabled"></el-input>
<el-input v-if="!disabled" type="number" v-model="scope.row.missCheckHis" :disabled="disabled"></el-input>
<span v-else>{{ scope.row.missCheckHis ? parseFloat((scope.row.missCheckHis *
100).toFixed(2)) +
'%' : undefined }}
100).toFixed(2)) +
'%' : null }}
</span>
</template>
</el-table-column>
<el-table-column prop="missCheckTrend" label="增减" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.missCheckTrend" :disabled="!disabled">
<el-input v-if="!disabled" type="number" v-model="scope.row.missCheckTrend" :disabled="!disabled">
</el-input>
<span v-else>{{ scope.row.missCheckTrend ? parseFloat((scope.row.missCheckTrend *
100).toFixed(2)) +
'%' : undefined }}
</span>
<span v-else>{{ scope.row.missCheckTrend && scope.row.missCheckTrend != 0 ?
parseFloat((scope.row.missCheckTrend * 100).toFixed(2)) + '%' : scope.row.missCheckTrend ==
0 ?
0 :
null
}} </span>
</template>
</el-table-column>
</el-table-column>
<el-table-column label="综合良品率" align="center">
<el-table-column prop="goodProductPassNow" label="本周" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.goodProductPassNow" :disabled="!disabled">
<el-input v-if="!disabled" type="number" v-model="scope.row.goodProductPassNow" :disabled="!disabled">
</el-input>
<span v-else>{{ scope.row.goodProductPassNow ? parseFloat((scope.row.goodProductPassNow *
100).toFixed(2)) +
'%' : undefined }}
100).toFixed(2)) +
'%' : null }}
</span>
</template>
</el-table-column>
<el-table-column prop="goodProductPassHis" label="上周" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.goodProductPassHis" :disabled="!disabled">
<el-input v-if="!disabled" type="number" v-model="scope.row.goodProductPassHis" :disabled="!disabled">
</el-input>
<span v-else>{{ scope.row.goodProductPassHis ? parseFloat((scope.row.goodProductPassHis *
100).toFixed(2)) +
'%' : undefined }}
100).toFixed(2)) +
'%' : null }}
</span>
</template>
</el-table-column>
<el-table-column prop="goodProductPassTrend" label="增减" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.goodProductPassTrend" :disabled="!disabled">
<el-input v-if="!disabled" type="number" v-model="scope.row.goodProductPassTrend" :disabled="!disabled">
</el-input>
<span v-else>{{ scope.row.goodProductPassTrend ? parseFloat((scope.row.goodProductPassTrend *
100).toFixed(2)) +
'%' : undefined }}
</span>
<span v-else>{{ scope.row.goodProductPassTrend && scope.row.goodProductPassTrend != 0 ?
parseFloat((scope.row.goodProductPassTrend * 100).toFixed(2)) + '%' : scope.row.goodProductPassTrend ==
0 ?
0 :
null
}} </span>
</template>
</el-table-column>
</el-table-column>
@@ -296,6 +308,7 @@ export default {
isSave:false,
disabled: true,
sumArr: [],
noData:false,
weekValue1: null,//最多24周
weekValue2: null,
searchBarFormConfig: [
@@ -629,6 +642,11 @@ export default {
})
// console.log(this.queryParams);
console.log(res)
if (res.data.list.length == 0) {
this.noData = true
} else {
this.noData = false
}
// let sum = undefined
// res.data.list.forEach((ele, index) => {
// if (ele.det === false) {

View File

@@ -1,7 +1,7 @@
<!--
* @Author: zhp
* @Date: 2023-12-12 13:45:25
* @LastEditTime: 2024-03-26 09:42:08
* @LastEditTime: 2024-03-29 09:48:49
* @LastEditors: zhp
* @Description:
-->
@@ -21,7 +21,7 @@
<el-button v-if="isSave" type="success" @click="saveDataList()">保存</el-button> -->
</el-form>
<el-row style="float: right; margin-bottom: 5px">
<el-button v-if="!isSave" size="small" @click="editDataList()">编辑</el-button>
<el-button v-if="!isSave" :disabled="noData" size="small" @click="editDataList()">编辑</el-button>
<el-button v-if="isSave" size="small" @click="handleReturn()">返回</el-button>
<el-button size="small" v-if="isSave" @click="saveDataList()">保存</el-button>
</el-row>
@@ -33,114 +33,126 @@
<el-table-column :label="'许昌安彩年成品生产汇总' + '(' + timeTips + ')'" align="center">
<el-table-column prop="lineId" label="生产线" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.lineName" :disabled="disabled"></el-input>
<el-input v-if="!disabled" type="number" v-model="scope.row.lineName" :disabled="disabled"></el-input>
<span v-else>{{ scope.row.lineName }} </span>
</template>
</el-table-column>
<el-table-column label="投入数㎡" align="center">
<el-table-column prop="inputNow" label="今年" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.inputNow" :disabled="disabled"></el-input>
<el-input v-if="!disabled" type="number" v-model="scope.row.inputNow" :disabled="disabled"></el-input>
<span v-else>{{ scope.row.inputNow }} </span>
</template>
</el-table-column>
<el-table-column prop="inputHis" label="去年" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.inputHis" :disabled="disabled"></el-input>
<el-input v-if="!disabled" type="number" v-model="scope.row.inputHis" :disabled="disabled"></el-input>
<span v-else>{{ scope.row.inputHis }} </span>
</template>
</el-table-column>
<el-table-column prop="inputTrend" label="增减" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.inputTrend" :disabled="!disabled"></el-input>
<span v-else>{{ scope.row.inputTrend ? parseFloat((scope.row.inputTrend *
100).toFixed(2)) + '%' : undefined }} </span>
<el-input v-if="!disabled" type="number" v-model="scope.row.inputTrend" :disabled="!disabled"></el-input>
<span v-else>{{ scope.row.inputTrend && scope.row.inputTrend != 0 ?
parseFloat((scope.row.inputTrend * 100).toFixed(2)) + '%' : scope.row.inputTrend ==
0 ?
0 :
null
}} </span>
</template>
</el-table-column>
</el-table-column>
<el-table-column label="完成良品产量" align="center">
<el-table-column prop="goodProductNow" label="今年" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.goodProductNow" :disabled="disabled">
<el-input v-if="!disabled" type="number" v-model="scope.row.goodProductNow" :disabled="disabled">
</el-input>
<span v-else>{{ scope.row.goodProductNow }} </span>
</template>
</el-table-column>
<el-table-column prop="goodProductHis" label="去年" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.goodProductHis" :disabled="disabled">
<el-input v-if="!disabled" type="number" v-model="scope.row.goodProductHis" :disabled="disabled">
</el-input>
<span v-else>{{ scope.row.goodProductHis }} </span>
</template>
</el-table-column>
<el-table-column prop="goodProductTrend" label="增减" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.goodProductTrend" :disabled="!disabled">
<el-input v-if="!disabled" type="number" v-model="scope.row.goodProductTrend" :disabled="!disabled">
</el-input>
<span v-else>{{ scope.row.goodProductTrend ? parseFloat((scope.row.goodProductTrend *
100).toFixed(2)) + '%' : undefined }} </span>
<span v-else>{{ scope.row.goodProductTrend && scope.row.goodProductTrend != 0 ?
parseFloat((scope.row.goodProductTrend * 100).toFixed(2)) + '%' : scope.row.goodProductTrend ==
0 ?
0 :
null
}} </span>
</template>
</el-table-column>
</el-table-column>
<el-table-column label="原片漏检率" align="center">
<el-table-column prop="missCheckNow" label="今年" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.missCheckNow" :disabled="disabled"></el-input>
<el-input v-if="!disabled" type="number" v-model="scope.row.missCheckNow" :disabled="disabled"></el-input>
<span v-else>{{ scope.row.missCheckNow ? parseFloat((scope.row.missCheckNow *
100).toFixed(2)) +
'%' : undefined }}
100).toFixed(2)) +
'%' : null }}
</span>
</template>
</el-table-column>
<el-table-column prop="missCheckHis" label="去年" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.missCheckHis" :disabled="disabled"></el-input>
<el-input v-if="!disabled" type="number" v-model="scope.row.missCheckHis" :disabled="disabled"></el-input>
<span v-else>{{ scope.row.missCheckHis ? parseFloat((scope.row.missCheckHis *
100).toFixed(2)) +
'%' : undefined }}
100).toFixed(2)) +
'%' : null }}
</span>
</template>
</el-table-column>
<el-table-column prop="missCheckTrend" label="增减" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.missCheckTrend" :disabled="!disabled">
<el-input v-if="!disabled" type="number" v-model="scope.row.missCheckTrend" :disabled="!disabled">
</el-input>
<span v-else>{{ scope.row.missCheckTrend ? parseFloat((scope.row.missCheckTrend *
100).toFixed(2)) +
'%' : undefined }}
</span>
<span v-else>{{ scope.row.missCheckTrend && scope.row.missCheckTrend != 0 ?
parseFloat((scope.row.missCheckTrend * 100).toFixed(2)) + '%' : scope.row.missCheckTrend ==
0 ?
0 :
null
}} </span>
</template>
</el-table-column>
</el-table-column>
<el-table-column label="综合良品率" align="center">
<el-table-column prop="goodProductPassNow" label="今年" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.goodProductPassNow" :disabled="!disabled">
<el-input v-if="!disabled" type="number" v-model="scope.row.goodProductPassNow" :disabled="!disabled">
</el-input>
<span v-else>{{ scope.row.goodProductPassNow ? parseFloat((scope.row.goodProductPassNow *
100).toFixed(2)) +
'%' : undefined }}
100).toFixed(2)) +
'%' : null }}
</span>
</template>
</el-table-column>
<el-table-column prop="goodProductPassHis" label="去年" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.goodProductPassHis" :disabled="!disabled">
<el-input v-if="!disabled" type="number" v-model="scope.row.goodProductPassHis" :disabled="!disabled">
</el-input>
<span v-else>{{ scope.row.goodProductPassHis ? parseFloat((scope.row.goodProductPassHis *
100).toFixed(2)) +
'%' : undefined }}
100).toFixed(2)) +
'%' : null }}
</span>
</template>
</el-table-column>
<el-table-column prop="goodProductPassTrend" label="增减" align="center">
<template slot-scope="scope">
<el-input v-if="!disabled" v-model="scope.row.goodProductPassTrend" :disabled="!disabled">
<el-input v-if="!disabled" type="number" v-model="scope.row.goodProductPassTrend" :disabled="!disabled">
</el-input>
<span v-else>{{ scope.row.goodProductPassTrend ? parseFloat((scope.row.goodProductPassTrend *
100).toFixed(2)) +
'%' : undefined }}
</span>
<span v-else>{{ scope.row.goodProductPassTrend && scope.row.goodProductPassTrend != 0 ?
parseFloat((scope.row.goodProductPassTrend * 100).toFixed(2)) + '%' : scope.row.goodProductPassTrend ==
0 ?
0 :
null
}} </span>
</template>
</el-table-column>
</el-table-column>
@@ -291,6 +303,7 @@ export default {
yearValue2: null,
// dynamicProps: [],
tableProps,
noData:false,
reportTime: [],
startTimeStamp: '',
endTimeStamp: '',
@@ -567,7 +580,12 @@ export default {
})
// console.log(this.queryParams);
console.log(res)
let sum = undefined
if (res.data.list.length == 0) {
this.noData = true
} else {
this.noData = false
}
// let sum = undefined
// res.data.list.forEach((ele, index) => {
// if (ele.det === false) {
// sum = res.data.list.splice(index, 1)

View File

@@ -9,10 +9,11 @@
<div class="itemBox">
<div class="itemClass" v-for="(item, index) in realtimeList" :key='index'>
<div class="itemSub">
<p class="itemNum">{{item.checkValue ? (item.checkValue).toFixed(2) : '-'}}</p>
<p class="itemNum">{{ item.checkValue ? (item.checkValue).toFixed(2) : '-' }}</p>
<p class="itemDescribe" :title="item.name">
<img src="./../../../../../assets/images/detectionData.png" alt="">
{{item.name}}</p>
{{ item.name }}
</p>
</div>
</div>
</div>
@@ -24,8 +25,8 @@
<span>检测指标趋势图</span>
</div>
<!-- 搜索工作栏 -->
<search-area @submit="getTrend"/>
<line-chart :chartData="chartData" v-show='Object.keys(chartData).length !== 0' :timeDim="queryParams.timeDim"/>
<search-area @submit="getTrend" />
<line-chart :chartData="chartData" v-show='Object.keys(chartData).length !== 0' :timeDim="queryParams.timeDim" />
<!-- 没有数据 -->
<div class="no-data-bg" v-show='Object.keys(chartData).length === 0'></div>
</div>
@@ -37,11 +38,11 @@ import LineChart from './../../components/lineChart'
import SearchArea from './../../components/searchArea'
import moment from 'moment'
export default {
name: 'Voc',
data(){
name: 'VocManagement',
data() {
return {
realtimeList:[],
queryParams:{
realtimeList: [],
queryParams: {
checkType: 3,
timeDim: null,
timeRange: []
@@ -53,18 +54,18 @@ export default {
mounted() {
this.getMsg()
this.queryParams.timeDim = this.getDictDatas(this.DICT_TYPE.TIME_DIM)[0].value // 默认时
this.queryParams.timeRange = [moment().startOf('day')+0, moment().endOf('day')-59*61*1000]
this.queryParams.timeRange = [moment().startOf('day') + 0, moment().endOf('day') - 59 * 61 * 1000]
this.getTrend()
},
methods: {
getMsg() {
environmentalCheckRealtime({checkType: 3}).then(res => {
environmentalCheckRealtime({ checkType: 3 }).then(res => {
console.log(res)
this.realtimeList = res.data || []
})
},
getTrend() {
environmentalCheckRealtimeTrend({...this.queryParams}).then(res => {
environmentalCheckRealtimeTrend({ ...this.queryParams }).then(res => {
if (res.code === 0) {
this.chartData = res.data
} else {
@@ -85,34 +86,41 @@ export default {
<style lang='scss' scoped>
.voc {
background-color: #f2f4f9;
.box1 {
height: 172px;
padding: 12px 16px 0;
margin-bottom: 8px;
background-color: #fff;
border-radius: 9px;
.itemBox {
display: flex;
flex-flow: row nowrap;
overflow: auto;
.itemClass {
width: 198px;
height: 88px;
// border: 1px solid green;
padding: 12px 0px 12px 18px;
.itemSub {
width: 176px;
height: 65px;
padding-right: 26px;
border-right: 1px solid #E8E8E8;
P{
P {
margin: 0;
img {
width: 24px;
height: 24px;
vertical-align: middle;
}
}
.itemNum {
font-size: 32px;
font-weight: 600;
@@ -120,33 +128,41 @@ export default {
color: #3E6AF7;
text-align: right;
}
.itemDescribe {
font-size: 16px;
text-align: right;
white-space: nowrap; /* 防止换行 */
overflow: hidden; /* 隐藏超出部分 */
text-overflow: ellipsis; /* 添加省略号 */
white-space: nowrap;
/* 防止换行 */
overflow: hidden;
/* 隐藏超出部分 */
text-overflow: ellipsis;
/* 添加省略号 */
}
}
}
}
.itemClass:last-child > .itemSub{
.itemClass:last-child>.itemSub {
border: none !important;
}
}
.box2 {
background-color: #fff;
border-radius: 9px;
padding: 16px;
height: calc(100vh - 308px);
}
.boxTitle {
display: inline-block;
font-size: 16px;
font-weight: 400;
color: #000000;
margin:0 10px 20px 0;
margin: 0 10px 20px 0;
}
.blueTitle {
content: '';
display: inline-block;

View File

@@ -19,6 +19,7 @@
:page="queryParams.pageNo"
:limit="queryParams.pageSize"
:table-data="list"
:max-height="tableH"
@emitFun="handleEmitFun">
<method-btn
v-if="tableBtn.length"
@@ -55,7 +56,6 @@
:has-files="true"
:disabled="editMode === 'detail'"
:rows="computedRows" />
<!-- :has-files="['files', 'files2']" -->
</base-dialog>
<!-- 设备 详情 - 编辑 -->
@@ -91,13 +91,13 @@
pageSize: 10,
},
tableBtn: [
this.$auth.hasPermi('base:core-equipment-attr:update')
this.$auth.hasPermi('base:core-equipment-attr:update') // ('se:firefight-attr:update') // TODO 此处需用firefight-attr权限还是core-equipment-attr 权限?
? {
type: 'edit',
btnName: '修改',
}
: undefined,
this.$auth.hasPermi('base:core-equipment-attr:delete')
this.$auth.hasPermi('base:core-equipment-attr:delete') // ('se:firefight-attr:delete')
? {
type: 'delete',
btnName: '删除',
@@ -116,6 +116,7 @@
<script>
import moment from 'moment';
import basicPageMixin from '@/mixins/lb/basicPageMixin';
import tableHeightMixin from '@/mixins/lb/tableHeightMixin';
import EquipmentDrawer from '../components/firefightingDrawer';
import BaseDialogWrapper from '../components/BaseDialogWrapper.vue';
@@ -134,26 +135,26 @@ export default {
components: {
Editor,
EquipmentDrawer,
BaseDialog: BaseDialogWrapper
BaseDialog: BaseDialogWrapper,
},
mixins: [basicPageMixin],
mixins: [basicPageMixin, tableHeightMixin],
data() {
return {
searchBarKeys: ['name', 'code'],
tableBtn: [
this.$auth.hasPermi(`base:core-equipment:update`)
this.$auth.hasPermi(`se:firefight:update`)
? {
type: 'detail',
btnName: '详情',
}
: undefined,
this.$auth.hasPermi('base:core-equipment:update')
this.$auth.hasPermi('se:firefight:update')
? {
type: 'edit',
btnName: '修改',
}
: undefined,
this.$auth.hasPermi('base:core-equipment:delete')
this.$auth.hasPermi('se:firefight:delete')
? {
type: 'delete',
btnName: '删除',
@@ -161,24 +162,28 @@ export default {
: undefined,
].filter((v) => v),
tableProps: [
// {
// prop: 'createTime',
// label: '添加时间',
// fixed: true,
// width: 180,
// filter: (val) => moment(val).format('yyyy-MM-DD HH:mm:ss'),
// },
{ prop: 'name', label: '设备名称' },
{ width: 256, prop: 'code', label: '设备编码' },
{ prop: 'location', label: '位置' },
{ prop: 'responsiblePeopleName', label: '负责人' },
{ prop: 'name', label: '设备名称', showOverflowtooltip: true },
{
width: 200,
prop: 'code',
label: '设备编码',
showOverflowtooltip: true,
},
{ prop: 'location', label: '位置', showOverflowtooltip: true },
{
prop: 'responsiblePeopleName',
label: '负责人',
showOverflowtooltip: true,
},
{
prop: 'dueDate',
width: 200,
label: '有效期至',
howOverflowTooltip: true,
filter: (val) =>
val != null ? moment(val).format('yyyy-MM-DD HH:mm:ss') : '--',
},
{ prop: 'remark', label: '备注' },
{ prop: 'remark', label: '备注', showOverflowtooltip: true },
],
searchBarFormConfig: [
{
@@ -203,18 +208,14 @@ export default {
type: 'separate',
},
{
type: this.$auth.hasPermi('base:core-equipment:export')
? 'button'
: '',
type: this.$auth.hasPermi('se:firefight:export') ? 'button' : '',
btnName: '导出',
name: 'export',
plain: true,
color: 'primary',
},
{
type: this.$auth.hasPermi('base:core-equipment:create')
? 'button'
: '',
type: this.$auth.hasPermi('se:firefight:create') ? 'button' : '',
btnName: '新增',
name: 'add',
plain: true,
@@ -293,7 +294,7 @@ export default {
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
pageSize: 20,
code: '',
name: '',
},
@@ -345,9 +346,6 @@ export default {
mounted() {
this.getList();
},
activated() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
@@ -413,34 +411,16 @@ export default {
this.$nextTick(() => {
this.$refs['drawer'].init();
});
// this.reset();
// this.showUploadComponents = false;
// this.editMode = 'edit';
// const id = row.id;
// getEquipment(id).then((response) => {
// this.form = response.data;
// this.open = true;
// this.title = '修改设备';
// });
},
handleDetail(row, mode='detail') {
const {id} = row;
handleDetail(row, mode = 'detail') {
const { id } = row;
this.form.id = id;
// 打开抽屉
this.editMode = mode;
this.editVisible = true;
this.$nextTick(() => {
this.$refs['drawer'].init();
})
// this.reset();
// this.showUploadComponents = false;
// const id = row.id;
// this.editMode = 'detail';
// getEquipment(id).then((response) => {
// this.form = response.data;
// this.open = true;
// this.title = '查看详情';
// });
});
},
/** 提交按钮 */
submitForm() {
@@ -514,33 +494,6 @@ export default {
this.$refs['drawer'].init();
});
},
// overwrite basicPageMixin 里的 处理表格按钮 方法
// handleTableBtnClick({ data, type }) {
// switch (type) {
// case 'edit':
// this.reset();
// this.editMode = 'edit';
// this.showUploadComponents = true;
// this.form.id = data.id;
// this.editVisible = true;
// this.$nextTick(() => {
// this.$refs['drawer'].init();
// });
// break;
// case 'delete':
// this.handleDelete(data);
// break;
// case 'detail':
// const { id } = data;
// this.viewDetail(id);
// break;
// }
// },
},
};
</script>
<style scoped lang="scss">
.SpecialEquipmentForFireFighting {
}
</style>

View File

@@ -19,6 +19,7 @@
:page="queryParams.pageNo"
:limit="queryParams.pageSize"
:table-data="list"
:max-height="tableH"
@emitFun="handleEmitFun">
<method-btn
v-if="tableBtn.length"
@@ -115,6 +116,7 @@
<script>
import moment from 'moment';
import basicPageMixin from '@/mixins/lb/basicPageMixin';
import tableHeightMixin from '@/mixins/lb/tableHeightMixin';
import EquipmentDrawer from '../components/manageDrawer';
import BaseDialogWrapper from '../components/BaseDialogWrapper.vue';
@@ -133,26 +135,26 @@ export default {
components: {
Editor,
EquipmentDrawer,
BaseDialog: BaseDialogWrapper
BaseDialog: BaseDialogWrapper,
},
mixins: [basicPageMixin],
mixins: [basicPageMixin, tableHeightMixin],
data() {
return {
searchBarKeys: ['name', 'code'],
tableBtn: [
this.$auth.hasPermi(`base:core-equipment:update`)
this.$auth.hasPermi(`se:manage:update`)
? {
type: 'detail',
btnName: '详情',
}
: undefined,
this.$auth.hasPermi('base:core-equipment:update')
this.$auth.hasPermi('se:manage:update')
? {
type: 'edit',
btnName: '修改',
}
: undefined,
this.$auth.hasPermi('base:core-equipment:delete')
this.$auth.hasPermi('se:manage:delete')
? {
type: 'delete',
btnName: '删除',
@@ -167,11 +169,20 @@ export default {
// width: 180,
// filter: (val) => moment(val).format('yyyy-MM-DD HH:mm:ss'),
// },
{ prop: 'name', label: '设备名称' },
{ width: 256, prop: 'code', label: '设备编码' },
{ prop: 'location', label: '位置' },
{ prop: 'responsiblePeopleName', label: '负责人' },
{ prop: 'remark', label: '备注' },
{ prop: 'name', label: '设备名称', showOverflowtooltip: true },
{
width: 256,
prop: 'code',
label: '设备编码',
showOverflowtooltip: true,
},
{ prop: 'location', label: '位置', showOverflowtooltip: true },
{
prop: 'responsiblePeopleName',
label: '负责人',
showOverflowtooltip: true,
},
{ prop: 'remark', label: '备注', showOverflowtooltip: true },
],
searchBarFormConfig: [
{
@@ -196,7 +207,7 @@ export default {
type: 'separate',
},
{
type: this.$auth.hasPermi('base:core-equipment:export')
type: this.$auth.hasPermi('se:manage:export')
? 'button'
: '',
btnName: '导出',
@@ -205,7 +216,7 @@ export default {
color: 'primary',
},
{
type: this.$auth.hasPermi('base:core-equipment:create')
type: this.$auth.hasPermi('se:manage:create')
? 'button'
: '',
btnName: '新增',
@@ -277,7 +288,7 @@ export default {
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
pageSize: 20,
code: '',
name: '',
},
@@ -329,9 +340,6 @@ export default {
mounted() {
this.getList();
},
activated() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
@@ -407,15 +415,15 @@ export default {
// this.title = '修改设备';
// });
},
handleDetail(row, mode='detail') {
const {id} = row;
handleDetail(row, mode = 'detail') {
const { id } = row;
this.form.id = id;
// 打开抽屉
this.editMode = mode;
this.editVisible = true;
this.$nextTick(() => {
this.$refs['drawer'].init();
})
});
// this.reset();
// this.showUploadComponents = false;
// const id = row.id;

View File

@@ -19,6 +19,7 @@
:page="queryParams.pageNo"
:limit="queryParams.pageSize"
:table-data="list"
:max-height="tableH"
@emitFun="handleEmitFun">
<method-btn
v-if="tableBtn.length"
@@ -90,13 +91,13 @@
pageSize: 10,
},
tableBtn: [
this.$auth.hasPermi('base:core-equipment-attr:update')
this.$auth.hasPermi('se:safety-attr:update')
? {
type: 'edit',
btnName: '修改',
}
: undefined,
this.$auth.hasPermi('base:core-equipment-attr:delete')
this.$auth.hasPermi('se:safety-attr:delete')
? {
type: 'delete',
btnName: '删除',
@@ -115,6 +116,7 @@
<script>
import moment from 'moment';
import basicPageMixin from '@/mixins/lb/basicPageMixin';
import tableHeightMixin from '@/mixins/lb/tableHeightMixin';
import EquipmentDrawer from '../components/safetyDrawer';
import {
createEquipment,
@@ -128,30 +130,30 @@ import AssetsUpload from '../components/AssetsUpload.vue';
import BaseDialogWrapper from '../components/BaseDialogWrapper.vue';
export default {
name: 'SpecialEquipmentForSafety',
name: 'specialEquipmentForSafety',
components: {
Editor,
EquipmentDrawer,
BaseDialog: BaseDialogWrapper,
},
mixins: [basicPageMixin],
mixins: [basicPageMixin, tableHeightMixin],
data() {
return {
searchBarKeys: ['name', 'code'],
tableBtn: [
this.$auth.hasPermi(`base:core-equipment:update`)
this.$auth.hasPermi(`se:safety:update`)
? {
type: 'detail',
btnName: '详情',
}
: undefined,
this.$auth.hasPermi('base:core-equipment:update')
this.$auth.hasPermi('se:safety:update')
? {
type: 'edit',
btnName: '修改',
}
: undefined,
this.$auth.hasPermi('base:core-equipment:delete')
this.$auth.hasPermi('se:safety:delete')
? {
type: 'delete',
btnName: '删除',
@@ -166,11 +168,20 @@ export default {
// width: 180,
// filter: (val) => moment(val).format('yyyy-MM-DD HH:mm:ss'),
// },
{ prop: 'name', label: '设备名称' },
{ width: 256, prop: 'code', label: '设备编码' },
{ prop: 'location', label: '位置' },
{ prop: 'responsiblePeopleName', label: '负责人' },
{ prop: 'remark', label: '备注' },
{ prop: 'name', label: '设备名称', showOverflowtooltip: true },
{
width: 200,
prop: 'code',
label: '设备编码',
showOverflowtooltip: true,
},
{ prop: 'location', label: '位置', showOverflowtooltip: true },
{
prop: 'responsiblePeopleName',
label: '负责人',
showOverflowtooltip: true,
},
{ prop: 'remark', label: '备注', showOverflowtooltip: true },
],
searchBarFormConfig: [
{
@@ -195,7 +206,7 @@ export default {
type: 'separate',
},
{
type: this.$auth.hasPermi('base:core-equipment:export')
type: this.$auth.hasPermi('se:safety:export')
? 'button'
: '',
btnName: '导出',
@@ -204,7 +215,7 @@ export default {
color: 'primary',
},
{
type: this.$auth.hasPermi('base:core-equipment:create')
type: this.$auth.hasPermi('se:safety:create')
? 'button'
: '',
btnName: '新增',
@@ -275,7 +286,7 @@ export default {
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
pageSize: 20,
code: '',
name: '',
},
@@ -327,9 +338,6 @@ export default {
mounted() {
this.getList();
},
activated() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {

Some files were not shown because too many files have changed in this diff Show More