dev2 #1

Open
panjunjie wants to merge 174 commits from dev2 into main
9 changed files with 1158 additions and 214 deletions
Showing only changes of commit 4b535ea385 - Show all commits

View File

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

View File

@ -126,7 +126,23 @@ export async function syncShopImages(data) {
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 {
getProductMapperList,
getShopList,
@ -141,4 +157,6 @@ export default {
syncProductMaping,
importGoodsData,
syncShopImages,
getImgList,
saveBatchBarcode
}

View File

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

View File

@ -1,22 +1,27 @@
<template>
<el-dialog
:visible.sync="visible"
append-to-body
title="处理举报"
:visible.sync="visible"
width="600px"
:before-close="handleClose"
>
<el-form ref="form" :model="form" :rules="rules" label-width="100px">
<!-- 处理状态选择 -->
<el-form-item label="处理状态" prop="processingStatus">
<el-select
v-model="form.processingStatus"
placeholder="请选择处理状态"
@change="handleStatusChange"
clearable
>
<el-option label="受理中" value="1" />
<el-option label="证据不足" value="2" />
<el-option label="证据不足(已驳回)" value="2" />
<el-option label="已处理" value="3" />
</el-select>
</el-form-item>
<!-- 处理结果状态2/3显示 -->
<el-form-item
label="处理结果"
prop="processingResult"
@ -26,150 +31,233 @@
v-model="form.processingResult"
type="textarea"
rows="3"
placeholder="请输入处理结果"
placeholder="请输入处理结果已核实违规封禁账号7天"
/>
</el-form-item>
<!-- 处理佐证材料仅状态3时显示 -->
<el-form-item label="佐证材料" v-if="form.processingStatus === '3'">
<PicUpload
:width="148"
:height="148"
:showFileList="true"
:fileList="uploadFiles"
@get-file="handleGetFile"
@remove-file="handleRemoveFile"
/>
<div style="margin-top: 10px; font-size: 12px; color: #666">
支持上传图片作为处理佐证
<!-- 核心上传区域仅状态3显示适配项目路由的OSS上传接口 -->
<el-form-item
label="佐证材料"
v-if="form.processingStatus === '3'"
prop="processingEvidenceImages"
>
<el-upload
ref="evidenceUpload"
:action="uploadUrl"
:auto-upload="true"
: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>
</el-form-item>
</el-form>
<div slot="footer">
<el-button @click="handleClose">取消</el-button>
<el-button type="primary" @click="handleSubmit">提交处理</el-button>
</div>
</el-dialog>
</template>
<script>
import { dealReport } from '@/api/base/config';
import PicUpload from '@/components/material/PicUpload.vue';
import { URL } from '@/config';
import { dealReport } from '@/api/base/config';
import { getToken } from '@/utils/token';
export default {
components: {
PicUpload,
},
name: 'ReportDeal',
data() {
return {
visible: false,
form: {
id: null, // ID
processingStatus: '', // 1/2/3
processingResult: '', // 2/3
processingEvidenceImages: [], // 3
id: null,
processingStatus: '',
processingResult: '',
processingEvidenceImages: [] //
},
//
rules: {
processingStatus: [
{ required: true, message: '请选择处理状态', trigger: 'change' },
{ required: true, message: '请选择处理状态', trigger: 'change' }
],
processingResult: [
{ 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: {
//
open(row) {
this.visible = true;
this.form = {
...this.form,
id: row.id,
processingStatus: '',
processingResult: '',
processingEvidenceImages: [],
id: row.id, // ID
processingStatus: '',
processingResult: '',
processingEvidenceImages: []
};
this.uploadFiles = []; //
this.uploadFileList = []; //
this.visible = true;
},
//
// ""
handleStatusChange(val) {
if (val === '1') {
this.form.processingResult = '';
this.uploadFiles = [];
this.form.processingEvidenceImages = [];
} else if (val === '2') {
this.uploadFiles = [];
if (val !== '3') {
this.uploadFileList = [];
this.form.processingEvidenceImages = [];
} else {
// ""gallery_type
this.uploadParams.gallery_type = 'image';
}
},
//
handleGetFile(files) {
this.uploadFiles = files;
this.form.processingEvidenceImages = files.map(file => ({
url: file,
description: '处理佐证材料'
}));
},
//
handleRemoveFile(files) {
this.uploadFiles = files;
this.form.processingEvidenceImages = files.map(file => ({
url: file,
description: '处理佐证材料'
//
handleUploadSuccess(response, file, fileList) {
if (response.code == 0) {
file.url = response.data.media_url; // URL
this.form.processingEvidenceImages = fileList.map(item => ({
url: item.url,
description: `佐证材料_${item.name}`
}));
this.$message.success(`文件 ${file.name} 上传成功`);
} else {
this.$message.error(`文件 ${file.name} 上传失败:${response.msg || '未知错误'}`);
//
this.uploadFileList = this.uploadFileList.filter(item => item.uid !== file.uid);
}
},
//
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() {
this.$refs.form.validate(async (valid) => {
if (!valid) return;
//
//
const submitData = {
id: this.form.id,
processingStatus: this.form.processingStatus,
processingResult: this.form.processingResult
};
// 23
if (['2', '3'].includes(this.form.processingStatus)) {
submitData.processingResult = this.form.processingResult;
}
// 3
// 3JSON
if (this.form.processingStatus === '3') {
if (this.form.processingEvidenceImages.length === 0) {
return this.$message.warning('请上传佐证材料');
}
// JSON
submitData.processingEvidenceImages = JSON.stringify(this.form.processingEvidenceImages);
}
//
try {
await dealReport(submitData); //
this.$message.success('处理成功');
await dealReport(submitData);
this.$message.success('处理成功');
this.visible = false;
this.$emit('success'); //
this.$emit('success'); //
} catch (err) {
this.$message.error('处理失败:' + (err.message || '未知错误'));
this.$message.error(`处理失败:${err.message || '未知错误'}`);
}
});
},
//
handleClose() {
this.visible = false;
this.$refs.form.resetFields(); //
this.uploadFiles = []; //
},
},
this.$refs.form?.resetFields();
this.uploadFileList = [];
this.form.processingEvidenceImages = [];
}
}
};
</script>
<style scoped>
.upload-demo {
margin-top: 10px;
::v-deep .el-upload {
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>