You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
910 lines
24 KiB
910 lines
24 KiB
<template>
|
|
<div class="detail-materials-tab">
|
|
<!-- <template v-if="taskItemList.length === 0">
|
|
<div class="empty-state">
|
|
<van-image :src="noDataImg" width="151" height="120" />
|
|
<div class="empty-text-main">暂无资料上传</div>
|
|
<div class="empty-text-sub">暂无材料要求</div>
|
|
</div>
|
|
</template> -->
|
|
<template v-if="Number(taskStatus) === 5 && !taskItemList.some(item => (item.itemValue || []).length > 0)">
|
|
<div class="empty-state">
|
|
<van-image :src="noDataImg" width="151" height="120" />
|
|
<div class="empty-text-main">暂无资料上传</div>
|
|
<div class="empty-text-sub"></div>
|
|
</div>
|
|
<div class="result-card">
|
|
<div class="result-title">检查结果</div>
|
|
<div class="readonly-result-text">检查结果:已办结(<span class="overdue-text">超期</span>)</div>
|
|
<div class="readonly-result-text" >检查时间:{{ inspectResult.checkTime }}</div>
|
|
</div>
|
|
</template>
|
|
<template v-else>
|
|
<div v-for="(item, index) in taskItemList" :key="index" class="material-card">
|
|
<div class="material-title">
|
|
<van-icon v-if="Number(item.pass) === 0" name="info-o" class="required-mark" />
|
|
<span v-else class="required-mark">*</span>
|
|
{{ item.itemName }}
|
|
<span class="material-desc">(小于200M/个,最多30个文件)</span>
|
|
</div>
|
|
|
|
<div class="material-media" v-if="!isEdit">
|
|
<template v-for="(url, fileIndex) in item.itemValue" :key="fileIndex">
|
|
<div v-if="isVideo(url)" class="media-item media-item--video" @click="onPreview(url, item.itemValue)">
|
|
<video :src="resolveMediaUrl(url)" muted preload="metadata" class="video-thumb"></video>
|
|
<van-icon name="play-circle-o" class="play-icon" />
|
|
</div>
|
|
<div v-else-if="isPdf(url)" class="media-item media-item--pdf" @click="onPreview(url, item.itemValue)">
|
|
<div class="pdf-icon-wrapper">
|
|
<van-icon name="description" class="pdf-icon" />
|
|
<span class="pdf-text">PDF</span>
|
|
</div>
|
|
</div>
|
|
<van-image v-else :src="resolveMediaUrl(url)" fit="cover" class="media-item" @click="onPreview(url, item.itemValue)" />
|
|
</template>
|
|
</div>
|
|
|
|
<div class="material-media-edit" v-else>
|
|
<!-- 开发环境:原 van-uploader 不变 -->
|
|
<van-uploader
|
|
v-if="!isProd"
|
|
v-model="item.fileList"
|
|
multiple
|
|
:max-count="20"
|
|
:max-size="200 * 1024 * 1024"
|
|
accept="image/*,video/*,.pdf"
|
|
:after-read="onAfterReadImage"
|
|
:before-read="onBeforeRead"
|
|
@oversize="onOversize"
|
|
>
|
|
<template #default>
|
|
<div class="uploader-custom-btn">
|
|
<van-icon name="photograph" size="24" color="#dcdee0" />
|
|
</div>
|
|
</template>
|
|
</van-uploader>
|
|
|
|
<!-- 生产环境:自定义上传按钮,点击弹出 action-sheet -->
|
|
<div v-else class="uploader-custom-btn" @click="onOpenUploadAction(item)">
|
|
<van-icon name="photograph" size="24" color="#dcdee0" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="objection-card" v-if="Number(item.pass) === 0">
|
|
<div class="objection-row">
|
|
<span class="objection-label">审核:</span>
|
|
<span class="objection-value">不通过</span>
|
|
</div>
|
|
<div class="objection-row" v-if="item.reason">
|
|
<span class="objection-label">原因:</span>
|
|
<span class="objection-value">{{ item.reason }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="result-card" v-if="Number(taskStatus) >= 2">
|
|
<div class="result-title">检查结果</div>
|
|
<div class="readonly-result-text" style="padding-top: 4px">
|
|
检查结果:{{
|
|
TASK_STATUS_TEXT[Number(taskStatus)]
|
|
}}
|
|
</div>
|
|
<div class="readonly-result-text">检查时间:{{ inspectResult.checkTime }}</div>
|
|
<div class="readonly-result-text" v-if="inspectResult.checkResult === 2 && inspectResult.deadline">
|
|
补正时限:{{ inspectResult.deadline }}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 展示云笔录 -->
|
|
<div class="cloud-record-card" v-if="Number(taskStatus) === 4 && Number(digitalVoiceLoggingStatus) !== 0">
|
|
<div class="cloud-record-row">
|
|
<span class="cloud-record-label">云笔录</span>
|
|
<van-button class="btn-view" size="small" plain type="primary" @click="onViewCloudRecord"> 查看 </van-button>
|
|
</div>
|
|
</div>
|
|
<div class="evaluate-card" v-if="Number(taskStatus) === 4">
|
|
<div class="card-title">反馈</div>
|
|
|
|
<!-- 表情选择 -->
|
|
<div class="emoji-section">
|
|
<div
|
|
class="emoji-item"
|
|
:class="{
|
|
active: evaluateForm.satisfactionLevel === 3,
|
|
disabled: readonly
|
|
}"
|
|
@click="!readonly && (evaluateForm.satisfactionLevel = 3)"
|
|
>
|
|
<img class="emoji-img" :src="evaluateForm.satisfactionLevel === 3 ? veryGoodImg : veryGoodDisImg" alt="满意" />
|
|
<span class="emoji-label">满意</span>
|
|
</div>
|
|
<div
|
|
class="emoji-item"
|
|
:class="{
|
|
active: evaluateForm.satisfactionLevel === 2,
|
|
disabled: readonly
|
|
}"
|
|
@click="!readonly && (evaluateForm.satisfactionLevel = 2)"
|
|
>
|
|
<img class="emoji-img" :src="evaluateForm.satisfactionLevel === 2 ? goodImg : goodDisImg" alt="一般" />
|
|
<span class="emoji-label">一般</span>
|
|
</div>
|
|
<div
|
|
class="emoji-item"
|
|
:class="{
|
|
active: evaluateForm.satisfactionLevel === 1,
|
|
disabled: readonly
|
|
}"
|
|
@click="!readonly && (evaluateForm.satisfactionLevel = 1)"
|
|
>
|
|
<img class="emoji-img" :src="evaluateForm.satisfactionLevel === 1 ? badImg : badDisImg" alt="不满意" />
|
|
<span class="emoji-label">不满意</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 反馈原因 -->
|
|
<div class="reason-section" v-if="evaluateForm.satisfactionLevel === 1 || evaluateForm.satisfactionLevel === 2">
|
|
<div class="reason-row">
|
|
<span class="reason-label"><span class="required-mark" v-if="!readonly">*</span>原因:</span>
|
|
<div class="reason-input-wrap">
|
|
<van-field
|
|
v-model="evaluateForm.commentReason"
|
|
type="textarea"
|
|
rows="3"
|
|
maxlength="300"
|
|
:show-word-limit="!readonly"
|
|
placeholder="请输入"
|
|
:readonly="readonly"
|
|
class="reason-field"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="submit-btn-wrap" v-if="!readonly">
|
|
<van-button class="btn-submit-eval" type="primary" block @click="onSubmitEvaluate">提交反馈</van-button>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<van-popup v-model:show="showDatePicker" round position="bottom" teleport="body">
|
|
<van-date-picker v-model="pickerDate" title="选择补正时限" :min-date="minDate" @confirm="onDateConfirm" @cancel="showDatePicker = false" />
|
|
</van-popup>
|
|
|
|
<!-- 视频预览弹窗 -->
|
|
<van-popup v-model:show="showVideoPreview" class="video-preview-popup" teleport="body" closeable @closed="previewVideoUrl = ''">
|
|
<video v-if="previewVideoUrl" :src="previewVideoUrl" controls autoplay playsinline webkit-playsinline class="preview-video"></video>
|
|
</van-popup>
|
|
|
|
<!-- 生产环境:上传方式选择 -->
|
|
<van-action-sheet
|
|
v-if="isEdit"
|
|
v-model:show="showUploadAction"
|
|
:actions="uploadActions"
|
|
cancel-text="取消"
|
|
close-on-click-action
|
|
teleport="body"
|
|
@select="onSelectUploadAction"
|
|
/>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup name="NosDetailMaterialsTab">
|
|
import { showImagePreview, showToast, showSuccessToast } from 'vant'
|
|
import { useRoute } from 'vue-router'
|
|
import noDataImg from '@/assets/images/nos/no-data.png'
|
|
import { uploadFile } from '@/util/fileUpload'
|
|
import { toFullFileUrl } from '@/util/fileUrl'
|
|
import { nosTaskCommentAPi } from '@/api/nos/index'
|
|
import { appEnv } from '@/config/env'
|
|
import { $globalStore } from '@/common/globalStore'
|
|
import {
|
|
takePhotoWithWaterMark,
|
|
startVideoRecorder,
|
|
uploadLocalFileToBackend,
|
|
} from '@/util/w6sMedia'
|
|
import 'vant/es/toast/style';
|
|
import veryGoodImg from '@/assets/images/eval-verygood.png'
|
|
import veryGoodDisImg from '@/assets/images/eval-verygood-dis.png'
|
|
import goodImg from '@/assets/images/eval-good.png'
|
|
import goodDisImg from '@/assets/images/eval-good-dis.png'
|
|
import badImg from '@/assets/images/eval-bad.png'
|
|
import badDisImg from '@/assets/images/eval-bad-dis.png'
|
|
|
|
const resolveMediaUrl = (url) => toFullFileUrl(url)
|
|
|
|
// 与 NosListPanel STATUS_OPTIONS 保持一致
|
|
const TASK_STATUS_TEXT = {
|
|
1: '待处理',
|
|
2: '待修改',
|
|
3: '待审核',
|
|
4: '已办结',
|
|
5: '超期'
|
|
}
|
|
|
|
const props = defineProps({
|
|
taskStatus: {
|
|
type: [Number, String],
|
|
default: ''
|
|
},
|
|
isEdit: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
evaluateData: {
|
|
type: Object,
|
|
default: null
|
|
}
|
|
})
|
|
|
|
const emit = defineEmits(['view-cloud-record'])
|
|
|
|
const onViewCloudRecord = () => {
|
|
emit('view-cloud-record')
|
|
}
|
|
|
|
const taskItemList = defineModel('taskItemList', {
|
|
type: Array,
|
|
default: () => []
|
|
})
|
|
const inspectResult = defineModel('inspectResult', {
|
|
type: Object,
|
|
default: () => ({
|
|
checkResult: null,
|
|
deadline: '',
|
|
checkTime: ''
|
|
})
|
|
})
|
|
const digitalVoiceLoggingStatus = defineModel('digitalVoiceLoggingStatus', {
|
|
type: [Number, String],
|
|
default: ''
|
|
})
|
|
const route = useRoute()
|
|
const taskId = route.query.id
|
|
|
|
// ========== 反馈相关 ==========
|
|
const hasEvaluated = ref(false)
|
|
const readonly = computed(() => hasEvaluated.value)
|
|
|
|
const evaluateForm = ref({
|
|
satisfactionLevel: 3, // 3: 满意, 2: 一般, 1: 不满意
|
|
commentReason: ''
|
|
})
|
|
|
|
watch(
|
|
() => props.evaluateData,
|
|
(val) => {
|
|
if (val && val.satisfaction !== undefined) {
|
|
const satMap = { 0: 3, 1: 2, 2: 1 }
|
|
evaluateForm.value.satisfactionLevel = satMap[val.satisfaction] !== undefined ? satMap[val.satisfaction] : 3
|
|
|
|
if (val.commentReason) {
|
|
evaluateForm.value.commentReason = val.commentReason
|
|
}
|
|
hasEvaluated.value = true
|
|
}
|
|
},
|
|
{ immediate: true, deep: true }
|
|
)
|
|
|
|
const onSubmitEvaluate = async () => {
|
|
if ((evaluateForm.value.satisfactionLevel === 1 || evaluateForm.value.satisfactionLevel === 2) && !evaluateForm.value.commentReason?.trim()) {
|
|
showToast('请输入原因')
|
|
return
|
|
}
|
|
|
|
try {
|
|
const params = {
|
|
taskId: taskId,
|
|
satisfaction: evaluateForm.value.satisfactionLevel === 3 ? 0 : evaluateForm.value.satisfactionLevel === 2 ? 1 : 2,
|
|
reason: evaluateForm.value.commentReason?.trim()
|
|
}
|
|
|
|
const res = await nosTaskCommentAPi(params)
|
|
if (res && res.code === 200) {
|
|
showSuccessToast('反馈提交成功')
|
|
hasEvaluated.value = true
|
|
router.back()
|
|
} else {
|
|
showToast(res.msg || '反馈提交失败')
|
|
}
|
|
} catch (err) {
|
|
console.error(err)
|
|
// showToast("反馈提交失败");
|
|
}
|
|
}
|
|
|
|
const showDatePicker = ref(false)
|
|
const minDate = new Date()
|
|
|
|
const showVideoPreview = ref(false)
|
|
const previewVideoUrl = ref('')
|
|
|
|
// ========== 上传入口(生产环境用 w6s SDK) ==========
|
|
const isProd = computed(() => appEnv === 'prod')
|
|
const showUploadAction = ref(false)
|
|
const activeUploadItem = ref(null)
|
|
const uploadActions = [
|
|
{ name: '拍照' },
|
|
{ name: '录制视频' },
|
|
]
|
|
|
|
const pickerDate = ref([String(minDate.getFullYear()), String(minDate.getMonth() + 1).padStart(2, '0'), String(minDate.getDate()).padStart(2, '0')])
|
|
|
|
const onOversize = () => {
|
|
showToast('文件大小不能超过 200MB')
|
|
}
|
|
|
|
const isVideo = (url) => {
|
|
if (!url) return false
|
|
return /\.(mp4|webm|ogg|mov)$/i.test(url)
|
|
}
|
|
|
|
const isPdf = (url) => {
|
|
if (!url) return false
|
|
return /\.pdf$/i.test(url)
|
|
}
|
|
|
|
const IMAGE_EXT_RE = /\.(png|jpe?g|gif|webp|bmp)$/i
|
|
const VIDEO_EXT_RE = /\.(mp4|webm|ogg|mov)$/i
|
|
const PDF_EXT_RE = /\.pdf$/i
|
|
|
|
const isAllowedUploadFile = (file) => {
|
|
const name = (file.name || '').toLowerCase()
|
|
const type = (file.type || '').toLowerCase()
|
|
|
|
if (type.startsWith('image/') || IMAGE_EXT_RE.test(name)) return true
|
|
if (type.startsWith('video/') || VIDEO_EXT_RE.test(name)) return true
|
|
if (type === 'application/pdf' || PDF_EXT_RE.test(name)) return true
|
|
return false
|
|
}
|
|
|
|
// 附件上传前校验,限制图片、视频、PDF文件
|
|
const onBeforeRead = (file) => {
|
|
const files = Array.isArray(file) ? file : [file]
|
|
for (const item of files) {
|
|
if (!isAllowedUploadFile(item)) {
|
|
showToast('仅支持上传图片、视频、PDF 格式文件')
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
const onAfterReadImage = (file) => {
|
|
const files = Array.isArray(file) ? file : [file]
|
|
files.forEach((f) => {
|
|
uploadFile(f, (res) => {
|
|
const data = res && res.data ? res.data : res
|
|
const filePath = typeof data === 'string' ? data : (data && (data.data || data.url || data.fileUrl || data.path)) || ''
|
|
if (filePath) {
|
|
f.url = toFullFileUrl(filePath)
|
|
if (isVideo(filePath) || isPdf(filePath)) {
|
|
f.isImage = false
|
|
}
|
|
f.status = 'done'
|
|
} else {
|
|
f.status = 'failed'
|
|
f.message = '上传失败'
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
// ========== 生产环境:w6s SDK 上传(仅现场拍摄/录制) ==========
|
|
const onOpenUploadAction = (item) => {
|
|
if ((item.fileList?.length || 0) >= 20) {
|
|
showToast('已达到最多 20 个文件上限')
|
|
return
|
|
}
|
|
activeUploadItem.value = item
|
|
showUploadAction.value = true
|
|
}
|
|
|
|
const onSelectUploadAction = ({ name }) => {
|
|
const item = activeUploadItem.value
|
|
if (!item) return
|
|
if (name === '拍照') onTakePhoto(item)
|
|
else if (name === '录制视频') onRecordVideo(item)
|
|
}
|
|
|
|
// 拍照带水印(时间戳 + 经纬度自动叠加)
|
|
const onTakePhoto = async (item) => {
|
|
console.log('[NosDetail] onTakePhoto 开始, item:', item, 'fileList:', item?.fileList)
|
|
try {
|
|
const watermarkContent = $globalStore.useCommon?.enterpriseName || '深圳市行政执法监督码'
|
|
const res = await takePhotoWithWaterMark({ content: watermarkContent })
|
|
console.log('[NosDetail] 拍照返回 res:', res, 'imageURL:', res?.imageURL, 'key:', res?.key)
|
|
const localPath = res.imageURL || res.key
|
|
console.log('[NosDetail] 解析出的 localPath:', localPath)
|
|
await pushSdkFileToFileList(item, {
|
|
localPath,
|
|
fileName: `photo-${Date.now()}.jpg`,
|
|
mimeType: 'image/jpeg',
|
|
isImage: true,
|
|
})
|
|
} catch (err) {
|
|
console.error('[NosDetail] w6s 拍照失败', err)
|
|
showToast('拍照失败或已取消')
|
|
}
|
|
}
|
|
|
|
// 录制视频
|
|
const onRecordVideo = async (item) => {
|
|
try {
|
|
const res = await startVideoRecorder({ duration: 60, quality: 1 })
|
|
const info = res.info || res
|
|
await pushSdkFileToFileList(item, {
|
|
localPath: info.video_path,
|
|
fileName: `video-${Date.now()}.mp4`,
|
|
mimeType: 'video/mp4',
|
|
isImage: false,
|
|
})
|
|
} catch (err) {
|
|
console.error('w6s 录制失败', err)
|
|
showToast('录制失败或已取消')
|
|
}
|
|
}
|
|
|
|
// 通用:把 SDK 返回的本地文件上传后端 → 写入 fileList
|
|
const pushSdkFileToFileList = async (item, { localPath, fileName, mimeType, isImage }) => {
|
|
console.log('[NosDetail] pushSdkFileToFileList 开始, 入参:', { localPath, fileName, mimeType, isImage })
|
|
if (!localPath) {
|
|
console.warn('[NosDetail] pushSdkFileToFileList localPath 为空, 直接返回')
|
|
return
|
|
}
|
|
const fileObj = {
|
|
url: '',
|
|
isImage,
|
|
status: 'uploading',
|
|
message: '上传中...',
|
|
}
|
|
item.fileList.push(fileObj)
|
|
console.log('[NosDetail] 已 push 到 fileList, 当前 fileList 长度:', item.fileList.length, 'fileObj:', fileObj)
|
|
try {
|
|
const res = await uploadLocalFileToBackend({ fileURL: localPath, fileName, mimeType })
|
|
console.log('[NosDetail] 上传后端返回 res:', res)
|
|
const data = res && res.data ? res.data : res
|
|
const filePath = typeof data === 'string'
|
|
? data
|
|
: (data && (data.data || data.url || data.fileUrl || data.path)) || ''
|
|
console.log('[NosDetail] 解析出的 filePath:', filePath, 'data:', data)
|
|
if (filePath) {
|
|
fileObj.url = toFullFileUrl(filePath)
|
|
fileObj.status = 'done'
|
|
fileObj.message = ''
|
|
console.log('[NosDetail] 上传成功, fileObj.url:', fileObj.url)
|
|
} else {
|
|
fileObj.status = 'failed'
|
|
fileObj.message = '上传失败'
|
|
console.warn('[NosDetail] 上传失败: filePath 为空')
|
|
}
|
|
} catch (err) {
|
|
console.error('[NosDetail] w6s 上传后端失败:', err)
|
|
fileObj.status = 'failed'
|
|
fileObj.message = '上传失败'
|
|
}
|
|
}
|
|
|
|
watch(
|
|
() => taskItemList.value,
|
|
(newList) => {
|
|
if (newList && newList.length > 0) {
|
|
newList.forEach((item) => {
|
|
if (!item.fileList) {
|
|
// Initialize fileList from itemValue if editing is possible
|
|
item.fileList = (item.itemValue || []).map((url) => ({
|
|
url: toFullFileUrl(url),
|
|
isImage: !isVideo(url) && !isPdf(url)
|
|
}))
|
|
}
|
|
})
|
|
}
|
|
},
|
|
{ immediate: true, deep: true }
|
|
)
|
|
|
|
const onAuditChange = (item) => {
|
|
if (Number(item.pass) !== 0) {
|
|
item.reason = ''
|
|
}
|
|
}
|
|
|
|
const onResultTypeChange = () => {
|
|
if (inspectResult.value.checkResult !== 2) {
|
|
inspectResult.value.deadline = ''
|
|
}
|
|
}
|
|
|
|
const onDateConfirm = ({ selectedValues }) => {
|
|
inspectResult.value.deadline = selectedValues.join('-')
|
|
showDatePicker.value = false
|
|
}
|
|
|
|
const onPreview = (url, itemValue) => {
|
|
if (!url) return
|
|
const fullUrl = resolveMediaUrl(url)
|
|
if (isVideo(url)) {
|
|
previewVideoUrl.value = fullUrl
|
|
showVideoPreview.value = true
|
|
return
|
|
}
|
|
if (isPdf(url)) {
|
|
window.open(fullUrl, '_blank')
|
|
return
|
|
}
|
|
const images = itemValue.filter((u) => !isVideo(u) && !isPdf(u)).map((u) => resolveMediaUrl(u))
|
|
const start = images.indexOf(fullUrl)
|
|
showImagePreview({
|
|
images,
|
|
startPosition: start >= 0 ? start : 0
|
|
})
|
|
}
|
|
</script>
|
|
|
|
<style lang="less" scoped>
|
|
.empty-state {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding-top: 100px;
|
|
|
|
.empty-text-main {
|
|
margin-top: 16px;
|
|
font-size: 16px;
|
|
color: #323233;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.empty-text-sub {
|
|
margin-top: 8px;
|
|
font-size: 14px;
|
|
color: #969799;
|
|
}
|
|
}
|
|
|
|
.detail-materials-tab {
|
|
padding-bottom: 80px;
|
|
}
|
|
|
|
.material-card,
|
|
.result-card {
|
|
background: #fff;
|
|
margin-bottom: 8px;
|
|
padding: 14px 16px;
|
|
}
|
|
|
|
.material-title,
|
|
.result-title {
|
|
font-size: 15px;
|
|
font-weight: 600;
|
|
color: #323233;
|
|
margin-bottom: 12px;
|
|
}
|
|
|
|
.required-mark {
|
|
color: #ee0a24;
|
|
margin-right: 2px;
|
|
}
|
|
|
|
.material-desc {
|
|
font-size: 12px;
|
|
color: #969799;
|
|
font-weight: normal;
|
|
}
|
|
|
|
.material-media,
|
|
.material-media-edit {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 8px;
|
|
margin-bottom: 12px;
|
|
}
|
|
|
|
.uploader-custom-btn {
|
|
width: 72px;
|
|
height: 72px;
|
|
background-color: #f7f8fa;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border-radius: 4px;
|
|
border: 1px dashed #ebedf0;
|
|
}
|
|
|
|
.media-item {
|
|
position: relative;
|
|
flex: none;
|
|
width: 72px;
|
|
height: 72px;
|
|
border-radius: 4px;
|
|
overflow: hidden;
|
|
|
|
:deep(.van-image__img) {
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
|
|
&--video {
|
|
cursor: pointer;
|
|
|
|
.video-thumb {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
display: block;
|
|
background-color: #000;
|
|
}
|
|
|
|
.play-icon {
|
|
position: absolute;
|
|
left: 50%;
|
|
top: 50%;
|
|
transform: translate(-50%, -50%);
|
|
font-size: 28px;
|
|
color: rgba(255, 255, 255, 0.9);
|
|
}
|
|
}
|
|
|
|
&--pdf {
|
|
background-color: #f7f8fa;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
cursor: pointer;
|
|
border: 1px solid #ebedf0;
|
|
|
|
.pdf-icon-wrapper {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
color: #ee0a24;
|
|
|
|
.pdf-icon {
|
|
font-size: 28px;
|
|
}
|
|
|
|
.pdf-text {
|
|
font-size: 12px;
|
|
margin-top: 4px;
|
|
font-weight: 500;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.audit-field {
|
|
padding: 8px 0;
|
|
|
|
:deep(.van-field__label) {
|
|
width: 48px;
|
|
color: #323233;
|
|
}
|
|
|
|
:deep(.van-radio-group) {
|
|
flex: 1;
|
|
justify-content: flex-end;
|
|
gap: 16px;
|
|
}
|
|
|
|
:deep(.van-radio) {
|
|
margin-right: 0;
|
|
}
|
|
|
|
:deep(.van-radio__label) {
|
|
font-size: 14px;
|
|
}
|
|
}
|
|
|
|
.reason-field {
|
|
padding: 8px 0 0;
|
|
|
|
:deep(.van-field__label) {
|
|
width: 48px;
|
|
color: #646566;
|
|
}
|
|
}
|
|
|
|
.result-options {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 12px;
|
|
margin-bottom: 8px;
|
|
|
|
:deep(.van-radio) {
|
|
align-items: center;
|
|
}
|
|
|
|
:deep(.van-radio__label) {
|
|
line-height: 1.5;
|
|
color: #323233;
|
|
font-size: 14px;
|
|
}
|
|
}
|
|
|
|
.deadline-field {
|
|
padding: 8px 0 0;
|
|
|
|
:deep(.van-field__label) {
|
|
width: 72px;
|
|
color: #323233;
|
|
}
|
|
}
|
|
|
|
.objection-card {
|
|
background-color: #fffbe8;
|
|
border-radius: 4px;
|
|
padding: 12px;
|
|
margin-top: 8px;
|
|
}
|
|
|
|
.objection-row {
|
|
display: flex;
|
|
font-size: 13px;
|
|
line-height: 1.5;
|
|
color: #ed6a0c;
|
|
margin-bottom: 4px;
|
|
|
|
&:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
}
|
|
|
|
.objection-label {
|
|
flex: none;
|
|
}
|
|
|
|
.objection-value {
|
|
flex: 1;
|
|
word-break: break-all;
|
|
}
|
|
|
|
.readonly-result-text {
|
|
color: #323233;
|
|
font-size: 14px;
|
|
line-height: 1.5;
|
|
padding-bottom: 8px;
|
|
}
|
|
|
|
.video-preview-popup {
|
|
width: 100vw;
|
|
height: 100vh;
|
|
max-width: none !important;
|
|
margin: 0 !important;
|
|
background-color: #000;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
|
|
.preview-video {
|
|
width: 100%;
|
|
max-height: 100%;
|
|
object-fit: cover;
|
|
outline: none;
|
|
}
|
|
|
|
:deep(.van-popup__close-icon) {
|
|
color: #fff;
|
|
font-size: 24px;
|
|
z-index: 2;
|
|
}
|
|
}
|
|
|
|
.evaluate-card {
|
|
background: #fff;
|
|
margin-top: 8px;
|
|
padding: 14px 16px;
|
|
border-radius: 4px;
|
|
|
|
.card-title {
|
|
font-size: 15px;
|
|
font-weight: 600;
|
|
color: #323233;
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.emoji-section {
|
|
display: flex;
|
|
justify-content: space-around;
|
|
padding: 10px 0 24px;
|
|
border-bottom: 1px solid #f5f6f7;
|
|
|
|
.emoji-item {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
opacity: 0.6;
|
|
transition: all 0.3s;
|
|
|
|
&.active {
|
|
opacity: 1;
|
|
transform: scale(1.1);
|
|
.emoji-label {
|
|
color: #fca000;
|
|
font-weight: 600;
|
|
}
|
|
}
|
|
|
|
&.disabled {
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
.emoji-img {
|
|
width: 48px;
|
|
height: 48px;
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.emoji-label {
|
|
font-size: 13px;
|
|
color: #969799;
|
|
}
|
|
}
|
|
}
|
|
|
|
.reason-section {
|
|
padding-top: 20px;
|
|
|
|
.reason-row {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
margin-bottom: 16px;
|
|
|
|
.reason-label {
|
|
width: 52px;
|
|
font-size: 14px;
|
|
color: #323233;
|
|
margin-top: 8px;
|
|
flex: none;
|
|
}
|
|
|
|
.reason-input-wrap {
|
|
flex: 1;
|
|
min-width: 0;
|
|
}
|
|
|
|
.reason-field {
|
|
padding: 8px;
|
|
background: #f7f8fa;
|
|
border-radius: 4px;
|
|
}
|
|
}
|
|
}
|
|
|
|
.submit-btn-wrap {
|
|
margin-top: 24px;
|
|
|
|
.btn-submit-eval {
|
|
height: 44px;
|
|
border-radius: 22px;
|
|
font-size: 16px;
|
|
}
|
|
}
|
|
}
|
|
|
|
.cloud-record-card {
|
|
background: #fff;
|
|
margin-top: 8px;
|
|
padding: 14px 16px;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.cloud-record-row {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
}
|
|
|
|
.cloud-record-label {
|
|
font-size: 15px;
|
|
font-weight: 600;
|
|
color: #323233;
|
|
}
|
|
|
|
.btn-view {
|
|
height: 28px;
|
|
padding: 0 12px;
|
|
font-size: 12px;
|
|
}
|
|
</style>
|
|
|