2026-03-11 11:16:35 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<div class="page-content">
|
|
|
|
|
|
<div class="customer-toolbar boxshadow">
|
|
|
|
|
|
<div class="customer-search-group">
|
|
|
|
|
|
<el-form-item label="关键字:" label-width="70px">
|
|
|
|
|
|
<el-input style="min-width: 120px" v-model="keyword" placeholder="请输入关键字" />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="审核状态:" label-width="80px">
|
|
|
|
|
|
<el-select v-model="auditStatus" placeholder="请选择状态" style="min-width: 120px">
|
|
|
|
|
|
<el-option label="全部" value="" />
|
|
|
|
|
|
<el-option label="待审核" :value="0" />
|
|
|
|
|
|
<el-option label="审核通过" :value="1" />
|
|
|
|
|
|
<el-option label="审核拒绝" :value="2" />
|
2026-03-12 16:48:21 +08:00
|
|
|
|
<el-option label="抓取成功" :value="3" />
|
|
|
|
|
|
<el-option label="推送成功" :value="4" />
|
|
|
|
|
|
<el-option label="推送失败" :value="5" />
|
2026-03-11 11:16:35 +08:00
|
|
|
|
</el-select>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="boxshadow">
|
2026-03-12 16:48:21 +08:00
|
|
|
|
<div style="text-align: right; padding: 12px 0">
|
|
|
|
|
|
<el-button type="primary" @click="fetchpublishfn"> 抓取 </el-button>
|
|
|
|
|
|
</div>
|
2026-03-11 11:16:35 +08:00
|
|
|
|
<ArtTable
|
|
|
|
|
|
ref="tableRef"
|
|
|
|
|
|
row-key="id"
|
|
|
|
|
|
:loading="loading"
|
|
|
|
|
|
:data="tableData"
|
|
|
|
|
|
:currentPage="pagination.currentPage"
|
|
|
|
|
|
:pageSize="pagination.pageSize"
|
|
|
|
|
|
:total="pagination.total"
|
|
|
|
|
|
@size-change="handleSizeChange"
|
|
|
|
|
|
@current-change="handleCurrentChange"
|
|
|
|
|
|
>
|
|
|
|
|
|
<template #default>
|
|
|
|
|
|
<ElTableColumn label="编号" prop="code" width="120px" />
|
|
|
|
|
|
<ElTableColumn label="产品名称" prop="product_name" width="150px" />
|
|
|
|
|
|
<ElTableColumn label="产品图" width="80px">
|
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
|
<div class="thumb-list" v-if="parsePreviewList(scope.row.images_pull).length">
|
|
|
|
|
|
<img
|
|
|
|
|
|
:src="parsePreviewList(scope.row.images_pull)[0]"
|
|
|
|
|
|
alt="产品图"
|
|
|
|
|
|
class="avatarimg"
|
|
|
|
|
|
@click="handleAvatarClick1(parsePreviewList(scope.row.images_pull)[0])"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<span v-else>--</span>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</ElTableColumn>
|
|
|
|
|
|
<ElTableColumn label="车价(¥)" prop="price" width="100px" />
|
|
|
|
|
|
<ElTableColumn label="车源地" prop="source_place" width="120px" />
|
|
|
|
|
|
<ElTableColumn label="上牌时间" prop="plate_time" width="120px" />
|
|
|
|
|
|
<ElTableColumn label="排量" prop="displacement" width="100px" />
|
|
|
|
|
|
<ElTableColumn label="里程(Km)" prop="mileage" width="100px" />
|
|
|
|
|
|
<ElTableColumn label="马力" prop="power" width="80px" />
|
|
|
|
|
|
<ElTableColumn label="车身颜色" prop="body_color" width="100px" />
|
|
|
|
|
|
<ElTableColumn label="审核状态" prop="status" width="100px">
|
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
|
<el-tag :type="getAuditStatusColor(scope.row.status)">
|
|
|
|
|
|
{{ getAuditStatusText(scope.row.status) }}
|
|
|
|
|
|
</el-tag>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</ElTableColumn>
|
|
|
|
|
|
<ElTableColumn label="创建时间" prop="created_at" width="160px" />
|
|
|
|
|
|
<ElTableColumn fixed="right" label="操作" width="240px">
|
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
|
<ElRow>
|
|
|
|
|
|
<el-button link @click="showDialog('detail', scope.row)"> 详情 </el-button>
|
2026-03-12 16:48:21 +08:00
|
|
|
|
<el-button
|
|
|
|
|
|
link
|
|
|
|
|
|
type="primary"
|
|
|
|
|
|
@click="editFn(scope.row)"
|
|
|
|
|
|
v-if="scope.row.status === 0 || scope.row.status === 2"
|
|
|
|
|
|
>
|
|
|
|
|
|
编辑
|
|
|
|
|
|
</el-button>
|
2026-03-11 11:16:35 +08:00
|
|
|
|
<el-button link type="success" @click="showAuditRecords(scope.row)"> 审核记录 </el-button>
|
|
|
|
|
|
<el-button link type="danger" @click="deleteFn(scope.row)"> 删除 </el-button>
|
|
|
|
|
|
</ElRow>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</ElTableColumn>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</ArtTable>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 详情弹窗 -->
|
|
|
|
|
|
<el-dialog v-model="detailDialogVisible" title="车辆详情" width="800px">
|
|
|
|
|
|
<el-scrollbar max-height="60vh">
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<el-descriptions v-if="detailData" :column="2" border label-width="100px">
|
|
|
|
|
|
<el-descriptions-item label="编号">{{ detailData.code || '--' }}</el-descriptions-item>
|
|
|
|
|
|
<el-descriptions-item label="产品_name">{{ detailData.product_name || '--' }}</el-descriptions-item>
|
|
|
|
|
|
<el-descriptions-item label="车价(¥)">{{ detailData.price || '--' }}</el-descriptions-item>
|
|
|
|
|
|
<el-descriptions-item label="车源地">{{ detailData.source_place || '--' }}</el-descriptions-item>
|
|
|
|
|
|
<el-descriptions-item label="上牌时间">{{ detailData.plate_time || '--' }}</el-descriptions-item>
|
|
|
|
|
|
<el-descriptions-item label="排量">{{ detailData.displacement || '--' }}</el-descriptions-item>
|
|
|
|
|
|
<el-descriptions-item label="里程(Km)">{{ detailData.mileage || '--' }}</el-descriptions-item>
|
|
|
|
|
|
<el-descriptions-item label="马力">{{ detailData.power || '--' }}</el-descriptions-item>
|
|
|
|
|
|
<el-descriptions-item label="车身颜色">{{ detailData.body_color || '--' }}</el-descriptions-item>
|
|
|
|
|
|
<el-descriptions-item label="审核状态">
|
|
|
|
|
|
<el-tag :type="getAuditStatusColor(detailData.status)">
|
|
|
|
|
|
{{ getAuditStatusText(detailData.status) }}
|
|
|
|
|
|
</el-tag>
|
|
|
|
|
|
</el-descriptions-item>
|
|
|
|
|
|
<el-descriptions-item label="创建时间" :span="2">{{ detailData.created_at || '--' }}</el-descriptions-item>
|
|
|
|
|
|
<el-descriptions-item label="审核意见" :span="2" v-if="detailData.audit_opinion">
|
|
|
|
|
|
{{ detailData.audit_opinion }}
|
|
|
|
|
|
</el-descriptions-item>
|
|
|
|
|
|
<el-descriptions-item label="车况介绍" :span="2">
|
|
|
|
|
|
{{ detailData.condition_desc || '--' }}
|
|
|
|
|
|
</el-descriptions-item>
|
|
|
|
|
|
<!-- 视频展示 -->
|
|
|
|
|
|
<el-descriptions-item label="介绍视频" :span="2" v-if="detailVideoList.length">
|
|
|
|
|
|
<div class="video-preview-list">
|
|
|
|
|
|
<div v-for="(video, index) in detailVideoList" :key="index" class="video-item">
|
|
|
|
|
|
<video :src="video.url" controls class="video-preview"></video>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</el-descriptions-item>
|
|
|
|
|
|
<!-- FAQ 展示 -->
|
2026-03-12 16:48:21 +08:00
|
|
|
|
<!-- <el-descriptions-item label="FAQ 问答" :span="2" v-if="detailData.faqs && detailData.faqs.length">
|
2026-03-11 11:16:35 +08:00
|
|
|
|
<div class="faq-display">
|
|
|
|
|
|
<div v-for="(faq, index) in detailData.faqs" :key="index" class="faq-item">
|
|
|
|
|
|
<div class="faq-question">
|
|
|
|
|
|
<span class="faq-label">Q{{ index + 1 }}:</span>
|
|
|
|
|
|
{{ faq.question || '--' }}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="faq-answer">
|
|
|
|
|
|
<span class="faq-label">A{{ index + 1 }}:</span>
|
|
|
|
|
|
{{ faq.answer || '--' }}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2026-03-12 16:48:21 +08:00
|
|
|
|
</el-descriptions-item> -->
|
2026-03-11 11:16:35 +08:00
|
|
|
|
<!-- 产品图 -->
|
|
|
|
|
|
<el-descriptions-item label="产品图" :span="2">
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="thumb-list"
|
|
|
|
|
|
v-if="parsePreviewList(detailData.images_pull || detailData.images).length"
|
|
|
|
|
|
>
|
|
|
|
|
|
<img
|
|
|
|
|
|
v-for="(img, index) in parsePreviewList(
|
|
|
|
|
|
detailData.images_pull || detailData.images
|
|
|
|
|
|
)"
|
|
|
|
|
|
:key="index"
|
|
|
|
|
|
:src="img"
|
|
|
|
|
|
alt="产品图"
|
|
|
|
|
|
class="avatarimg"
|
|
|
|
|
|
@click="handleAvatarClick(index)"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<span v-else>--</span>
|
|
|
|
|
|
</el-descriptions-item>
|
|
|
|
|
|
</el-descriptions>
|
|
|
|
|
|
<div style="margin-top: 20px">
|
|
|
|
|
|
<FilePreview title="附件" :files="contractFiles" empty-text="暂无附件" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</el-scrollbar>
|
|
|
|
|
|
</el-dialog>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 编辑弹窗 -->
|
|
|
|
|
|
<el-dialog v-model="editDialogVisible" title="编辑发布" width="900px" :close-on-click-modal="false">
|
|
|
|
|
|
<el-scrollbar max-height="60vh">
|
|
|
|
|
|
<el-form
|
|
|
|
|
|
ref="editFormRef"
|
|
|
|
|
|
:model="editForm"
|
|
|
|
|
|
:rules="editRules"
|
|
|
|
|
|
label-width="110px"
|
|
|
|
|
|
label-position="right"
|
|
|
|
|
|
>
|
|
|
|
|
|
<ElRow :gutter="12" class="item-row-wrap">
|
|
|
|
|
|
<ElCol :sm="24" :md="12" :lg="12">
|
|
|
|
|
|
<el-form-item label="产品名称" prop="product_name">
|
|
|
|
|
|
<div class="bind-name-wrapper">
|
|
|
|
|
|
<el-input
|
|
|
|
|
|
v-model="editForm.product_name"
|
|
|
|
|
|
placeholder="请输入车辆的品牌 系列查找车型"
|
|
|
|
|
|
clearable
|
|
|
|
|
|
/>
|
|
|
|
|
|
<el-button type="primary" @click="onEditBindByName" :loading="editBindState.loading">
|
|
|
|
|
|
绑定
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<!-- 绑定结果提示 -->
|
|
|
|
|
|
<el-form-item label=" ">
|
|
|
|
|
|
<div class="bind-tip" v-if="editBindState.boundProduct">
|
|
|
|
|
|
<span class="bind-tip-text">
|
|
|
|
|
|
已绑定:{{
|
|
|
|
|
|
editBindState.boundProduct.name ||
|
|
|
|
|
|
editBindState.boundProduct.product_name ||
|
|
|
|
|
|
editBindState.boundProduct.title ||
|
|
|
|
|
|
'-'
|
|
|
|
|
|
}}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
<el-button link type="danger" @click="clearEditBind">清除</el-button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="bind-tip" v-else>
|
|
|
|
|
|
<span>例:比亚迪海狮07DM</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</ElCol>
|
|
|
|
|
|
<ElCol :sm="24" :md="12" :lg="12">
|
|
|
|
|
|
<el-form-item label="车价(¥)" prop="price">
|
|
|
|
|
|
<el-input v-model="editForm.price" placeholder="请输入车价" />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</ElCol>
|
|
|
|
|
|
<ElCol :sm="24" :md="12" :lg="12">
|
|
|
|
|
|
<el-form-item label="车源地" prop="source_place">
|
|
|
|
|
|
<el-input v-model="editForm.source_place" placeholder="请输入车源地" />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</ElCol>
|
|
|
|
|
|
<ElCol :sm="24" :md="12" :lg="12">
|
|
|
|
|
|
<el-form-item label="上牌时间" prop="plate_time">
|
|
|
|
|
|
<el-date-picker
|
|
|
|
|
|
v-model="editForm.plate_time"
|
|
|
|
|
|
type="date"
|
|
|
|
|
|
value-format="YYYY-MM-DD"
|
|
|
|
|
|
placeholder="请选择时间"
|
|
|
|
|
|
style="width: 100%"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</ElCol>
|
|
|
|
|
|
<ElCol :sm="24" :md="12" :lg="12">
|
|
|
|
|
|
<el-form-item label="排量" prop="displacement">
|
|
|
|
|
|
<el-input v-model="editForm.displacement" placeholder="请输入排量" />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</ElCol>
|
|
|
|
|
|
<ElCol :sm="24" :md="12" :lg="12">
|
|
|
|
|
|
<el-form-item label="马力" prop="power">
|
|
|
|
|
|
<el-input v-model="editForm.power" placeholder="请输入马力" />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</ElCol>
|
|
|
|
|
|
<ElCol :sm="24" :md="12" :lg="12">
|
|
|
|
|
|
<el-form-item label="里程(Km)" prop="mileage">
|
|
|
|
|
|
<el-input v-model="editForm.mileage" placeholder="请输入里程" />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</ElCol>
|
|
|
|
|
|
<ElCol :sm="24" :md="12" :lg="12">
|
|
|
|
|
|
<el-form-item label="车身颜色" prop="body_color">
|
|
|
|
|
|
<el-input v-model="editForm.body_color" placeholder="请输入车身颜色" />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</ElCol>
|
|
|
|
|
|
<ElCol :sm="24" :md="24" :lg="24">
|
|
|
|
|
|
<el-form-item label="车况介绍">
|
|
|
|
|
|
<el-input
|
|
|
|
|
|
v-model="editForm.condition_desc"
|
|
|
|
|
|
type="textarea"
|
|
|
|
|
|
:rows="4"
|
|
|
|
|
|
placeholder="请输入车况介绍"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</ElCol>
|
|
|
|
|
|
<ElCol :sm="24" :md="24" :lg="24">
|
|
|
|
|
|
<el-form-item label="产品图">
|
|
|
|
|
|
<el-upload
|
|
|
|
|
|
v-model:file-list="editImageFileList"
|
|
|
|
|
|
class="upload-list"
|
|
|
|
|
|
list-type="picture-card"
|
|
|
|
|
|
:limit="maxMedia"
|
|
|
|
|
|
:action="uploadAction"
|
|
|
|
|
|
:headers="uploadHeaders"
|
|
|
|
|
|
name="file"
|
|
|
|
|
|
multiple
|
|
|
|
|
|
:before-upload="handleImagePreview"
|
|
|
|
|
|
:on-success="handleEditImageSuccess"
|
|
|
|
|
|
:on-remove="handleEditImageRemove"
|
|
|
|
|
|
:on-exceed="handleEditImageExceed"
|
|
|
|
|
|
>
|
|
|
|
|
|
<el-icon><Plus /></el-icon>
|
|
|
|
|
|
</el-upload>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</ElCol>
|
|
|
|
|
|
<!-- 视频编辑 -->
|
|
|
|
|
|
<ElCol :sm="24" :md="24" :lg="24">
|
|
|
|
|
|
<el-form-item label="介绍视频">
|
|
|
|
|
|
<el-upload
|
|
|
|
|
|
v-model:file-list="editVideoFileList"
|
|
|
|
|
|
class="upload-list"
|
|
|
|
|
|
:limit="maxVideos"
|
|
|
|
|
|
:action="uploadActionVideo"
|
|
|
|
|
|
:headers="uploadHeaders"
|
|
|
|
|
|
name="file"
|
|
|
|
|
|
accept="video/*"
|
|
|
|
|
|
multiple
|
|
|
|
|
|
:before-upload="handleVideoPreview"
|
|
|
|
|
|
:on-success="handleEditVideoSuccess"
|
|
|
|
|
|
:on-remove="handleEditVideoRemove"
|
|
|
|
|
|
:on-exceed="handleVideoExceed"
|
|
|
|
|
|
>
|
|
|
|
|
|
<el-button type="primary">
|
|
|
|
|
|
<el-icon><Plus /></el-icon>
|
|
|
|
|
|
上传视频
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
<template #tip>
|
|
|
|
|
|
<div class="el-upload__tip">支持 mp4/avi/mov 等视频格式</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-upload>
|
|
|
|
|
|
<!-- 视频预览列表 -->
|
|
|
|
|
|
<div v-if="editVideoPreviewList.length" class="video-preview-list">
|
|
|
|
|
|
<div v-for="(video, index) in editVideoPreviewList" :key="index" class="video-item">
|
|
|
|
|
|
<video :src="video.url" controls class="video-preview"></video>
|
|
|
|
|
|
<el-button link type="danger" @click="removeEditVideoByIndex(index)" size="small">
|
|
|
|
|
|
删除
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</ElCol>
|
|
|
|
|
|
<ElCol :sm="24" :md="24" :lg="24">
|
|
|
|
|
|
<el-form-item label="附件">
|
|
|
|
|
|
<MultiFileUpload
|
|
|
|
|
|
v-model="editAttachmentFileList"
|
|
|
|
|
|
upload-text="点击上传附件"
|
2026-03-12 16:48:21 +08:00
|
|
|
|
hint-text="仅支持单个文件上传"
|
|
|
|
|
|
:max-count="1"
|
2026-03-11 11:16:35 +08:00
|
|
|
|
:action-image-uplode="uploadActionFile"
|
|
|
|
|
|
@change="handleEditAttachmentChange"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</ElCol>
|
|
|
|
|
|
<!-- FAQ 编辑 -->
|
2026-03-12 16:48:21 +08:00
|
|
|
|
<!-- <ElCol :sm="24" :md="24" :lg="24">
|
2026-03-11 11:16:35 +08:00
|
|
|
|
<el-form-item label="FAQ 问答">
|
|
|
|
|
|
<div class="faq-container">
|
|
|
|
|
|
<div v-for="(faq, index) in editForm.faqs" :key="index" class="faq-item">
|
|
|
|
|
|
<div class="faq-header">
|
|
|
|
|
|
<span class="faq-index">Q{{ index + 1 }}</span>
|
|
|
|
|
|
<el-button
|
|
|
|
|
|
link
|
|
|
|
|
|
type="danger"
|
|
|
|
|
|
@click="removeEditFaq(index)"
|
|
|
|
|
|
v-if="editForm.faqs.length > 1"
|
|
|
|
|
|
>
|
|
|
|
|
|
删除
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<el-form-item :label="`问题 ${index + 1}`">
|
|
|
|
|
|
<el-input v-model="faq.question" placeholder="请输入问题" clearable />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item :label="`答案 ${index + 1}`">
|
|
|
|
|
|
<el-input
|
|
|
|
|
|
v-model="faq.answer"
|
|
|
|
|
|
type="textarea"
|
|
|
|
|
|
:rows="3"
|
|
|
|
|
|
placeholder="请输入答案"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<el-button type="primary" @click="addEditFaq" :icon="Plus">
|
|
|
|
|
|
添加问答
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</el-form-item>
|
2026-03-12 16:48:21 +08:00
|
|
|
|
</ElCol> -->
|
2026-03-11 11:16:35 +08:00
|
|
|
|
</ElRow>
|
|
|
|
|
|
</el-form>
|
|
|
|
|
|
</el-scrollbar>
|
|
|
|
|
|
<template #footer>
|
|
|
|
|
|
<span class="dialog-footer">
|
|
|
|
|
|
<el-button @click="editDialogVisible = false">取 消</el-button>
|
|
|
|
|
|
<el-button type="primary" :loading="editSubmitLoading" @click="handleEditSubmit">
|
|
|
|
|
|
确 认
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-dialog>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 编辑绑定产品弹窗 -->
|
|
|
|
|
|
<el-dialog
|
|
|
|
|
|
v-model="editBindState.showDialog"
|
|
|
|
|
|
title="选择要绑定的产品"
|
|
|
|
|
|
width="700px"
|
|
|
|
|
|
@close="closeEditBindDialog"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div class="bind-search" v-if="editBindState.list && editBindState.list.length">
|
|
|
|
|
|
<el-input
|
|
|
|
|
|
v-model="editBindState.keyword"
|
|
|
|
|
|
placeholder="搜索产品名称"
|
|
|
|
|
|
clearable
|
|
|
|
|
|
style="max-width: 320px"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<el-scrollbar max-height="360px">
|
|
|
|
|
|
<div v-if="editBindState.loading" class="bind-loading">加载中…</div>
|
|
|
|
|
|
<div v-else-if="!editBindState.list.length" class="bind-empty">未找到匹配的产品</div>
|
|
|
|
|
|
<div v-else-if="!filteredEditBindList.length" class="bind-empty">未找到匹配结果</div>
|
|
|
|
|
|
<div v-else>
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="bind-item"
|
|
|
|
|
|
v-for="(it, idx) in filteredEditBindList"
|
|
|
|
|
|
:key="getEditBindItemId(it, idx)"
|
|
|
|
|
|
:class="{ active: editBindState.selectedId === getEditBindItemId(it, idx) }"
|
|
|
|
|
|
@click="selectEditBind(it, idx)"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div class="bind-item-main">
|
|
|
|
|
|
<div class="bind-item-title">
|
|
|
|
|
|
{{ it.name || it.product_name || it.title || `产品 ${idx + 1}` }}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="bind-item-sub">
|
|
|
|
|
|
{{ formatEditBindSub(it) }}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="bind-item-check">
|
|
|
|
|
|
<span class="check-dot" v-if="editBindState.selectedId === getEditBindItemId(it, idx)" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</el-scrollbar>
|
|
|
|
|
|
|
|
|
|
|
|
<template #footer>
|
|
|
|
|
|
<el-button @click="closeEditBindDialog">取 消</el-button>
|
|
|
|
|
|
<el-button type="primary" :disabled="!editBindState.selectedId" @click="confirmEditBind">
|
|
|
|
|
|
确认绑定
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-dialog>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 审核记录弹窗 -->
|
|
|
|
|
|
<el-dialog v-model="auditRecordsDialogVisible" title="审核记录" width="700px">
|
|
|
|
|
|
<div v-loading="auditRecordsLoading" class="audit-records-container">
|
|
|
|
|
|
<el-empty v-if="!auditRecordsList.length" description="暂无审核记录" />
|
|
|
|
|
|
<el-timeline v-else>
|
|
|
|
|
|
<el-timeline-item
|
|
|
|
|
|
v-for="(record, index) in auditRecordsList"
|
|
|
|
|
|
:key="index"
|
|
|
|
|
|
:timestamp="record.created_at"
|
|
|
|
|
|
placement="top"
|
|
|
|
|
|
:type="getAuditRecordStatusColor(record.audit_status) === 'success' ? 'success' :
|
|
|
|
|
|
getAuditRecordStatusColor(record.audit_status) === 'danger' ? 'danger' : 'warning'"
|
|
|
|
|
|
>
|
|
|
|
|
|
<el-card class="audit-record-card">
|
|
|
|
|
|
<div class="audit-record-header">
|
|
|
|
|
|
<el-tag :type="getAuditRecordStatusColor(record.audit_status)">
|
|
|
|
|
|
{{ record.audit_status_text || '--' }}
|
|
|
|
|
|
</el-tag>
|
|
|
|
|
|
<span class="auditor-info">
|
|
|
|
|
|
<el-icon><User /></el-icon>
|
|
|
|
|
|
{{ record.auditor_name || '--' }}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="audit-record-content" v-if="record.audit_reason">
|
|
|
|
|
|
<div class="content-label">审核理由:</div>
|
|
|
|
|
|
<div class="content-value">{{ record.audit_reason }}</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="audit-record-status-change">
|
|
|
|
|
|
<span class="status-before">{{ getStatusText(record.before_status) }}</span>
|
|
|
|
|
|
<el-icon class="status-arrow"><ArrowRight /></el-icon>
|
|
|
|
|
|
<span class="status-after">{{ getStatusText(record.after_status) }}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</el-card>
|
|
|
|
|
|
</el-timeline-item>
|
|
|
|
|
|
</el-timeline>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<template #footer>
|
|
|
|
|
|
<span class="dialog-footer">
|
|
|
|
|
|
<el-button @click="auditRecordsDialogVisible = false">关 闭</el-button>
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-dialog>
|
|
|
|
|
|
|
2026-03-12 16:48:21 +08:00
|
|
|
|
<!-- 抓取弹窗 -->
|
|
|
|
|
|
<el-dialog
|
|
|
|
|
|
v-model="fetchDialogVisible"
|
|
|
|
|
|
title="抓取数据"
|
|
|
|
|
|
width="800px"
|
|
|
|
|
|
:close-on-click-modal="false"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div class="fetch-dialog-content">
|
|
|
|
|
|
<el-form :model="fetchForm" label-width="70px" style="margin-bottom: 20px; display: flex">
|
|
|
|
|
|
<el-form-item label="抓取链接">
|
|
|
|
|
|
<el-input
|
|
|
|
|
|
v-model="fetchForm.url"
|
|
|
|
|
|
placeholder="请输入要抓取的链接"
|
|
|
|
|
|
clearable
|
|
|
|
|
|
style="width: 100%"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item>
|
|
|
|
|
|
<el-button type="primary" @click="handleFetch" :loading="fetchLoading">抓取</el-button>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</el-form>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 抓取结果显示 -->
|
|
|
|
|
|
<div v-if="fetchLoading" class="fetch-loading">
|
|
|
|
|
|
<el-icon class="is-loading"><Loading /></el-icon>
|
|
|
|
|
|
<span style="margin-left: 8px">正在抓取中...</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<el-form v-else-if="fetchData" :model="fetchForm" label-width="100px" class="fetch-result-form">
|
|
|
|
|
|
<ElRow :gutter="12">
|
|
|
|
|
|
<ElCol :sm="24" :md="12" :lg="12">
|
|
|
|
|
|
<el-form-item label="产品名称">
|
|
|
|
|
|
<el-input v-model="fetchForm.product_name" placeholder="请输入产品名称" />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</ElCol>
|
|
|
|
|
|
<ElCol :sm="24" :md="12" :lg="12">
|
|
|
|
|
|
<el-form-item label="车价(¥)">
|
|
|
|
|
|
<el-input v-model="fetchForm.price" placeholder="请输入车价" />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</ElCol>
|
|
|
|
|
|
<ElCol :sm="24" :md="12" :lg="12">
|
|
|
|
|
|
<el-form-item label="车源地">
|
|
|
|
|
|
<el-input v-model="fetchForm.source_place" placeholder="请输入车源地" />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</ElCol>
|
|
|
|
|
|
<ElCol :sm="24" :md="12" :lg="12">
|
|
|
|
|
|
<el-form-item label="上牌时间">
|
|
|
|
|
|
<el-input v-model="fetchForm.plate_time" placeholder="请输入上牌时间" />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</ElCol>
|
|
|
|
|
|
<ElCol :sm="24" :md="12" :lg="12">
|
|
|
|
|
|
<el-form-item label="排量">
|
|
|
|
|
|
<el-input v-model="fetchForm.displacement" placeholder="请输入排量" />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</ElCol>
|
|
|
|
|
|
<ElCol :sm="24" :md="12" :lg="12">
|
|
|
|
|
|
<el-form-item label="里程(Km)">
|
|
|
|
|
|
<el-input v-model="fetchForm.mileage" placeholder="请输入里程" />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</ElCol>
|
|
|
|
|
|
<ElCol :sm="24" :md="12" :lg="12">
|
|
|
|
|
|
<el-form-item label="马力">
|
|
|
|
|
|
<el-input v-model="fetchForm.power" placeholder="请输入马力" />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</ElCol>
|
|
|
|
|
|
<ElCol :sm="24" :md="12" :lg="12">
|
|
|
|
|
|
<el-form-item label="车身颜色">
|
|
|
|
|
|
<el-input v-model="fetchForm.body_color" placeholder="请输入车身颜色" />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</ElCol>
|
|
|
|
|
|
<ElCol :sm="24" :md="24" :lg="24">
|
|
|
|
|
|
<el-form-item label="车况介绍">
|
|
|
|
|
|
<el-input
|
|
|
|
|
|
v-model="fetchForm.condition_desc"
|
|
|
|
|
|
type="textarea"
|
|
|
|
|
|
:rows="3"
|
|
|
|
|
|
placeholder="请输入车况介绍"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</ElCol>
|
|
|
|
|
|
</ElRow>
|
|
|
|
|
|
</el-form>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<template #footer>
|
|
|
|
|
|
<span class="dialog-footer">
|
|
|
|
|
|
<el-button @click="fetchDialogVisible = false">关 闭</el-button>
|
|
|
|
|
|
<el-button type="primary" @click="handleFetchConfirm" :disabled="!fetchData">确认添加</el-button>
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-dialog>
|
|
|
|
|
|
|
2026-03-11 11:16:35 +08:00
|
|
|
|
<!-- 图片预览组件 -->
|
|
|
|
|
|
<ElImageViewer
|
|
|
|
|
|
v-if="showImageViewer1"
|
|
|
|
|
|
:url-list="[currentPdfFiles]"
|
|
|
|
|
|
:hide-on-click-modal="true"
|
|
|
|
|
|
@close="showImageViewer1 = false"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<ElImageViewer
|
|
|
|
|
|
v-if="showImageViewer"
|
|
|
|
|
|
:url-list="imgList"
|
|
|
|
|
|
:initial-index="viewerIndex"
|
|
|
|
|
|
:hide-on-click-modal="true"
|
|
|
|
|
|
@close="showImageViewer = false"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
|
import { reactive, ref, computed } from 'vue'
|
|
|
|
|
|
import {
|
|
|
|
|
|
ElMessage,
|
|
|
|
|
|
ElMessageBox,
|
|
|
|
|
|
ElLoading,
|
2026-03-12 16:48:21 +08:00
|
|
|
|
ElRow,
|
|
|
|
|
|
ElCol,
|
2026-03-11 11:16:35 +08:00
|
|
|
|
type FormInstance,
|
|
|
|
|
|
type FormRules,
|
|
|
|
|
|
type UploadFile
|
|
|
|
|
|
} from 'element-plus'
|
2026-03-12 16:48:21 +08:00
|
|
|
|
import { Plus, Loading, User, ArrowRight } from '@element-plus/icons-vue'
|
2026-03-11 11:16:35 +08:00
|
|
|
|
import { manageService } from '@/api/adminApi'
|
|
|
|
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
|
import FilePreview from '@/components/custom/FilePreview.vue'
|
|
|
|
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
|
import MultiFileUpload from '@/components/core/forms/MultiFileUpload.vue'
|
|
|
|
|
|
import { useUploadHeaders } from '@/composables/useUploadHeaders'
|
|
|
|
|
|
import request from '@/utils/http'
|
|
|
|
|
|
|
|
|
|
|
|
defineOptions({ name: 'PublishMyListPro' })
|
|
|
|
|
|
|
|
|
|
|
|
const { uploadHeaders } = useUploadHeaders()
|
|
|
|
|
|
|
|
|
|
|
|
const maxMedia = 200
|
|
|
|
|
|
const maxVideos = 5
|
|
|
|
|
|
|
|
|
|
|
|
interface FileItem {
|
|
|
|
|
|
name: string
|
|
|
|
|
|
url: string
|
|
|
|
|
|
file_name: string
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
interface FAQItem {
|
|
|
|
|
|
question: string
|
|
|
|
|
|
answer: string
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const keyword = ref<string>('')
|
|
|
|
|
|
const auditStatus = ref<string | number>('')
|
|
|
|
|
|
const loading = ref(false)
|
|
|
|
|
|
const pagination = reactive({
|
|
|
|
|
|
currentPage: 1,
|
|
|
|
|
|
pageSize: 10,
|
|
|
|
|
|
total: 0
|
|
|
|
|
|
})
|
|
|
|
|
|
const tableData = ref<any[]>([])
|
|
|
|
|
|
|
|
|
|
|
|
// 审核记录相关
|
|
|
|
|
|
const auditRecordsDialogVisible = ref(false)
|
|
|
|
|
|
const auditRecordsList = ref<any[]>([])
|
|
|
|
|
|
const auditRecordsLoading = ref(false)
|
|
|
|
|
|
|
|
|
|
|
|
// 详情相关
|
|
|
|
|
|
const detailDialogVisible = ref(false)
|
|
|
|
|
|
const detailData = ref<any>(null)
|
|
|
|
|
|
const contractFiles = ref<FileItem[]>([])
|
|
|
|
|
|
const detailVideoList = ref<{ url: string; name: string }[]>([])
|
|
|
|
|
|
|
|
|
|
|
|
// 编辑相关
|
|
|
|
|
|
const editDialogVisible = ref(false)
|
|
|
|
|
|
const editSubmitLoading = ref(false)
|
|
|
|
|
|
const editFormRef = ref<FormInstance>()
|
|
|
|
|
|
|
|
|
|
|
|
interface EditForm {
|
|
|
|
|
|
id: number | string
|
|
|
|
|
|
product_name: string
|
|
|
|
|
|
price: string
|
|
|
|
|
|
source_place: string
|
|
|
|
|
|
plate_time: string
|
|
|
|
|
|
displacement: string
|
|
|
|
|
|
power: string
|
|
|
|
|
|
body_color: string
|
|
|
|
|
|
condition_desc: string
|
|
|
|
|
|
mileage: string
|
2026-03-12 16:48:21 +08:00
|
|
|
|
images: string[] // 用于显示(images_pull)
|
|
|
|
|
|
original_images: string[] // 用于保存提交(原始 images)
|
2026-03-11 11:16:35 +08:00
|
|
|
|
intro_video: string[]
|
|
|
|
|
|
attachment: { name: string; src: string }[]
|
|
|
|
|
|
faqs: FAQItem[]
|
|
|
|
|
|
crawl_title: string
|
|
|
|
|
|
crawl_brand: string
|
|
|
|
|
|
crawl_series: string
|
|
|
|
|
|
crawl_year: string
|
|
|
|
|
|
crawl_msrp: string
|
|
|
|
|
|
crawl_car_id: string
|
|
|
|
|
|
crawl_brand_id: string
|
|
|
|
|
|
crawl_series_id: string
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const editForm = reactive<EditForm>({
|
|
|
|
|
|
id: '',
|
|
|
|
|
|
product_name: '',
|
|
|
|
|
|
price: '',
|
|
|
|
|
|
source_place: '',
|
|
|
|
|
|
plate_time: '',
|
|
|
|
|
|
displacement: '',
|
|
|
|
|
|
power: '',
|
|
|
|
|
|
body_color: '',
|
|
|
|
|
|
condition_desc: '',
|
|
|
|
|
|
mileage: '',
|
|
|
|
|
|
images: [],
|
2026-03-12 16:48:21 +08:00
|
|
|
|
original_images: [],
|
2026-03-11 11:16:35 +08:00
|
|
|
|
intro_video: [],
|
|
|
|
|
|
attachment: [],
|
|
|
|
|
|
faqs: [{ question: '', answer: '' }],
|
|
|
|
|
|
crawl_title: '',
|
|
|
|
|
|
crawl_brand: '',
|
|
|
|
|
|
crawl_series: '',
|
|
|
|
|
|
crawl_year: '',
|
|
|
|
|
|
crawl_msrp: '',
|
|
|
|
|
|
crawl_car_id: '',
|
|
|
|
|
|
crawl_brand_id: '',
|
|
|
|
|
|
crawl_series_id: ''
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const editRules = reactive<FormRules>({
|
|
|
|
|
|
product_name: [{ required: true, message: '请输入产品名称', trigger: 'blur' }],
|
|
|
|
|
|
price: [{ required: true, message: '请输入车价', trigger: 'blur' }]
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 编辑绑定相关状态
|
|
|
|
|
|
const editBindState = reactive<{
|
|
|
|
|
|
showDialog: boolean
|
|
|
|
|
|
loading: boolean
|
|
|
|
|
|
list: any[]
|
|
|
|
|
|
selectedId: string
|
|
|
|
|
|
boundProduct: any | null
|
|
|
|
|
|
keyword: string
|
|
|
|
|
|
_selected?: any
|
|
|
|
|
|
}>({
|
|
|
|
|
|
showDialog: false,
|
|
|
|
|
|
loading: false,
|
|
|
|
|
|
list: [],
|
|
|
|
|
|
selectedId: '',
|
|
|
|
|
|
boundProduct: null,
|
|
|
|
|
|
keyword: ''
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const formatEditBindSub = (it: any) => {
|
|
|
|
|
|
const parts: string[] = []
|
|
|
|
|
|
if (it.sku || it.code) parts.push(`编码:${it.sku || it.code}`)
|
|
|
|
|
|
if (it.category_name) parts.push(`分类:${it.category_name}`)
|
|
|
|
|
|
if (it.price) parts.push(`价格:${it.price}`)
|
|
|
|
|
|
return parts.join(' · ')
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const filteredEditBindList = computed(() => {
|
|
|
|
|
|
const list = Array.isArray(editBindState.list) ? editBindState.list : []
|
|
|
|
|
|
const kw = String(editBindState.keyword || '')
|
|
|
|
|
|
.trim()
|
|
|
|
|
|
.toLowerCase()
|
|
|
|
|
|
if (!kw) return list
|
|
|
|
|
|
return list.filter((it, idx) => {
|
|
|
|
|
|
const id = String(it.id || it.product_id || it.code || idx).toLowerCase()
|
|
|
|
|
|
const name = String(it.name || it.product_name || it.title || '').toLowerCase()
|
|
|
|
|
|
const sub = String(formatEditBindSub(it) || '').toLowerCase()
|
|
|
|
|
|
return id.includes(kw) || name.includes(kw) || sub.includes(kw)
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const uploadAction = `${import.meta.env.VITE_API_URL}/upload?scene=usedcarimg`
|
|
|
|
|
|
const uploadActionFile = `${import.meta.env.VITE_API_URL}/upload?scene=usedcarfile`
|
|
|
|
|
|
const uploadActionVideo = `${import.meta.env.VITE_API_URL}/upload?scene=usedcarvideo`
|
|
|
|
|
|
|
|
|
|
|
|
type UploadFileWithResponse = UploadFile & {
|
|
|
|
|
|
response?: any
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const editImageFileList = ref<UploadFileWithResponse[]>([])
|
|
|
|
|
|
const editVideoFileList = ref<UploadFileWithResponse[]>([])
|
|
|
|
|
|
const editAttachmentFileList = ref<FileItem[]>([])
|
|
|
|
|
|
const editVideoPreviewList = ref<{ url: string; name: string }[]>([])
|
|
|
|
|
|
|
|
|
|
|
|
// 图片预览相关
|
|
|
|
|
|
const showImageViewer = ref(false)
|
|
|
|
|
|
const showImageViewer1 = ref(false)
|
|
|
|
|
|
const viewerIndex = ref(0)
|
|
|
|
|
|
const currentPdfFiles = ref('')
|
2026-03-12 16:48:21 +08:00
|
|
|
|
const fetchImgList = ref<string[]>([])
|
|
|
|
|
|
const imgList = computed(() => {
|
|
|
|
|
|
// 如果抓取弹窗打开且有图片,显示抓取的图片
|
|
|
|
|
|
if (fetchDialogVisible.value && fetchImgList.value.length > 0) {
|
|
|
|
|
|
return fetchImgList.value
|
|
|
|
|
|
}
|
|
|
|
|
|
return parsePreviewList(detailData.value?.images_pull || detailData.value?.images || [])
|
|
|
|
|
|
})
|
2026-03-11 11:16:35 +08:00
|
|
|
|
|
|
|
|
|
|
const handleAvatarClick1 = (index: string) => {
|
|
|
|
|
|
if (index !== null && index !== undefined) {
|
|
|
|
|
|
currentPdfFiles.value = index
|
|
|
|
|
|
showImageViewer1.value = true
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const handleAvatarClick = (index: number) => {
|
|
|
|
|
|
if (index !== null && index !== undefined) {
|
|
|
|
|
|
viewerIndex.value = index
|
|
|
|
|
|
showImageViewer.value = true
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const parsePaths = (value: any): string[] => {
|
|
|
|
|
|
if (!value) return []
|
|
|
|
|
|
if (Array.isArray(value)) return value.filter(Boolean)
|
|
|
|
|
|
if (typeof value === 'string') {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const parsed = JSON.parse(value)
|
|
|
|
|
|
if (Array.isArray(parsed)) return parsed.filter(Boolean)
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
// ignore
|
|
|
|
|
|
}
|
|
|
|
|
|
return value
|
|
|
|
|
|
.split(',')
|
|
|
|
|
|
.map((p: string) => p.trim())
|
|
|
|
|
|
.filter(Boolean)
|
|
|
|
|
|
}
|
|
|
|
|
|
return []
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 审核状态相关方法
|
|
|
|
|
|
const getAuditStatusText = (status: number) => {
|
|
|
|
|
|
const statusMap: Record<number, string> = {
|
|
|
|
|
|
0: '待审核',
|
|
|
|
|
|
1: '审核通过',
|
2026-03-12 16:48:21 +08:00
|
|
|
|
2: '审核拒绝',
|
|
|
|
|
|
3: '推送中',
|
|
|
|
|
|
4: '推送成功',
|
|
|
|
|
|
5: '推送失败'
|
2026-03-11 11:16:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
return statusMap[status] || '未知'
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const getAuditStatusColor = (status: number): 'success' | 'warning' | 'danger' | 'info' => {
|
|
|
|
|
|
const colorMap: Record<number, 'success' | 'warning' | 'danger' | 'info'> = {
|
|
|
|
|
|
0: 'warning',
|
|
|
|
|
|
1: 'success',
|
2026-03-12 16:48:21 +08:00
|
|
|
|
2: 'danger',
|
|
|
|
|
|
3: 'info',
|
|
|
|
|
|
4: 'success',
|
|
|
|
|
|
5: 'danger'
|
2026-03-11 11:16:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
return colorMap[status] || 'info'
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 审核记录相关函数
|
|
|
|
|
|
const getStatusText = (status: number) => getAuditStatusText(status)
|
|
|
|
|
|
|
|
|
|
|
|
const getAuditRecordStatusColor = (status: number): 'success' | 'warning' | 'danger' | 'info' => {
|
|
|
|
|
|
const colorMap: Record<number, 'success' | 'warning' | 'danger' | 'info'> = {
|
|
|
|
|
|
0: 'warning',
|
|
|
|
|
|
1: 'success',
|
2026-03-12 16:48:21 +08:00
|
|
|
|
2: 'danger',
|
|
|
|
|
|
3: 'info',
|
|
|
|
|
|
4: 'success',
|
|
|
|
|
|
5: 'danger'
|
2026-03-11 11:16:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
return colorMap[status] || 'info'
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const showAuditRecords = async (row: any) => {
|
|
|
|
|
|
auditRecordsLoading.value = true
|
|
|
|
|
|
auditRecordsDialogVisible.value = true
|
|
|
|
|
|
try {
|
|
|
|
|
|
const records = await manageService.getAuditRecords({ id: row.id })
|
|
|
|
|
|
auditRecordsList.value = records || []
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('获取审核记录失败:', error)
|
|
|
|
|
|
ElMessage.error('获取审核记录失败')
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
auditRecordsLoading.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const parsePreviewList = (value: any): string[] => parsePaths(value)
|
|
|
|
|
|
|
|
|
|
|
|
const processFiles = (files: any[]) => {
|
|
|
|
|
|
if (!files || !Array.isArray(files)) return []
|
|
|
|
|
|
return files.map((file: any) => ({
|
|
|
|
|
|
name: file.name,
|
|
|
|
|
|
url: file.src_pull || file.file_url,
|
|
|
|
|
|
file_name: file.src
|
|
|
|
|
|
}))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const showDialog = async (_type: string, row: any) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 根据新的 API 获取详情
|
|
|
|
|
|
const res: any = await request.get({
|
|
|
|
|
|
url: '/common/usedcardetail/show',
|
|
|
|
|
|
params: { id: row.id }
|
|
|
|
|
|
})
|
|
|
|
|
|
detailData.value = res
|
|
|
|
|
|
|
|
|
|
|
|
// 处理合同文件
|
|
|
|
|
|
const file_list = res?.attachment_pull
|
|
|
|
|
|
if (file_list) {
|
|
|
|
|
|
contractFiles.value = processFiles(file_list)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
contractFiles.value = []
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 处理视频列表
|
|
|
|
|
|
const videoList = res?.video_pull || []
|
|
|
|
|
|
detailVideoList.value = Array.isArray(videoList)
|
|
|
|
|
|
? videoList.map((v: any) => ({
|
|
|
|
|
|
url: v.file_url || v.src_pull || v,
|
|
|
|
|
|
name: v.name || '视频'
|
|
|
|
|
|
}))
|
|
|
|
|
|
: []
|
|
|
|
|
|
|
|
|
|
|
|
detailDialogVisible.value = true
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('获取详情失败:', error)
|
|
|
|
|
|
ElMessage.error('获取详情失败')
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 编辑功能
|
|
|
|
|
|
const editFn = async (row: any) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const res: any = await request.get({
|
|
|
|
|
|
url: '/common/usedcardetail/show',
|
|
|
|
|
|
params: { id: row.id }
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 填充表单
|
|
|
|
|
|
editForm.id = res.id
|
|
|
|
|
|
editForm.product_name = res.product_name || ''
|
|
|
|
|
|
editForm.price = res.price || ''
|
|
|
|
|
|
editForm.source_place = res.source_place || ''
|
|
|
|
|
|
editForm.plate_time = res.plate_time || ''
|
|
|
|
|
|
editForm.displacement = res.displacement || ''
|
|
|
|
|
|
editForm.power = res.power || ''
|
|
|
|
|
|
editForm.body_color = res.body_color || ''
|
|
|
|
|
|
editForm.condition_desc = res.condition_desc || ''
|
|
|
|
|
|
editForm.mileage = res.mileage || ''
|
|
|
|
|
|
|
2026-03-12 16:48:21 +08:00
|
|
|
|
// 处理图片:images_pull 用于显示,images 用于保存
|
|
|
|
|
|
const displayImages = parsePreviewList(res.images_pull || res.images || [])
|
|
|
|
|
|
const saveImages = parsePreviewList(res.images || [])
|
|
|
|
|
|
|
|
|
|
|
|
editForm.images = displayImages // 用于显示
|
|
|
|
|
|
editForm.original_images = saveImages // 用于保存提交
|
|
|
|
|
|
|
|
|
|
|
|
editImageFileList.value = displayImages.map((url, idx) => ({
|
2026-03-11 11:16:35 +08:00
|
|
|
|
name: `image_${idx}`,
|
|
|
|
|
|
url,
|
|
|
|
|
|
status: 'success' as const,
|
|
|
|
|
|
uid: idx,
|
2026-03-12 16:48:21 +08:00
|
|
|
|
response: { file_url: url, file_name: saveImages[idx] || url }
|
2026-03-11 11:16:35 +08:00
|
|
|
|
}))
|
|
|
|
|
|
|
|
|
|
|
|
// 处理视频
|
|
|
|
|
|
const videoList = res.video_pull || []
|
|
|
|
|
|
const videos = Array.isArray(videoList) ? videoList : []
|
|
|
|
|
|
editForm.intro_video = videos.map((v: any) => v.file_name || v.src || v)
|
|
|
|
|
|
editVideoPreviewList.value = videos.map((v: any) => ({
|
|
|
|
|
|
url: v.file_url || v.src_pull || v,
|
|
|
|
|
|
name: v.name || '视频'
|
|
|
|
|
|
}))
|
|
|
|
|
|
editVideoFileList.value = videos.map((v: any, idx: number) => ({
|
|
|
|
|
|
name: v.name || 'video',
|
|
|
|
|
|
url: v.file_url || v.src_pull || v,
|
|
|
|
|
|
status: 'success',
|
|
|
|
|
|
uid: idx,
|
|
|
|
|
|
response: { file_url: v.file_url || v.src_pull || v, file_name: v.file_name || v.src || v }
|
|
|
|
|
|
}))
|
|
|
|
|
|
|
|
|
|
|
|
// 处理附件
|
|
|
|
|
|
const attachmentList = res.attachment_pull || []
|
|
|
|
|
|
editForm.attachment = attachmentList.map((a: any) => ({
|
|
|
|
|
|
name: a.name,
|
|
|
|
|
|
src: a.src
|
|
|
|
|
|
}))
|
|
|
|
|
|
editAttachmentFileList.value = attachmentList.map((a: any, idx: number) => ({
|
|
|
|
|
|
name: a.name,
|
|
|
|
|
|
url: a.src_pull || a.file_url,
|
|
|
|
|
|
file_name: a.src,
|
|
|
|
|
|
status: 'success',
|
|
|
|
|
|
uid: idx,
|
|
|
|
|
|
response: { file_url: a.src_pull || a.file_url, file_name: a.src }
|
|
|
|
|
|
}))
|
|
|
|
|
|
|
|
|
|
|
|
// 处理 FAQ
|
|
|
|
|
|
editForm.faqs =
|
|
|
|
|
|
res.faqs && Array.isArray(res.faqs) && res.faqs.length > 0
|
|
|
|
|
|
? res.faqs
|
|
|
|
|
|
: [{ question: '', answer: '' }]
|
|
|
|
|
|
|
|
|
|
|
|
// 处理绑定信息
|
|
|
|
|
|
if (res.crawl_title || res.crawl_brand) {
|
|
|
|
|
|
editBindState.boundProduct = {
|
|
|
|
|
|
title: res.crawl_title,
|
|
|
|
|
|
brand_name: res.crawl_brand,
|
|
|
|
|
|
series_name: res.crawl_series,
|
|
|
|
|
|
year: res.crawl_year,
|
|
|
|
|
|
msrp: res.crawl_msrp,
|
|
|
|
|
|
car_id: res.crawl_car_id,
|
|
|
|
|
|
brand_id: res.crawl_brand_id,
|
|
|
|
|
|
series_id: res.crawl_series_id
|
|
|
|
|
|
}
|
|
|
|
|
|
editForm.crawl_title = res.crawl_title || ''
|
|
|
|
|
|
editForm.crawl_brand = res.crawl_brand || ''
|
|
|
|
|
|
editForm.crawl_series = res.crawl_series || ''
|
|
|
|
|
|
editForm.crawl_year = res.crawl_year || ''
|
|
|
|
|
|
editForm.crawl_msrp = res.crawl_msrp || ''
|
|
|
|
|
|
editForm.crawl_car_id = res.crawl_car_id || ''
|
|
|
|
|
|
editForm.crawl_brand_id = res.crawl_brand_id || ''
|
|
|
|
|
|
editForm.crawl_series_id = res.crawl_series_id || ''
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
editDialogVisible.value = true
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('获取编辑数据失败:', error)
|
|
|
|
|
|
ElMessage.error('获取编辑数据失败')
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 编辑绑定功能
|
|
|
|
|
|
const onEditBindByName = () => {
|
|
|
|
|
|
const name = String(editForm.product_name || '').trim()
|
|
|
|
|
|
if (!name) {
|
|
|
|
|
|
ElMessage.warning('请先输入产品名称')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
editBindState.showDialog = true
|
|
|
|
|
|
editBindState.loading = true
|
|
|
|
|
|
editBindState.list = []
|
|
|
|
|
|
editBindState.selectedId = ''
|
|
|
|
|
|
editBindState.keyword = ''
|
|
|
|
|
|
|
|
|
|
|
|
request
|
|
|
|
|
|
.get({
|
|
|
|
|
|
url: '/common/usedcar/crawl',
|
|
|
|
|
|
params: { keyword: name }
|
|
|
|
|
|
})
|
|
|
|
|
|
.then((res: any) => {
|
|
|
|
|
|
const data = res.model || []
|
|
|
|
|
|
editBindState.list = data
|
|
|
|
|
|
})
|
|
|
|
|
|
.catch(() => {
|
|
|
|
|
|
ElMessage.error('拉取产品失败,请稍后重试')
|
|
|
|
|
|
})
|
|
|
|
|
|
.finally(() => {
|
|
|
|
|
|
editBindState.loading = false
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const closeEditBindDialog = () => {
|
|
|
|
|
|
editBindState.showDialog = false
|
|
|
|
|
|
editBindState.keyword = ''
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const getEditBindItemId = (it: any, idx: number) =>
|
|
|
|
|
|
String(it.id || it.product_id || it.requisition_item_id || it.code || idx)
|
|
|
|
|
|
|
|
|
|
|
|
const selectEditBind = (it: any, idx: number) => {
|
|
|
|
|
|
editBindState.selectedId = getEditBindItemId(it, idx)
|
|
|
|
|
|
editBindState._selected = it
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const confirmEditBind = () => {
|
|
|
|
|
|
if (!editBindState.selectedId || !editBindState._selected) return
|
|
|
|
|
|
const it = editBindState._selected
|
|
|
|
|
|
|
|
|
|
|
|
editBindState.boundProduct = it
|
|
|
|
|
|
editForm.crawl_title = it.title || ''
|
|
|
|
|
|
editForm.crawl_brand = it.brand_name || ''
|
|
|
|
|
|
editForm.crawl_series = it.series_name || ''
|
|
|
|
|
|
editForm.crawl_year = it.year || ''
|
|
|
|
|
|
editForm.crawl_msrp = it.msrp || ''
|
|
|
|
|
|
editForm.crawl_car_id = it.car_id || ''
|
|
|
|
|
|
editForm.crawl_brand_id = it.brand_id || ''
|
|
|
|
|
|
editForm.crawl_series_id = it.series_id || ''
|
|
|
|
|
|
|
|
|
|
|
|
closeEditBindDialog()
|
|
|
|
|
|
ElMessage.success('绑定成功')
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const clearEditBind = () => {
|
|
|
|
|
|
editForm.crawl_title = ''
|
|
|
|
|
|
editForm.crawl_brand = ''
|
|
|
|
|
|
editForm.crawl_series = ''
|
|
|
|
|
|
editForm.crawl_year = ''
|
|
|
|
|
|
editForm.crawl_msrp = ''
|
|
|
|
|
|
editForm.crawl_car_id = ''
|
|
|
|
|
|
editForm.crawl_brand_id = ''
|
|
|
|
|
|
editForm.crawl_series_id = ''
|
|
|
|
|
|
editBindState.boundProduct = null
|
|
|
|
|
|
ElMessage.success('已清除绑定')
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 图片上传处理
|
|
|
|
|
|
const loadingInstance = ref<ReturnType<typeof ElLoading.service> | null>(null)
|
|
|
|
|
|
|
|
|
|
|
|
const handleImagePreview = () => {
|
|
|
|
|
|
loadingInstance.value = ElLoading.service({
|
|
|
|
|
|
lock: true,
|
|
|
|
|
|
text: '正在上传中…',
|
|
|
|
|
|
background: 'rgba(0, 0, 0, 0.45)'
|
|
|
|
|
|
})
|
|
|
|
|
|
return true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const handleEditImageSuccess = (response: any, file: UploadFile, fileList: UploadFile[]) => {
|
|
|
|
|
|
const url = response?.file_url || response?.data?.file_url || ''
|
|
|
|
|
|
const fileName = response?.file_name || response?.data?.file_name || ''
|
|
|
|
|
|
;(file as UploadFileWithResponse).response = response
|
|
|
|
|
|
|
|
|
|
|
|
if (url && !file.url) file.url = url
|
|
|
|
|
|
|
|
|
|
|
|
if (fileName && !editForm.images.includes(fileName)) {
|
|
|
|
|
|
editForm.images.push(fileName)
|
2026-03-12 16:48:21 +08:00
|
|
|
|
editForm.original_images.push(fileName)
|
2026-03-11 11:16:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
editImageFileList.value = fileList as UploadFileWithResponse[]
|
|
|
|
|
|
loadingInstance.value?.close()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const handleEditImageRemove = (file: UploadFile, fileList: UploadFile[]) => {
|
|
|
|
|
|
const resp = (file as UploadFileWithResponse).response
|
|
|
|
|
|
const fileName = resp?.file_name || resp?.data?.file_name || ''
|
|
|
|
|
|
if (fileName) {
|
|
|
|
|
|
const idx = editForm.images.indexOf(fileName)
|
2026-03-12 16:48:21 +08:00
|
|
|
|
if (idx > -1) {
|
|
|
|
|
|
editForm.images.splice(idx, 1)
|
|
|
|
|
|
editForm.original_images.splice(idx, 1)
|
|
|
|
|
|
}
|
2026-03-11 11:16:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
editImageFileList.value = fileList as UploadFileWithResponse[]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const handleEditImageExceed = () => {
|
|
|
|
|
|
ElMessage.warning(`最多只能上传 ${maxMedia} 张图片`)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 视频上传处理
|
|
|
|
|
|
const handleVideoPreview = () => {
|
|
|
|
|
|
loadingInstance.value = ElLoading.service({
|
|
|
|
|
|
lock: true,
|
|
|
|
|
|
text: '正在上传视频…',
|
|
|
|
|
|
background: 'rgba(0, 0, 0, 0.45)'
|
|
|
|
|
|
})
|
|
|
|
|
|
return true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const handleEditVideoSuccess = (response: any, file: UploadFile, fileList: UploadFile[]) => {
|
|
|
|
|
|
const url = response?.file_url || response?.data?.file_url || ''
|
|
|
|
|
|
const fileName = response?.file_name || response?.data?.file_name || ''
|
|
|
|
|
|
;(file as UploadFileWithResponse).response = response
|
|
|
|
|
|
|
|
|
|
|
|
if (url && !file.url) file.url = url
|
|
|
|
|
|
|
|
|
|
|
|
if (fileName && !editForm.intro_video.includes(fileName)) {
|
|
|
|
|
|
editForm.intro_video.push(fileName)
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!editVideoPreviewList.value.find((v) => v.url === url)) {
|
|
|
|
|
|
editVideoPreviewList.value.push({ url, name: file.name })
|
|
|
|
|
|
}
|
|
|
|
|
|
editVideoFileList.value = fileList as UploadFileWithResponse[]
|
|
|
|
|
|
loadingInstance.value?.close()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const handleEditVideoRemove = (file: UploadFile, fileList: UploadFile[]) => {
|
|
|
|
|
|
const resp = (file as UploadFileWithResponse).response
|
|
|
|
|
|
const fileName = resp?.file_name || resp?.data?.file_name || ''
|
|
|
|
|
|
if (fileName) {
|
|
|
|
|
|
const idx = editForm.intro_video.indexOf(fileName)
|
|
|
|
|
|
if (idx > -1) editForm.intro_video.splice(idx, 1)
|
|
|
|
|
|
}
|
|
|
|
|
|
const url = resp?.file_url || file.url || ''
|
|
|
|
|
|
const previewIdx = editVideoPreviewList.value.findIndex((v) => v.url === url)
|
|
|
|
|
|
if (previewIdx > -1) {
|
|
|
|
|
|
editVideoPreviewList.value.splice(previewIdx, 1)
|
|
|
|
|
|
}
|
|
|
|
|
|
editVideoFileList.value = fileList as UploadFileWithResponse[]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const handleVideoExceed = () => {
|
|
|
|
|
|
ElMessage.warning(`最多只能上传 ${maxVideos} 个视频`)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const removeEditVideoByIndex = (index: number) => {
|
|
|
|
|
|
editForm.intro_video.splice(index, 1)
|
|
|
|
|
|
editVideoPreviewList.value.splice(index, 1)
|
|
|
|
|
|
editVideoFileList.value.splice(index, 1)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 附件变化处理
|
|
|
|
|
|
const handleEditAttachmentChange = (files: FileItem[]) => {
|
|
|
|
|
|
editForm.attachment = files.map((file) => ({
|
|
|
|
|
|
name: file.name,
|
|
|
|
|
|
src: file.file_name
|
|
|
|
|
|
}))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// FAQ 操作
|
|
|
|
|
|
const addEditFaq = () => {
|
|
|
|
|
|
editForm.faqs.push({ question: '', answer: '' })
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const removeEditFaq = (index: number) => {
|
|
|
|
|
|
if (editForm.faqs.length > 1) {
|
|
|
|
|
|
editForm.faqs.splice(index, 1)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 编辑提交
|
|
|
|
|
|
const handleEditSubmit = () => {
|
|
|
|
|
|
if (!editFormRef.value) return
|
|
|
|
|
|
editFormRef.value.validate(async (valid) => {
|
|
|
|
|
|
if (!valid) return
|
|
|
|
|
|
|
|
|
|
|
|
editSubmitLoading.value = true
|
|
|
|
|
|
try {
|
|
|
|
|
|
const validFaqs = editForm.faqs.filter((faq) => faq.question.trim() || faq.answer.trim())
|
|
|
|
|
|
|
|
|
|
|
|
const payload: any = {
|
|
|
|
|
|
id: editForm.id,
|
|
|
|
|
|
product_name: editForm.product_name,
|
|
|
|
|
|
price: editForm.price,
|
|
|
|
|
|
source_place: editForm.source_place,
|
|
|
|
|
|
plate_time: editForm.plate_time,
|
|
|
|
|
|
displacement: editForm.displacement,
|
|
|
|
|
|
power: editForm.power,
|
|
|
|
|
|
body_color: editForm.body_color,
|
|
|
|
|
|
condition_desc: editForm.condition_desc,
|
|
|
|
|
|
mileage: editForm.mileage,
|
2026-03-12 16:48:21 +08:00
|
|
|
|
images: editForm.original_images.map((s) => String(s || '').trim()).filter(Boolean).join(','),
|
2026-03-11 11:16:35 +08:00
|
|
|
|
intro_video: editForm.intro_video.map((s) => String(s || '').trim()).filter(Boolean).join(','),
|
|
|
|
|
|
attachment: editForm.attachment,
|
|
|
|
|
|
faqs: validFaqs.length > 0 ? validFaqs : undefined,
|
|
|
|
|
|
crawl_title: editForm.crawl_title,
|
|
|
|
|
|
crawl_brand: editForm.crawl_brand,
|
|
|
|
|
|
crawl_series: editForm.crawl_series,
|
|
|
|
|
|
crawl_year: editForm.crawl_year,
|
|
|
|
|
|
crawl_msrp: editForm.crawl_msrp,
|
|
|
|
|
|
crawl_car_id: editForm.crawl_car_id,
|
|
|
|
|
|
crawl_brand_id: editForm.crawl_brand_id,
|
|
|
|
|
|
crawl_series_id: editForm.crawl_series_id
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
await manageService.publisheditpro(payload)
|
|
|
|
|
|
ElMessage.success('编辑成功')
|
|
|
|
|
|
editDialogVisible.value = false
|
|
|
|
|
|
getWorkList()
|
|
|
|
|
|
} catch (error: any) {
|
|
|
|
|
|
ElMessage.error(error?.message || '编辑失败,请稍后重试')
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
editSubmitLoading.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 标记售出
|
|
|
|
|
|
const statusfn = (row: any) => {
|
|
|
|
|
|
ElMessageBox.confirm('确定要标记售出吗?', '提示', {
|
|
|
|
|
|
confirmButtonText: '确定',
|
|
|
|
|
|
cancelButtonText: '取消',
|
|
|
|
|
|
type: 'warning'
|
|
|
|
|
|
})
|
|
|
|
|
|
.then(async () => {
|
|
|
|
|
|
await manageService.updatestatus({ id: row.id, status: 2 })
|
|
|
|
|
|
ElMessage.success('标记售出成功')
|
|
|
|
|
|
getWorkList()
|
|
|
|
|
|
})
|
|
|
|
|
|
.catch(() => {
|
|
|
|
|
|
// 用户取消
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 删除
|
|
|
|
|
|
const deleteFn = (row: any) => {
|
|
|
|
|
|
ElMessageBox.confirm('确定要删除此发布吗?', '提示', {
|
|
|
|
|
|
confirmButtonText: '确定',
|
|
|
|
|
|
cancelButtonText: '取消',
|
|
|
|
|
|
type: 'warning'
|
|
|
|
|
|
})
|
|
|
|
|
|
.then(async () => {
|
|
|
|
|
|
await manageService.publishdeletepro({ id: row.id })
|
|
|
|
|
|
ElMessage.success('删除成功')
|
|
|
|
|
|
getWorkList()
|
|
|
|
|
|
})
|
|
|
|
|
|
.catch(() => {
|
|
|
|
|
|
// 用户取消
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 跳转新增
|
|
|
|
|
|
const goToAdd = () => {
|
|
|
|
|
|
window.location.href = '/admin/publish/publish_addpro'
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取列表
|
|
|
|
|
|
const getWorkList = async () => {
|
|
|
|
|
|
loading.value = true
|
|
|
|
|
|
try {
|
|
|
|
|
|
const { currentPage, pageSize } = pagination
|
|
|
|
|
|
const params: any = {
|
|
|
|
|
|
current: currentPage,
|
|
|
|
|
|
size: pageSize
|
|
|
|
|
|
}
|
|
|
|
|
|
if (keyword.value) params.keyword = keyword.value
|
|
|
|
|
|
if (auditStatus.value !== '') params.audit_status = auditStatus.value
|
|
|
|
|
|
|
|
|
|
|
|
// 使用新的 API
|
|
|
|
|
|
const { records, current, size, total } = await manageService.getpublishListpro(params)
|
|
|
|
|
|
tableData.value = records
|
|
|
|
|
|
Object.assign(pagination, { currentPage: current, pageSize: size, total })
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('获取列表失败:', error)
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
loading.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
watch([keyword, auditStatus], () => {
|
|
|
|
|
|
pagination.currentPage = 1
|
|
|
|
|
|
getWorkList()
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const handleSizeChange = (newPageSize: number) => {
|
|
|
|
|
|
pagination.pageSize = newPageSize
|
|
|
|
|
|
getWorkList()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const handleCurrentChange = (newCurrentPage: number) => {
|
|
|
|
|
|
pagination.currentPage = newCurrentPage
|
|
|
|
|
|
getWorkList()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-12 16:48:21 +08:00
|
|
|
|
// 抓取相关
|
|
|
|
|
|
const fetchDialogVisible = ref(false)
|
|
|
|
|
|
const fetchForm = reactive({
|
|
|
|
|
|
url: '',
|
|
|
|
|
|
product_name: '',
|
|
|
|
|
|
price: '',
|
|
|
|
|
|
source_place: '',
|
|
|
|
|
|
plate_time: '',
|
|
|
|
|
|
displacement: '',
|
|
|
|
|
|
power: '',
|
|
|
|
|
|
body_color: '',
|
|
|
|
|
|
condition_desc: '',
|
|
|
|
|
|
mileage: ''
|
|
|
|
|
|
})
|
|
|
|
|
|
const fetchLoading = ref(false)
|
|
|
|
|
|
const fetchData = ref<any>(null)
|
|
|
|
|
|
|
|
|
|
|
|
// 打开抓取弹窗
|
|
|
|
|
|
const fetchpublishfn = () => {
|
|
|
|
|
|
fetchDialogVisible.value = true
|
|
|
|
|
|
fetchForm.url = ''
|
|
|
|
|
|
fetchForm.product_name = ''
|
|
|
|
|
|
fetchForm.price = ''
|
|
|
|
|
|
fetchForm.source_place = ''
|
|
|
|
|
|
fetchForm.plate_time = ''
|
|
|
|
|
|
fetchForm.displacement = ''
|
|
|
|
|
|
fetchForm.power = ''
|
|
|
|
|
|
fetchForm.body_color = ''
|
|
|
|
|
|
fetchForm.condition_desc = ''
|
|
|
|
|
|
fetchForm.mileage = ''
|
|
|
|
|
|
fetchData.value = null
|
|
|
|
|
|
fetchImgList.value = []
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 执行抓取
|
|
|
|
|
|
const handleFetch = async () => {
|
|
|
|
|
|
if (!fetchForm.url || !fetchForm.url.trim()) {
|
|
|
|
|
|
ElMessage.warning('请输入抓取链接')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fetchLoading.value = true
|
|
|
|
|
|
fetchData.value = null
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const res: any = await request.post({
|
|
|
|
|
|
url: '/common/usedcardetail/crawl',
|
|
|
|
|
|
params: { url: fetchForm.url.trim() }
|
|
|
|
|
|
})
|
|
|
|
|
|
fetchData.value = res?.data || res || {}
|
|
|
|
|
|
|
|
|
|
|
|
// 填充表单数据
|
|
|
|
|
|
fetchForm.product_name = fetchData.value.product_name || ''
|
|
|
|
|
|
fetchForm.price = fetchData.value.price || ''
|
|
|
|
|
|
fetchForm.source_place = fetchData.value.source_place || ''
|
|
|
|
|
|
fetchForm.plate_time = fetchData.value.plate_time || ''
|
|
|
|
|
|
fetchForm.displacement = fetchData.value.displacement || ''
|
|
|
|
|
|
fetchForm.power = fetchData.value.power || ''
|
|
|
|
|
|
fetchForm.body_color = fetchData.value.body_color || ''
|
|
|
|
|
|
fetchForm.condition_desc = fetchData.value.condition_desc || ''
|
|
|
|
|
|
fetchForm.mileage = fetchData.value.mileage || ''
|
|
|
|
|
|
|
|
|
|
|
|
// 处理图片列表用于预览
|
|
|
|
|
|
fetchImgList.value = parsePreviewList(fetchData.value.image_pull || fetchData.value.image || [])
|
|
|
|
|
|
ElMessage.success(res?.message || '抓取成功')
|
|
|
|
|
|
console.log('抓取接口返回数据:', fetchData.value)
|
|
|
|
|
|
} catch (error: any) {
|
|
|
|
|
|
console.error('抓取失败:', error)
|
|
|
|
|
|
fetchData.value = null
|
|
|
|
|
|
ElMessage.error(error?.message || '抓取失败,请稍后重试')
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
fetchLoading.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 确认添加抓取的数据
|
|
|
|
|
|
// 确认添加抓取的数据
|
|
|
|
|
|
const handleFetchConfirm = async () => {
|
|
|
|
|
|
// 合并抓取接口返回的原始数据和用户修改的数据
|
|
|
|
|
|
const formData = {
|
|
|
|
|
|
...fetchData.value, // 保存所有抓取接口返回的原始数据
|
|
|
|
|
|
// 用表单中修改的值替换对应字段
|
|
|
|
|
|
product_name: fetchForm.product_name,
|
|
|
|
|
|
price: fetchForm.price,
|
|
|
|
|
|
source_place: fetchForm.source_place,
|
|
|
|
|
|
plate_time: fetchForm.plate_time,
|
|
|
|
|
|
displacement: fetchForm.displacement,
|
|
|
|
|
|
power: fetchForm.power,
|
|
|
|
|
|
body_color: fetchForm.body_color,
|
|
|
|
|
|
condition_desc: fetchForm.condition_desc,
|
|
|
|
|
|
mileage: fetchForm.mileage
|
|
|
|
|
|
}
|
|
|
|
|
|
console.log('保存抓取数据:', formData)
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const res: any = await request.post({
|
|
|
|
|
|
url: '/common/usedcardetail/crawl/store',
|
|
|
|
|
|
params: formData
|
|
|
|
|
|
})
|
|
|
|
|
|
ElMessage.success(res?.message)
|
|
|
|
|
|
fetchDialogVisible.value = false
|
|
|
|
|
|
getWorkList() // 刷新列表
|
|
|
|
|
|
} catch (error: any) {
|
|
|
|
|
|
console.error('保存抓取数据失败:', error)
|
|
|
|
|
|
ElMessage.error(error?.message || '保存失败,请稍后重试')
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 抓取图片预览点击
|
|
|
|
|
|
const handleFetchAvatarClick = (index: number) => {
|
|
|
|
|
|
viewerIndex.value = index
|
|
|
|
|
|
showImageViewer.value = true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 格式化年月(如:2024年01月 -> 2024-01-01)
|
|
|
|
|
|
function formatCnYearMonth(str: string) {
|
|
|
|
|
|
const match = str.match(/(\d{4})年(\d{1,2})月/)
|
|
|
|
|
|
if (!match) return str
|
|
|
|
|
|
const year = match[1]
|
|
|
|
|
|
const month = match[2].padStart(2, '0')
|
|
|
|
|
|
return `${year}-${month}-01`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-11 11:16:35 +08:00
|
|
|
|
onMounted(() => {
|
|
|
|
|
|
getWorkList()
|
|
|
|
|
|
})
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
|
:deep(.el-table__empty-block) {
|
|
|
|
|
|
height: 100% !important;
|
|
|
|
|
|
min-height: 200px;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.page-content {
|
|
|
|
|
|
.svg-icon {
|
|
|
|
|
|
width: 1.8em;
|
|
|
|
|
|
height: 1.8em;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
vertical-align: -8px;
|
|
|
|
|
|
fill: currentcolor;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.thumb-list {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
gap: 6px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.avatarimg {
|
|
|
|
|
|
width: 48px;
|
|
|
|
|
|
height: 48px;
|
|
|
|
|
|
object-fit: cover;
|
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
transition: opacity 0.2s;
|
|
|
|
|
|
&:hover {
|
|
|
|
|
|
opacity: 0.8;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.customer-search-group {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
gap: 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.bind-name-wrapper {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
gap: 12px;
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
|
|
|
|
|
|
:deep(.el-input) {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.bind-tip {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 8px;
|
|
|
|
|
|
color: #909399;
|
|
|
|
|
|
|
|
|
|
|
|
.bind-tip-text {
|
|
|
|
|
|
color: #606266;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.video-preview-list {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
gap: 12px;
|
|
|
|
|
|
|
|
|
|
|
|
.video-item {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
width: 200px;
|
|
|
|
|
|
|
|
|
|
|
|
.video-preview {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: auto;
|
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
|
border: 1px solid #dcdfe6;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.el-button {
|
|
|
|
|
|
margin-top: 4px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.faq-display {
|
|
|
|
|
|
.faq-item {
|
|
|
|
|
|
padding: 12px;
|
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
|
background-color: #f5f7fa;
|
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
|
|
|
|
|
|
|
&:last-child {
|
|
|
|
|
|
margin-bottom: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.faq-question,
|
|
|
|
|
|
.faq-answer {
|
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
|
|
|
|
|
|
|
&:last-child {
|
|
|
|
|
|
margin-bottom: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.faq-label {
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
color: #409eff;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.faq-container {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
|
|
|
|
|
|
.faq-item {
|
|
|
|
|
|
padding: 16px;
|
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
|
background-color: #f5f7fa;
|
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
|
|
|
|
|
|
|
.faq-header {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
|
|
|
|
|
|
|
.faq-index {
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
color: #409eff;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
:deep(.el-form-item) {
|
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
|
|
|
|
|
|
|
&:last-child {
|
|
|
|
|
|
margin-bottom: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.upload-list {
|
|
|
|
|
|
:deep(.el-upload-list__item-thumbnail) {
|
|
|
|
|
|
object-fit: cover;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.bind-search {
|
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.bind-loading,
|
|
|
|
|
|
.bind-empty {
|
|
|
|
|
|
padding: 16px;
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
color: #909399;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.bind-item {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
padding: 10px 12px;
|
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
transition: background-color 0.2s;
|
|
|
|
|
|
|
|
|
|
|
|
& + .bind-item {
|
|
|
|
|
|
margin-top: 6px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
|
|
background-color: #f5f7fa;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
&.active {
|
|
|
|
|
|
background-color: #ecf5ff;
|
|
|
|
|
|
border: 1px solid #409eff;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.bind-item-main {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.bind-item-title {
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
color: #303133;
|
|
|
|
|
|
margin-bottom: 4px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.bind-item-sub {
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
color: #909399;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.bind-item-check {
|
|
|
|
|
|
width: 18px;
|
|
|
|
|
|
height: 18px;
|
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
|
border: 1px solid #dcdfe6;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
margin-left: 10px;
|
|
|
|
|
|
|
|
|
|
|
|
.check-dot {
|
|
|
|
|
|
width: 10px;
|
|
|
|
|
|
height: 10px;
|
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
|
background-color: #409eff;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 审核记录时间轴样式
|
|
|
|
|
|
.audit-records-container {
|
|
|
|
|
|
max-height: 500px;
|
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
|
padding: 10px 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.audit-record-card {
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
|
|
|
|
|
|
.audit-record-header {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
|
|
|
|
|
|
|
.auditor-info {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 4px;
|
|
|
|
|
|
color: #606266;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.audit-record-content {
|
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
|
|
|
|
|
|
|
.content-label {
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
color: #606266;
|
|
|
|
|
|
margin-bottom: 4px;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.content-value {
|
|
|
|
|
|
color: #303133;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
line-height: 1.5;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.audit-record-status-change {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 8px;
|
|
|
|
|
|
padding: 8px 12px;
|
|
|
|
|
|
background: #f5f7fa;
|
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
|
|
|
|
|
|
|
.status-before,
|
|
|
|
|
|
.status-after {
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.status-before {
|
|
|
|
|
|
color: #909399;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.status-after {
|
|
|
|
|
|
color: #67c23a;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.status-arrow {
|
|
|
|
|
|
color: #409eff;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-03-12 16:48:21 +08:00
|
|
|
|
|
|
|
|
|
|
// 抓取弹窗样式
|
|
|
|
|
|
.fetch-dialog-content {
|
|
|
|
|
|
.fetch-loading {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
padding: 40px 0;
|
|
|
|
|
|
color: #909399;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.fetch-result {
|
|
|
|
|
|
.thumb-list {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
gap: 6px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.avatarimg {
|
|
|
|
|
|
width: 60px;
|
|
|
|
|
|
height: 60px;
|
|
|
|
|
|
object-fit: cover;
|
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
transition: opacity 0.2s;
|
|
|
|
|
|
&:hover {
|
|
|
|
|
|
opacity: 0.8;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-03-11 11:16:35 +08:00
|
|
|
|
</style>
|