shot.vue 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894
  1. <template>
  2. <headerBar
  3. title="拍摄商品"
  4. showUser
  5. :menu="menu"
  6. />
  7. <hardware-check/>
  8. <div class="photography-page flex-col">
  9. <div class="main-container">
  10. <div class="content-wrapper flex-col">
  11. <div class="step-section">
  12. <div class="step-number flex-col"><span class="text_22">1</span></div>
  13. <div class="step-one flex-col">
  14. <div class="step-header flex-row">
  15. <span class="step-title">第一步:获取商品货号</span>
  16. </div>
  17. <div class="step-content">
  18. <div class="input-container flex-row">
  19. <el-input class="input-item" ref="goodsArtNo" clearable v-model="goods_art_no" placeholder="请输入货号"> </el-input>
  20. </div>
  21. <div class="auto-method flex-row justify-between">
  22. <img class="step-icon" referrerpolicy="no-referrer" src="@/assets/images/Photography/step1-icon.png" />
  23. <div class="text-method-tag flex-col mar-left-10"><span class="text_4">自动获取</span></div>
  24. <span class="method-description mar-left-10">用遥控器扫描商品资料二维码</span>
  25. </div>
  26. </div>
  27. </div>
  28. </div>
  29. <div class="step-section">
  30. <div class="step-number flex-col"><span class="text_22">2</span></div>
  31. <div class="step-two flex-col justify-between">
  32. <span class="step-title">第二步:启动拍摄(根据按遥控器左右键启动)</span>
  33. <div class="shooting-container flex-col">
  34. <div class="shooting-tips flex-row justify-between">
  35. <img class="info-icon" referrerpolicy="no-referrer" src="@/assets/images/Photography/info-icon.png" />
  36. <span class="tips-text">遥控左右按键可启动拍摄,中间按钮可在拍摄5张主图后解锁,用于拍摄自定义图</span>
  37. </div>
  38. <div class="wifi mar-top-20">
  39. <img referrerpolicy="no-referrer"
  40. src="@/assets/images/Photography/wifi.png" style="width: 60px" />
  41. </div>
  42. <div class="remote-control-wrap">
  43. <RemoteControl
  44. @onRemoteControl="onRemoteControl"
  45. :canStop="runLoading || takePictureLoading"
  46. />
  47. </div>
  48. </div>
  49. </div>
  50. </div>
  51. </div>
  52. <div class="last-photo" v-show="showlastPhoto && (lastPhoto as any)?.file_path" v-key="(lastPhoto as any)?.file_path">
  53. <el-button
  54. class="close-btn"
  55. type="danger"
  56. icon="Close"
  57. circle
  58. @click="closeLastPhoto"
  59. />
  60. <el-image :src="getFilePath((lastPhoto as any)?.file_path || '')" fit="contain" ></el-image>
  61. </div>
  62. <div class="history-section flex-col koutu-section">
  63. <div class="history-warp" ref="containerRef">
  64. <div v-if="!goodsList.length" class="fs-14 c-666 mar-top-50">
  65. {{ loading ? '数据正在加载中,请稍候...' : '暂无数据,请先进行拍摄'}}
  66. </div>
  67. <div v-else class="history-item" v-for="item in goodsList" :key="item.goods_art_no" >
  68. <div class="history-item-header">
  69. <div class="history-item-left">
  70. <el-checkbox
  71. :model-value="selectedGoods.has(item.goods_art_no)"
  72. @change="toggleGoods(item.goods_art_no)"
  73. class="goods-checkbox"
  74. />
  75. <span class="goods-art-no">{{ item.goods_art_no }}</span>
  76. <div class="history-item-meta ">
  77. <span class="action-time flex left">
  78. <img src="@/assets/images/processImage.vue/riq.png" />
  79. {{ getTime(item.action_time) }}</span>
  80. <span class="image-count mar-left-10 flex left">
  81. <img src="@/assets/images/processImage.vue/tup.png" />
  82. {{ item.items?.length || 0 }}张图片</span>
  83. <span v-if="!item.syncConfig" class="mar-left-10">无法读取到该商品拍摄配置,如需重新生成请删除该商品并重新拍摄</span>
  84. </div>
  85. </div>
  86. <div class="history-item-right">
  87. <el-dropdown :disabled="runLoading || takePictureLoading" trigger="click">
  88. <el-button :disabled="runLoading || takePictureLoading" size="small" plain>高级生成</el-button>
  89. <template #dropdown>
  90. <el-dropdown-menu>
  91. <el-dropdown-item
  92. v-for="menu in generate.children"
  93. @click.native="onGenerateCLick(menu,item)">{{ menu.name }}</el-dropdown-item>
  94. </el-dropdown-menu>
  95. </template>
  96. </el-dropdown>
  97. <el-button size="small" v-if="item.syncConfig" class="mar-left-10" :disabled="runLoading || takePictureLoading" type="primary" @click="reTakePictureNos(item.goods_art_no,item)" plain v-log="{ describe: { action: '重拍货号', goods_art_no: item.goods_art_no } }">重拍</el-button>
  98. <el-button style="color: #FF4C00" size="small" class="mar-left-10" :disabled="runLoading || takePictureLoading" @click="delGoods({goods_art_nos:[item.goods_art_no]})" v-log="{ describe: { action: '删除货号', goods_art_no: item.goods_art_no } }">删除</el-button>
  99. </div>
  100. </div>
  101. <div class="history-item-images" >
  102. <div
  103. v-for="(image, index) in item.items"
  104. :key="image.action_id || image.action_name"
  105. class="history-item_image"
  106. v-loading="!image.PhotoRecord.image_path && runAction.goods_art_no == item.goods_art_no"
  107. >
  108. <!-- <span class="tag" v-if="!image.PhotoRecord.image_path">{{ image.action_name }}</span>-->
  109. <div class="el-image_view" >
  110. <el-image
  111. :src="thumbnailMap[image.PhotoRecord.image_path] || getFilePath(image.PhotoRecord.image_path)"
  112. :preview-src-list="getPreviewImageList(item)"
  113. hide-on-click-modal
  114. :initial-index="getPreviewIndex(item, index)"
  115. class="preview-image"
  116. fit="contain"
  117. :preview-teleported="true"
  118. lazy
  119. >
  120. <template #placeholder>
  121. <span class="tag">{{ image.action_name }}</span>
  122. </template>
  123. <template #error>
  124. <div class="image-slot">
  125. <span class="tag">{{ image.action_name }}</span>
  126. </div>
  127. </template>
  128. </el-image>
  129. <el-button v-if="image.action_name && !runLoading && !takePictureLoading" :disabled="runLoading || takePictureLoading" class="reset-button" @click="reTakePicture(image.PhotoRecord)" v-log="{ describe: { action: '重拍单张图片', goods_art_no: image.PhotoRecord.goods_art_no, action_name: image.action_name } }">重拍</el-button>
  130. </div>
  131. </div>
  132. </div>
  133. </div>
  134. </div>
  135. <div class="footer-controls">
  136. <div class="footer-left">
  137. <el-checkbox
  138. :model-value="isSelectAll"
  139. :indeterminate="isIndeterminate"
  140. @change="toggleSelectAll"
  141. class="select-all-checkbox"
  142. >
  143. 全选
  144. </el-checkbox>
  145. <span class="image-count-text">
  146. 已选择 <span style="color: #2957FF">{{ selectedImageCount }}</span> 张图片 共 <span style="color: #2957FF">{{ totalImageCount }}</span> 张图片
  147. </span>
  148. </div>
  149. <div class="footer-right">
  150. <div class="pagination-container" style="padding: 10px 0; text-align: center;">
  151. <el-pagination
  152. :page-count="totalPages"
  153. :current-page="currentPage"
  154. @current-change="(page) => getPhotoRecords({ page })"
  155. layout="prev, pager, next"
  156. :page-size="pageSize"
  157. />
  158. </div>
  159. <el-button
  160. :disabled="selectedGoods.size === 0 || runLoading || takePictureLoading"
  161. @click="deleteSelected"
  162. v-log="{ describe: { action: '删除选中货号' } }"
  163. >
  164. 删除
  165. </el-button>
  166. <el-button
  167. type="primary"
  168. :disabled="!goodsList.length || runLoading || takePictureLoading"
  169. @click="openPhotographyDetail()"
  170. v-log="{ describe: { action: '点击开始生成' } }"
  171. >
  172. <img src="@/assets/images/processImage.vue/sc.png" />
  173. 开始生成
  174. <img src="@/assets/images/processImage.vue/go.png" class="go"/>
  175. </el-button>
  176. </div>
  177. </div>
  178. </div>
  179. </div>
  180. </div>
  181. </template>
  182. <script setup lang="ts">
  183. import headerBar from '@/components/header-bar/index.vue'
  184. import { onMounted, onBeforeUnmount, ref, computed, watch, nextTick } from 'vue'
  185. import HardwareCheck from '@/components/check/index.vue'
  186. // @ts-ignore
  187. import RemoteControl from '@/views/RemoteControl/index.vue'
  188. import usePhotography from './mixin/usePhotography'
  189. import { useThumbnails } from './composables/useThumbnails'
  190. import generate from '@/utils/menus/generate'
  191. import { ElMessageBox } from 'element-plus'
  192. const {
  193. loading,
  194. runLoading,
  195. takePictureLoading,
  196. goodsList,
  197. goods_art_no,
  198. runAction,
  199. lastPhoto,
  200. showlastPhoto,
  201. goodsArtNo,
  202. menu,
  203. getTime,
  204. getFilePath,
  205. getPhotoRecords,
  206. delGoods,
  207. del,
  208. reTakePicture,
  209. reTakePictureNos,
  210. onRemoteControl,
  211. openPhotographyDetail,
  212. onGenerateCLick,
  213. initEventListeners,
  214. cleanupEventListeners,
  215. pageSize,
  216. currentPage,
  217. totalPages,
  218. } = usePhotography()
  219. // 关闭最后拍摄的照片预览
  220. const closeLastPhoto = () => {
  221. showlastPhoto.value = false
  222. }
  223. // 选中的货号列表
  224. const selectedGoods = ref<Set<string>>(new Set())
  225. // thumbnails
  226. const containerRef = ref<HTMLElement | null>(null)
  227. const { thumbnailMap, observe, stop } = useThumbnails(getFilePath)
  228. // 全选状态
  229. const isSelectAll = computed(() => {
  230. return goodsList.value.length > 0 && selectedGoods.value.size === goodsList.value.length
  231. })
  232. // 是否半选状态
  233. const isIndeterminate = computed(() => {
  234. return selectedGoods.value.size > 0 && selectedGoods.value.size < goodsList.value.length
  235. })
  236. // 切换单个货号的选中状态
  237. const toggleGoods = (goodsArtNo: string) => {
  238. if (selectedGoods.value.has(goodsArtNo)) {
  239. selectedGoods.value.delete(goodsArtNo)
  240. } else {
  241. selectedGoods.value.add(goodsArtNo)
  242. }
  243. }
  244. // 全选/取消全选
  245. const toggleSelectAll = () => {
  246. if (isSelectAll.value) {
  247. selectedGoods.value.clear()
  248. } else {
  249. goodsList.value.forEach((item: any) => {
  250. selectedGoods.value.add(item.goods_art_no)
  251. })
  252. }
  253. }
  254. // 计算已选择的图片数量
  255. const selectedImageCount = computed(() => {
  256. let count = 0
  257. goodsList.value.forEach((item: any) => {
  258. if (selectedGoods.value.has(item.goods_art_no)) {
  259. count += item.items?.length || 0
  260. }
  261. })
  262. return count
  263. })
  264. // 计算总图片数量
  265. const totalImageCount = computed(() => {
  266. let count = 0
  267. goodsList.value.forEach((item: any) => {
  268. count += item.items?.length || 0
  269. })
  270. return count
  271. })
  272. // 删除选中的货号
  273. const deleteSelected = async () => {
  274. if (selectedGoods.value.size === 0) {
  275. return
  276. }
  277. try {
  278. await ElMessageBox.confirm(
  279. `确定要删除选中的 ${selectedGoods.value.size} 个货号的拍摄数据吗?`,
  280. '提示',
  281. {
  282. confirmButtonText: '确定',
  283. cancelButtonText: '取消',
  284. }
  285. )
  286. const goodsArtNos = Array.from(selectedGoods.value)
  287. await del({ goods_art_nos: goodsArtNos })
  288. // 删除成功后清空选中状态
  289. selectedGoods.value.clear()
  290. } catch (e) {
  291. // 用户取消
  292. }
  293. }
  294. // 获取预览图片列表(只包含有图片路径的,保持原始顺序)
  295. const getPreviewImageList = (item: any) => {
  296. if (!item || !item.items) return []
  297. return item.items
  298. .filter((img: any) => img.PhotoRecord?.image_path)
  299. .map((img: any) => getFilePath(img.PhotoRecord.image_path))
  300. }
  301. // 获取当前图片在预览列表中的索引
  302. const getPreviewIndex = (item: any, currentIndex: number) => {
  303. if (!item || !item.items) return 0
  304. // 计算当前图片在过滤后的预览列表中的索引
  305. let previewIndex = 0
  306. for (let i = 0; i <= currentIndex; i++) {
  307. if (item.items[i]?.PhotoRecord?.image_path) {
  308. if (i === currentIndex) break
  309. previewIndex++
  310. }
  311. }
  312. return previewIndex
  313. }
  314. onMounted(async () => {
  315. await getPhotoRecords()
  316. initEventListeners()
  317. nextTick(() => {
  318. observe(containerRef, goodsList)
  319. })
  320. })
  321. onBeforeUnmount(() => {
  322. cleanupEventListeners()
  323. stop()
  324. })
  325. watch(goodsList, () => {
  326. nextTick(() => observe(containerRef, goodsList))
  327. })
  328. </script>
  329. <style lang="scss">
  330. .koutu-image-popper {
  331. width: calc(100vw - 470px) !important;
  332. right: 70px !important;
  333. top: 100px !important;
  334. height: calc(100vh - 170px) !important;
  335. transform: translate(0px, 0px) !important;
  336. .el-image {
  337. width: 100%;
  338. height:100%;
  339. display: block;
  340. .el-image__inner {
  341. width: 100%;
  342. height:100%;
  343. display: block;
  344. }
  345. }
  346. }
  347. </style>
  348. <style scoped lang="scss">
  349. .photography-page {
  350. background-color: rgba(255, 255, 255, 1);
  351. position: relative;
  352. .main-container {
  353. position: relative;
  354. display: flex;
  355. .content-wrapper {
  356. flex-grow: 1 ;
  357. position: relative;
  358. top: 0;
  359. left: 0;
  360. right: 0;
  361. bottom: 0;
  362. width: 510px;
  363. padding: 0 50px;
  364. height: calc(100vh - 30px);
  365. margin: auto;
  366. justify-content: flex-start;
  367. flex-direction: column;
  368. overflow: hidden;
  369. border-right:1px solid rgba(0,0,0,.1);
  370. .step-section {
  371. display: flex;
  372. margin-bottom: 20px;
  373. &:last-child {
  374. margin-bottom: 0;
  375. }
  376. }
  377. .step-number {
  378. background-color: rgba(22, 119, 255, 1);
  379. border-radius: 50%;
  380. height: 32px;
  381. margin-top: 51px;
  382. width: 32px;
  383. .text_22 {
  384. width: 6px;
  385. height: 22px;
  386. overflow-wrap: break-word;
  387. color: rgba(255, 255, 255, 1);
  388. font-size: 14px;
  389. font-weight: NaN;
  390. text-align: right;
  391. white-space: nowrap;
  392. line-height: 22px;
  393. margin: 5px 0 0 13px;
  394. }
  395. }
  396. .step-one {
  397. width: 350px;
  398. height: auto;
  399. margin: 55px 0 0 5px;
  400. .step-header {
  401. width: 391px;
  402. height: 24px;
  403. margin-left: 3px;
  404. .step-title {
  405. width: 160px;
  406. height: 24px;
  407. overflow-wrap: break-word;
  408. color: rgba(0, 0, 0, 0.85);
  409. font-size: 16px;
  410. font-family: PingFangSC-Medium;
  411. font-weight: 500;
  412. text-align: left;
  413. white-space: nowrap;
  414. line-height: 24px;
  415. }
  416. .step-icon {
  417. width: 32px;
  418. height: 20px;
  419. margin-top: 4px;
  420. }
  421. .step-divider {
  422. width: 191px;
  423. height: 1px;
  424. margin: 13px 0 0 8px;
  425. }
  426. }
  427. .step-content {
  428. width: 350px;
  429. .input-container {
  430. width: calc(100% - 20px );
  431. height: 36px;
  432. margin: 10px 10px 0;
  433. .input-item {
  434. :deep(.el-input__inner){
  435. height: 36px;
  436. line-height: 36px;
  437. }
  438. }
  439. }
  440. .auto-method {
  441. width: 253px;
  442. height: 24px;
  443. margin: 28px 0 0 14px;
  444. .text-method-tag {
  445. background-color: rgba(0, 174, 30, 1);
  446. height: 24px;
  447. width: 65px;
  448. .text_4 {
  449. width: 56px;
  450. height: 20px;
  451. overflow-wrap: break-word;
  452. color: rgba(255, 255, 255, 1);
  453. font-size: 14px;
  454. font-family: PingFangSC-Semibold;
  455. font-weight: 600;
  456. text-align: left;
  457. white-space: nowrap;
  458. line-height: 20px;
  459. margin: 2px 0 0 4px;
  460. }
  461. }
  462. .method-description {
  463. width: 182px;
  464. height: 20px;
  465. overflow-wrap: break-word;
  466. color: rgba(71, 71, 71, 1);
  467. font-size: 14px;
  468. font-family: PingFangSC-Semibold;
  469. font-weight: 600;
  470. text-align: left;
  471. white-space: nowrap;
  472. line-height: 20px;
  473. margin-top: 2px;
  474. }
  475. }
  476. }
  477. }
  478. .step-two {
  479. width: 350px;
  480. height: auto;
  481. margin: 55px 0 0 5px;
  482. .step-title {
  483. width: 350px;
  484. height: 24px;
  485. overflow-wrap: break-word;
  486. color: rgba(0, 0, 0, 0.85);
  487. font-size: 16px;
  488. font-family: PingFangSC-Medium;
  489. font-weight: 500;
  490. text-align: left;
  491. white-space: nowrap;
  492. line-height: 24px;
  493. }
  494. .shooting-container {
  495. width: 353px;
  496. height: auto;
  497. .remote-control-wrap {
  498. width: 353px;
  499. height: 300px;
  500. }
  501. .shooting-tips {
  502. width: 325px;
  503. height: 40px;
  504. margin: 12px 0 0 15px;
  505. .info-icon {
  506. width: 16px;
  507. height: 16px;
  508. margin-top: 2px;
  509. }
  510. .tips-text {
  511. width: 302px;
  512. height: 40px;
  513. overflow-wrap: break-word;
  514. color: rgba(255, 76, 0, 1);
  515. font-size: 14px;
  516. font-weight: NaN;
  517. text-align: left;
  518. line-height: 20px;
  519. }
  520. }
  521. }
  522. }
  523. }
  524. }
  525. }
  526. .history-section {
  527. width: calc(100vw - 510px);
  528. height: calc(100vh - 30px);
  529. display: flex;
  530. flex-direction: column;
  531. padding: 20px;
  532. overflow-y: auto;
  533. background:#F5F6F7;
  534. ::v-deep {
  535. .el-checkbox__input {
  536. transform: scale(1.4);
  537. }
  538. }
  539. .history-warp {
  540. flex: 1;
  541. .history-item {
  542. background: #FFFFFF;
  543. box-shadow: 0px 2px 4px 0px rgba(23,33,71,0.1);
  544. border-radius: 10px;
  545. border: 1px solid #D9DEE6;
  546. margin-bottom: 20px;
  547. .history-item-header {
  548. display: flex;
  549. justify-content: space-between;
  550. align-items: center;
  551. height: 40px;
  552. padding: 0 10px;
  553. background: linear-gradient( 90deg, #F4ECFF 0%, #DFEDFF 100%);
  554. border-radius: 10px 10px 0px 0px;
  555. .history-item-left {
  556. display: flex;
  557. align-items: center;
  558. gap: 10px;
  559. .goods-checkbox {
  560. margin-right: 0;
  561. }
  562. .goods-art-no {
  563. font-size: 16px;
  564. font-weight: 500;
  565. color: #333;
  566. }
  567. }
  568. .history-item-right {
  569. display: flex;
  570. align-items: center;
  571. ::v-deep {
  572. .el-button { height: 30px; line-height: 30px;}
  573. }
  574. }
  575. }
  576. .history-item-meta {
  577. display: flex;
  578. justify-content: space-between;
  579. align-items: center;
  580. font-size: 12px;
  581. color: #666;
  582. img {
  583. height: 14px;
  584. margin-right: 2px;
  585. }
  586. .action-time {
  587. color: #666;
  588. }
  589. .image-count {
  590. color: #666;
  591. }
  592. }
  593. .history-item-images {
  594. display: grid;
  595. grid-template-columns: repeat(5, 1fr);
  596. gap: 10px;
  597. padding: 15px;
  598. border-top: 1px solid #f0f0f0;
  599. overflow-x: auto;
  600. // 如果图片数量超过5个,允许横向滚动
  601. @media (min-width: 1200px) {
  602. grid-template-columns: repeat(5, 1fr);
  603. }
  604. // 响应式:小屏幕时每行3个
  605. @media (max-width: 768px) {
  606. grid-template-columns: repeat(3, 1fr);
  607. }
  608. }
  609. .history-item_image_wrap {
  610. padding-bottom: 0;
  611. border-bottom: none;
  612. }
  613. .history-item_image {
  614. position: relative;
  615. width: 100%;
  616. aspect-ratio: 1;
  617. background: #F7F7F7;
  618. border-radius: 10px;
  619. overflow: hidden;
  620. cursor: pointer;
  621. border: 1px solid #D9DEE6;
  622. transition: all 0.3s;
  623. .tag {
  624. color: #bbb;
  625. position: absolute;
  626. left: 0;
  627. right: 0;
  628. top: 50%;
  629. margin-top: -10px;
  630. line-height: 20px;
  631. text-align: center;
  632. font-size: 12px;
  633. z-index: 1;
  634. pointer-events: none;
  635. }
  636. .preview-image {
  637. width: 100%;
  638. height: 100%;
  639. :deep(.el-image__inner) {
  640. width: 100%;
  641. height: 100%;
  642. object-fit: cover;
  643. }
  644. }
  645. .image-placeholder {
  646. width: 100%;
  647. height: 100%;
  648. display: flex;
  649. align-items: center;
  650. justify-content: center;
  651. background: #F7F7F7;
  652. }
  653. .image-slot {
  654. width: 100%;
  655. height: 100%;
  656. display: flex;
  657. align-items: center;
  658. justify-content: center;
  659. background: #F7F7F7;
  660. }
  661. &:hover {
  662. border-color: #409eff;
  663. transform: scale(1.02);
  664. box-shadow: 0 2px 8px rgba(64, 158, 255, 0.2);
  665. }
  666. &.el-loading-parent--relative{
  667. ::v-deep {
  668. .el-loading-mask { display: none}
  669. }
  670. }
  671. }
  672. .el-image_view {
  673. display: flex;
  674. width: 100%;
  675. height: 100%;
  676. .reset-button {
  677. width: 40px;
  678. text-align: center;
  679. height: 20px;
  680. position: absolute;
  681. left:50%;
  682. top:50%;
  683. padding: 0px;
  684. margin-left:-20px;
  685. margin-top:-10px;
  686. color: #ffffff;
  687. font-size: 14px;
  688. background: rgba(0,0,0,0.6);
  689. border-radius: 12px;
  690. display: none;
  691. cursor: pointer;
  692. }
  693. &:hover {
  694. .reset-button {
  695. display: block;
  696. }
  697. }
  698. }
  699. p:first-of-type {
  700. ::v-deep {
  701. .el-loading-mask { display: block !important;}
  702. }
  703. }
  704. }
  705. }
  706. .footer-controls {
  707. display: flex;
  708. justify-content: space-between;
  709. align-items: center;
  710. padding: 0px 20px;
  711. border-top: 1px solid #D9DEE6;
  712. background-color: #fff;
  713. min-height: 50px;
  714. flex-shrink: 0;
  715. position: fixed;
  716. bottom:0;
  717. left: 510px;
  718. right: 0;
  719. font-size: 14px;
  720. z-index: 100;
  721. img {
  722. height: 12px;
  723. margin: 0 5px;
  724. }
  725. .go {
  726. height: 12px;
  727. opacity: .8;
  728. }
  729. ::v-deep {
  730. .el-button {
  731. border-radius: 10px;
  732. height: 40px;
  733. line-height: 40px;
  734. }
  735. }
  736. .footer-left {
  737. display: flex;
  738. align-items: center;
  739. gap: 10px;
  740. .select-all-checkbox {
  741. margin-right: 0;
  742. ::v-deep {
  743. .el-checkbox__label {
  744. font-size: 14px;
  745. color: #666;
  746. }
  747. }
  748. }
  749. .image-count-text {
  750. font-size: 14px;
  751. color: #333;
  752. margin-left: 0;
  753. }
  754. }
  755. .footer-right {
  756. display: flex;
  757. align-items: center;
  758. gap: 10px;
  759. }
  760. }
  761. }
  762. .last-photo{
  763. position: fixed;
  764. padding: 10px;
  765. box-shadow: 0 0 5px rgb(0 0 0 / 50%);
  766. left: 510px;
  767. top: 30px;
  768. bottom: 0px;
  769. right: 10px;
  770. z-index: 1000;
  771. background-color: rgba(0,0,0,.5);
  772. .close-btn {
  773. position: absolute;
  774. top: 20px;
  775. right: 20px;
  776. z-index: 1001;
  777. width: 32px;
  778. height: 32px;
  779. ::v-deep(.el-icon) {
  780. font-size: 16px;
  781. }
  782. }
  783. .el-image {
  784. width: 100%;
  785. height:100%;
  786. display: block;
  787. .el-image__inner {
  788. width: 100%;
  789. height:100%;
  790. display: block;
  791. }
  792. }
  793. }
  794. </style>