This commit is contained in:
yanran200730
2022-04-06 10:19:49 +08:00
parent 35e1dd482e
commit 7c413fa9b2
23 changed files with 6330 additions and 0 deletions

View File

@@ -0,0 +1,273 @@
<template>
<section class="already-list">
<ai-list isTabs>
<template #content>
<ai-search-bar bottomBorder>
<template #right>
<el-input size="small" v-model="search.title" placeholder="标题/编号"
@keyup.enter.native="page.current=1,getAppLeaveMessage()"
prefix-icon="iconfont iconSearch" clearable></el-input>
<el-button size="mini" type="primary" icon="iconfont iconSearch" style="margin-left:5px;"
@click="page.current=1,getAppLeaveMessage()">查询
</el-button>
<el-button size="mini" icon="el-icon-refresh-right" style="margin-left:5px;" @click="resetSearch">
重置
</el-button>
</template>
</ai-search-bar>
<ai-table :tableData="tableData"
:col-configs="colConfigs"
:total="total"
ref="aitableex"
:current.sync="search.current"
:size.sync="search.size"
@getList="getAppLeaveMessage">
<el-table-column label="是否公示" slot="isPublic" align="center" width="150">
<template v-slot="{row}">
<el-switch v-model="row.isPublic" @change="onChange(row)" active-value="0" inactive-value="1"
active-color="#D0D4DC" inactive-color="#5088FF"/>
</template>
</el-table-column>
<el-table-column label="操作" slot="options" align="center" width="150">
<template v-slot="{row}">
<el-button type="text" title="详情" @click="toDetail(row.id)" v-if="$permissions('app_appleavemessagereply_detail')">详情</el-button>
</template>
</el-table-column>
</ai-table>
</template>
</ai-list>
</section>
</template>
<script>
import {mapState} from "vuex";
import day from 'dayjs'
export default {
name: "alreadyList",
props: {
instance: Function,
dict: Object,
permissions: Function,
activeName: String,
areaId: String
},
data() {
return {
tableData: [],
columns: [
{
label: '留言编号',
prop: 'msgCode',
type: '',
dict: ''
},
{
label: '留言类型',
prop: 'type',
type: 'select',
dict: 'leaveMessageType'
},
{
label: '标题',
prop: 'title',
type: '',
dict: ''
},
{
label: '留言人',
prop: 'leaveName',
type: '',
dict: ''
},
{
label: '创建时间',
prop: 'createTime',
type: 'time',
dict: ''
},
{
label: '最后回复时间',
prop: 'lastReplyTime',
type: 'time',
dict: ''
},
{
label: '处理状态0、待回复1、已回复',
prop: 'status',
type: '',
dict: ''
},
{
label: '操作',
prop: 'operate',
type: '',
dict: ''
},
],
search: {
style: {},
title: ""
},
page: {
current: 1,
size: 10,
},
total: 0,
detailId: '',
}
},
methods: {
onChange({id, isPublic}) {
this.instance.post(`/app/appleavemessage/public?id=${id}`).then(res => {
if (res.code == 0) {
console.log(isPublic)
this.$message.success(isPublic == 1 ? "已公示" : "不公示")
this.getAppLeaveMessage()
}
})
},
navClick(item) {
this.navStatus = item.status
},
isPermit(params) {
return this.permissions ? this.permissions(params) : false
},
resetSearch() {
this.page.current = 1
this.page.size = 10
this.columns.map(c => {
if (c.type) this.search[c.prop] = null
})
Object.keys(this.search).forEach((e) => {
this.search[e] = null;
})
this.getAppLeaveMessage()
},
handleCurrentChange(val) {
this.page.current = val
this.getAppLeaveMessage()
},
handleSizeChange(val) {
this.page.size = val;
this.getAppLeaveMessage();
},
getAppLeaveMessage() {
this.search.status = this.activeName
this.search.areaId = this.user.info.areaId
this.instance.post("/app/appleavemessage/list", null, {
params: {
...this.search,
...this.page,
areaId: this.areaId
}
}).then(res => {
this.tableData = res.data.records
this.total = res.data.total
}
)
},
addOrUpdateAppLeaveMessage() {
this.instance.post("/app/appleavemessage/addOrUpdate", this.dialog.add).then(() => {
this.getAppLeaveMessage()
this.$message.success("添加或修改成功!")
}
)
},
deleteAppLeaveMessage(ids) {
this.$confirm("是否要删除这些账号?", {
type: 'warning'
}).then(() => {
this.instance.post("/app/appleavemessage/delete", null, {
params: {
ids: ids
}
}).then(() => {
this.getAppLeaveMessage()
this.$message.success("删除成功!")
}
)
}).catch(() => {
}
)
},
toDetail(id) {
this.$emit('toDetail', id)
}
},
filters: {
format(time) {
return time ? day(time).format("YYYY-MM-DD HH:mm") : '-'
}
},
mounted() {
if (this.dict) this.dict.load(this.columns.map(e => e.type == 'select' ? e.dict : ''))
this.resetSearch()
},
computed: {
...mapState(['user']),
colConfigs() {
return [
{ prop: 'msgCode', label: '编号', align: 'center' },
{ prop: 'title', label: '标题', align: 'center' },
{ prop: 'type', label: '类型', align: 'center',
render:(h,{row})=>[<span>{this.dict.getLabel('leaveMessageType', row.type)}</span>] },
{ prop: 'leaveName', label: '留言人', align: 'center' },
{ prop: 'createTime', label: '留言提交时间', align: 'center' },
{ prop: 'lastReplyTime', label: '最后回复时间', align: 'center' },
{ slot: 'isPublic'},
{ slot: 'options'},
]
}
}
}
</script>
<style lang="scss" scoped>
.already-list {
background-color: #f3f6f9;
width: 100%;
height: 100%;
overflow: hidden;
.el-table::before {
background-color: #fff !important;
}
.header {
padding: 0 16px;
width: 100%;
background-color: #ffffff;
height: 48px;
line-height: 48px;
box-shadow: inset 0px -1px 0px 0px #d8dce3;
}
.main-content {
box-sizing: border-box;
margin: 16px;
height: calc(100% - 80px);
background-color: white;
border: 1px solid #eee;
padding: 12px 16px;
.searchBar {
.el-col {
margin-bottom: 12px;
}
}
}
}
</style>

View File

@@ -0,0 +1,461 @@
<template>
<div class="message-detail iconPhoto-content">
<ai-detail>
<ai-title slot="title" :title="titleText" isShowBack isShowBottomBorder @onBackClick="$emit('back')"></ai-title>
<template #rightBtn>
<p class="resident_top_area" style="text-align: right;" v-if="data.status != '2' && $permissions('app_appleavemessagereply_edit')">
<el-button type="primary" class="el-icon-edit" v-if="data.status == '0'" @click="maskShow = true" style="width:104px">回复留言</el-button>
<el-button class="el-icon-switch-button del-btn-list" @click="close()" style="width:104px">关闭留言</el-button>
</p>
</template>
<template #content>
<ai-card :title="data.title">
<template #content>
<div class="content-main">
<div class="main-header mar-b16">
<h6 style="font-weight:600;">{{ data.title }}</h6>
<div class="time">
<span class="time-label" style="width: 243px;"
>留言编号<span style="color:#333">{{ data.msgCode }}</span></span
>
<span class="time-label"
>留言时间<span style="color:#333">{{ data.createTime }}</span></span
>
<span class="time-label" style="width: 258px;text-align: right"
>留言人<span style="color:#333">{{ data.leaveName }}&nbsp;&nbsp;{{ data.leavePhone }}</span></span
>
<svg class="status-icon" aria-hidden="true">
<use xlink:href="#iconno_response" v-if="data.status == '0'"></use>
<use xlink:href="#iconreplied" v-if="data.status == '1'"></use>
<use xlink:href="#iconfinished" v-if="data.status == '2'"></use>
</svg>
</div>
<p class="message-text border-t" style="padding: 16px">{{ data.content }}</p>
<div v-if="data.images.length">
<div class="content-img" v-viewer>
<img v-for="(item, index) in data.images" :src="item.accessUrl" alt="" :key="index" />
</div>
</div>
</div>
<div class="main-list">
<div class="main-header mar-b16">
<h6 style="font-weight:600;">沟通记录</h6>
</div>
<div v-for="(item, index) in data.appLeaveMessageReplyList" :key="index" :class="item.headPortrait == null ? 'message-for reply' : 'message-for'" v-if="data.appLeaveMessageReplyList.length > 0">
<div class="message-title">
<img :src="item.headPortrait" alt="" class="user-img" v-if="item.headPortrait" />
<span class="iconfont iconProfile_Picture" v-else></span>
<span class="user-name" v-if="!item.headPortrait">
<span style="color:#5088FF">{{ item.createUnitName }} &nbsp;<span style="color:#333">&nbsp;回复</span></span
><br />
<span style="color:#999;font-size:12px">操作员{{ item.createUserName }}{{ item.createUserPhone }}</span>
</span>
<span class="user-name" v-else>{{ item.createUserName }}</span>
<span style="color:#999;vertical-align: text-bottom">{{ item.createTime }}</span>
</div>
<p class="message-text">{{ item.content }}</p>
<div v-if="item.images.length">
<div class="message-img-list" v-viewer>
<img class="message-img" v-for="(items, index) in item.images" :src="items.accessUrl" :key="index" v-if="items.accessUrl" />
</div>
</div>
</div>
<div v-if="data.appLeaveMessageReplyList.length == 0" style="width:100%;text-align:center;color:#999;font-size:14px">暂无沟通记录</div>
</div>
</div>
</template>
</ai-card>
</template>
</ai-detail>
<ai-dialog :title="maskText" :visible.sync="maskShow" @onConfirm="confirm('ruleForm')" @onCancel="hideMask" :before-close="hideMask" width="720px">
<el-form :rules="rules" ref="ruleForm" :model="ruleForm" label-width="auto">
<el-form-item label="回复内容:" prop="content">
<el-input type="textarea" v-model.trim="ruleForm.content" :row="4" show-word-limit :maxlength="1000" placeholder="请输入回复内容"></el-input>
</el-form-item>
<el-form-item label="图片附件:" class="user">
<span class="upload-more left-84">(最多9张)</span>
<el-upload class="upload-demo upload-list-small" ref="upload" multiple action list-type="picture-card" :file-list="images" :http-request="uploadFile" :on-remove="handleRemove" :on-change="handleChange" accept="jpeg/jpg/png">
<div class="upload-img-small">
<span class="iconfont iconPhoto iconPhoto2"></span>
<div class="upload-text">上传照片</div>
</div>
</el-upload>
</el-form-item>
</el-form>
</ai-dialog>
</div>
</template>
<script>
import { mapState } from 'vuex'
// import 'viewerjs/dist/viewer.css'
import Viewer from 'v-viewer'
import Vue from 'vue'
Vue.use(Viewer)
export default {
name: 'messageDetail',
props: {
instance: Function,
dict: Object,
permissions: Function,
detailId: String,
},
data() {
return {
titleText: '留言详情',
maskShow: false,
maskText: '回复留言',
images: [],
data: {
appLeaveMessageReplyList: [],
},
rules: {
content: [{ required: true, message: '请输入回复内容', trigger: 'blur' }],
},
ruleForm: {
content: '',
},
}
},
computed: {
...mapState(['user']),
},
created() {},
mounted() {
console.log(this.user)
this.getDetailInfo()
},
methods: {
getDetailInfo() {
this.data.appLeaveMessageReplyList = []
this.instance.post(`app/appleavemessage/queryDetailById?id=` + this.detailId).then((res) => {
this.data = res.data
this.data.images = JSON.parse(res.data.images)
if (this.data.appLeaveMessageReplyList.length) {
this.data.appLeaveMessageReplyList.map((item) => {
if (item.images) {
item.images = JSON.parse(item.images || '[]')
}
return item
})
}
this.data.appLeaveMessageReplyList.reverse()
})
},
confirm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
this.confirmFn()
} else {
return false
}
})
},
confirmFn() {
let images = []
this.images.map((item) => {
images.push({ fileId: item.fileId, accessUrl: item.accessUrl })
})
let params = {
content: this.ruleForm.content,
images: JSON.stringify(images),
msgCode: this.data.msgCode,
userType: '1',
createUnitId: this.user.info.unitId,
createUnitName: this.user.info.unitName,
}
this.instance.post(`app/appleavemessagereply/addOrUpdate`, params).then((res) => {
console.log(res)
this.maskShow = false
this.getDetailInfo()
})
},
hideMask() {
this.maskShow = false
this.images = []
this.$nextTick(() => {
this.$refs.ruleForm.resetFields()
})
},
close() {
this.$confirm('关闭留言之后,双方都将无法再进行回复,是否确定关闭本次留言?', {
type: 'warning',
})
.then(() => {
let params = this.data
params.status = '2'
params.images = JSON.stringify(params.images)
if (params.appLeaveMessageReplyList.length) {
params.appLeaveMessageReplyList.map((item) => {
item.images = JSON.stringify(item.images)
return item
})
}
this.instance.post(`app/appleavemessage/addOrUpdate`, params).then((res) => {
this.getDetailInfo()
})
})
.catch(() => {})
},
// 上传照片
uploadFile(file) {
console.log(this.images.length > 9)
if (this.images.length > 9) {
this.$message.warning('上传图片不能超过9张')
this.images.map((item, index) => {
if (item.uid == file.file.uid) {
this.images.splice(index, 1)
}
return this.images
})
}
const isLt2M = file.file.size / 1024 / 1024 < 2
if (!isLt2M) {
this.$message.warning('图片大小不能超过 2MB!')
return
}
let formData = new FormData()
formData.append('file', file.file)
let file2 = formData
this.instance.post(`/admin/file/add`, file2, { withCredentials: false }).then((res) => {
if (res.code == 0) {
let imgInfo = res.data[0].split(';')
let img = {
fileId: imgInfo[1],
accessUrl: imgInfo[0],
}
this.images.map((item, index) => {
if (item.uid == file.file.uid) {
this.images[index].fileId = img.fileId
this.images[index].accessUrl = img.accessUrl
this.images[index].url = img.accessUrl
}
return this.images
})
}
})
},
handleChange(file, fileList) {
this.images = fileList
},
handleRemove(file, fileList) {
this.images = fileList
},
},
}
</script>
<style lang="scss" scoped>
.message-detail {
width: 100%;
height: 100%;
overflow: auto;
position: relative;
background-color: #f3f6f9;
.left-84 {
left: -84px;
}
.iconProfile_Picture {
font-size: 40px;
}
.iconBack_Large {
width: 16px;
height: 16px;
color: #2266ff;
cursor: pointer;
}
.content {
padding-top: 24px;
width: 100%;
height: calc(100% - 80px);
overflow-y: scroll;
.content-main {
width: 760px;
margin: 0 auto;
.main-header {
width: 100%;
box-sizing: border-box;
background-color: #fff;
border: 1px solid #eee;
h6 {
font-size: 16px;
color: #333;
padding: 0 16px;
min-height: 54px;
line-height: 54px;
box-sizing: border-box;
}
.time {
height: 54px;
line-height: 54px;
font-size: 12px;
padding: 0 16px;
box-sizing: border-box;
position: relative;
overflow: hidden;
.time-label {
color: #999;
display: inline-block;
}
}
.status-icon {
position: absolute;
width: 66px;
height: 66px;
top: -5px;
right: 0;
}
}
.main-list {
background-color: #fff;
padding-bottom: 80px;
.message-for {
width: 728px;
margin: 0 auto 24px auto;
padding: 16px 16px 8px;
box-sizing: border-box;
font-size: 14px;
.message-title {
height: 40px;
line-height: 40px;
margin-bottom: 6px;
.user-img {
display: inline-block;
width: 40px;
height: 40px;
border-radius: 50%;
background-color: red;
}
.user-name {
display: inline-block;
width: 510px;
color: #333;
vertical-align: text-bottom;
padding-left: 8px;
box-sizing: border-box;
}
}
.message-text {
padding-left: 48px;
color: #666;
margin-bottom: 16px;
}
.message-img-list {
padding-left: 48px;
.message-img {
display: inline-block;
width: 113px;
height: 113px;
margin: 0 16px 16px 0;
}
}
}
.reply {
background-color: #f5f6f7;
.message-title {
.user-name {
line-height: 20px;
}
}
}
}
}
}
.content-img {
padding: 0 16px;
img {
width: 84px;
height: 84px;
margin: 0 16px 16px 0;
}
}
.operation-foot {
overflow: hidden;
// position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 64px;
line-height: 64px;
display: flex;
align-items: center;
justify-content: center;
background-color: #f3f6f9;
box-shadow: inset 0px 1px 0px 0px #eeeeee;
button {
width: 92px;
height: 32px;
padding: 0 !important;
}
.delete-btn {
background-color: #fff;
}
}
.mask {
overflow: hidden;
.el-form-item__content {
float: left;
width: calc(100% - 100px) p {
font-size: 14px;
color: #222222;
}
}
.el-form-item__label {
display: inline-block;
width: 130px;
text-align: right;
font-size: 14px;
float: left;
}
.operation {
overflow: hidden;
// position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 64px;
line-height: 64px;
display: flex;
align-items: center;
justify-content: center;
background-color: #f3f6f9;
box-shadow: inset 0px 1px 0px 0px #eeeeee;
}
}
.status {
span {
display: inline-block;
width: 24px;
height: 24px;
line-height: 24px;
text-align: center;
}
}
.status0 {
background-color: #eff6ff;
color: #2266ff;
}
.status1 {
background-color: #e8ecff;
color: #2244ff;
}
.status2 {
background-color: #fff3e8;
color: #ff8822;
}
.status3 {
background-color: #eaf5e8;
color: #2ea222;
}
.icon {
display: inline-block;
width: 32px;
height: 32px;
margin-top: 16px;
}
}
</style>