productInfo.vue 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  1. <script >
  2. import langMap from "@/map/langMap";
  3. import {unescapeHtml} from "@/until/unescapeHtml";
  4. import {timestampToTime} from "@/until/time";
  5. // 导入ant-design-vue的组件
  6. import {Tabs, Tag} from 'ant-design-vue'
  7. // 引入样式, 这里使用单独抽离出来的antd样式
  8. import '@/assets/antd_tabs.css'
  9. import imageViewer from "@/components/public/imageViewer.vue";
  10. /**
  11. * emit 事件
  12. * tryBuy 用户尝试购买
  13. */
  14. export default {
  15. name: "productInfo",
  16. components: {
  17. ATabs: Tabs,
  18. ATabPane: Tabs.TabPane,
  19. ATag: Tag,
  20. imageViewer
  21. },
  22. props: {
  23. productId: {
  24. type: Number,
  25. },
  26. lang: {
  27. type: String,
  28. default: langMap.lang.zh
  29. },
  30. unescape: {
  31. type: Boolean,
  32. default: true
  33. },
  34. product: {
  35. type: Object,
  36. default: () => {
  37. return {}
  38. }
  39. }
  40. },
  41. data() {
  42. return {
  43. loading: false,
  44. showImage: '',
  45. productInfo: {
  46. }
  47. }
  48. },
  49. watch: {
  50. // 监听product变化
  51. product(newVal, oldVal) {
  52. this.productInfoChange();
  53. }
  54. },
  55. beforeMount() {
  56. this.productInfoChange();
  57. },
  58. mounted() {
  59. this.$forceUpdate()
  60. },
  61. methods: {
  62. productInfoChange(){
  63. let data = this.product;
  64. if(this.unescape){
  65. data.detail = unescapeHtml(data.detail);
  66. data.overview = unescapeHtml(data.overview);
  67. data.parameter = unescapeHtml(data.parameter);
  68. }
  69. data.add_time = timestampToTime(data.add_time);
  70. console.log(data)
  71. this.$nextTick(()=>{
  72. this.showImage = data.image;
  73. //this.$forceUpdate()
  74. })
  75. // 将产品主图添加至第一个子图中
  76. if( data.sub_images){
  77. console.log(data.image)
  78. data.sub_images.unshift(data.image);
  79. }
  80. this.productInfo = data;
  81. },
  82. parseTags(tagArrStr){
  83. console.log(tagArrStr)
  84. let tags = tagArrStr
  85. if (!tags) {
  86. return [];
  87. }
  88. // 去除空字符串
  89. tags = tags.replace(/\s/g,'')
  90. let tagArr = tags.split(',')
  91. // 标签去重
  92. tagArr = [...new Set(tagArr)]
  93. // 移除空标签
  94. tagArr = tagArr.filter(item => item)
  95. return tagArr
  96. },
  97. getLangText(str) {
  98. return langMap.getText(this.lang, str);
  99. },
  100. /**
  101. * 图片路径处理, 兼容手动上传的图片与后期自动增加的图片
  102. * @param text
  103. * @return {*|string|string}
  104. */
  105. imagePathBabel(text){
  106. let result = text?text.charAt(0) == '/'? text : '/public/'+text : ''
  107. return result
  108. },
  109. changeShowImages(nextIndex){
  110. let ul = this.$refs.imageList;
  111. let ulWidth = ul.offsetWidth;
  112. let parentWidth = ul.parentElement.offsetWidth;
  113. let nextImage = this.productInfo.sub_images[nextIndex]
  114. // 计算 top偏移值
  115. let left = 0;
  116. left = nextIndex * 100 + (10 * nextIndex);
  117. // 偏移位置锁定
  118. if (ulWidth - parentWidth < left) {
  119. left = ulWidth - parentWidth;
  120. }
  121. if (left < 0) {
  122. left = 0;
  123. }
  124. // 移动图片组件
  125. ul.style.left = `-${left}px`
  126. this.showImage = nextImage
  127. },
  128. tryBuy(){
  129. this.$emit('tryBuy')
  130. }
  131. }
  132. }
  133. </script>
  134. <template>
  135. <div class="product_view">
  136. <div class="grid product">
  137. <div class="product_image">
  138. <div class="product-image">
  139. <img class="active"
  140. :src="imagePathBabel(showImage)" ></img>
  141. </div>
  142. <div class="sub_images">
  143. <div class="con-btn prev">
  144. <svg-icon icon-class="prev" />
  145. </div>
  146. <ul class="image-list" ref="imageList" >
  147. <li class="image-item"
  148. v-for="(subImage, index) in productInfo.sub_images"
  149. :key="`${productInfo.proid}-sub-${index}-${subImage}`"
  150. @click="changeShowImages(index)"
  151. >
  152. <img :src="imagePathBabel(subImage)">
  153. </li>
  154. </ul>
  155. <div class="con-btn next">
  156. <svg-icon icon-class="next" />
  157. </div>
  158. </div>
  159. </div>
  160. <div class="product_brief">
  161. <h1>{{ productInfo.name }}</h1>
  162. <div class="tags">
  163. <a-tag
  164. v-for="tag in parseTags(productInfo.seo_key)"
  165. :key="tag"
  166. color="#2db7f5"
  167. >
  168. {{ tag }}
  169. </a-tag>
  170. </div>
  171. <div class="description" v-html="productInfo.overview">
  172. </div>
  173. <button class="add-to-cart" @click="tryBuy">{{ getLangText("联系购买") }}</button>
  174. </div>
  175. </div>
  176. <div class="product_detail">
  177. <!-- 产品介绍-->
  178. <!-- 产品详情-->
  179. <a-tabs default-active-key="detail" >
  180. <a-tab-pane key="detail" :tab="getLangText(`介绍`)">
  181. <div class="description" v-html="productInfo.detail">
  182. </div>
  183. </a-tab-pane>
  184. <a-tab-pane key="parameter" :tab="getLangText(`参数`)">
  185. <div class="description" v-html="productInfo.parameter">
  186. </div>
  187. </a-tab-pane>
  188. </a-tabs>
  189. </div>
  190. </div>
  191. </template>
  192. <style scoped>
  193. h1{
  194. font-size: 2.5rem;
  195. }
  196. .product_view{
  197. width: 100%;
  198. height: 100%;
  199. padding: 0.5rem;
  200. position: relative;
  201. }
  202. .product{
  203. width: 100%;
  204. height: auto;
  205. display: flex;
  206. flex-wrap: wrap;
  207. }
  208. .product .product_image{
  209. width: 40%;
  210. height: auto;
  211. position: relative;
  212. display: flex;
  213. flex-direction: column;
  214. align-items: center;
  215. padding: 1rem;
  216. box-sizing: border-box;
  217. }
  218. .product .product_brief{
  219. width: calc(60% - 10px);
  220. height: auto;
  221. min-height: 510px;
  222. padding: 5px 10px;
  223. box-sizing: border-box;
  224. border-left: 1px solid #e3dddd;
  225. }
  226. .product_image .product-image{
  227. width: calc(100% - 5px);
  228. min-width: 200px;
  229. max-width: 400px;
  230. height: auto;
  231. }
  232. .product_image .product-image img{
  233. animation: fadeImg 1s;
  234. width: 100%;
  235. height: auto;
  236. }
  237. .product_image .sub_images{
  238. width: 100%;
  239. height: 110px;
  240. padding: 5px 0;
  241. box-sizing: border-box;
  242. display: flex;
  243. position: relative;
  244. overflow: hidden;
  245. }
  246. .product_image .sub_images .con-btn{
  247. width: 30px;
  248. height: calc(100% - 10px);
  249. position: absolute;
  250. top: 5px;
  251. display: flex;
  252. justify-content: center;
  253. align-items: center;
  254. cursor: pointer;
  255. }
  256. .product_image .sub_images .con-btn:hover{
  257. background-color: rgba(0,0,0,0.1);
  258. }
  259. .sub_images .prev{
  260. left: 0;
  261. }
  262. .sub_images .next{
  263. right: 0;
  264. }
  265. .sub_images .image-list{
  266. width: auto;
  267. height: 100px;
  268. display: flex;
  269. position: absolute;
  270. top: 0;
  271. }
  272. .sub_images .image-list .image-item{
  273. width: 100px;
  274. margin-right: 10px;
  275. height: 100%;
  276. }
  277. .sub_images .image-list .image-item img{
  278. object-fit: cover;
  279. width: 100%;
  280. height: 100%;
  281. }
  282. .tags{
  283. width: 100%;
  284. display: flex;
  285. flex-wrap: wrap;
  286. justify-content: flex-start;
  287. align-items: center;
  288. padding: 5px;
  289. box-sizing: border-box;
  290. }
  291. .description {
  292. padding: 1rem;
  293. }
  294. .add-to-cart{
  295. position: relative;
  296. display: inline-block;
  297. background: #3e3e3f;
  298. color: #fff;
  299. border: none;
  300. border-radius: 0;
  301. padding: 1rem 2.5rem;
  302. font-size: 1rem;
  303. text-transform: uppercase;
  304. cursor: pointer;
  305. transform: translateZ(0);
  306. transition: color 0.3s ease;
  307. letter-spacing: 0.0625rem;
  308. }
  309. .add-to-cart:hover::before {
  310. transform: scaleX(1);
  311. }
  312. .add-to-cart::before {
  313. position: absolute;
  314. content: "";
  315. z-index: -1;
  316. top: 0;
  317. left: 0;
  318. right: 0;
  319. bottom: 0;
  320. background: #565657;
  321. transform: scaleX(0);
  322. transform-origin: 0 50%;
  323. transition: transform 0.3s ease-out;
  324. }
  325. @keyframes fadeImg {
  326. from {
  327. opacity: 0;
  328. }
  329. to {
  330. opacity: 1;
  331. }
  332. }
  333. </style>
  334. <style>
  335. /** table style */
  336. .table {
  337. background: white;
  338. border-radius:3px;
  339. border-collapse: collapse;
  340. padding:5px;
  341. width: auto;
  342. box-shadow: 0 5px 10px rgba(0, 0, 0, 0.1);
  343. animation: float 5s infinite;
  344. }
  345. th {
  346. color:#D5DDE5;;
  347. background:#1b1e24;
  348. border-bottom:4px solid #9ea7af;
  349. border-right: 1px solid #343a45;
  350. font-size:23px;
  351. font-weight: 100;
  352. padding:24px;
  353. text-align:left;
  354. text-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
  355. vertical-align:middle;
  356. }
  357. th:first-child {
  358. border-top-left-radius:3px;
  359. }
  360. th:last-child {
  361. border-top-right-radius:3px;
  362. border-right:none;
  363. }
  364. tr {
  365. border-top: 1px solid #C1C3D1;
  366. border-bottom-: 1px solid #C1C3D1;
  367. color:#666B85;
  368. font-size:16px;
  369. font-weight:normal;
  370. text-shadow: 0 1px 1px rgba(256, 256, 256, 0.1);
  371. }
  372. tr:hover td {
  373. background:#4E5066;
  374. color:#FFFFFF;
  375. border-top: 1px solid #22262e;
  376. }
  377. tr:first-child {
  378. border-top:none;
  379. }
  380. tr:last-child {
  381. border-bottom:none;
  382. }
  383. tr:nth-child(odd) td {
  384. background:#EBEBEB;
  385. }
  386. tr:nth-child(odd):hover td {
  387. background:#4E5066;
  388. }
  389. tr:last-child td:first-child {
  390. border-bottom-left-radius:3px;
  391. }
  392. tr:last-child td:last-child {
  393. border-bottom-right-radius:3px;
  394. }
  395. td {
  396. background:#FFFFFF;
  397. padding:20px;
  398. text-align:left;
  399. vertical-align:middle;
  400. font-weight:300;
  401. font-size:18px;
  402. text-shadow: -1px -1px 1px rgba(0, 0, 0, 0.1);
  403. border-right: 1px solid #C1C3D1;
  404. }
  405. td:last-child {
  406. border-right: 0px;
  407. }
  408. th.text-left {
  409. text-align: left;
  410. }
  411. th.text-center {
  412. text-align: center;
  413. }
  414. th.text-right {
  415. text-align: right;
  416. }
  417. td.text-left {
  418. text-align: left;
  419. }
  420. td.text-center {
  421. text-align: center;
  422. }
  423. td.text-right {
  424. text-align: right;
  425. }
  426. </style>