|
|
@@ -4,31 +4,61 @@
|
|
|
|
|
|
<div class="detail-container">
|
|
|
<div>
|
|
|
- <!-- 处理模式 -->
|
|
|
- <div class="logo-section flex left top" >
|
|
|
- <div class="section-title" style="margin-bottom: 0px;">
|
|
|
+ <!-- 顶部:勾选服务内容(多选) -->
|
|
|
+ <div class="service-section flex between top">
|
|
|
+ <div class="section-title">
|
|
|
<img src="@/assets/images/Photography/zhuangshi.png" style="width: 32px; height: 32px;" />
|
|
|
- 处理模式:
|
|
|
-
|
|
|
- <el-checkbox v-model="form.is_only_cutout"
|
|
|
- true-value="1"
|
|
|
- false-value="0"
|
|
|
- true-label="1"
|
|
|
- false-label="0"
|
|
|
- label="仅抠图模式"/>
|
|
|
- <span class="flex left" style="font-weight: normal; font-size: 12px; color: #666">
|
|
|
- <el-icon><QuestionFilled /></el-icon>
|
|
|
- 如您勾选仅抠图模式,将只做抠图处理,不再自动生成详情页。
|
|
|
- </span>
|
|
|
+ 勾选服务内容(多选):
|
|
|
+ </div>
|
|
|
+ <div class="service-cards">
|
|
|
+ <div class="service-card" :class="{ active: form.services.includes('is_product_scene') }" @click="toggleService('is_product_scene')" v-log="{ describe: { action: '点击服务卡片', service: '场景图生成' } }">
|
|
|
+ <div class="service-checkbox" @click.stop>
|
|
|
+ <el-checkbox size="large" :model-value="form.services.includes('is_product_scene')" @change="toggleService('is_product_scene')" />
|
|
|
+ </div>
|
|
|
+ <div class="service-content">
|
|
|
+ <div class="service-image">
|
|
|
+ <img src="@/assets/images/Photography/cj.png" alt="场景图生成" />
|
|
|
+ <div class="service-icon" @click.stop="openScenePromptDialog" v-log="{ describe: { action: '点击服务卡片图标', service: '场景图生成-弹窗' } }">
|
|
|
+ <el-icon><EditPen /></el-icon>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="service-name">场景图生成</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="service-card" :class="{ active: form.services.includes('is_upper_footer') }" @click="toggleService('is_upper_footer')" v-log="{ describe: { action: '点击服务卡片', service: '模特图生成' } }">
|
|
|
+ <div class="service-checkbox" @click.stop>
|
|
|
+ <el-checkbox size="large" :model-value="form.services.includes('is_upper_footer')" @change="toggleService('is_upper_footer')" />
|
|
|
+ </div>
|
|
|
+ <div class="service-content">
|
|
|
+ <div class="service-image">
|
|
|
+ <img src="@/assets/images/Photography/mt.png" alt="模特图生成" />
|
|
|
+ <div class="service-icon" @click.stop="openModelDialog" v-log="{ describe: { action: '点击服务卡片图标', service: '模特图生成-弹窗' } }">
|
|
|
+ <el-icon><EditPen /></el-icon>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="service-name">模特图生成</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="service-card" :class="{ active: form.services.includes('is_detail') }" @click="toggleService('is_detail')" v-log="{ describe: { action: '点击服务卡片', service: '详情页生成' } }">
|
|
|
+ <div class="service-checkbox" @click.stop>
|
|
|
+ <el-checkbox size="large" :model-value="form.services.includes('is_detail')" @change="toggleService('is_detail')" />
|
|
|
+ </div>
|
|
|
+ <div class="service-content">
|
|
|
+ <div class="service-image">
|
|
|
+ <img src="@/assets/images/Photography/xq.png" alt="详情页生成" />
|
|
|
+ </div>
|
|
|
+ <div class="service-name">详情页生成</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
- <template v-if="form.is_only_cutout == 0">
|
|
|
<!-- 主图LOGO部分 -->
|
|
|
+<!--
|
|
|
<div class="logo-section flex left top" >
|
|
|
<div class="section-title" style="margin-bottom: 0px;">
|
|
|
<img src="@/assets/images/Photography/zhuangshi.png" style="width: 32px; height: 32px;" />
|
|
|
- 主图LOGO:
|
|
|
+ 主图LOGO 与 选择详情模板:
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="logo-section flex left top multi-line">
|
|
|
@@ -43,6 +73,7 @@
|
|
|
></upload>
|
|
|
<upload @input="onInput"></upload>
|
|
|
</div>
|
|
|
+-->
|
|
|
|
|
|
<el-divider />
|
|
|
<!-- <!– 图片抠图与货号图生成 –>
|
|
|
@@ -89,40 +120,85 @@
|
|
|
</div>
|
|
|
<el-divider />-->
|
|
|
|
|
|
- <!-- 选择详情模板部分 -->
|
|
|
- <div class="template-section ">
|
|
|
- <div class="flex between">
|
|
|
- <div class="section-title">
|
|
|
- <img src="@/assets/images/Photography/zhuangshi.png" style="width: 32px; height: 32px;" />
|
|
|
- 选择详情模版
|
|
|
+ <!-- 主图LOGO 与 选择详情模板:左右布局 -->
|
|
|
+ <div class="logo-template-row">
|
|
|
+ <!-- 左:主图LOGO -->
|
|
|
+ <div class="logo-col">
|
|
|
+ <div class="logo-section flex left top" >
|
|
|
+ <div class="section-title" style="margin-bottom: 0px;">
|
|
|
+ <img src="@/assets/images/Photography/zhuangshi.png" style="width: 32px; height: 32px;" />
|
|
|
+ 主图LOGO:
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- <div class="template-pagination">
|
|
|
- <el-pagination background layout="prev, pager, next" v-model:current-page="queryParams.current"
|
|
|
- v-model:page-size.sync="queryParams.size" :total="totalPage" @current-change="onCurrentChange"
|
|
|
- @size-change="onSizeChange" />
|
|
|
+ <div class="logo-section flex left top multi-line">
|
|
|
+ <upload v-for="item,index in logoList" :value="item" :key="item"
|
|
|
+ v-show="item"
|
|
|
+ @input="onRemove(index)"
|
|
|
+ class="mar-right-10 upload-item"
|
|
|
+ :class="{
|
|
|
+ active: item === form.logo_path
|
|
|
+ }"
|
|
|
+ @click.native="form.logo_path = item"
|
|
|
+ ></upload>
|
|
|
+ <upload @input="onInput"></upload>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
- <div class="template-list">
|
|
|
- <div v-for="(template, index) in visibleTemplates" :key="index" class="template-item"
|
|
|
- @click="form.selectTemplate = template">
|
|
|
- <el-image :src="template.template_cover_image" fit="contain" class="cur-p"
|
|
|
- style="width: 100%; display: block;" />
|
|
|
- <div class="select-warp" :class="form.selectTemplate.id == template.id ? 'active' : ''">
|
|
|
- <el-icon color="#FFFFFF">
|
|
|
- <Select />
|
|
|
- </el-icon>
|
|
|
+ <!-- 右:选择详情模板 -->
|
|
|
+ <div class="template-col">
|
|
|
+ <div class="template-section ">
|
|
|
+ <div class="flex between">
|
|
|
+ <div class="section-title">
|
|
|
+ <img src="@/assets/images/Photography/zhuangshi.png" style="width: 32px; height: 32px;" />
|
|
|
+ 选择详情模版
|
|
|
+ </div>
|
|
|
+ <div class="template-pagination">
|
|
|
+ <el-pagination background layout="prev, pager, next" v-model:current-page="queryParams.current"
|
|
|
+ v-model:page-size.sync="queryParams.size" :total="totalPage" @current-change="onCurrentChange"
|
|
|
+ @size-change="onSizeChange" />
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- <div class="template-info">
|
|
|
- <span class="mar-left-10 chaochu_1">{{ template.template_name }}</span>
|
|
|
- <div class="template-view" @click="viewTemplate(template)">查看</div>
|
|
|
+
|
|
|
+ <div class="template-list">
|
|
|
+ <div v-for="(template, index) in visibleTemplates" :key="index" class="template-item"
|
|
|
+ @click="form.selectTemplate = template" v-log="{ describe: { action: '点击选择详情模板', template_name: template.template_name } }">
|
|
|
+ <el-image :src="template.template_cover_image" fit="contain" class="cur-p"
|
|
|
+ style="width: 100%; display: block;" />
|
|
|
+ <div class="select-warp" :class="form.selectTemplate.id == template.id ? 'active' : ''">
|
|
|
+ <el-icon color="#FFFFFF">
|
|
|
+ <Select />
|
|
|
+ </el-icon>
|
|
|
+ </div>
|
|
|
+ <div class="template-info">
|
|
|
+ <span class="mar-left-10 chaochu_1">{{ template.template_name }}</span>
|
|
|
+ <div class="template-view" @click="viewTemplate(template)" v-log="{ describe: { action: '点击查看模板详情', template_name: template.template_name } }">查看</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
|
|
|
- <div class="template-tips c-333 fs-14 line-20 te-l mar-top-20 flex left">
|
|
|
- <el-icon><WarningFilled /></el-icon>
|
|
|
- <span class="mar-left-10">该模版图片顺序说明:{{form.selectTemplate.template_image_order}}</span>
|
|
|
+ <div class="template-tips c-333 fs-14 line-20 te-l mar-top-20 flex left">
|
|
|
+ <el-icon><WarningFilled /></el-icon>
|
|
|
+ <span class="mar-left-10">该模版图片顺序说明:{{form.selectTemplate.template_image_order}}</span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 模板下:一键上架 和 国内电商平台(多选) -->
|
|
|
+ <div class="publish-section flex left" v-if="onlineStores.length">
|
|
|
+ <div class="form-item flex left">
|
|
|
+ <div class="fw-b">一键上架:</div>
|
|
|
+ </div>
|
|
|
+ <div class="form-item flex left mar-top-10">
|
|
|
+ <div class="label">国内电商平台:</div>
|
|
|
+ <el-select v-model="domesticPlatforms" multiple placeholder="请选择平台" style="min-width: 200px;">
|
|
|
+ <el-option
|
|
|
+ v-for="store in onlineStores"
|
|
|
+ :key="store"
|
|
|
+ :label="store"
|
|
|
+ :value="store"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
<el-divider />
|
|
|
@@ -171,7 +247,7 @@
|
|
|
<img src="@/assets/images/Photography/zhuangshi.png" style="width: 32px; height: 32px;" />
|
|
|
详情资料准备 (2选1)
|
|
|
|
|
|
- <el-button v-if="form.dataType == '1'" type="text" class="mar-left-10 fs-16" @click="downloadExcel">下载商品基础资料模版</el-button>
|
|
|
+ <el-button v-if="form.dataType == '1'" type="text" class="mar-left-10 fs-16" @click="downloadExcel" v-log="{ describe: { action: '点击下载Excel模板' } }">下载商品基础资料模版</el-button>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
@@ -187,19 +263,18 @@
|
|
|
<div class="flex bottom mar-left-20" style="flex-grow: 1;">
|
|
|
<el-input type="textarea" v-model="form.excel_path" />
|
|
|
</div>
|
|
|
- <el-button class="select-button button--primary1 mar-left-20" type="primary" @click="selectExcel">
|
|
|
+ <el-button class="select-button button--primary1 mar-left-20" type="primary" @click="selectExcel" v-log="{ describe: { action: '点击选择Excel文件' } }">
|
|
|
<img src="@/assets/images/Photography/wenjian.png" style="width: 16px; margin-right: 4px;" />
|
|
|
选择</el-button>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
- </template>
|
|
|
</div>
|
|
|
<!-- 底部按钮 -->
|
|
|
<div class="footer">
|
|
|
<!-- <el-button class="button--primary1 footer-button" type="primary" @click="saveConfig">保存配置</el-button> -->
|
|
|
<!-- <el-button class="button--primary1 footer-button" type="primary" @click="startProcess">开始处理</el-button> -->
|
|
|
- <el-button class="button--primary1 footer-button" type="primary" @click="generate">开始生成</el-button>
|
|
|
+ <el-button class="button--primary1 footer-button" type="primary" @click="generate" v-log="{ describe: { action: '点击开始生成详情页' } }">开始生成</el-button>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
@@ -211,12 +286,48 @@
|
|
|
<span>{{ item.goods_art_no }}</span>:<span>{{ item.info }}</span>
|
|
|
</div>
|
|
|
</template>
|
|
|
-
|
|
|
+ <template #progressMessages>
|
|
|
+ <div class="progress-messages" v-if="progressMessages.length">
|
|
|
+ <div class="message-header">
|
|
|
+ <span>处理进度</span>
|
|
|
+ <div class="flex right" style="gap:8px; align-items:center;">
|
|
|
+<!-- <el-button type="text" @click="openOutputDir" v-log="{ describe: { action: '点击打开输出目录' } }">打开目录</el-button>-->
|
|
|
+ <el-button type="text" @click="showMessageHistory = !showMessageHistory" v-log="{ describe: { action: '点击查看进度详情' } }">
|
|
|
+ {{ showMessageHistory ? '收起' : '查看详情' }}
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="message-list" v-if="showMessageHistory" ref="messageListRef">
|
|
|
+ <div v-for="(msg, index) in progressMessages" :key="index" class="message-item flex left">
|
|
|
+ <div class="message-time">{{ formatTime(msg.timestamp) }}</div>
|
|
|
+ <div class="message-content mar-left-10">
|
|
|
+ <span class="goods-no">货号{{ msg.goods_art_nos.join(', ') }}:</span>
|
|
|
+ <span class="message-text">{{ msg.msg }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
</loading-dialog>
|
|
|
<el-dialog v-model="dialogVisible">
|
|
|
<img style="width: 100%;" :src="dialogImageUrl" alt="Preview Image" />
|
|
|
</el-dialog>
|
|
|
|
|
|
+ <!-- 模特生成弹窗 -->
|
|
|
+ <ModelGenerationDialog
|
|
|
+ v-model="modelDialogVisible"
|
|
|
+ :initial-models="selectedModels"
|
|
|
+ @confirm="handleModelSelection"
|
|
|
+ @cancel="modelDialogVisible = false"
|
|
|
+ />
|
|
|
+
|
|
|
+ <!-- 场景提示词弹窗 -->
|
|
|
+ <ScenePromptDialog
|
|
|
+ v-model="scenePromptDialogVisible"
|
|
|
+ :initial-prompt="scenePrompt"
|
|
|
+ @confirm="handleScenePromptConfirm"
|
|
|
+ @cancel="scenePromptDialogVisible = false"
|
|
|
+ />
|
|
|
|
|
|
|
|
|
</template>
|
|
|
@@ -227,23 +338,35 @@ import { getCompanyTemplatesApi } from '@/apis/other'
|
|
|
import tokenInfo from '@/stores/modules/token';
|
|
|
import useUserInfo from "@/stores/modules/user";
|
|
|
import { useRoute, useRouter } from 'vue-router'
|
|
|
+import { clickLog, setLogInfo } from '@/utils/log'
|
|
|
|
|
|
|
|
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
|
|
|
|
import headerBar from '@/components/header-bar/index.vue'
|
|
|
-import { ref, computed, reactive, onMounted } from 'vue';
|
|
|
-import { Select } from '@element-plus/icons-vue'
|
|
|
+import { ref, computed, reactive, onMounted, onBeforeUnmount, nextTick } from 'vue';
|
|
|
+import { Select, EditPen } from '@element-plus/icons-vue'
|
|
|
import upload from '@/components/upload'
|
|
|
import client from "@/stores/modules/client";
|
|
|
import icpList from '@/utils/ipc'
|
|
|
const clientStore = client();
|
|
|
import { getRouterUrl } from '@/utils/appfun'
|
|
|
+import { useUuidStore } from '@/stores/modules/uuid'
|
|
|
+import socket from "@/stores/modules/socket";
|
|
|
+const socketStore = socket();
|
|
|
+import ModelGenerationDialog from '@/components/ModelGeneration/index.vue'
|
|
|
+import ScenePromptDialog from '@/components/ScenePromptDialog/index.vue'
|
|
|
|
|
|
import { Close, Warning } from '@element-plus/icons-vue'
|
|
|
import LoadingDialog from '@/views/Photography/components/LoadingDialog.vue'
|
|
|
|
|
|
|
|
|
+import configInfo from "@/stores/modules/config";
|
|
|
+
|
|
|
+const useConfigInfoStore = configInfo();
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
import { useCheckInfo } from '@/composables/userCheck';
|
|
|
useCheckInfo();
|
|
|
|
|
|
@@ -258,6 +381,7 @@ const specificPage = ref('') // 可指定页面独修改
|
|
|
// 路由和状态管理初始化
|
|
|
const route = useRoute();
|
|
|
const router = useRouter();
|
|
|
+const uuidStore = useUuidStore();
|
|
|
|
|
|
// 完成目录
|
|
|
const completeDirectory = ref('')
|
|
|
@@ -267,6 +391,27 @@ const loadingDialogVisible = ref(false)
|
|
|
const progress = ref(0)
|
|
|
const message = ref('正在为您处理,请稍后')
|
|
|
const disabledButton = ref(true)
|
|
|
+// 进度消息队列
|
|
|
+const progressMessages = ref<Array<{
|
|
|
+ goods_no: string
|
|
|
+ temp_name: string
|
|
|
+ status: string
|
|
|
+ goods_art_nos: string[]
|
|
|
+ msg: string
|
|
|
+ timestamp: number
|
|
|
+}>>([])
|
|
|
+const showMessageHistory = ref(true)
|
|
|
+const messageListRef = ref<HTMLElement | null>(null)
|
|
|
+
|
|
|
+// 新消息时自动滚动到底部
|
|
|
+const scrollMessageListToBottom = () => {
|
|
|
+ nextTick(() => {
|
|
|
+ const el = messageListRef.value
|
|
|
+ if (el) {
|
|
|
+ el.scrollTop = el.scrollHeight
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
|
|
|
let templates = ref([])
|
|
|
let goods_art_nos = ref([])
|
|
|
@@ -299,13 +444,28 @@ const form = reactive({
|
|
|
dataType: '1', // 1: 选择excel文件 2: 系统对接
|
|
|
logo_path: '', // 主图LOGO
|
|
|
excel_path: '', // 商品基础资料EXCEL文件选择
|
|
|
- is_only_cutout:0, //是否仅抠图模式
|
|
|
+ services: ['is_detail'], // 勾选服务内容(多选)默认包含详情页生成
|
|
|
})
|
|
|
onMounted(() => {
|
|
|
+ // 页面访问埋点
|
|
|
+
|
|
|
const goods_art_data = route.query.goods_art_nos
|
|
|
goods_art_nos.value = Array.isArray(goods_art_data) ? goods_art_data : [goods_art_data]
|
|
|
getCompanyTemplates()
|
|
|
getLogolist()
|
|
|
+
|
|
|
+ loadDetailCache()
|
|
|
+})
|
|
|
+
|
|
|
+// 页面卸载时清理监听器
|
|
|
+onBeforeUnmount(() => {
|
|
|
+ clientStore.ipc.removeAllListeners(icpList.socket.message + '_detail_progress');
|
|
|
+ clientStore.ipc.removeAllListeners(icpList.socket.message + '_segment_progress');
|
|
|
+ clientStore.ipc.removeAllListeners(icpList.socket.message + '_upper_footer_progress');
|
|
|
+ clientStore.ipc.removeAllListeners(icpList.socket.message + '_scene_progress');
|
|
|
+ clientStore.ipc.removeAllListeners(icpList.socket.message + '_upload_goods_progress');
|
|
|
+ clientStore.ipc.removeAllListeners(icpList.socket.message + '_detail_result_progress');
|
|
|
+ clearInterval(INTERVAL.value);
|
|
|
})
|
|
|
|
|
|
|
|
|
@@ -326,6 +486,7 @@ const viewTemplate = (template) => {
|
|
|
const getCompanyTemplates = async () => {
|
|
|
const { data } = await getCompanyTemplatesApi()
|
|
|
templates.value = data.list
|
|
|
+ onlineStores.value = data.online_stores || [] // 获取电商平台列表
|
|
|
// 默认选中第一个模板
|
|
|
if (templates.value.length > 0) {
|
|
|
form.selectTemplate = templates.value[0]
|
|
|
@@ -345,6 +506,106 @@ const downloadExcel = () => {
|
|
|
document.body.removeChild(a);
|
|
|
}, 1000);
|
|
|
}
|
|
|
+// 服务内容切换
|
|
|
+const toggleService = (key: string) => {
|
|
|
+ const idx = form.services.indexOf(key)
|
|
|
+ if (idx > -1) form.services.splice(idx, 1)
|
|
|
+ else form.services.push(key)
|
|
|
+}
|
|
|
+
|
|
|
+// 国内电商平台多选与一键上架
|
|
|
+const domesticPlatforms = ref<string[]>([])
|
|
|
+const onlineStores = ref<any[]>([]) // 从接口获取的电商平台列表
|
|
|
+const publishToPlatforms = () => {
|
|
|
+ if (!domesticPlatforms.value.length) {
|
|
|
+ ElMessage.warning('请选择至少一个电商平台')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ clickLog({ describe: { action: '点击一键上架', platforms: domesticPlatforms.value } }, route)
|
|
|
+ // 具体上架逻辑按后续接口对接
|
|
|
+}
|
|
|
+
|
|
|
+// 模特与场景弹窗
|
|
|
+const modelDialogVisible = ref(false)
|
|
|
+const scenePromptDialogVisible = ref(false)
|
|
|
+const selectedModels = ref<{ female: any; male: any } | null>(null)
|
|
|
+const scenePrompt = ref('')
|
|
|
+
|
|
|
+// 本地缓存键(与弹窗组件保持一致)
|
|
|
+const DETAIL_MODEL_CACHE_KEY = 'model_selection_cache'
|
|
|
+const DETAIL_SCENE_PROMPT_CACHE_KEY = 'scene_prompt_cache'
|
|
|
+
|
|
|
+// 读取本地缓存
|
|
|
+const loadDetailCache = () => {
|
|
|
+ console.log('loadDetailCache');
|
|
|
+ try {
|
|
|
+ const m = localStorage.getItem(DETAIL_MODEL_CACHE_KEY)
|
|
|
+ if (m) {
|
|
|
+ const parsed = JSON.parse(m)
|
|
|
+ if (parsed && (parsed.female || parsed.male)) {
|
|
|
+ selectedModels.value = parsed
|
|
|
+ console.log('loadDetailCache');
|
|
|
+ console.log(selectedModels.value);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch {}
|
|
|
+ try {
|
|
|
+ const p = localStorage.getItem(DETAIL_SCENE_PROMPT_CACHE_KEY)
|
|
|
+ if (p) {
|
|
|
+ console.log('scenePrompt');
|
|
|
+ scenePrompt.value = p
|
|
|
+ console.log(scenePrompt.value);
|
|
|
+ }
|
|
|
+ } catch {}
|
|
|
+}
|
|
|
+
|
|
|
+// 保存到本地缓存(仅保存必要字段)
|
|
|
+const saveModelsToCache = (models: { female: any; male: any }) => {
|
|
|
+ try {
|
|
|
+ const payload = {
|
|
|
+ female: models?.female ? {
|
|
|
+ id: models.female.id,
|
|
|
+ name: models.female.name,
|
|
|
+ image_url: models.female.image_url,
|
|
|
+ gender: models.female.gender,
|
|
|
+ keywords: models.female.keywords,
|
|
|
+ status: models.female.status
|
|
|
+ } : null,
|
|
|
+ male: models?.male ? {
|
|
|
+ id: models.male.id,
|
|
|
+ name: models.male.name,
|
|
|
+ image_url: models.male.image_url,
|
|
|
+ gender: models.male.gender,
|
|
|
+ keywords: models.male.keywords,
|
|
|
+ status: models.male.status
|
|
|
+ } : null
|
|
|
+ }
|
|
|
+ localStorage.setItem(DETAIL_MODEL_CACHE_KEY, JSON.stringify(payload))
|
|
|
+ } catch {}
|
|
|
+}
|
|
|
+const saveScenePromptToCache = (prompt: string) => {
|
|
|
+ try {
|
|
|
+ const v = (prompt || '').trim()
|
|
|
+ if (v) localStorage.setItem(DETAIL_SCENE_PROMPT_CACHE_KEY, v)
|
|
|
+ } catch {}
|
|
|
+}
|
|
|
+
|
|
|
+const openModelDialog = () => {
|
|
|
+ modelDialogVisible.value = true
|
|
|
+}
|
|
|
+const openScenePromptDialog = () => {
|
|
|
+ scenePromptDialogVisible.value = true
|
|
|
+}
|
|
|
+const handleModelSelection = (models: { female: any; male: any }) => {
|
|
|
+ selectedModels.value = models
|
|
|
+ saveModelsToCache(models)
|
|
|
+ modelDialogVisible.value = false
|
|
|
+ ElMessage.success('模特选择完成!')
|
|
|
+}
|
|
|
+const handleScenePromptConfirm = (prompt: string) => {
|
|
|
+ scenePrompt.value = prompt
|
|
|
+ saveScenePromptToCache(prompt)
|
|
|
+}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -357,15 +618,175 @@ const onSizeChange = (data) => {
|
|
|
|
|
|
|
|
|
|
|
|
+// 格式化时间
|
|
|
+const formatTime = (timestamp: number) => {
|
|
|
+ const date = new Date(timestamp)
|
|
|
+ return `${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}:${date.getSeconds().toString().padStart(2, '0')}`
|
|
|
+}
|
|
|
+
|
|
|
+// 处理进度消息
|
|
|
+const handleProgressMessage = (data: any) => {
|
|
|
+ console.log("detail_progress", data);
|
|
|
+
|
|
|
+ if (data.code === 0 && data.msg_type === 'detail_progress') {
|
|
|
+ const messageData = {
|
|
|
+ goods_no: data.data.goods_no,
|
|
|
+ temp_name: data.data.temp_name,
|
|
|
+ status: data.data.status,
|
|
|
+ goods_art_nos: data.data.goods_art_nos,
|
|
|
+ msg: data.msg,
|
|
|
+ timestamp: Date.now()
|
|
|
+ }
|
|
|
+ progressMessages.value.push(messageData)
|
|
|
+ // 更新当前显示的消息
|
|
|
+ message.value = `货号${data.data.goods_art_nos.join(', ')}:${data.msg}`
|
|
|
+ scrollMessageListToBottom()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 处理抠图进度消息
|
|
|
+const handleSegmentProgressMessage = (data: any) => {
|
|
|
+ console.log("segment_progress", data);
|
|
|
+
|
|
|
+ if (data.code === 0 && data.msg_type === 'segment_progress') {
|
|
|
+ const messageData = {
|
|
|
+ goods_no: '',
|
|
|
+ temp_name: '',
|
|
|
+ status: data.data.status,
|
|
|
+ goods_art_nos: data.data.goods_art_nos,
|
|
|
+ msg: data.msg,
|
|
|
+ timestamp: Date.now()
|
|
|
+ }
|
|
|
+ progressMessages.value.push(messageData)
|
|
|
+ // 更新当前显示的消息
|
|
|
+ message.value = `货号${data.data.goods_art_nos.join(', ')}:${data.msg}`
|
|
|
+ scrollMessageListToBottom()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 处理上脚图进度
|
|
|
+const handleUpperFooterProgressMessage = (data: any) => {
|
|
|
+ console.log("upper_footer_progress", data);
|
|
|
+ if (data.code === 0 && data.msg_type === 'upper_footer_progress') {
|
|
|
+ const messageData = {
|
|
|
+ goods_no: '',
|
|
|
+ temp_name: '',
|
|
|
+ status: data.data?.status || '',
|
|
|
+ goods_art_nos: data.data?.goods_art_nos || [],
|
|
|
+ msg: data.msg,
|
|
|
+ timestamp: Date.now()
|
|
|
+ }
|
|
|
+ progressMessages.value.push(messageData)
|
|
|
+ message.value = `货号${(data.data?.goods_art_nos || []).join(', ')}:${data.msg}`
|
|
|
+ scrollMessageListToBottom()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 处理场景图进度
|
|
|
+const handleSceneProgressMessage = (data: any) => {
|
|
|
+ console.log("scene_progress", data);
|
|
|
+ if (data.code === 0 && data.msg_type === 'scene_progress') {
|
|
|
+ const messageData = {
|
|
|
+ goods_no: '',
|
|
|
+ temp_name: '',
|
|
|
+ status: data.data?.status || '',
|
|
|
+ goods_art_nos: data.data?.goods_art_nos || [],
|
|
|
+ msg: data.msg,
|
|
|
+ timestamp: Date.now()
|
|
|
+ }
|
|
|
+ progressMessages.value.push(messageData)
|
|
|
+ message.value = `货号${(data.data?.goods_art_nos || []).join(', ')}:${data.msg}`
|
|
|
+ scrollMessageListToBottom()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 处理商品上传进度
|
|
|
+const handleUploadGoodsProgressMessage = (data: any) => {
|
|
|
+ console.log("upload_goods_progress", data);
|
|
|
+ if (data.code === 0 && data.msg_type === 'upload_goods_progress') {
|
|
|
+ const messageData = {
|
|
|
+ goods_no: '',
|
|
|
+ temp_name: '',
|
|
|
+ status: data.data?.status || '',
|
|
|
+ goods_art_nos: data.data?.goods_art_nos || [],
|
|
|
+ msg: data.msg,
|
|
|
+ timestamp: Date.now()
|
|
|
+ }
|
|
|
+ progressMessages.value.push(messageData)
|
|
|
+ message.value = `货号${(data.data?.goods_art_nos || []).join(', ')}:${data.msg}`
|
|
|
+ scrollMessageListToBottom()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 打开输出目录:appConfig.appPath + '/build/extraResources/py/output'
|
|
|
+const openOutputDir = () => {
|
|
|
+ try {
|
|
|
+ const appPath = useConfigInfoStore?.appConfig?.appPath || ''
|
|
|
+ if (!appPath) {
|
|
|
+ ElMessage.error('未获取到应用目录 appPath')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const fullPath = `${appPath}/build/extraResources/py/output`
|
|
|
+ clientStore.ipc.removeAllListeners(icpList.utils.shellFun);
|
|
|
+ clientStore.ipc.send(icpList.utils.shellFun, {
|
|
|
+ action: 'openPath',
|
|
|
+ params: fullPath.replaceAll('/', '\\')
|
|
|
+ });
|
|
|
+ } catch (e) {
|
|
|
+ console.error(e)
|
|
|
+ ElMessage.error('打开目录失败')
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
// 开始生成操作
|
|
|
const generate = async function () {
|
|
|
- if(form.is_only_cutout == 0 ){
|
|
|
|
|
|
+ if(form.services.length == 0){
|
|
|
+ ElMessage.error('请选择服务内容')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ // 埋点:开始生成详情页
|
|
|
+ clickLog({
|
|
|
+ describe: {
|
|
|
+ action: '点击开始生成详情页',
|
|
|
+ services: form.services,
|
|
|
+ dataType: form.dataType,
|
|
|
+ template_name: form.selectTemplate.template_name,
|
|
|
+ goods_count: goods_art_nos.value.length,
|
|
|
+ goods_art_nos: goods_art_nos.value
|
|
|
+ }
|
|
|
+ }, route);
|
|
|
+
|
|
|
+ // 必填验证
|
|
|
+ if (form.services.includes('is_upper_footer') && !( selectedModels.value && selectedModels.value.male?.id && selectedModels.value.female?.id)) {
|
|
|
+ openModelDialog();
|
|
|
+ setTimeout(()=>{
|
|
|
+ ElMessage.error('请选择模特')
|
|
|
+ },200)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if (form.services.includes('is_product_scene') && !scenePrompt.value) {
|
|
|
+ openScenePromptDialog();
|
|
|
+ setTimeout(()=>{
|
|
|
+ ElMessage.error('请设置场景提示词')
|
|
|
+ },200)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if(form.services.includes('is_detail')){
|
|
|
if ( form.dataType == '1' && !form.excel_path) {
|
|
|
ElMessage.error('请上传商品基础资料')
|
|
|
return
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ const useConfigInfoStore = configInfo();
|
|
|
+ console.log(useConfigInfoStore.appConfig);
|
|
|
+
|
|
|
+
|
|
|
const tokenInfoStore = tokenInfo();
|
|
|
const token = tokenInfoStore.getToken; // 使用 getToken() 获取 token
|
|
|
let temp_list = []
|
|
|
@@ -375,29 +796,104 @@ const generate = async function () {
|
|
|
template_local_classes: item.template_local_classes,
|
|
|
})
|
|
|
})
|
|
|
+ // 根据选择的服务内容设置参数
|
|
|
+ const isDetail = form.services.includes('is_detail') ? 1 : 0
|
|
|
+ const isProductScene = form.services.includes('is_product_scene') ? 1 : 0
|
|
|
+ const isUpperFooter = form.services.includes('is_upper_footer') ? 1 : 0
|
|
|
+
|
|
|
const params = {
|
|
|
goods_art_no: JSON.parse(JSON.stringify(goods_art_nos.value)),
|
|
|
- is_only_cutout: form.is_only_cutout || '0',
|
|
|
logo_path: form.logo_path || '',
|
|
|
temp_name: form.selectTemplate.template_id || '',
|
|
|
excel_path: form.dataType == '1' ? form.excel_path : '',
|
|
|
template_image_order: form.selectTemplate.template_image_order,
|
|
|
temp_list,
|
|
|
token,
|
|
|
+ uuid: uuidStore.getUuid || '',
|
|
|
+ // 新增服务参数
|
|
|
+ online_stores: Object.values(domesticPlatforms.value || {}),
|
|
|
+ is_detail: isDetail,
|
|
|
+ is_product_scene: isProductScene,
|
|
|
+ is_upper_footer: isUpperFooter,
|
|
|
+ upper_footer_params: selectedModels.value ? {
|
|
|
+ man_id: selectedModels.value.male?.id || "",
|
|
|
+ women_id: selectedModels.value.female?.id || ""
|
|
|
+ } : {},
|
|
|
+ product_scene_prompt: scenePrompt.value || ''
|
|
|
}
|
|
|
+
|
|
|
+ console.log(params)
|
|
|
// 开启进度弹窗
|
|
|
requesting.value = false
|
|
|
- console.log("params", "color:#3f7cff", params);
|
|
|
partErrList.value = []
|
|
|
message.value = '正在为您处理,请稍后'
|
|
|
progress.value = 0
|
|
|
+ // 清空之前的进度消息
|
|
|
+ progressMessages.value = []
|
|
|
+ showMessageHistory.value = true
|
|
|
+
|
|
|
openLoadingDialog(goods_art_nos.value.length * 10)
|
|
|
- clientStore.ipc.removeAllListeners(icpList.generate.generatePhotoDetail);
|
|
|
+ clientStore.ipc.removeAllListeners(icpList.socket.message + '_detail_result_progress');
|
|
|
clientStore.ipc.send(icpList.generate.generatePhotoDetail, params);
|
|
|
- clientStore.ipc.on(icpList.generate.generatePhotoDetail, (event, result) => {
|
|
|
+
|
|
|
+
|
|
|
+ /*
|
|
|
+ * detail_result_progress
|
|
|
+ *
|
|
|
+ * */
|
|
|
+ // 监听进度消息
|
|
|
+ clientStore.ipc.removeAllListeners(icpList.socket.message + '_detail_progress');
|
|
|
+ clientStore.ipc.on(icpList.socket.message + '_detail_progress', (event, data) => {
|
|
|
+ console.log('_detail_progress',data);
|
|
|
+ handleProgressMessage(data)
|
|
|
+ });
|
|
|
+
|
|
|
+ // 监听抠图进度消息
|
|
|
+ clientStore.ipc.removeAllListeners(icpList.socket.message + '_segment_progress');
|
|
|
+ clientStore.ipc.on(icpList.socket.message + '_segment_progress', (event, data) => {
|
|
|
+ console.log('_segment_progress',data);
|
|
|
+ handleSegmentProgressMessage(data)
|
|
|
+ });
|
|
|
+ // 监听上脚图进度消息
|
|
|
+ clientStore.ipc.removeAllListeners(icpList.socket.message + '_upper_footer_progress');
|
|
|
+ clientStore.ipc.on(icpList.socket.message + '_upper_footer_progress', (event, data) => {
|
|
|
+ console.log('_upper_footer_progress',data);
|
|
|
+ handleUpperFooterProgressMessage(data)
|
|
|
+ });
|
|
|
+ // 监听场景图进度消息
|
|
|
+ clientStore.ipc.removeAllListeners(icpList.socket.message + '_scene_progress');
|
|
|
+ clientStore.ipc.on(icpList.socket.message + '_scene_progress', (event, data) => {
|
|
|
+ console.log('_scene_progress',data);
|
|
|
+ handleSceneProgressMessage(data)
|
|
|
+ });
|
|
|
+ // 监听商品上传进度消息
|
|
|
+ clientStore.ipc.removeAllListeners(icpList.socket.message + '_upload_goods_progress');
|
|
|
+ clientStore.ipc.on(icpList.socket.message + '_upload_goods_progress', (event, data) => {
|
|
|
+ console.log('_upload_goods_progress',data);
|
|
|
+ handleUploadGoodsProgressMessage(data)
|
|
|
+ });
|
|
|
+
|
|
|
+ clientStore.ipc.on(icpList.socket.message + '_detail_result_progress', (event, result) => {
|
|
|
+ if(result.code !== 0 ){
|
|
|
+ if(result.msg){
|
|
|
+ handleFail(result.msg)
|
|
|
+ message.value = 'result.msg'
|
|
|
+ }
|
|
|
+ progress.value = 0
|
|
|
+ loadingDialogVisible.value = false
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
console.log('result', result)
|
|
|
requesting.value = true
|
|
|
- clientStore.ipc.removeAllListeners(icpList.generate.generatePhotoDetail);
|
|
|
+ setTimeout(() => {
|
|
|
+ clientStore.ipc.removeAllListeners(icpList.socket.message + '_detail_result_progress');
|
|
|
+ clientStore.ipc.removeAllListeners(icpList.socket.message + '_detail_progress');
|
|
|
+ clientStore.ipc.removeAllListeners(icpList.socket.message + '_segment_progress');
|
|
|
+ clientStore.ipc.removeAllListeners(icpList.socket.message + '_upper_footer_progress');
|
|
|
+ clientStore.ipc.removeAllListeners(icpList.socket.message + '_scene_progress');
|
|
|
+ clientStore.ipc.removeAllListeners(icpList.socket.message + '_upload_goods_progress');
|
|
|
+ }, 100)
|
|
|
clearInterval(INTERVAL.value)
|
|
|
if (result.code === 0) {
|
|
|
const { output_folder, list } = result.data
|
|
|
@@ -420,18 +916,27 @@ const generate = async function () {
|
|
|
//生成失败 (接口请求失败)
|
|
|
function handleFail(errorMsg: string) {
|
|
|
// loadingDialogVisible.value = false
|
|
|
- disabledButton.value = false
|
|
|
+ // disabledButton.value = false
|
|
|
if (errorMsg) {
|
|
|
ElMessage.error(errorMsg)
|
|
|
}
|
|
|
+
|
|
|
+ // 处理完成后停止监听进度消息
|
|
|
+ // clientStore.ipc.removeAllListeners(icpList.socket.message + '_detail_progress');
|
|
|
+ // clientStore.ipc.removeAllListeners(icpList.socket.message + '_segment_progress');
|
|
|
}
|
|
|
|
|
|
// 全部生成成功
|
|
|
function handleSuccess(href, loadingMsg) {
|
|
|
+ // 埋点:生成完成
|
|
|
+ setLogInfo(route, { action: '生成完成', output_folder: href, message: loadingMsg });
|
|
|
+
|
|
|
completeDirectory.value = href
|
|
|
progress.value = 100
|
|
|
disabledButton.value = false
|
|
|
message.value = loadingMsg
|
|
|
+
|
|
|
+
|
|
|
handleComplete()
|
|
|
}
|
|
|
// 部分成功
|
|
|
@@ -447,6 +952,10 @@ const generate = async function () {
|
|
|
}
|
|
|
// 全部生成失败
|
|
|
function handleFailure(partSuccessList) {
|
|
|
+ // 埋点:生成失败(携带货号)
|
|
|
+ const failedGoods = (partSuccessList || []).map(item => item.goods_art_no).filter(Boolean)
|
|
|
+ setLogInfo(route, { action: '生成失败', error_count: partSuccessList.length, goods_art_nos: goods_art_nos.value, failed_goods_art_nos: failedGoods });
|
|
|
+
|
|
|
let errorList = []
|
|
|
partSuccessList.map(item => {
|
|
|
if (!item.success) {
|
|
|
@@ -458,6 +967,7 @@ const generate = async function () {
|
|
|
progress.value = 100
|
|
|
disabledButton.value = true
|
|
|
message.value = '全部货号生成失败'
|
|
|
+
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
@@ -585,6 +1095,10 @@ function openPhotographySeniorDetail() {
|
|
|
const handleComplete = () => {
|
|
|
// loadingDialogVisible.value = false
|
|
|
// 这里可以添加打开目录的逻辑
|
|
|
+ if(!completeDirectory.value){
|
|
|
+ openOutputDir()
|
|
|
+ return
|
|
|
+ }
|
|
|
clientStore.ipc.removeAllListeners(icpList.utils.shellFun);
|
|
|
let params = {
|
|
|
action: 'openPath',
|
|
|
@@ -616,12 +1130,126 @@ const selectFolder = () => {
|
|
|
|
|
|
}
|
|
|
|
|
|
+.service-section {
|
|
|
+ margin-bottom: 20px;
|
|
|
+ .service-cards {
|
|
|
+ display: flex;
|
|
|
+ gap: 60px;
|
|
|
+ margin-top: 16px;
|
|
|
+ }
|
|
|
+ .service-card {
|
|
|
+ width: 240px;
|
|
|
+ height: 140px;
|
|
|
+
|
|
|
+ border-radius: 8px;
|
|
|
+ background: #fff;
|
|
|
+ border: 1px solid #e0e0e0;
|
|
|
+ display: flex;
|
|
|
+ align-items: flex-start;
|
|
|
+ padding: 12px;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+ position: relative;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ border-color: #2957FF;
|
|
|
+ box-shadow: 0 2px 8px rgba(41, 87, 255, 0.1);
|
|
|
+ }
|
|
|
+
|
|
|
+ &.active {
|
|
|
+ border: 3px solid #2957FF;
|
|
|
+ background: #f8f9ff;
|
|
|
+ box-shadow: 0 4px 12px rgba(41, 87, 255, 0.25);
|
|
|
+ }
|
|
|
+
|
|
|
+ .service-checkbox {
|
|
|
+ position: absolute;
|
|
|
+ left: -40px;
|
|
|
+ top: 10px;
|
|
|
+ width: 30px;
|
|
|
+ transform: scale(1.5);
|
|
|
+ z-index: 10;
|
|
|
+ ::v-deep{
|
|
|
+ .el-checkbox {
|
|
|
+ display: block;
|
|
|
+ margin-right: 0;
|
|
|
+ }
|
|
|
+ .el-checkbox__input {
|
|
|
+ cursor: pointer;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .service-content {
|
|
|
+ flex: 1;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ position: absolute;
|
|
|
+ left: 0;
|
|
|
+ top: 0;
|
|
|
+ right: 0;
|
|
|
+ bottom: 0;;
|
|
|
+ }
|
|
|
+
|
|
|
+ .service-image {
|
|
|
+ position: absolute;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+
|
|
|
+ img {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ object-fit: cover;
|
|
|
+ border-radius: 4px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .service-icon {
|
|
|
+ position: absolute;
|
|
|
+ top: 10px;
|
|
|
+ right: 10px;
|
|
|
+ width: 30px;
|
|
|
+ height: 30px;
|
|
|
+ background: #2957FF;
|
|
|
+ border-radius: 50%;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ color: white;
|
|
|
+ font-size: 18px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .service-name {
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 500;
|
|
|
+ text-align: center;
|
|
|
+ position: absolute;
|
|
|
+ background: rgba(0,0,0,.5);
|
|
|
+ height: 30px;
|
|
|
+ line-height: 30px;
|
|
|
+ color: #fff;
|
|
|
+ left: 0;
|
|
|
+ right: 0;
|
|
|
+ bottom:0px
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
.logo-section,
|
|
|
.template-section,
|
|
|
.data-prep-section {
|
|
|
margin-bottom: 20px;
|
|
|
}
|
|
|
|
|
|
+.logo-template-row {
|
|
|
+ display: flex;
|
|
|
+ gap: 24px;
|
|
|
+ align-items: flex-start;
|
|
|
+ .logo-col { flex: 2; min-width: 300px; }
|
|
|
+ .template-col { flex: 3; }
|
|
|
+}
|
|
|
+
|
|
|
.logo-section {
|
|
|
.upload-item {
|
|
|
border: 2px solid rgba(0,0,0,0);
|
|
|
@@ -713,6 +1341,13 @@ const selectFolder = () => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+.publish-section {
|
|
|
+ .label {
|
|
|
+ min-width: 120px;
|
|
|
+ margin-right: 12px;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
|
|
|
|
|
|
.excel-upload {
|
|
|
@@ -881,4 +1516,61 @@ const selectFolder = () => {
|
|
|
padding-right: 20px;
|
|
|
color: #2957FF !important;
|
|
|
}
|
|
|
+
|
|
|
+.progress-messages {
|
|
|
+ margin-top: 20px;
|
|
|
+ border-top: 1px solid #e4e7ed;
|
|
|
+ padding-top: 20px;
|
|
|
+ width: 100%;
|
|
|
+
|
|
|
+ .message-header {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ margin-bottom: 15px;
|
|
|
+ font-weight: 500;
|
|
|
+ color: #606266;
|
|
|
+ }
|
|
|
+
|
|
|
+ .message-list {
|
|
|
+ max-height: 200px;
|
|
|
+ overflow-y: auto;
|
|
|
+ border: 1px solid #e4e7ed;
|
|
|
+ border-radius: 4px;
|
|
|
+ padding: 10px;
|
|
|
+ background: #fafafa;
|
|
|
+
|
|
|
+ .message-item {
|
|
|
+ margin-bottom: 12px;
|
|
|
+ padding-bottom: 12px;
|
|
|
+ border-bottom: 1px solid #f0f0f0;
|
|
|
+
|
|
|
+ &:last-child {
|
|
|
+ margin-bottom: 0;
|
|
|
+ padding-bottom: 0;
|
|
|
+ border-bottom: none;
|
|
|
+ }
|
|
|
+
|
|
|
+ .message-time {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #909399;
|
|
|
+ margin-bottom: 4px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .message-content {
|
|
|
+ font-size: 14px;
|
|
|
+ line-height: 1.4;
|
|
|
+
|
|
|
+ .goods-no {
|
|
|
+ color: #2957FF;
|
|
|
+ font-weight: 500;
|
|
|
+ }
|
|
|
+
|
|
|
+ .message-text {
|
|
|
+ color: #606266;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
</style>
|