materialSelectorFromWork.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570
  1. <template>
  2. <div class="table-select">
  3. <span class="title">{{ $i18n.t(`gather.select_material`) }}</span>
  4. <div class="close-btn">
  5. <i class="iconfont icon-close" @click="$emit('cancle')"></i>
  6. </div>
  7. <div class="material-tab">
  8. <a
  9. class="material-tab-item"
  10. @click.prevent="currentMaterialType = 'pano'"
  11. >
  12. <span
  13. class="text"
  14. :class="{ active: currentMaterialType === 'pano' }"
  15. >{{ $i18n.t(`gather.panorama`) }}</span
  16. >
  17. <div v-if="currentMaterialType === 'pano'" class="bottom-line"></div>
  18. </a>
  19. <a class="material-tab-item" @click.prevent="currentMaterialType = '3D'">
  20. <span class="text" :class="{ active: currentMaterialType === '3D' }">{{
  21. $i18n.t(`gather.scene`)
  22. }}</span>
  23. <div v-if="currentMaterialType === '3D'" class="bottom-line"></div>
  24. </a>
  25. </div>
  26. <div class="filter" :class="{ active: isSearchKeyInputActive }">
  27. <input
  28. type="text"
  29. :placeholder="$i18n.t(`gather.keywords`)"
  30. v-model="searchKey"
  31. @focus="isSearchKeyInputActive = true"
  32. @blur="isSearchKeyInputActive = false"
  33. />
  34. <i v-if="!searchKey" class="iconfont icon-editor_search search-icon" />
  35. <i
  36. v-if="searchKey"
  37. @click="searchKey = ''"
  38. class="iconfont icon-toast_red clear-icon"
  39. ></i>
  40. </div>
  41. <div class="table table-pano" v-show="currentMaterialType === 'pano'">
  42. <div v-show="panoList.length !== 0" class="table-body">
  43. <!-- 测试数据-- {{
  44. panoList.map((i) => {
  45. let t = {};
  46. t["id"] = i.id;
  47. t["sceneTitle"] = i.sceneTitle;
  48. return t;
  49. })
  50. }} -->
  51. <div
  52. class="table-body-row"
  53. v-for="(item) in panoList"
  54. :key="`${item.id}`"
  55. @click="onClickRow"
  56. >
  57. <span class="table-data">
  58. <RadioOrCheckbox
  59. class="checkbox"
  60. :isLightTheme="false"
  61. :isMultiSelection="false"
  62. :isCheckedInitial="select.some((i) => i.id === item.id)"
  63. @change="(v) => selectItem(item, v)"
  64. />
  65. </span>
  66. <span class="table-data">
  67. <div class="list-img">
  68. <img :src="item.icon + $imgsuffix" alt="" />
  69. </div>
  70. </span>
  71. <span class="table-data">
  72. <span class="name ellipsis" v-title="item.sceneTitle">{{
  73. item.sceneTitle
  74. }}</span>
  75. </span>
  76. </div>
  77. <!-- <div class="no-more-data">
  78. - {{$i18n.t('gather.no_more_data')}} -
  79. </div> -->
  80. </div>
  81. <!-- 无数据时的提示 -->
  82. <div v-show="!(panoList.length !== 0)" class="no-data">
  83. <div v-if="latestUsedSearchKey">
  84. <img
  85. :src="require('@/assets/images/default/empty_search_dark.png')"
  86. alt=""
  87. />
  88. <span>{{ $i18n.t(`gather.no_search_result`) }}</span>
  89. </div>
  90. <div v-if="!latestUsedSearchKey">
  91. <img
  92. :src="require('@/assets/images/default/empty_dark.png')"
  93. alt=""
  94. />
  95. <span>{{ $i18n.t(`gather.no_material_result`) }}</span>
  96. </div>
  97. </div>
  98. </div>
  99. <div class="table table-3D" v-show="currentMaterialType === '3D'">
  100. <div v-show="scene3DList.length !== 0" class="table-body">
  101. <div
  102. class="table-body-row"
  103. v-for="(item) in scene3DList"
  104. :key="`${item.id}`"
  105. @click="onClickRow"
  106. >
  107. <span class="table-data">
  108. <RadioOrCheckbox
  109. class="checkbox"
  110. :isLightTheme="false"
  111. :isMultiSelection="false"
  112. :isCheckedInitial="select.some((i) => i.id === item.id)"
  113. @change="(v) => selectItem(item, v)"
  114. />
  115. </span>
  116. <span class="table-data">
  117. <div class="list-img">
  118. <img :src="item.icon + $imgsuffix" alt="" />
  119. </div>
  120. </span>
  121. <span class="table-data">
  122. <span class="name ellipsis" v-title="item.sceneTitle">{{
  123. item.sceneTitle
  124. }}</span>
  125. </span>
  126. </div>
  127. <!-- <div class="no-more-data">
  128. - {{$i18n.t('gather.no_more_data')}} -
  129. </div> -->
  130. </div>
  131. <!-- 无数据时的提示 -->
  132. <div v-show="!(scene3DList.length !== 0)" class="no-data">
  133. <div v-if="latestUsedSearchKey">
  134. <img
  135. :src="require('@/assets/images/default/empty_search_dark.png')"
  136. alt=""
  137. />
  138. <span>{{ $i18n.t(`gather.no_search_result`) }}</span>
  139. </div>
  140. <div v-if="!latestUsedSearchKey">
  141. <img
  142. :src="require('@/assets/images/default/empty_dark.png')"
  143. alt=""
  144. />
  145. <span>{{ $i18n.t(`gather.no_material_result`) }}</span>
  146. </div>
  147. </div>
  148. </div>
  149. <div class="btns">
  150. <div>
  151. <button class="ui-button deepcancel" @click="$emit('cancle')">
  152. {{ $i18n.t(`gather.cancel`) }}
  153. </button>
  154. <button
  155. class="ui-button submit"
  156. :class="{ disable: !select.length }"
  157. @click="onClickComfirm"
  158. >
  159. {{ $i18n.t(`gather.comfirm`) }}
  160. </button>
  161. </div>
  162. </div>
  163. </div>
  164. </template>
  165. <script>
  166. import { mapGetters } from "vuex";
  167. import { debounce } from "@/utils/other.js";
  168. import RadioOrCheckbox from "@/components/shared/RadioOrCheckbox.vue";
  169. export default {
  170. props: {
  171. primaryKey: {
  172. default: "id",
  173. },
  174. isMultiSelection: {
  175. type: Boolean,
  176. default: false,
  177. },
  178. },
  179. components: {
  180. RadioOrCheckbox,
  181. },
  182. computed: {
  183. ...mapGetters(["info"]),
  184. panoList() {
  185. return this.info.scenes
  186. .filter((item) => {
  187. return item.type === "pano";
  188. })
  189. .filter((item) => {
  190. if (this.searchKey) {
  191. return item.sceneTitle.includes(this.searchKey);
  192. } else {
  193. return item;
  194. }
  195. });
  196. },
  197. scene3DList() {
  198. return this.info.scenes
  199. .filter((item) => {
  200. return item.type === "4dkk";
  201. })
  202. .filter((item) => {
  203. if (this.searchKey) {
  204. return item.sceneTitle.includes(this.searchKey);
  205. } else {
  206. return item;
  207. }
  208. });
  209. },
  210. },
  211. watch: {
  212. searchKey: {
  213. handler(val, old) {
  214. if (val && old !== val) {
  215. console.log("need update");
  216. // this.$forceUpdate();
  217. }
  218. },
  219. },
  220. },
  221. data() {
  222. return {
  223. select: [],
  224. isSearchKeyInputActive: false,
  225. searchKey: "", // 搜索关键词
  226. latestUsedSearchKey: "",
  227. currentMaterialType: "pano",
  228. };
  229. },
  230. methods: {
  231. selectItem(item, v) {
  232. item.materialType = this.currentMaterialType; // 三维场景数据没有type字段来表明自己是三维场景。所以统一加一个字段。
  233. if (this.isMultiSelection) {
  234. if (v) {
  235. this.select.push(item);
  236. } else {
  237. const toDeleteIdx = this.select.findIndex((eachSelect) => {
  238. return eachSelect.id === item.id;
  239. });
  240. if (toDeleteIdx >= 0) {
  241. this.select.splice(toDeleteIdx, 1);
  242. }
  243. }
  244. } else {
  245. if (v) {
  246. this.select = [item];
  247. } else {
  248. this.select = [];
  249. }
  250. }
  251. },
  252. onClickRow(e) {
  253. const checkboxNodeList = e.currentTarget.getElementsByClassName(
  254. "selection-click-target"
  255. );
  256. if (checkboxNodeList && checkboxNodeList[0]) {
  257. checkboxNodeList[0].click();
  258. }
  259. },
  260. onClickComfirm: debounce(function () {
  261. this.$emit("submit", this.select);
  262. }, 250),
  263. },
  264. mounted() {},
  265. };
  266. </script>
  267. <style lang="less" scoped>
  268. .ellipsis {
  269. text-overflow: ellipsis;
  270. overflow: hidden;
  271. white-space: nowrap;
  272. width: 100%;
  273. display: inline-block;
  274. }
  275. .table-select {
  276. position: absolute;
  277. z-index: 3;
  278. left: 50%;
  279. top: 50%;
  280. transform: translateX(-50%) translateY(-50%);
  281. width: 600px;
  282. height: 730px;
  283. background: #1a1b1d;
  284. border-radius: 4px;
  285. border: 1px solid #404040;
  286. padding: 26px;
  287. }
  288. .title {
  289. font-size: 18px;
  290. color: rgba(255, 255, 255, 0.6);
  291. }
  292. .close-btn {
  293. display: inline-block;
  294. position: absolute;
  295. top: 26px;
  296. right: 20px;
  297. font-size: 12px;
  298. color: #969799;
  299. cursor: pointer;
  300. padding: 6px;
  301. }
  302. .material-tab {
  303. margin-top: 35px;
  304. > .material-tab-item {
  305. display: inline-block;
  306. margin-right: 20px;
  307. position: relative;
  308. cursor: pointer;
  309. > .text {
  310. font-size: 14px;
  311. font-family: MicrosoftYaHei;
  312. color: rgba(255, 255, 255, 0.6);
  313. &.active {
  314. color: #fff;
  315. }
  316. }
  317. > .bottom-line {
  318. position: absolute;
  319. left: 50%;
  320. transform: translateX(-50%);
  321. bottom: -4px;
  322. width: 16px;
  323. height: 2px;
  324. background: #0076f6;
  325. border-radius: 1px;
  326. }
  327. }
  328. }
  329. .filter {
  330. margin-top: 28px;
  331. width: 100%;
  332. height: 36px;
  333. background: #252526;
  334. border-radius: 2px;
  335. border: 1px solid #404040;
  336. position: relative;
  337. &.active {
  338. border: 1px solid @color;
  339. }
  340. > input {
  341. box-sizing: border-box;
  342. width: calc(100% - 42px);
  343. height: 100%;
  344. border: none;
  345. padding-left: 16px;
  346. background: transparent;
  347. color: #fff;
  348. outline: none;
  349. }
  350. > .search-icon {
  351. position: absolute;
  352. top: 50%;
  353. transform: translateY(-50%);
  354. right: 18px;
  355. color: #404040;
  356. font-size: 20px;
  357. }
  358. > .clear-icon {
  359. position: absolute;
  360. top: 50%;
  361. transform: translateY(-50%);
  362. right: 18px;
  363. color: #404040;
  364. font-size: 20px;
  365. cursor: pointer;
  366. }
  367. }
  368. // @table-height: 440px;
  369. // @table-head-row-height: 40px;
  370. // @table-border-size: 1px;
  371. @table-height: 440px;
  372. @table-head-row-height: 0px;
  373. @table-border-size: 0px;
  374. .table {
  375. margin-top: 20px;
  376. // border: @table-border-size solid #404040;
  377. background: #1a1b1d;
  378. width: 100%;
  379. height: @table-height;
  380. > .table-body {
  381. height: calc(
  382. @table-height - @table-head-row-height - @table-border-size -
  383. @table-border-size
  384. );
  385. overflow: auto;
  386. display: inline-block;
  387. width: 100%;
  388. > .table-body-row {
  389. height: 50px;
  390. // border-bottom: 1px solid #404040;
  391. display: flex;
  392. align-items: center;
  393. &:hover {
  394. background: #252526;
  395. }
  396. > .table-data {
  397. font-size: 14px;
  398. line-height: 50px;
  399. height: 100%;
  400. color: #fff;
  401. > .list-img {
  402. position: relative;
  403. height: 100%;
  404. display: inline-block;
  405. width: 100%;
  406. > img {
  407. position: absolute;
  408. top: 50%;
  409. transform: translateY(-50%);
  410. width: 40px;
  411. height: 40px;
  412. object-fit: cover;
  413. }
  414. }
  415. > .name {
  416. display: inline-block;
  417. height: 100%;
  418. width: 50%;
  419. }
  420. }
  421. }
  422. > .table-body-row:hover {
  423. > .table-data {
  424. > .name {
  425. width: 100%;
  426. }
  427. }
  428. }
  429. .no-more-data {
  430. margin-top: 16px;
  431. margin-bottom: 16px;
  432. text-align: center;
  433. font-size: 12px;
  434. color: #969799;
  435. }
  436. }
  437. > .no-data {
  438. height: calc(
  439. @table-height - @table-head-row-height - @table-border-size -
  440. @table-border-size
  441. );
  442. width: 100%;
  443. position: relative;
  444. > div {
  445. position: absolute;
  446. top: 50%;
  447. left: 50%;
  448. transform: translate(-50%, -50%);
  449. text-align: center;
  450. > img {
  451. width: 116px;
  452. }
  453. > span {
  454. margin-top: 20px;
  455. display: block;
  456. font-size: 14px;
  457. color: rgba(255, 255, 255, 0.6);
  458. }
  459. }
  460. }
  461. }
  462. .table-pano .table-head,
  463. .table-pano .table-data {
  464. &:nth-of-type(1) {
  465. width: 50px;
  466. color: transparent;
  467. }
  468. &:nth-of-type(2) {
  469. width: calc(146px - 50px);
  470. }
  471. &:nth-of-type(3) {
  472. width: calc(100% - 146px);
  473. padding-right: 10px;
  474. }
  475. }
  476. .table-3D .table-head,
  477. .table-3D .table-data {
  478. &:nth-of-type(1) {
  479. width: 50px;
  480. color: transparent;
  481. }
  482. &:nth-of-type(2) {
  483. width: calc(146px - 50px);
  484. }
  485. &:nth-of-type(3) {
  486. width: calc(100% - 146px);
  487. padding-right: 10px;
  488. }
  489. }
  490. .checkbox {
  491. position: relative;
  492. width: 100%;
  493. height: 100%;
  494. input {
  495. width: 20px;
  496. height: 20px;
  497. position: absolute;
  498. left: 50%;
  499. top: 50%;
  500. transform: translate(-50%, -50%);
  501. cursor: pointer;
  502. opacity: 0;
  503. }
  504. .for-outer-circle {
  505. width: 16px;
  506. height: 16px;
  507. position: absolute;
  508. left: 50%;
  509. top: 50%;
  510. transform: translate(-50%, -50%);
  511. border-radius: 50%;
  512. border: 1px solid #404040;
  513. pointer-events: none;
  514. }
  515. .for-inner-circle {
  516. width: 8px;
  517. height: 8px;
  518. position: absolute;
  519. left: 50%;
  520. top: 50%;
  521. transform: translate(-50%, -50%);
  522. border-radius: 50%;
  523. background: #0076f6;
  524. pointer-events: none;
  525. opacity: 0;
  526. }
  527. }
  528. .checkbox > input:checked ~ .for-outer-circle {
  529. border: 1px solid #0076f6;
  530. }
  531. .checkbox > input:checked ~ .for-inner-circle {
  532. opacity: 1;
  533. }
  534. .checkbox > input:disabled {
  535. cursor: not-allowed;
  536. }
  537. .btns {
  538. display: flex;
  539. justify-content: flex-end;
  540. margin-top: 40px;
  541. > div {
  542. .deepcancel {
  543. margin-right: 16px;
  544. }
  545. }
  546. }
  547. </style>