update:新增简单举报详情,新增图库管理

This commit is contained in:
lihaoyuan 2025-09-04 16:25:14 +08:00
parent b6d789a975
commit 4b535ea385
9 changed files with 1158 additions and 214 deletions

View File

@ -64,10 +64,11 @@ export function getSmsRecord(params) {
params, params,
}) })
} }
export function getReportList(){ export function getReportList(params){
return request({ return request({
url:URL.account.base.config.report_list, url:URL.account.base.config.report_list,
method:'get', method:'get',
params:params
}) })
} }
export function dealReport(data){ export function dealReport(data){

View File

@ -126,7 +126,23 @@ export async function syncShopImages(data) {
method: 'post', method: 'post',
}) })
} }
export async function getImgList(data) {
data = stringify(data)
return request({
url: `/admin/shop/libraryProduct/list?${data}`,
method:'get',
})
}
export async function saveBatchBarcode(data) {
return request({
url:'/admin/shop/libraryProduct/saveBatch',
method:'put',
data,
headers: {
'Content-Type': 'application/json;charset=UTF-8'
}
})
}
export default { export default {
getProductMapperList, getProductMapperList,
getShopList, getShopList,
@ -141,4 +157,6 @@ export default {
syncProductMaping, syncProductMaping,
importGoodsData, importGoodsData,
syncShopImages, syncShopImages,
getImgList,
saveBatchBarcode
} }

View File

@ -30,7 +30,22 @@ export function convertRouter(asyncRoutes) {
route.children.push(obj) route.children.push(obj)
} }
if (route.meta.title == '商品' && route.name == 'Vab320') {
const obj = {
path: '/goodsImgs',
component: '@/views/product/goodsImg/goodsImgs',
name: 'Vab88000',
redirect: null,
meta: {
title: '商品图库管理',
icon: '',
noClosable: 0,
hidden: null,
},
menuHidden: false,
}
route.children.push(obj)
}
if (route.meta.title == '店铺' && route.name == 'Vab330') { if (route.meta.title == '店铺' && route.name == 'Vab330') {
const obj = { const obj = {
path: '/storeConf', path: '/storeConf',

View File

@ -0,0 +1,177 @@
<template>
<el-dialog
title="批量编辑商品编码"
:visible.sync="visible"
width="600px"
:close-on-click-modal="false"
:max-height="600"
@close="handleClose"
>
<div class="batch-tip">
共选中
{{ selectedData.length }} 个商品请为每个商品设置编码仅填写需要修改的
</div>
<!-- 批量编辑列表 -->
<div
class="edit-list"
style="max-height: 400px; overflow-y: auto; padding-right: 10px"
>
<el-form ref="batchForm" :model="formData" label-width="100px">
<!-- 循环渲染每个选中的商品 -->
<div
v-for="(item, index) in formData.items"
:key="item.id"
class="edit-item"
>
<el-form-item label="商品ID" :label-width="100">
<div class="product-id">{{ item.id }}</div>
</el-form-item>
<el-form-item label="商品名称" :label-width="100">
<div class="product-name">{{ item.name }}</div>
</el-form-item>
<el-form-item
label="商品编码"
:prop="`items[${index}].barcode`"
:rules="[
{ required: false, message: '请输入商品编码', trigger: 'blur' },
]"
:label-width="100"
>
<el-input
v-model="item.barcode"
placeholder="请输入商品编码"
clearable
maxlength="50"
/>
</el-form-item>
<el-divider v-if="index !== formData.items.length - 1" />
</div>
</el-form>
</div>
<template #footer>
<el-button @click="handleClose">取消</el-button>
<el-button type="primary" @click="handleSubmit">提交修改</el-button>
</template>
</el-dialog>
</template>
<script>
import GoodsToolApi from '@/api/goodsTool'
export default {
data() {
return {
visible: false,
selectedData: [], //
formData: {
items: [], //
},
}
},
methods: {
//
open(selection) {
this.visible = true
this.selectedData = [...selection]
//
this.formData.items = this.selectedData.map((item) => ({
id: item.id,
name: item.name || '未知名称',
barcode: item.barcode || '', //
}))
//
this.$nextTick(() => {
if (this.$refs.batchForm) {
this.$refs.batchForm.clearValidate()
}
})
},
handleClose() {
this.visible = false
this.selectedData = []
this.formData.items = []
if (this.$refs.batchForm) {
this.$refs.batchForm.clearValidate()
}
},
//
async handleSubmit() {
//
try {
await this.$refs.batchForm.validate()
} catch (error) {
return false
}
// -
const submitData = this.formData.items
.filter((item) => item.barcode.trim() !== '') //
.map((item) => ({
id: item.id,
name: item.name,
barcode: item.barcode.trim(),
}))
if (submitData.length === 0) {
this.$message.warning('请至少填写一个商品编码')
return
}
try {
//
const res = await GoodsToolApi.saveBatchBarcode(submitData)
console.log('批量修改结果:', res)
this.$message.success(`成功修改 ${submitData.length} 个商品编码`)
this.handleClose()
this.$emit('success') //
} catch (error) {
console.error('批量修改失败:', error)
this.$message.error('修改失败,请重试')
}
},
},
}
</script>
<style lang="scss" scoped>
.batch-tip {
color: #666;
padding: 8px 12px;
background-color: #f5f7fa;
border-radius: 4px;
margin-bottom: 16px;
font-size: 13px;
border-left: 3px solid #409eff;
}
.edit-item {
padding: 10px 0;
}
.product-name {
line-height: 32px;
color: #333;
max-width: 400px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.product-id {
line-height: 32px;
color: #666;
font-size: 13px;
}
::v-deep .el-form-item {
margin-bottom: 10px;
}
</style>

View File

@ -0,0 +1,158 @@
<template>
<el-dialog
:title="dialogTitle"
:visible.sync="visible"
width="600px"
:close-on-click-modal="false"
@close="handleClose"
>
<el-form
ref="editForm"
:model="formData"
:rules="formRules"
label-width="120px"
class="edit-form"
>
<el-form-item label="商品id" prop="id">
<el-input
v-model="formData.id"
placeholder="请输入商品id"
disabled
/>
</el-form-item>
<el-form-item label="商品名称" prop="name">
<el-input
v-model="formData.name"
placeholder="请输入商品名称"
disabled
/>
</el-form-item>
<el-form-item label="商品编码" prop="barcode">
<el-input
v-model="formData.barcode"
placeholder="请输入商品编码"
clearable
/>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="handleClose">取消</el-button>
<el-button type="primary" @click="handleSubmit">提交</el-button>
</template>
</el-dialog>
</template>
<script>
import GoodsToolApi from '@/api/goodsTool'
export default {
data() {
return {
visible: false,
isBatch: false, //
selectedData: [], //
formData: {
id: '',
barcode: '',
name: ''
},
formRules: {
barcode: [
{ required: false, message: '请输入商品编码', trigger: 'blur' }
],
}
}
},
computed: {
//
dialogTitle() {
return this.isBatch ? '批量编辑商品' : '编辑商品'
},
//
selectedCount() {
return this.selectedData.length
}
},
methods: {
//
open(options) {
this.isBatch = options.isBatch
this.selectedData = options.data instanceof Array ? options.data : [options.data]
this.visible = true
//
if (!this.isBatch && this.selectedData.length > 0) {
const item = this.selectedData[0]
this.formData = {
name: item.name || '',
barcode: item.barcode || '',
id: item.id || ''
}
} else {
//
this.formData = {
name: '',
barcode: '',
id: ''
}
}
//
this.$nextTick(() => {
this.$refs.editForm.clearValidate()
})
},
//
handleClose() {
this.visible = false
this.$refs.editForm.clearValidate()
},
//
async handleSubmit() {
//
try {
await this.$refs.editForm.validate()
} catch (error) {
return false
}
// -
const updateData = this.selectedData.map(item => {
const data = { id: item.id }
if (this.formData.barcode !== '') data.barcode = this.formData.barcode
return data
})
try {
let res=await GoodsToolApi.saveBatchBarcode(updateData)
this.$message.success(this.isBatch ? '批量编辑成功' : '编辑成功')
this.visible = false
//
this.$emit('success')
} catch (error) {
console.error('编辑商品失败:', error)
this.$message.error('编辑失败,请重试')
}
}
}
}
</script>
<style lang="scss" scoped>
.edit-form {
margin-top: 20px;
}
.batch-tip {
color: #666;
padding: 8px 12px;
background-color: #f5f7fa;
border-radius: 4px;
margin-bottom: 20px;
font-size: 13px;
border-left: 3px solid #409eff;
}
</style>

View File

@ -0,0 +1,301 @@
<template>
<div class="container">
<div class="filter">
<el-input
v-model="filter.name"
class="input_item"
clearable
placeholder="请输入商品名字"
prefix-icon="el-icon-search"
/>
<el-input
v-model="filter.barcode"
class="input_item"
clearable
placeholder="请输入商品编码"
prefix-icon="el-icon-barcode"
/>
<el-select
v-model="filter.barcodeEmty"
clearable
placeholder="编码是否为空"
class="input_item"
>
<el-option label="无编码" value="yes" />
<el-option label="有编码" value="no" />
</el-select>
<el-button size="medium" type="primary" @click="handleSearch">
查询
</el-button>
<el-button size="medium" @click="handleReset">重置</el-button>
<el-button size="medium" type="warning" @click="handleBatchEdit">
批量编辑
</el-button>
</div>
<div class="list">
<el-table
ref="imgTable"
:data="tableData"
style="width: 100%"
border
stripe
:empty-text="tableData.length === 0 ? '暂无商品数据' : '加载中...'"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="55" />
<el-table-column label="操作" width="120">
<template #default="scope">
<el-button
plain
size="mini"
type="primary"
@click="handleEdit(scope.row)"
>
编辑
</el-button>
</template>
</el-table-column>
<el-table-column label="商品id" prop="id" width="120px" />
<el-table-column label="商品主图" width="180px">
<template #default="scope">
<div v-if="scope.row.thumb" class="img-group">
<el-image
:src="scope.row.thumb"
:preview-src-list="[scope.row.thumb]"
class="img-thumb"
fit="cover"
:title="scope.row.name || '商品主图'"
/>
</div>
<span v-else class="no-data">无主图</span>
</template>
</el-table-column>
<el-table-column label="商品名称" prop="name" width="300">
<template #default="scope">
<div class="text-ellipsis" :title="scope.row.name">
{{ scope.row.name }}
</div>
</template>
</el-table-column>
<el-table-column label="商品编码" prop="barcode" width="150">
<template #default="scope">
<div :title="scope.row.barcode || '无编码'">
{{ scope.row.barcode || '无' }}
</div>
</template>
</el-table-column>
<el-table-column label="分类" prop="category" width="120" />
<el-table-column label="价格" width="120">
<template #default="scope">
{{ Number(scope.row.price).toFixed(2) }}
</template>
</el-table-column>
<el-table-column label="创建时间" prop="createdAt" width="180" />
<el-table-column label="更新时间" prop="updatedAt" width="180" />
</el-table>
<el-pagination
background
:current-page="pagination.pageNum"
:page-size="pagination.pageSize"
:total="pagination.total"
layout="total, sizes, prev, pager, next, jumper"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
:disabled="pagination.total === 0"
/>
</div>
<editGoodsImgs ref="goodsEdit" @success="handleEditSuccess" />
<batchEditBarcode ref="batchEditBarcode" @success="handleEditSuccess" />
</div>
</template>
<script>
import GoodsToolApi from '@/api/goodsTool'
import editGoodsImgs from './editGoodsImgs.vue'
import batchEditBarcode from './batchEditBarcode.vue'
export default {
data() {
return {
tableData: [],
pagination: {
pageNum: 1,
pageSize: 10,
total: 0,
pages: 0,
},
filter: {
name: '',
barcode: '',
barcodeEmty: '', // : 'yes', 'no'
},
multipleSelection: [],
}
},
components: {
editGoodsImgs,
batchEditBarcode,
},
mounted() {
this.handleImgList()
},
methods: {
async handleImgList() {
try {
//
const params = {
pageNum: this.pagination.pageNum,
pageSize: this.pagination.pageSize,
//
...(this.filter.name && { name: this.filter.name }),
...(this.filter.barcode && { barcode: this.filter.barcode }),
...(this.filter.barcodeEmty && {
barcodeEmty: this.filter.barcodeEmty,
}),
}
const res = await GoodsToolApi.getImgList(params)
this.tableData = res.records || []
this.pagination.total = res.total || 0
this.pagination.pages = res.pages || 0
this.pagination.current = res.current || 1
console.log('商品列表数据获取成功', {
tableData: this.tableData,
pagination: this.pagination,
params: params,
})
} catch (error) {
this.$message.error('获取数据失败,请重试')
this.tableData = []
}
},
//
handleSearch() {
//
this.pagination.pageNum = 1
this.handleImgList()
},
//
handleReset() {
//
this.filter = {
name: '',
barcode: '',
barcodeEmty: '',
}
//
this.pagination.pageNum = 1
this.handleImgList()
},
handleCurrentChange(pageNum) {
this.pagination.pageNum = pageNum
this.handleImgList()
},
handleSizeChange(pageSize) {
this.pagination.pageSize = pageSize
this.pagination.pageNum = 1
this.handleImgList()
},
handleSelectionChange(val) {
this.multipleSelection = val
},
// open
handleEdit(row) {
this.$refs.goodsEdit.open({
isBatch: false, //
data: row, //
})
},
handleBatchEdit() {
if (this.multipleSelection.length === 0) {
this.$message.warning('请先选中需要编辑的商品');
return;
}
this.$refs.batchEditBarcode.open(this.multipleSelection);
},
//
handleEditSuccess() {
this.handleImgList()
//
this.$refs.imgTable.clearSelection()
this.multipleSelection = []
},
},
}
</script>
<style lang="scss" scoped>
.container {
padding: 20px;
background-color: #f5f7fa;
}
.filter {
display: flex;
align-items: center;
padding: 15px 20px;
margin-bottom: 15px;
gap: 10px;
border-radius: 5px;
box-shadow: 1px 1px 6px rgba(0, 0, 0, 0.1);
background: #fff;
flex-wrap: wrap;
}
.input_item {
width: 200px;
}
.list {
background: #fff;
padding: 20px;
display: flex;
flex-direction: column;
gap: 10px;
border-radius: 5px;
box-shadow: 1px 1px 6px rgba(0, 0, 0, 0.1);
}
.img-group {
position: relative;
display: inline-block;
}
.img-thumb {
width: 50px;
height: 50px;
cursor: pointer;
border-radius: 3px;
}
.text-ellipsis {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.no-data {
color: #999;
font-size: 12px;
}
::v-deep .el-table__cell {
vertical-align: middle !important;
}
::v-deep .el-pagination {
margin-top: 10px;
text-align: right;
}
</style>

View File

@ -2,14 +2,14 @@
<div class="report_container"> <div class="report_container">
<div class="filter"> <div class="filter">
<el-input <el-input
v-model="filter.reportName" v-model="filter.reporterNickname"
class="input_item" class="input_item"
clearable clearable
placeholder="请输入举报人或举报人的手机号/ID/昵称" placeholder="请输入举报人昵称"
prefix-icon="el-icon-search" prefix-icon="el-icon-search"
/> />
<el-select <el-select
v-model="filter.state" v-model="filter.processingStatus"
clearable clearable
filterable filterable
placeholder="选择处理状态" placeholder="选择处理状态"
@ -31,56 +31,109 @@
ref="reportTable" ref="reportTable"
:data="tableData" :data="tableData"
style="width: 100%" style="width: 100%"
border
stripe
> >
<el-table-column label="举报正文" prop="reportContent" width="180px"/>
<el-table-column label="举报凭证" prop="reportVerify"> <el-table-column label="举报正文" prop="reportContent" width="180px">
<template #default="scope"> <template #default="scope">
<div v-if="scope.row.reportVerify && scope.row.reportVerify.length"> <div class="text-ellipsis" :title="scope.row.reportContent">
{{ scope.row.reportContent }}
</div>
</template>
</el-table-column>
<el-table-column label="举报凭证" prop="reportVerify" width="180px">
<template #default="scope">
<div
v-if="scope.row.reportVerify && scope.row.reportVerify.length"
class="img-group"
>
<el-image <el-image
v-for="(img, index) in scope.row.reportVerify" v-for="(img, index) in scope.row.reportVerify"
:key="index" :key="index"
:src="img.url" :src="img.url"
:preview-src-list="scope.row.reportVerify.map(item => item.url)" :preview-src-list="scope.row.reportVerify.map((item) => item.url)"
style="width: 50px; height: 50px; margin-right: 5px; cursor: pointer" class="img-thumb"
fit="cover" fit="cover"
:title="img.description || `举报凭证${index + 1}`"
v-if="index < 3"
/> />
<span v-if="scope.row.reportVerify.length > 3" class="img-count">
+{{ scope.row.reportVerify.length - 3 }}
</span>
</div> </div>
<span v-else>无凭证</span> <span v-else class="no-data">无凭证</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="举报人昵称" prop="reporterName" width="100" /> <el-table-column label="举报人昵称" prop="reporterNickname" width="120" />
<el-table-column <el-table-column
label="举报人手机号" label="举报人手机号"
prop="reporterNumber" prop="reporterPhone"
width="120" width="130"
/> />
<el-table-column label="举报时间" prop="reporterTime" /> <el-table-column label="被举报人昵称" prop="reportedNickname" width="120" />
<el-table-column label="状态" prop="state"> <el-table-column label="被举报人用户ID" prop="reportedUserId" width="140" />
<el-table-column label="举报时间" prop="reporterTime" width="180" />
<el-table-column label="状态" prop="processingStatus" width="110">
<template #default="scope"> <template #default="scope">
<el-tag <el-tag :type="getStatusTagType(scope.row.processingStatus)">
:type="scope.row.state === '未处理' ? 'warning' : 'success'" {{ getStatusText(scope.row.processingStatus) }}
>
{{ scope.row.state }}
</el-tag> </el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="被举报人名称" prop="reportedName" width="120" /> <!-- 处理凭证后台上传 -->
<el-table-column <el-table-column label="处理凭证" prop="processingVerify" width="180px">
label="被举报人手机号" <template #default="scope">
prop="reportedNumber" <div
width="130" v-if="scope.row.processingVerify && scope.row.processingVerify.length"
/> class="img-group"
<el-table-column label="被举报人用户ID" prop="reportedId" width="130" /> >
<el-table-column label="操作"> <el-image
v-for="(img, index) in scope.row.processingVerify"
:key="index"
:src="img.url"
:preview-src-list="scope.row.processingVerify.map((item) => item.url)"
class="img-thumb"
fit="cover"
:title="img.description || `处理凭证${index + 1}`"
v-if="index < 3"
/>
<span v-if="scope.row.processingVerify.length > 3" class="img-count">
+{{ scope.row.processingVerify.length - 3 }}
</span>
</div>
<span v-else class="no-data">无处理凭证</span>
</template>
</el-table-column>
<!-- 处理结果 -->
<el-table-column label="处理结果" prop="processingResult" width="180px">
<template #default="scope">
<div class="text-ellipsis" :title="scope.row.processingResult">
{{ scope.row.processingResult || '未处理' }}
</div>
</template>
</el-table-column>
<el-table-column label="操作" width="200">
<template #default="scope"> <template #default="scope">
<el-button <el-button
plain plain
size="mini" size="mini"
type="primary" type="primary"
@click="handleReport(scope.row)" @click="handleReport(scope.row)"
:disabled="scope.row.processingStatus === '处理完成'"
> >
处理举报 处理举报
</el-button> </el-button>
<el-button
plain
size="mini"
type="primary"
@click="handleDetail(scope.row)"
>
详情
</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -95,158 +148,216 @@
@size-change="handleSizeChange" @size-change="handleSizeChange"
/> />
<ReportDeal ref="handleReport" @success="handleSuccess" /> <ReportDeal ref="handleReport" @success="handleSuccess" />
<ReportDetail ref="handleDetail" @success="handleSuccess" />
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import ReportDeal from './reportDeal.vue'; import ReportDeal from './reportDeal.vue'
import { getReportList } from '@/api/base/config'; import ReportDetail from './reportDetail.vue'
import { getReportList } from '@/api/base/config'
export default { export default {
components: { components: {
ReportDeal ReportDeal,
ReportDetail,
}, },
data() { data() {
return { return {
filter: { filter: {
reportName: '', reporterNickname: '',
state: '', processingStatus: '',
}, },
//
reportList: [ reportList: [
{ report_id: 0, report_name: "未处理" }, { report_id: 0, report_name: '未处理' },
{ report_id: 1, report_name: "已驳回" }, { report_id: 1, report_name: '已受理' },
{report_id:2,report_name:'处理完成'}, { report_id: 2, report_name: '已驳回' },
{ report_id: 3, report_name: '处理完成' },
], ],
tableData: [], tableData: [],
pagination: { pagination: {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
total: 0, total: 0,
pages: 0 pages: 0,
}, },
multipleSelection: [] multipleSelection: [],
} }
}, },
mounted() { mounted() {
this.handleReportList(); this.handleReportList()
}, },
methods: { methods: {
//
handleReport(row) { handleReport(row) {
this.$refs.handleReport.open(row); this.$refs.handleReport.open(row)
},
handleDetail(row) {
this.$refs.handleDetail.open(row)
}, },
//
handleSuccess() { handleSuccess() {
this.handleReportList(); this.handleReportList()
}, },
//
handleSelectionChange(val) { handleSelectionChange(val) {
this.multipleSelection = val; this.multipleSelection = val
}, },
//
async handleReportList() { async handleReportList() {
try { try {
const params = { const params = {
pageNum: this.pagination.pageNum, pageNum: this.pagination.pageNum,
pageSize: this.pagination.pageSize, pageSize: this.pagination.pageSize,
reportName: this.filter.reportName, reporterNickname: this.filter.reporterNickname,
state: this.filter.state processingStatus: this.filter.processingStatus,
}; }
let res = await getReportList(params); const res = await getReportList(params)
console.log('ssssss', params)
// this.tableData = (res.records || []).map((item) => ({
this.tableData = (res.records || []).map(item => ({
id: item.id, id: item.id,
reportContent: item.reportContent, reportContent: item.reportContent,
reportVerify: item.evidenceImages //
reportVerify: item.evidenceImages
? JSON.parse(item.evidenceImages) ? JSON.parse(item.evidenceImages)
: [], : [],
reporterName: item.reporterNickname, //
reporterNumber: item.reporterPhone || '无', processingVerify: item.processingEvidenceImages
? JSON.parse(item.processingEvidenceImages)
: [],
//
processingResult: item.processingResult || '',
reporterNickname: item.reporterNickname,
reporterPhone: item.reporterPhone || '无',
reporterTime: item.createdAt, reporterTime: item.createdAt,
state: this.getStatusText(item.processingStatus), processingStatus: this.getStatusText(item.processingStatus),
reportedName: item.reportedNickname, reportedNickname: item.reportedNickname,
reportedNumber: item.reportedPhone || '无', reportedUserId: item.reportedUserId || '无',
reportedId: item.reportedUserId // 使
})); rawData: item,
}))
// this.pagination = {
this.pagination = { ...this.pagination,
...this.pagination, total: res.total || 0,
total: res.total || 0, pageNum: res.current || 1,
pageNum: res.current || 1, pageSize: res.size || 10,
pageSize: res.size || 10, pages: res.pages || 0,
pages: res.pages || 0 }
}; } catch (error) {
} catch (error) { console.error('获取举报列表失败:', error)
console.error('获取举报列表失败:', error); this.$message.error('获取数据失败,请重试')
this.$message.error('获取数据失败,请重试'); }
} },
}, handleCurrentChange(val) {
// this.pagination.pageNum = val
handleCurrentChange(val) { this.handleReportList()
this.pagination.pageNum = val; },
this.handleReportList(); handleSizeChange(val) {
}, this.pagination.pageSize = val
// this.pagination.pageNum = 1
handleSizeChange(val) { this.handleReportList()
this.pagination.pageSize = val; },
this.pagination.pageNum = 1; handleSearch() {
this.handleReportList(); this.pagination.pageNum = 1
}, this.handleReportList()
// },
handleSearch() { getStatusText(status) {
this.pagination.pageNum = 1; switch (status) {
this.handleReportList(); case 0:
}, return '未处理'
// case 1:
getStatusText(status) { return '已受理'
switch (status) { case 2:
case 0: return '已驳回'
return '未处理'; case 3:
case 1: return '处理完成'
return '已受理'; default:
case 2: return '未知状态'
return '已驳回'; }
case 3: },
return '处理完成'; getStatusTagType(state) {
default: switch (state) {
return '未知状态'; case '未处理':
} return 'warning'
case '已受理':
return 'info'
case '已驳回':
return 'danger'
case '处理完成':
return 'success'
default:
return 'default'
}
},
}, },
} }
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.filter { .filter {
display: flex; display: flex;
align-items: center; align-items: center;
padding: 20px; padding: 20px;
margin-bottom: 15px; margin-bottom: 15px;
gap: 10px; gap: 10px;
border-radius: 5px; border-radius: 5px;
box-shadow: 1px 1px 6px rgba(0, 0, 0, 0.1); box-shadow: 1px 1px 6px rgba(0, 0, 0, 0.1);
background: #fff; background: #fff;
.input_item { .input_item {
width: 250px; width: 250px;
}
} }
} .list {
.list { background: #fff;
background: #fff; padding: 20px;
padding: 20px; display: flex;
display: flex; flex-direction: column;
flex-direction: column; gap: 10px;
gap: 10px; border-radius: 5px;
border-radius: 5px; box-shadow: 1px 1px 6px rgba(0, 0, 0, 0.1);
box-shadow: 1px 1px 6px rgba(0, 0, 0, 0.1); }
}
.tool { //
display: flex; .img-group {
gap: 10px; position: relative;
::v-deep .el-button { display: inline-flex;
margin-left: 0 !important; gap: 5px;
}
.img-thumb {
width: 50px;
height: 50px;
cursor: pointer;
border-radius: 3px;
}
.img-count {
position: absolute;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.6);
color: white;
font-size: 12px;
width: 20px;
height: 20px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
//
.text-ellipsis {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
//
.no-data {
color: #999;
font-size: 12px;
}
//
::v-deep .el-table__cell {
vertical-align: middle !important;
} }
}
</style> </style>

View File

@ -1,22 +1,27 @@
<template> <template>
<el-dialog <el-dialog
:visible.sync="visible" append-to-body
title="处理举报" title="处理举报"
:visible.sync="visible"
width="600px" width="600px"
:before-close="handleClose" :before-close="handleClose"
> >
<el-form ref="form" :model="form" :rules="rules" label-width="100px"> <el-form ref="form" :model="form" :rules="rules" label-width="100px">
<!-- 处理状态选择 -->
<el-form-item label="处理状态" prop="processingStatus"> <el-form-item label="处理状态" prop="processingStatus">
<el-select <el-select
v-model="form.processingStatus" v-model="form.processingStatus"
placeholder="请选择处理状态" placeholder="请选择处理状态"
@change="handleStatusChange" @change="handleStatusChange"
clearable
> >
<el-option label="受理中" value="1" /> <el-option label="受理中" value="1" />
<el-option label="证据不足" value="2" /> <el-option label="证据不足(已驳回)" value="2" />
<el-option label="已处理" value="3" /> <el-option label="已处理" value="3" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<!-- 处理结果状态2/3显示 -->
<el-form-item <el-form-item
label="处理结果" label="处理结果"
prop="processingResult" prop="processingResult"
@ -26,150 +31,233 @@
v-model="form.processingResult" v-model="form.processingResult"
type="textarea" type="textarea"
rows="3" rows="3"
placeholder="请输入处理结果" placeholder="请输入处理结果已核实违规封禁账号7天"
/> />
</el-form-item> </el-form-item>
<!-- 处理佐证材料仅状态3时显示 --> <!-- 核心上传区域仅状态3显示适配项目路由的OSS上传接口 -->
<el-form-item label="佐证材料" v-if="form.processingStatus === '3'"> <el-form-item
<PicUpload label="佐证材料"
:width="148" v-if="form.processingStatus === '3'"
:height="148" prop="processingEvidenceImages"
:showFileList="true" >
:fileList="uploadFiles" <el-upload
@get-file="handleGetFile" ref="evidenceUpload"
@remove-file="handleRemoveFile" :action="uploadUrl"
/> :auto-upload="true"
<div style="margin-top: 10px; font-size: 12px; color: #666"> :data="uploadParams"
支持上传图片作为处理佐证 :file-list="uploadFileList"
:multiple="true"
:on-success="handleUploadSuccess"
:on-error="handleUploadError"
:on-remove="handleFileRemove"
list-type="picture-card"
accept=".png,.jpg,.jpeg,.mp4,.avi"
>
<i class="el-icon-plus"></i>
<div slot="tip" class="el-upload__tip">
支持上传 PNG/JPG/MP4/AVI 格式单个文件不超过50MB
</div>
</el-upload>
<!-- 已上传文件预览 -->
<div v-if="uploadFileList.length > 0" style="margin-top: 15px;">
<span class="el-text--primary">已上传文件</span>
<div style="display: flex; gap: 10px; margin-top: 8px; flex-wrap: wrap;">
<!-- 图片预览 -->
<div
v-for="(file, index) in uploadFileList.filter(f => f.raw?.type.startsWith('image/'))"
:key="index"
style="position: relative; width: 80px; height: 80px;"
>
<el-image
:src="file.url"
style="width: 100%; height: 100%; object-fit: cover;"
:preview-src-list="[file.url]"
/>
<el-button
type="text"
size="mini"
style="position: absolute; top: -5px; right: -5px; color: #f56c6c;"
@click.stop="handleFileRemove(file, index)"
>
<i class="el-icon-close"></i>
</el-button>
</div>
<!-- 视频预览 -->
<div
v-for="(file, index) in uploadFileList.filter(f => f.raw?.type.startsWith('video/'))"
:key="index"
style="position: relative; width: 80px; height: 80px; background: #f5f7fa; display: flex; flex-direction: column; align-items: center; justify-content: center;"
>
<i class="el-icon-video-play" style="font-size: 24px; color: #409eff;"></i>
<span style="font-size: 12px; margin-top: 5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; width: 70px;">
{{ file.name }}
</span>
<el-button
type="text"
size="mini"
style="position: absolute; top: -5px; right: -5px; color: #f56c6c;"
@click.stop="handleFileRemove(file, index)"
>
<i class="el-icon-close"></i>
</el-button>
</div>
</div>
</div> </div>
</el-form-item> </el-form-item>
</el-form> </el-form>
<div slot="footer"> <div slot="footer">
<el-button @click="handleClose">取消</el-button> <el-button @click="handleClose">取消</el-button>
<el-button type="primary" @click="handleSubmit">提交处理</el-button> <el-button type="primary" @click="handleSubmit">提交处理</el-button>
</div> </div>
</el-dialog> </el-dialog>
</template> </template>
<script> <script>
import { dealReport } from '@/api/base/config'; import { URL } from '@/config';
import PicUpload from '@/components/material/PicUpload.vue'; import { dealReport } from '@/api/base/config';
import { getToken } from '@/utils/token';
export default { export default {
components: { name: 'ReportDeal',
PicUpload,
},
data() { data() {
return { return {
visible: false, visible: false,
form: { form: {
id: null, // ID id: null,
processingStatus: '', // 1/2/3 processingStatus: '',
processingResult: '', // 2/3 processingResult: '',
processingEvidenceImages: [], // 3 processingEvidenceImages: [] //
}, },
//
rules: { rules: {
processingStatus: [ processingStatus: [
{ required: true, message: '请选择处理状态', trigger: 'change' }, { required: true, message: '请选择处理状态', trigger: 'change' }
], ],
processingResult: [ processingResult: [
{ required: true, message: '请输入处理结果', trigger: 'blur' }, { required: true, message: '请输入处理结果', trigger: 'blur' },
{ min: 10, message: '处理结果至少10个字符', trigger: 'blur' }
], ],
processingEvidenceImages: [
{ required: true, message: '请上传佐证材料', trigger: 'change' }
]
}, },
uploadFiles: [], // //
uploadUrl: URL.upload, // OSSapi_url + '/admin/oss/upload'
uploadParams: {
authorization: getToken(),
gallery_type: 'image' //
},
uploadFileList: [] //
}; };
}, },
methods: { methods: {
//
open(row) { open(row) {
this.visible = true;
this.form = { this.form = {
...this.form, ...this.form,
id: row.id, id: row.id, // ID
processingStatus: '', processingStatus: '',
processingResult: '', processingResult: '',
processingEvidenceImages: [], processingEvidenceImages: []
}; };
this.uploadFiles = []; // this.uploadFileList = []; //
this.visible = true;
}, },
// // ""
handleStatusChange(val) { handleStatusChange(val) {
if (val === '1') { if (val !== '3') {
this.form.processingResult = ''; this.uploadFileList = [];
this.uploadFiles = [];
this.form.processingEvidenceImages = [];
} else if (val === '2') {
this.uploadFiles = [];
this.form.processingEvidenceImages = []; this.form.processingEvidenceImages = [];
} else {
// ""gallery_type
this.uploadParams.gallery_type = 'image';
} }
}, },
// //
handleGetFile(files) { handleUploadSuccess(response, file, fileList) {
this.uploadFiles = files; if (response.code == 0) {
this.form.processingEvidenceImages = files.map(file => ({ file.url = response.data.media_url; // URL
url: file, this.form.processingEvidenceImages = fileList.map(item => ({
description: '处理佐证材料' url: item.url,
})); description: `佐证材料_${item.name}`
}, }));
this.$message.success(`文件 ${file.name} 上传成功`);
// } else {
handleRemoveFile(files) { this.$message.error(`文件 ${file.name} 上传失败:${response.msg || '未知错误'}`);
this.uploadFiles = files; //
this.form.processingEvidenceImages = files.map(file => ({ this.uploadFileList = this.uploadFileList.filter(item => item.uid !== file.uid);
url: file, }
description: '处理佐证材料' },
//
handleUploadError(error, file) {
this.$message.error(`文件 ${file.name} 上传失败:网络错误或接口异常`);
this.uploadFileList = this.uploadFileList.filter(item => item.uid !== file.uid);
},
//
handleFileRemove(file, index) {
this.uploadFileList.splice(index, 1);
//
this.form.processingEvidenceImages = this.uploadFileList.map(item => ({
url: item.url,
description: `佐证材料_${item.name}`
})); }));
}, },
//
async handleSubmit() { async handleSubmit() {
this.$refs.form.validate(async (valid) => { this.$refs.form.validate(async (valid) => {
if (!valid) return; if (!valid) return;
// //
const submitData = { const submitData = {
id: this.form.id, id: this.form.id,
processingStatus: this.form.processingStatus, processingStatus: this.form.processingStatus,
processingResult: this.form.processingResult
}; };
// 23 // 3JSON
if (['2', '3'].includes(this.form.processingStatus)) {
submitData.processingResult = this.form.processingResult;
}
// 3
if (this.form.processingStatus === '3') { if (this.form.processingStatus === '3') {
if (this.form.processingEvidenceImages.length === 0) {
return this.$message.warning('请上传佐证材料');
}
// JSON
submitData.processingEvidenceImages = JSON.stringify(this.form.processingEvidenceImages); submitData.processingEvidenceImages = JSON.stringify(this.form.processingEvidenceImages);
} }
//
try { try {
await dealReport(submitData); // await dealReport(submitData);
this.$message.success('处理成功'); this.$message.success('处理成功');
this.visible = false; this.visible = false;
this.$emit('success'); // this.$emit('success'); //
} catch (err) { } catch (err) {
this.$message.error('处理失败:' + (err.message || '未知错误')); this.$message.error(`处理失败:${err.message || '未知错误'}`);
} }
}); });
}, },
//
handleClose() { handleClose() {
this.visible = false; this.visible = false;
this.$refs.form.resetFields(); // this.$refs.form?.resetFields();
this.uploadFiles = []; // this.uploadFileList = [];
}, this.form.processingEvidenceImages = [];
}, }
}
}; };
</script> </script>
<style scoped> <style scoped>
.upload-demo { ::v-deep .el-upload {
margin-top: 10px; margin-bottom: 10px;
} }
</style> ::v-deep .el-image-viewer__mask {
background: rgba(0, 0, 0, 0.7) !important;
}
.el-upload__tip {
color: #666 !important;
font-size: 12px !important;
}
</style>

View File

@ -0,0 +1,75 @@
<template>
<el-dialog
:visible.sync="visible"
title="举报详情"
width="600px"
:before-close="handleClose"
>
<div>
<p>
<strong>举报正文</strong>
{{ detailData.reportContent }}
</p>
<div>
<p><strong>举报凭证</strong></p>
<div class="verify_container">
<div v-for="verify_item in detailData.reportVerify">
<img :src="verify_item.url" class="verify_img" />
</div>
</div>
</div>
<p>
<strong>举报人</strong>
{{ detailData.reporterName }}
</p>
<p>
<strong>举报时间</strong>
{{ detailData.reporterTime }}
</p>
<p>
<strong>状态</strong>
<el-tag :type="detailData.state === '未处理' ? 'warning' : 'success'">
{{ detailData.state }}
</el-tag>
</p>
</div>
</el-dialog>
</template>
<script>
export default {
name: 'ReportDetail',
data() {
return {
visible: false,
detailData: {},
}
},
methods: {
//
open(row) {
this.detailData = row
this.visible = true //
},
//
handleClose() {
this.visible = false
},
},
}
</script>
<style scoped>
.verify_container {
display: flex;
flex-direction: row;
gap: 10px;
flex-wrap: wrap;
margin: 10px 0;
}
.verify_img {
width: 100px;
height: 100px;
}
</style>