feat: 新增意见反馈功能。
This commit is contained in:
53
src/api/system/feedback.js
Normal file
53
src/api/system/feedback.js
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
// 查询意见反馈列表
|
||||||
|
export function listFeedback(query) {
|
||||||
|
return request({
|
||||||
|
url: '/system/feedback/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询意见反馈详细
|
||||||
|
export function getFeedback(id) {
|
||||||
|
return request({
|
||||||
|
url: '/system/feedback/' + id,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增意见反馈
|
||||||
|
export function addFeedback(data) {
|
||||||
|
return request({
|
||||||
|
url: '/system/feedback',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改意见反馈
|
||||||
|
export function updateFeedback(data) {
|
||||||
|
return request({
|
||||||
|
url: '/system/feedback',
|
||||||
|
method: 'put',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 回复意见反馈
|
||||||
|
export function replyFeedback(data) {
|
||||||
|
return request({
|
||||||
|
url: '/system/feedback/reply',
|
||||||
|
method: 'put',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除意见反馈
|
||||||
|
export function delFeedback(id) {
|
||||||
|
return request({
|
||||||
|
url: '/system/feedback/' + id,
|
||||||
|
method: 'delete'
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -158,11 +158,14 @@ function uploadedSuccessfully() {
|
|||||||
|
|
||||||
// 获取文件名称
|
// 获取文件名称
|
||||||
function getFileName(name) {
|
function getFileName(name) {
|
||||||
if (name.lastIndexOf('/') > -1) {
|
const fileName = String(name || '').split('?')[0]
|
||||||
return name.slice(name.lastIndexOf('/') + 1)
|
if (!fileName) {
|
||||||
} else {
|
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
if (fileName.lastIndexOf('/') > -1) {
|
||||||
|
return decodeURIComponent(fileName.slice(fileName.lastIndexOf('/') + 1))
|
||||||
|
}
|
||||||
|
return decodeURIComponent(fileName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 对象转成指定字符串分隔
|
// 对象转成指定字符串分隔
|
||||||
|
|||||||
@@ -50,6 +50,28 @@
|
|||||||
<el-table-column label="活动时长" align="center" prop="exerciseTimeStr" width="150"> </el-table-column>
|
<el-table-column label="活动时长" align="center" prop="exerciseTimeStr" width="150"> </el-table-column>
|
||||||
<el-table-column label="成员" align="center" prop="partner" />
|
<el-table-column label="成员" align="center" prop="partner" />
|
||||||
<el-table-column label="总费用(元)" width="120" align="center" prop="totalCost" />
|
<el-table-column label="总费用(元)" width="120" align="center" prop="totalCost" />
|
||||||
|
<el-table-column label="附件" align="center" prop="attachment" width="120">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-popover v-if="parseAttachments(scope.row.attachment).length" placement="left" width="280" trigger="click">
|
||||||
|
<template #reference>
|
||||||
|
<el-button link type="primary">查看{{ parseAttachments(scope.row.attachment).length }}个</el-button>
|
||||||
|
</template>
|
||||||
|
<div class="attachment-popover-list">
|
||||||
|
<el-link
|
||||||
|
v-for="(file, index) in parseAttachments(scope.row.attachment)"
|
||||||
|
:key="file"
|
||||||
|
type="primary"
|
||||||
|
:href="getAttachmentUrl(file)"
|
||||||
|
target="_blank"
|
||||||
|
:underline="false"
|
||||||
|
>
|
||||||
|
{{ getAttachmentName(file, index) }}
|
||||||
|
</el-link>
|
||||||
|
</div>
|
||||||
|
</el-popover>
|
||||||
|
<span v-else class="attachment-empty">无</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="操作" width="180" align="center" class-name="small-padding fixed-width">
|
<el-table-column label="操作" width="180" align="center" class-name="small-padding fixed-width">
|
||||||
<template v-slot="scope">
|
<template v-slot="scope">
|
||||||
<div class="ctrl-btn d-flex">
|
<div class="ctrl-btn d-flex">
|
||||||
@@ -115,6 +137,22 @@
|
|||||||
<el-form-item label="收获" style="width: 792px" prop="harvest">
|
<el-form-item label="收获" style="width: 792px" prop="harvest">
|
||||||
<el-input v-model="form.harvest" type="textarea" placeholder="请输入收获" />
|
<el-input v-model="form.harvest" type="textarea" placeholder="请输入收获" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="附件" style="width: 792px" prop="attachment">
|
||||||
|
<div v-if="title === '查看活动记录'" class="attachment-readonly-list attachment-form-list">
|
||||||
|
<el-link
|
||||||
|
v-for="(file, index) in parseAttachments(form.attachment)"
|
||||||
|
:key="file"
|
||||||
|
type="primary"
|
||||||
|
:href="getAttachmentUrl(file)"
|
||||||
|
target="_blank"
|
||||||
|
:underline="false"
|
||||||
|
>
|
||||||
|
{{ getAttachmentName(file, index) }}
|
||||||
|
</el-link>
|
||||||
|
<span v-if="!parseAttachments(form.attachment).length" class="attachment-empty">暂无附件</span>
|
||||||
|
</div>
|
||||||
|
<file-upload v-else v-model="form.attachment" :limit="9" :file-size="10" :file-type="attachmentFileTypes" />
|
||||||
|
</el-form-item>
|
||||||
<el-form-item label="备注" style="width: 792px" prop="remark">
|
<el-form-item label="备注" style="width: 792px" prop="remark">
|
||||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
|
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -190,6 +228,7 @@ const expenseList = ref([])
|
|||||||
const selectedExpenses = ref([])
|
const selectedExpenses = ref([])
|
||||||
const currentActivityId = ref(null)
|
const currentActivityId = ref(null)
|
||||||
const summaryTotal = ref(0)
|
const summaryTotal = ref(0)
|
||||||
|
const attachmentFileTypes = ['jpg', 'jpeg', 'png', 'pdf', 'doc', 'docx', 'xls', 'xlsx']
|
||||||
const operateList = ref([
|
const operateList = ref([
|
||||||
{ id: 'view', icon: 'View', title: '查看', hasPermi: ['health:activity:query'] },
|
{ id: 'view', icon: 'View', title: '查看', hasPermi: ['health:activity:query'] },
|
||||||
{ id: 'edit', icon: 'Edit', title: '修改', hasPermi: ['health:activity:edit'] },
|
{ id: 'edit', icon: 'Edit', title: '修改', hasPermi: ['health:activity:edit'] },
|
||||||
@@ -240,6 +279,39 @@ const handleOperate = (operate, row) => {
|
|||||||
|
|
||||||
const { queryParams, form, rules } = toRefs(data)
|
const { queryParams, form, rules } = toRefs(data)
|
||||||
|
|
||||||
|
function parseAttachments(attachment) {
|
||||||
|
if (!attachment) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
return String(attachment)
|
||||||
|
.split(',')
|
||||||
|
.map((item) => item.trim())
|
||||||
|
.filter(Boolean)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAttachmentUrl(file) {
|
||||||
|
if (!file) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
if (/^(https?:)?\/\//.test(file) || file.startsWith('/') || file.startsWith('blob:') || file.startsWith('data:')) {
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
return `/${file}`
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAttachmentName(file, index) {
|
||||||
|
const purePath = String(file || '').split('?')[0]
|
||||||
|
const name = purePath.slice(purePath.lastIndexOf('/') + 1)
|
||||||
|
if (!name) {
|
||||||
|
return `附件${index + 1}`
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return decodeURIComponent(name)
|
||||||
|
} catch {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** 查询活动记录列表 */
|
/** 查询活动记录列表 */
|
||||||
function getList() {
|
function getList() {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
@@ -277,7 +349,8 @@ function reset() {
|
|||||||
remark: null,
|
remark: null,
|
||||||
totalCost: 0,
|
totalCost: 0,
|
||||||
partner: null,
|
partner: null,
|
||||||
costDetail: null
|
costDetail: null,
|
||||||
|
attachment: null
|
||||||
}
|
}
|
||||||
proxy.resetForm('activityRef')
|
proxy.resetForm('activityRef')
|
||||||
}
|
}
|
||||||
@@ -501,3 +574,23 @@ function confirmSummary() {
|
|||||||
|
|
||||||
getList()
|
getList()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.attachment-popover-list,
|
||||||
|
.attachment-readonly-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 8px;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachment-form-list {
|
||||||
|
min-height: 32px;
|
||||||
|
padding: 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachment-empty {
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -63,6 +63,28 @@
|
|||||||
<el-table-column label="就诊时间" align="center" prop="visitingTime" width="160"> </el-table-column>
|
<el-table-column label="就诊时间" align="center" prop="visitingTime" width="160"> </el-table-column>
|
||||||
<el-table-column label="总费用(元)" align="center" width="120" prop="totalCost" />
|
<el-table-column label="总费用(元)" align="center" width="120" prop="totalCost" />
|
||||||
<el-table-column label="诊断结果" align="center" prop="diagnosis" />
|
<el-table-column label="诊断结果" align="center" prop="diagnosis" />
|
||||||
|
<el-table-column label="附件" align="center" prop="attachment" width="120">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-popover v-if="parseAttachments(scope.row.attachment).length" placement="left" width="280" trigger="click">
|
||||||
|
<template #reference>
|
||||||
|
<el-button link type="primary">查看{{ parseAttachments(scope.row.attachment).length }}个</el-button>
|
||||||
|
</template>
|
||||||
|
<div class="attachment-popover-list">
|
||||||
|
<el-link
|
||||||
|
v-for="(file, index) in parseAttachments(scope.row.attachment)"
|
||||||
|
:key="file"
|
||||||
|
type="primary"
|
||||||
|
:href="getAttachmentUrl(file)"
|
||||||
|
target="_blank"
|
||||||
|
:underline="false"
|
||||||
|
>
|
||||||
|
{{ getAttachmentName(file, index) }}
|
||||||
|
</el-link>
|
||||||
|
</div>
|
||||||
|
</el-popover>
|
||||||
|
<span v-else class="attachment-empty">无</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="操作" align="center" width="180" class-name="small-padding fixed-width">
|
<el-table-column label="操作" align="center" width="180" class-name="small-padding fixed-width">
|
||||||
<template v-slot="scope">
|
<template v-slot="scope">
|
||||||
<div class="ctrl-btn d-flex">
|
<div class="ctrl-btn d-flex">
|
||||||
@@ -132,6 +154,22 @@
|
|||||||
<el-form-item label="费用明细" style="width: 792px" prop="costDetail">
|
<el-form-item label="费用明细" style="width: 792px" prop="costDetail">
|
||||||
<el-input v-model="form.costDetail" type="textarea" placeholder="请输入费用明细" />
|
<el-input v-model="form.costDetail" type="textarea" placeholder="请输入费用明细" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="附件" style="width: 792px" prop="attachment">
|
||||||
|
<div v-if="title === '查看就医记录'" class="attachment-readonly-list attachment-form-list">
|
||||||
|
<el-link
|
||||||
|
v-for="(file, index) in parseAttachments(form.attachment)"
|
||||||
|
:key="file"
|
||||||
|
type="primary"
|
||||||
|
:href="getAttachmentUrl(file)"
|
||||||
|
target="_blank"
|
||||||
|
:underline="false"
|
||||||
|
>
|
||||||
|
{{ getAttachmentName(file, index) }}
|
||||||
|
</el-link>
|
||||||
|
<span v-if="!parseAttachments(form.attachment).length" class="attachment-empty">暂无附件</span>
|
||||||
|
</div>
|
||||||
|
<file-upload v-else v-model="form.attachment" :limit="9" :file-size="10" :file-type="attachmentFileTypes" />
|
||||||
|
</el-form-item>
|
||||||
<el-form-item label="备注" style="width: 792px" prop="remark">
|
<el-form-item label="备注" style="width: 792px" prop="remark">
|
||||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
|
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -287,6 +325,7 @@ const total = ref(0)
|
|||||||
const title = ref('')
|
const title = ref('')
|
||||||
const personList = ref([])
|
const personList = ref([])
|
||||||
const healthRecordList = ref([])
|
const healthRecordList = ref([])
|
||||||
|
const attachmentFileTypes = ['jpg', 'jpeg', 'png', 'pdf', 'doc', 'docx', 'xls', 'xlsx']
|
||||||
|
|
||||||
const detailOpen = ref(false)
|
const detailOpen = ref(false)
|
||||||
const openDetail = ref(false)
|
const openDetail = ref(false)
|
||||||
@@ -395,8 +434,41 @@ const handleOperate = (operate, row) => {
|
|||||||
|
|
||||||
const { queryParams, form, formDetail, rules, queryPersonParams, queryMedicineParams, queryHealthRecordParams, rulesDetail } = toRefs(data)
|
const { queryParams, form, formDetail, rules, queryPersonParams, queryMedicineParams, queryHealthRecordParams, rulesDetail } = toRefs(data)
|
||||||
|
|
||||||
|
function parseAttachments(attachment) {
|
||||||
|
if (!attachment) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
return String(attachment)
|
||||||
|
.split(',')
|
||||||
|
.map((item) => item.trim())
|
||||||
|
.filter(Boolean)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAttachmentUrl(file) {
|
||||||
|
if (!file) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
if (/^(https?:)?\/\//.test(file) || file.startsWith('/') || file.startsWith('blob:') || file.startsWith('data:')) {
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
return `/${file}`
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAttachmentName(file, index) {
|
||||||
|
const purePath = String(file || '').split('?')[0]
|
||||||
|
const name = purePath.slice(purePath.lastIndexOf('/') + 1)
|
||||||
|
if (!name) {
|
||||||
|
return `附件${index + 1}`
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return decodeURIComponent(name)
|
||||||
|
} catch {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const handlePersonChange = (personId) => {
|
const handlePersonChange = (personId) => {
|
||||||
queryHealthRecordParams.personId = personId
|
queryHealthRecordParams.value.personId = personId
|
||||||
listHealthRecord(queryHealthRecordParams).then((response) => {
|
listHealthRecord(queryHealthRecordParams).then((response) => {
|
||||||
healthRecordList.value = response.rows
|
healthRecordList.value = response.rows
|
||||||
if (response.rows.length > 0) {
|
if (response.rows.length > 0) {
|
||||||
@@ -405,7 +477,7 @@ const handlePersonChange = (personId) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
const handleQueryPersonChange = (personId) => {
|
const handleQueryPersonChange = (personId) => {
|
||||||
queryHealthRecordParams.personId = personId
|
queryHealthRecordParams.value.personId = personId
|
||||||
queryParams.value.healthRecordId = null
|
queryParams.value.healthRecordId = null
|
||||||
listHealthRecord(queryHealthRecordParams).then((response) => {
|
listHealthRecord(queryHealthRecordParams).then((response) => {
|
||||||
healthRecordList.value = response.rows
|
healthRecordList.value = response.rows
|
||||||
@@ -780,7 +852,8 @@ function reset() {
|
|||||||
personId: null,
|
personId: null,
|
||||||
totalCost: 0,
|
totalCost: 0,
|
||||||
partner: null,
|
partner: null,
|
||||||
costDetail: null
|
costDetail: null,
|
||||||
|
attachment: null
|
||||||
}
|
}
|
||||||
proxy.resetForm('doctorRecordRef')
|
proxy.resetForm('doctorRecordRef')
|
||||||
}
|
}
|
||||||
@@ -825,7 +898,7 @@ function handleSelectionChange(selection) {
|
|||||||
// 查看
|
// 查看
|
||||||
const handleView = (row) => {
|
const handleView = (row) => {
|
||||||
title.value = '查看就医记录'
|
title.value = '查看就医记录'
|
||||||
queryHealthRecordParams.personId = row.personId
|
queryHealthRecordParams.value.personId = row.personId
|
||||||
listHealthRecord(queryHealthRecordParams).then((response) => {
|
listHealthRecord(queryHealthRecordParams).then((response) => {
|
||||||
healthRecordList.value = response.rows
|
healthRecordList.value = response.rows
|
||||||
})
|
})
|
||||||
@@ -839,7 +912,7 @@ function handleAdd() {
|
|||||||
form.value.visitingTime = dayjs(new Date().getTime()).format('YYYY-MM-DD HH:mm:ss')
|
form.value.visitingTime = dayjs(new Date().getTime()).format('YYYY-MM-DD HH:mm:ss')
|
||||||
if (personList.value.length > 0) {
|
if (personList.value.length > 0) {
|
||||||
form.value.personId = personList.value[0].id
|
form.value.personId = personList.value[0].id
|
||||||
queryHealthRecordParams.personId = personList.value[0].id
|
queryHealthRecordParams.value.personId = personList.value[0].id
|
||||||
listHealthRecord(queryHealthRecordParams).then((response) => {
|
listHealthRecord(queryHealthRecordParams).then((response) => {
|
||||||
healthRecordList.value = response.rows
|
healthRecordList.value = response.rows
|
||||||
if (response.rows.length > 0) {
|
if (response.rows.length > 0) {
|
||||||
@@ -854,7 +927,7 @@ function handleAdd() {
|
|||||||
/** 修改按钮操作 */
|
/** 修改按钮操作 */
|
||||||
function handleUpdate(row) {
|
function handleUpdate(row) {
|
||||||
reset()
|
reset()
|
||||||
queryHealthRecordParams.personId = row.personId
|
queryHealthRecordParams.value.personId = row.personId
|
||||||
listHealthRecord(queryHealthRecordParams).then((response) => {
|
listHealthRecord(queryHealthRecordParams).then((response) => {
|
||||||
healthRecordList.value = response.rows
|
healthRecordList.value = response.rows
|
||||||
})
|
})
|
||||||
@@ -916,3 +989,23 @@ function handleExport() {
|
|||||||
getList()
|
getList()
|
||||||
getPersonList()
|
getPersonList()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.attachment-popover-list,
|
||||||
|
.attachment-readonly-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 8px;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachment-form-list {
|
||||||
|
min-height: 32px;
|
||||||
|
padding: 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachment-empty {
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -79,6 +79,28 @@
|
|||||||
<el-table-column label="包装" align="center" prop="packaging" width="150" />
|
<el-table-column label="包装" align="center" prop="packaging" width="150" />
|
||||||
<el-table-column label="品牌" align="center" prop="brand" width="120" />
|
<el-table-column label="品牌" align="center" prop="brand" width="120" />
|
||||||
<el-table-column label="生产厂家" align="center" prop="manufacturers" />
|
<el-table-column label="生产厂家" align="center" prop="manufacturers" />
|
||||||
|
<el-table-column label="附件" align="center" prop="attachment" width="120">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-popover v-if="parseAttachments(scope.row.attachment).length" placement="left" width="280" trigger="click">
|
||||||
|
<template #reference>
|
||||||
|
<el-button link type="primary">查看{{ parseAttachments(scope.row.attachment).length }}个</el-button>
|
||||||
|
</template>
|
||||||
|
<div class="attachment-popover-list">
|
||||||
|
<el-link
|
||||||
|
v-for="(file, index) in parseAttachments(scope.row.attachment)"
|
||||||
|
:key="file"
|
||||||
|
type="primary"
|
||||||
|
:href="getAttachmentUrl(file)"
|
||||||
|
target="_blank"
|
||||||
|
:underline="false"
|
||||||
|
>
|
||||||
|
{{ getAttachmentName(file, index) }}
|
||||||
|
</el-link>
|
||||||
|
</div>
|
||||||
|
</el-popover>
|
||||||
|
<span v-else class="attachment-empty">无</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||||
<template v-slot="scope">
|
<template v-slot="scope">
|
||||||
@@ -190,6 +212,22 @@
|
|||||||
<el-form-item label="不良反应" prop="adverseReaction" style="width: 1200px">
|
<el-form-item label="不良反应" prop="adverseReaction" style="width: 1200px">
|
||||||
<el-input v-model="form.adverseReaction" type="textarea" placeholder="请输入不良反应" />
|
<el-input v-model="form.adverseReaction" type="textarea" placeholder="请输入不良反应" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="附件" style="width: 1200px" prop="attachment">
|
||||||
|
<div v-if="title === '查看药品基础信息'" class="attachment-readonly-list attachment-form-list">
|
||||||
|
<el-link
|
||||||
|
v-for="(file, index) in parseAttachments(form.attachment)"
|
||||||
|
:key="file"
|
||||||
|
type="primary"
|
||||||
|
:href="getAttachmentUrl(file)"
|
||||||
|
target="_blank"
|
||||||
|
:underline="false"
|
||||||
|
>
|
||||||
|
{{ getAttachmentName(file, index) }}
|
||||||
|
</el-link>
|
||||||
|
<span v-if="!parseAttachments(form.attachment).length" class="attachment-empty">暂无附件</span>
|
||||||
|
</div>
|
||||||
|
<file-upload v-else v-model="form.attachment" :limit="9" :file-size="10" :file-type="attachmentFileTypes" />
|
||||||
|
</el-form-item>
|
||||||
<el-form-item label="备注" style="width: 1200px" prop="remark">
|
<el-form-item label="备注" style="width: 1200px" prop="remark">
|
||||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
|
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -229,6 +267,7 @@ const single = ref(true)
|
|||||||
const multiple = ref(true)
|
const multiple = ref(true)
|
||||||
const total = ref(0)
|
const total = ref(0)
|
||||||
const title = ref('')
|
const title = ref('')
|
||||||
|
const attachmentFileTypes = ['jpg', 'jpeg', 'png', 'pdf', 'doc', 'docx', 'xls', 'xlsx']
|
||||||
const operateList = ref([
|
const operateList = ref([
|
||||||
{ id: 'view', icon: 'View', title: '查看', hasPermi: ['health:medicineBasic:query'] },
|
{ id: 'view', icon: 'View', title: '查看', hasPermi: ['health:medicineBasic:query'] },
|
||||||
{ id: 'edit', icon: 'Edit', title: '修改', hasPermi: ['health:medicineBasic:edit'] },
|
{ id: 'edit', icon: 'Edit', title: '修改', hasPermi: ['health:medicineBasic:edit'] },
|
||||||
@@ -289,6 +328,39 @@ const handleOperate = (operate, row) => {
|
|||||||
|
|
||||||
const { queryParams, form, rules } = toRefs(data)
|
const { queryParams, form, rules } = toRefs(data)
|
||||||
|
|
||||||
|
function parseAttachments(attachment) {
|
||||||
|
if (!attachment) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
return String(attachment)
|
||||||
|
.split(',')
|
||||||
|
.map((item) => item.trim())
|
||||||
|
.filter(Boolean)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAttachmentUrl(file) {
|
||||||
|
if (!file) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
if (/^(https?:)?\/\//.test(file) || file.startsWith('/') || file.startsWith('blob:') || file.startsWith('data:')) {
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
return `/${file}`
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAttachmentName(file, index) {
|
||||||
|
const purePath = String(file || '').split('?')[0]
|
||||||
|
const name = purePath.slice(purePath.lastIndexOf('/') + 1)
|
||||||
|
if (!name) {
|
||||||
|
return `附件${index + 1}`
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return decodeURIComponent(name)
|
||||||
|
} catch {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** 查询药品基础信息列表 */
|
/** 查询药品基础信息列表 */
|
||||||
function getList() {
|
function getList() {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
@@ -347,7 +419,8 @@ function reset() {
|
|||||||
contentUnit: null,
|
contentUnit: null,
|
||||||
character: null,
|
character: null,
|
||||||
storage: null,
|
storage: null,
|
||||||
indications: null
|
indications: null,
|
||||||
|
attachment: null
|
||||||
}
|
}
|
||||||
proxy.resetForm('medicineBasicRef')
|
proxy.resetForm('medicineBasicRef')
|
||||||
}
|
}
|
||||||
@@ -463,3 +536,23 @@ function handleExport() {
|
|||||||
|
|
||||||
getList()
|
getList()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.attachment-popover-list,
|
||||||
|
.attachment-readonly-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 8px;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachment-form-list {
|
||||||
|
min-height: 32px;
|
||||||
|
padding: 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachment-empty {
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
379
src/views/system/feedback/index.vue
Normal file
379
src/views/system/feedback/index.vue
Normal file
@@ -0,0 +1,379 @@
|
|||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
<div class="search-con" ref="searchHeightRef">
|
||||||
|
<div class="title">查询条件</div>
|
||||||
|
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="80px">
|
||||||
|
<el-form-item label="反馈类型" prop="feedbackType">
|
||||||
|
<el-select v-model="queryParams.feedbackType" placeholder="请选择反馈类型" clearable style="width: 200px">
|
||||||
|
<el-option v-for="dict in feedbackTypeOptions" :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 style="width: 200px">
|
||||||
|
<el-option v-for="dict in feedbackStatusOptions" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="反馈来源" prop="source">
|
||||||
|
<el-select v-model="queryParams.source" placeholder="请选择反馈来源" clearable style="width: 200px">
|
||||||
|
<el-option v-for="item in sourceOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="提交用户" prop="createBy">
|
||||||
|
<el-input v-model="queryParams.createBy" placeholder="请输入提交用户" clearable style="width: 200px" @keyup.enter="handleQuery" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<div class="search-btn-con">
|
||||||
|
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||||
|
<el-button type="info" icon="Refresh" @click="resetQuery">重置</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="main-con" :style="{ height: mainStyleHeight }">
|
||||||
|
<div class="title-con">
|
||||||
|
<div class="title">反馈列表</div>
|
||||||
|
<div class="operate-btn-con">
|
||||||
|
<el-button icon="Check" :disabled="single" @click="handleProcess" v-hasPermi="['system:feedback:edit']">标记已处理</el-button>
|
||||||
|
<el-button icon="Delete" :disabled="multiple" @click="handleDelete" v-hasPermi="['system:feedback:remove']">删除</el-button>
|
||||||
|
<el-button icon="Download" @click="handleExport" v-hasPermi="['system:feedback:export']">导出</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="content-con" v-loading="loading">
|
||||||
|
<el-table :data="feedbackList" @selection-change="handleSelectionChange" stripe height="calc(100% - 0.62rem)">
|
||||||
|
<el-table-column type="selection" width="50" align="center" />
|
||||||
|
<el-table-column type="index" label="序号" :index="indexMethod" width="60" align="center" />
|
||||||
|
<el-table-column label="反馈类型" align="center" prop="feedbackType" width="110" />
|
||||||
|
<el-table-column label="反馈内容" prop="content" min-width="240" :show-overflow-tooltip="true">
|
||||||
|
<template #default="scope">
|
||||||
|
<span class="content-text">{{ scope.row.content }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="联系方式" align="center" prop="contact" width="140" :show-overflow-tooltip="true" />
|
||||||
|
<el-table-column label="截图" align="center" width="100">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-image
|
||||||
|
v-if="getImageList(scope.row).length"
|
||||||
|
class="feedback-thumb"
|
||||||
|
:src="getImageList(scope.row)[0]"
|
||||||
|
:preview-src-list="getImageList(scope.row)"
|
||||||
|
fit="cover"
|
||||||
|
/>
|
||||||
|
<span v-else class="empty-text">无</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="来源" align="center" prop="source" width="90" />
|
||||||
|
<el-table-column label="状态" align="center" prop="status" width="100">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag :type="statusTagType(scope.row.status)">{{ statusText(scope.row.status) }}</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="提交用户" align="center" prop="createBy" width="120" :show-overflow-tooltip="true" />
|
||||||
|
<el-table-column label="提交时间" align="center" prop="createTime" width="160">
|
||||||
|
<template #default="scope">
|
||||||
|
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" align="center" fixed="right" width="180" class-name="small-padding fixed-width">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tooltip content="详情" placement="top">
|
||||||
|
<el-button circle icon="View" @click="handleView(scope.row)" v-hasPermi="['system:feedback:query']"></el-button>
|
||||||
|
</el-tooltip>
|
||||||
|
<el-tooltip content="回复" placement="top">
|
||||||
|
<el-button circle icon="ChatDotRound" @click="handleReply(scope.row)" v-hasPermi="['system:feedback:reply']"></el-button>
|
||||||
|
</el-tooltip>
|
||||||
|
<el-tooltip content="删除" placement="top">
|
||||||
|
<el-button circle icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:feedback:remove']"></el-button>
|
||||||
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<el-pagination
|
||||||
|
small
|
||||||
|
background
|
||||||
|
layout="total, prev, pager, next"
|
||||||
|
:total="total"
|
||||||
|
v-model:current-page="currentPage"
|
||||||
|
@current-change="handleCurrentChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-dialog title="反馈详情" v-model="detailOpen" width="760px" append-to-body>
|
||||||
|
<el-descriptions :column="2" border>
|
||||||
|
<el-descriptions-item label="反馈类型">{{ detailForm.feedbackType }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="处理状态">
|
||||||
|
<el-tag :type="statusTagType(detailForm.status)">{{ statusText(detailForm.status) }}</el-tag>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="联系方式">{{ detailForm.contact || '未填写' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="反馈来源">{{ detailForm.source || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="提交用户">{{ detailForm.createBy || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="提交时间">{{ parseTime(detailForm.createTime) }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="反馈内容" :span="2">
|
||||||
|
<div class="detail-content">{{ detailForm.content }}</div>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="截图" :span="2">
|
||||||
|
<div v-if="getImageList(detailForm).length" class="image-list">
|
||||||
|
<el-image
|
||||||
|
v-for="image in getImageList(detailForm)"
|
||||||
|
:key="image"
|
||||||
|
class="detail-image"
|
||||||
|
:src="image"
|
||||||
|
:preview-src-list="getImageList(detailForm)"
|
||||||
|
fit="cover"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<span v-else>无</span>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="回复内容" :span="2">{{ detailForm.replyContent || '暂无回复' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="回复人">{{ detailForm.replyBy || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="回复时间">{{ parseTime(detailForm.replyTime) || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="备注" :span="2">{{ detailForm.remark || '-' }}</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button type="primary" @click="handleReply(detailForm)" v-hasPermi="['system:feedback:reply']">回复</el-button>
|
||||||
|
<el-button @click="detailOpen = false">关 闭</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<el-dialog title="回复反馈" v-model="replyOpen" width="600px" append-to-body>
|
||||||
|
<el-form ref="replyRef" :model="replyForm" :rules="replyRules" label-width="80px">
|
||||||
|
<el-form-item label="回复内容" prop="replyContent">
|
||||||
|
<el-input v-model="replyForm.replyContent" type="textarea" :rows="5" maxlength="1000" show-word-limit placeholder="请输入回复内容" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button type="primary" @click="submitReply">确 定</el-button>
|
||||||
|
<el-button @click="replyOpen = false">取 消</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup name="Feedback">
|
||||||
|
import { listFeedback, getFeedback, updateFeedback, replyFeedback, delFeedback } from '@/api/system/feedback'
|
||||||
|
|
||||||
|
const { proxy } = getCurrentInstance()
|
||||||
|
const { feedback_type, feedback_status } = proxy.useDict('feedback_type', 'feedback_status')
|
||||||
|
|
||||||
|
const defaultFeedbackTypeOptions = [
|
||||||
|
{ label: '功能建议', value: '功能建议' },
|
||||||
|
{ label: '问题反馈', value: '问题反馈' },
|
||||||
|
{ label: '数据异常', value: '数据异常' },
|
||||||
|
{ label: '其他', value: '其他' }
|
||||||
|
]
|
||||||
|
const defaultFeedbackStatusOptions = [
|
||||||
|
{ label: '待处理', value: '0' },
|
||||||
|
{ label: '已处理', value: '1' },
|
||||||
|
{ label: '已回复', value: '2' }
|
||||||
|
]
|
||||||
|
const sourceOptions = [
|
||||||
|
{ label: '小程序', value: '小程序' },
|
||||||
|
{ label: 'H5', value: 'H5' },
|
||||||
|
{ label: 'App', value: 'App' }
|
||||||
|
]
|
||||||
|
|
||||||
|
const feedbackTypeOptions = computed(() => (feedback_type.value && feedback_type.value.length ? feedback_type.value : defaultFeedbackTypeOptions))
|
||||||
|
const feedbackStatusOptions = computed(() => (feedback_status.value && feedback_status.value.length ? feedback_status.value : defaultFeedbackStatusOptions))
|
||||||
|
|
||||||
|
const feedbackList = ref([])
|
||||||
|
const loading = ref(true)
|
||||||
|
const showSearch = ref(true)
|
||||||
|
const ids = ref([])
|
||||||
|
const single = ref(true)
|
||||||
|
const multiple = ref(true)
|
||||||
|
const total = ref(0)
|
||||||
|
const currentPage = ref(1)
|
||||||
|
const mainStyleHeight = ref(0)
|
||||||
|
const searchHeightRef = ref(null)
|
||||||
|
const detailOpen = ref(false)
|
||||||
|
const replyOpen = ref(false)
|
||||||
|
const detailForm = ref({})
|
||||||
|
const replyForm = ref({})
|
||||||
|
|
||||||
|
const data = reactive({
|
||||||
|
queryParams: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
feedbackType: undefined,
|
||||||
|
status: undefined,
|
||||||
|
source: undefined,
|
||||||
|
createBy: undefined
|
||||||
|
},
|
||||||
|
replyRules: {
|
||||||
|
replyContent: [{ required: true, message: '回复内容不能为空', trigger: 'blur' }]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const { queryParams, replyRules } = toRefs(data)
|
||||||
|
|
||||||
|
function getList() {
|
||||||
|
loading.value = true
|
||||||
|
listFeedback(queryParams.value).then((response) => {
|
||||||
|
feedbackList.value = response.rows
|
||||||
|
total.value = response.total
|
||||||
|
loading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleQuery() {
|
||||||
|
queryParams.value.pageNum = 1
|
||||||
|
currentPage.value = 1
|
||||||
|
getList()
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetQuery() {
|
||||||
|
proxy.resetForm('queryRef')
|
||||||
|
handleQuery()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSelectionChange(selection) {
|
||||||
|
ids.value = selection.map((item) => item.id)
|
||||||
|
single.value = selection.length !== 1
|
||||||
|
multiple.value = !selection.length
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleView(row) {
|
||||||
|
getFeedback(row.id).then((response) => {
|
||||||
|
detailForm.value = response.data || {}
|
||||||
|
detailOpen.value = true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleReply(row) {
|
||||||
|
const id = row.id
|
||||||
|
getFeedback(id).then((response) => {
|
||||||
|
const data = response.data || row
|
||||||
|
replyForm.value = {
|
||||||
|
id: data.id,
|
||||||
|
replyContent: data.replyContent || ''
|
||||||
|
}
|
||||||
|
replyOpen.value = true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function submitReply() {
|
||||||
|
proxy.$refs.replyRef.validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
replyFeedback(replyForm.value).then(() => {
|
||||||
|
proxy.$modal.msgSuccess('回复成功')
|
||||||
|
replyOpen.value = false
|
||||||
|
detailOpen.value = false
|
||||||
|
getList()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleProcess(row) {
|
||||||
|
const id = row.id || ids.value[0]
|
||||||
|
if (!id) return
|
||||||
|
proxy.$modal
|
||||||
|
.confirm('是否确认将该反馈标记为已处理?')
|
||||||
|
.then(() => updateFeedback({ id, status: '1' }))
|
||||||
|
.then(() => {
|
||||||
|
proxy.$modal.msgSuccess('操作成功')
|
||||||
|
getList()
|
||||||
|
})
|
||||||
|
.catch(() => {})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDelete(row) {
|
||||||
|
const feedbackIds = row.id || ids.value
|
||||||
|
proxy.$modal
|
||||||
|
.confirm('是否确认删除意见反馈编号为"' + feedbackIds + '"的数据项?')
|
||||||
|
.then(() => delFeedback(feedbackIds))
|
||||||
|
.then(() => {
|
||||||
|
getList()
|
||||||
|
proxy.$modal.msgSuccess('删除成功')
|
||||||
|
})
|
||||||
|
.catch(() => {})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleExport() {
|
||||||
|
proxy.download(
|
||||||
|
'system/feedback/export',
|
||||||
|
{
|
||||||
|
...queryParams.value
|
||||||
|
},
|
||||||
|
`feedback_${new Date().getTime()}.xlsx`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCurrentChange = (val) => {
|
||||||
|
queryParams.value.pageNum = val
|
||||||
|
getList()
|
||||||
|
}
|
||||||
|
|
||||||
|
const indexMethod = (index) => {
|
||||||
|
const nowPage = queryParams.value.pageNum
|
||||||
|
const nowLimit = queryParams.value.pageSize
|
||||||
|
return index + 1 + (nowPage - 1) * nowLimit
|
||||||
|
}
|
||||||
|
|
||||||
|
function getImageList(row) {
|
||||||
|
if (!row) return []
|
||||||
|
if (Array.isArray(row.imageList) && row.imageList.length) {
|
||||||
|
return row.imageList.filter(Boolean)
|
||||||
|
}
|
||||||
|
if (row.images) {
|
||||||
|
return String(row.images)
|
||||||
|
.split(',')
|
||||||
|
.map((item) => item.trim())
|
||||||
|
.filter(Boolean)
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
function statusText(status) {
|
||||||
|
const text = proxy.selectDictLabel(feedbackStatusOptions.value, status)
|
||||||
|
return text || '-'
|
||||||
|
}
|
||||||
|
|
||||||
|
function statusTagType(status) {
|
||||||
|
const map = {
|
||||||
|
0: 'warning',
|
||||||
|
1: 'info',
|
||||||
|
2: 'success'
|
||||||
|
}
|
||||||
|
return map[status] || 'info'
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (searchHeightRef.value) {
|
||||||
|
mainStyleHeight.value = `calc(100% - ${(searchHeightRef.value.clientHeight + 10) / 100}rem)`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
getList()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.content-text {
|
||||||
|
display: inline-block;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
.empty-text {
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
.feedback-thumb {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
.detail-content {
|
||||||
|
line-height: 24px;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
.image-list {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
.detail-image {
|
||||||
|
width: 96px;
|
||||||
|
height: 96px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user