shot.vue 26 KB

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