app-goods.vue 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140
  1. <template>
  2. <view :style="marginStyle" ref="goods" v-if="goods">
  3. <view class="app-goods" :class="appGoodsClass" :style="appGoodsStyle">
  4. <slot name="extra"></slot>
  5. <!-- 商品角标 -->
  6. <image @click="toDetail" v-if="showGoodsTag" :src="goodsTagPicUrl" :style="{'border-top-left-radius': c_border_top + 'rpx'}" class="goods-tag"></image>
  7. <!-- 套餐组合商品图 -->
  8. <view @click="toDetail" v-if="goods.composition_id > 0"
  9. :style="compositionStyle">
  10. <app-diy-composition-image
  11. :mode="fill === 1 ? 'aspectFill' : 'aspectFit'"
  12. :image-list="goods.cover_pic_list"></app-diy-composition-image>
  13. </view>
  14. <!-- 商品图 -->
  15. <view @click="toDetail" v-else>
  16. <app-image :borderRadius="coverRadius" :img-src="goods.cover_pic?goods.cover_pic:goods.picUrl" :width="listStyle == -1 ? coverHeight : '100%'" :height="coverHeight" :mode="fill === 1 ? 'aspectFill' : 'aspectFit'"></app-image>
  17. </view>
  18. <view @click="toDetail" :style="coverStyle" class="out-dialog goods-cover" v-if="(goods.goods_num == 0 || goods.goods_stock == 0) && appSetting.is_show_stock == '1'">
  19. <image :style="coverStyle" :src="appSetting.is_use_stock == '1' ? appImg.plugins_out : appSetting.sell_out_pic"></image>
  20. </view>
  21. <!-- 一行一个活动倒计时 -->
  22. <view @click="toDetail" class="activity-info" :style="{'background-color': themeColor.background}" v-if="(sec > 0 || min > 0 || hour > 0) && listStyle == 1">
  23. <image :src="activityImage"></image>
  24. <view class="countdown dir-right-nowrap cross-center">
  25. <view class="timing">{{sec}}</view>
  26. <view>:</view>
  27. <view class="timing">{{min}}</view>
  28. <view>:</view>
  29. <view class="timing">{{hour}}</view>
  30. <view class="title">距结束</view>
  31. </view>
  32. </view>
  33. <!-- 活动倒计时 -->
  34. <view @click="toDetail" class="countdown-info main-between cross-center" :class="listStyle == 2 ? 'big':'small'" v-if="(sec > 0 || min > 0 || hour > 0) && listStyle != 1 && listStyle != -1">
  35. <view>距结束</view>
  36. <view class="countdown dir-right-nowrap cross-center">
  37. <view class="timing">{{sec}}</view>
  38. <view>:</view>
  39. <view class="timing">{{min}}</view>
  40. <view>:</view>
  41. <view class="timing">{{hour}}</view>
  42. </view>
  43. </view>
  44. <!-- 活动结束图 -->
  45. <image class="end-info" v-if="endImage && listStyle != -1" :style="{'height': endHeight + 'rpx'}" :src="endImage"></image>
  46. <!-- 商品 -->
  47. <view @click="toDetail" class="goods-info dir-top-nowrap main-between box-grow-1" :class="[textStyle == 2 ? 'main-center' :'',showTag && pluginTag ? '' : 'no-tag']" v-if="showGoodsName || isUnderLinePrice || showGoodsPrice">
  48. <view class="goods-top">
  49. <view v-if="showGoodsName" class="goods-name t-omit" :style="{textAlign: textStyle == 2 ? 'center' : 'left'}">
  50. <text v-if="showTag && pluginTag" :style="{'background-color': themeColor.background}" class="tag">{{pluginTag}}</text>
  51. <text>{{goods.name || goods.goods_name}}</text>
  52. </view>
  53. <slot name="name-end"></slot>
  54. <view class="main-between cross-bottom" v-if="listStyle == -1 && goods.sign != 'flash_sale' && !endImage">
  55. <!-- 列表活动倒计时 -->
  56. <view class="list-countdown dir-left-nowrap cross-center" v-if="sec > 0 || min > 0 || hour > 0">
  57. <slot name="timing-before"></slot>
  58. <view>距结束</view>
  59. <view class="timing">{{hour}}</view>
  60. <view>:</view>
  61. <view class="timing">{{min}}</view>
  62. <view>:</view>
  63. <view class="timing">{{sec}}</view>
  64. </view>
  65. <!-- 超级会员卡 -->
  66. <template v-if="goods.vip_card_appoint && goods.vip_card_appoint.discount && ['advance','miaosha'].indexOf(goods.sign) !== -1">
  67. <app-sup-vip :new_icon="true" :discount="goods.vip_card_appoint.discount" :is_vip_card_user="goods.vip_card_appoint.is_vip_card_user" :margin="listSvipMargin"></app-sup-vip>
  68. </template>
  69. </view>
  70. <!-- 列表活动结束 -->
  71. <view class="list-countdown main-between cross-center" v-if="endImage && listStyle == -1">
  72. <view>已结束</view>
  73. <!-- 超级会员卡 -->
  74. <template v-if="goods.vip_card_appoint && goods.vip_card_appoint.discount">
  75. <app-sup-vip :new_icon="true" :discount="goods.vip_card_appoint.discount" :is_vip_card_user="goods.vip_card_appoint.is_vip_card_user" :margin="listSvipMargin"></app-sup-vip>
  76. </template>
  77. </view>
  78. <!-- 额外信息 -->
  79. <view class="goods-extra" v-if="['weekly_buy','pintuan','advance', 'flash_sale', 'pick'].indexOf(goods.sign) !== -1 || extra || (goods.vip_card_appoint && goods.vip_card_appoint.discount && !(listStyle == -1 && ['advance', 'flash_sale','miaosha'].indexOf(goods.sign) !== -1))" :class="[scrollWidth > 208 ? 'center-bottom' : listStyle == 0 || listStyle == 3 || goods.sign == 'flash_sale' ? 'dir-top-nowrap' : 'cross-center', textStyle == 2 ? 'main-center cross-center' :'main-between',listStyle == -1 && goods.sign == 'advance' ? 'small-margin': '']">
  80. <view v-if="extra" class="extra-info">{{extra}}</view>
  81. <view v-else-if="goods.sign == 'weekly_buy' && !no_extra" class="extra-info">{{goods.type_name}}</view>
  82. <view v-else-if="goods.sign == 'pintuan' && !no_extra" class="extra-info">{{goods.people_num}}人团</view>
  83. <view v-else-if="goods.sign == 'advance' && !no_extra" class="sales-info" :class="goods.vip_card_appoint && goods.vip_card_appoint.discount && goods.vip_card_appoint.discount != 0 ? '' : 'all'">
  84. <view :style="{'color': themeColor.color,'border-color': themeColor.border}">定金{{ Number(goods.deposit) }}抵{{ Number(goods.swell_deposit) }}</view>
  85. </view>
  86. <view v-else-if="goods.sign == 'pick' && !no_extra" class="sales-info" :class="goods.vip_card_appoint && goods.vip_card_appoint.discount && goods.vip_card_appoint.discount != 0 ? '' : 'all'">
  87. <view :style="{'color': themeColor.color,'border-color': themeColor.border}">{{ Number(goods.rule_price) }}元任选{{ Number(goods.rule_num) }}{{goods.unit ? goods.unit : goods.goodsWarehouse ? goods.goodsWarehouse.unit : '件'}}</view>
  88. </view>
  89. <view v-else-if="goods.sign == 'flash_sale' && !(listStyle == 0 &&(!isIndex || scrollWidth > 208))" class="sales-progress" :class="showProgressBar ? 'line' : ''">
  90. <view v-if="showProgressBar" class="progress main-center cross-center" :class="listStyle == -1 ? 'short': ''" :style="{'color': themeColor.color,'border-color': themeColor.background}">
  91. <view :style="{'width': rate, 'background-color': themeColor.background}" class="rate"></view>
  92. <view v-if="listStyle != 0 && listStyle != 3" class="main-center cross-center" style="flex-shrink: 0;white-space: nowrap;">
  93. <i class="iconfont">&#xe6e7;</i>
  94. <text style="display: block;" v-if="goods.discount_type === 1">{{goods.min_discount}}折</text>
  95. <text style="display: block;" v-if="goods.discount_type === 2">减{{goods.min_discount}}元</text>
  96. </view>
  97. <view :style="{'background-color': themeColor.background}" v-if="listStyle != 0 && listStyle != 3" class="line"></view>
  98. <view class="t-omit">已抢购{{goods.sales}}{{goods.unit ? goods.unit : goods.goodsWarehouse ? goods.goodsWarehouse.unit : '件'}}</view>
  99. </view>
  100. <view v-if="!showProgressBar || (showProgressBar && (listStyle == 0 || listStyle == 3))" class="show-sale dir-left-nowrap cross-center" :style="{'font-size': !showProgressBar ? '24rpx' : '20rpx','color': themeColor.color, 'margin-top': showProgressBar && (listStyle == 0 || listStyle == 3) ? '10rpx' : '0'}" :class="textStyle == 2 ? 'main-center' : 'dir-left-nowrap'">
  101. <i class="iconfont" :style="{'font-size': !showProgressBar ? '22rpx' : '20rpx'}">&#xe6e7;</i>
  102. <text v-if="goods.discount_type === 1">{{goods.min_discount}}折</text>
  103. <text v-if="goods.discount_type === 2">减{{goods.min_discount}}元</text>
  104. </view>
  105. </view>
  106. <!-- 超级会员卡 -->
  107. <template v-if="goods.vip_card_appoint && goods.vip_card_appoint.discount && !(listStyle == -1 && ['advance', 'flash_sale','miaosha'].indexOf(goods.sign) !== -1)">
  108. <app-sup-vip :discount="goods.vip_card_appoint.discount" :new_icon="true" :is_vip_card_user="goods.vip_card_appoint.is_vip_card_user" :margin="svipMargin"></app-sup-vip>
  109. </template>
  110. </view>
  111. <view class="main-between cross-center" v-if="listStyle == -1 && goods.sign == 'flash_sale'">
  112. <!-- 列表活动倒计时 -->
  113. <view class="list-countdown dir-left-nowrap cross-center" v-if="sec > 0 || min > 0 || hour > 0">
  114. <slot name="timing-before"></slot>
  115. <view>距结束</view>
  116. <view class="timing">{{hour}}</view>
  117. <view>:</view>
  118. <view class="timing">{{min}}</view>
  119. <view>:</view>
  120. <view class="timing">{{sec}}</view>
  121. </view>
  122. <!-- 超级会员卡 -->
  123. <app-sup-vip :discount="goods.vip_card_appoint.discount" :new_icon="true" :is_vip_card_user="goods.vip_card_appoint.is_vip_card_user" v-if="goods.vip_card_appoint && goods.vip_card_appoint.discount" :margin="listSvipMargin"></app-sup-vip>
  124. </view>
  125. </view>
  126. <view class="box-grow-1 cross-bottom" :class="[listStyle == 0 && show_sale ? 'dir-bottom-nowrap':'',textStyle == 2 ? 'main-center' :'main-between']">
  127. <view class="sales" style='width: 100%;' v-if="show_sale && listStyle == 0">{{goods.sales}}</view>
  128. <view class="price-content dir-bottom-nowrap box-grow-1">
  129. <view class="sales" v-if="show_sale && listStyle == -1">{{goods.sales}}</view>
  130. <view v-if="goods.sign == 'flash_sale' && scrollWidth > 208 && showProgressBar" class="sales-progress line bottom">
  131. <view class="progress main-center cross-center" :class="listStyle == -1 ? 'short': ''" :style="{'color': themeColor.color,'border-color': themeColor.background}">
  132. <view :style="{'width': rate, 'background-color': themeColor.background}" class="rate"></view>
  133. <view class="main-center cross-center" style="flex-shrink: 0;white-space: nowrap;">
  134. <i class="iconfont">&#xe6e7;</i>
  135. <text style="display: block" v-if="goods.discount_type === 1">{{goods.min_discount}}折</text>
  136. <text style="display: block" v-if="goods.discount_type === 2">减{{goods.min_discount}}元</text>
  137. </view>
  138. <view class="line" :style="{'background-color': themeColor.background}"></view>
  139. <view class="t-omit">已抢购{{goods.sales}}{{goods.unit ? goods.unit : goods.goodsWarehouse ? goods.goodsWarehouse.unit : '件'}}</view>
  140. </view>
  141. </view>
  142. <view :class="priceFlex">
  143. <view class="goods-price" v-if="priceFlex != 'dir-bottom-nowrap' && showGoodsPrice" :class="listStyle == 0 || listStyle == 3 ? 'small': ''" :style="{'color': themeColor.color, 'margin-right': '10rpx','line-height': '1','margin-top': '8rpx'}">
  144. <text v-if="price_extra" style="margin-right: 5rpx;display: inline-block;vertical-align: top;margin-top: 8rpx;" class="price-float">{{price_extra}}</text>
  145. <text class="price-float">{{price.priceInt > 0 || price.priceInt == 0 ? '¥' :''}}</text>
  146. <text class="price-int" :class="price.priceInt > 0 || price.priceInt == 0 ? '' : 'text'">{{price.priceInt}}</text>
  147. <text class="price-float">{{price.priceFloat}}</text>
  148. </view>
  149. <view class="original-price" :class="listStyle == -1 || (listStyle == 1 && textStyle == 1) ? 'overflow' :''" v-if="isUnderLinePrice && goods.original_price && !(showMemberPrice && showGoodsPrice) && goods.is_negotiable != 1">
  150. ¥{{ goods.original_price }}
  151. </view>
  152. <view class="box-grow-1" style="height: 28rpx;line-height: 1" v-if="showMemberPrice && showGoodsPrice">
  153. <view :style="{'color':themeColor.color,'background-color': themeColor.background_o}" class="goods-member-price">会员价{{goods.level_price == 0 ? ' 免费': '¥' + memberPrice.priceInt + memberPrice.priceFloat}}</view>
  154. </view>
  155. <view class="goods-price overflow" v-if="priceFlex == 'dir-bottom-nowrap' && showGoodsPrice" :class="listStyle == 0 || listStyle == 3 ? 'small': ''" :style="{'color': themeColor.color}">
  156. <template v-if="goods.sign == 'integral_mall'">
  157. <text class="price-int list-style-string">{{Number(goods.integral)}}</text>
  158. <text class="price-string">积分{{goods.price > 0 ? '+':''}}</text>
  159. <text v-if="goods.price > 0" class="price-int list-style-string">{{goods.price}}</text>
  160. <text v-if="goods.price > 0" class="price-string">元</text>
  161. </template>
  162. <template v-else-if="goods.sign == 'step'">
  163. <text class="price-int list-style-string">{{Number(goods.currency)}}</text>
  164. <text class="price-string">{{goods.currency_name ? goods.currency_name : '活力币'}}{{goods.price > 0 ? '+':''}}</text>
  165. <text v-if="goods.price > 0" class="price-int list-style-string">{{goods.price}}</text>
  166. <text v-if="goods.price > 0" class="price-string">元</text>
  167. </template>
  168. <template v-else>
  169. <text class="price-float">{{price.priceInt > 0 || price.priceInt == 0 ? '¥' :''}}</text>
  170. <text class="price-int" :class="price.priceInt > 0 || price.priceInt == 0 ? '' : 'text'">{{price.priceInt}}</text>
  171. <text class="price-float">{{price.priceFloat}}</text>
  172. </template>
  173. </view>
  174. <slot name="price-after"></slot>
  175. </view>
  176. </view>
  177. <!--购物车-->
  178. <view v-if="showBtn" @click.stop="clickBtn" :style="btnStyle" class="buy-btn-class main-center cross-center" :class="listStyle == 1 ? 'big-btn' : ''">{{buyBtnText}}</view>
  179. <!--购物车-->
  180. <view v-if="showBuyBtn && buyBtnImage && textStyle == 1 && goods.is_negotiable != 1" @click.stop="clickBtn" :style="btnImageStyle">{{buyBtnText}}</view>
  181. <view class="sales" v-else-if="show_sale && listStyle > 0">{{goods.sales}}</view>
  182. </view>
  183. </view>
  184. <u-attr
  185. v-if="attrGoods.attrShow || attrGoods.goods"
  186. v-model="attrGoods.attrShow"
  187. :checked="attrGoods.select"
  188. :goods="attrGoods.goods"
  189. :theme="themeColor"
  190. @check="checkClick"
  191. @cart="cartResult"
  192. ></u-attr>
  193. </view>
  194. </view>
  195. </template>
  196. <script>
  197. import {mapState,mapGetters} from "vuex";
  198. import appDiyCompositionImage from '../../page-component/app-diy-goods-list/app-diy-composition-image';
  199. import uAttr from '../../page-component/goods/u-attr.vue';
  200. export default {
  201. name: "app-goods",
  202. components: {
  203. uAttr,
  204. appDiyCompositionImage
  205. },
  206. data() {
  207. return {
  208. attrGoods: {
  209. goods: null,
  210. attrShow: 0,
  211. select: null,
  212. disable: 'disable'
  213. },
  214. rate: 0,
  215. endHeight: 56,
  216. endImage: '',
  217. time: 0,
  218. countdown: null,
  219. hour: '00',
  220. min: '00',
  221. sec: '00',
  222. activityImage: '',
  223. itemWidth: 0,
  224. price: {
  225. priceFloat: '',
  226. priceInt: '',
  227. },
  228. memberPrice: {
  229. priceFloat: '',
  230. priceInt: '',
  231. }
  232. }
  233. },
  234. props: {
  235. price_extra: String, // 价格额外字段
  236. click: { // 自定义点击事件
  237. type: Boolean,
  238. default() {
  239. return false
  240. }
  241. },
  242. click_btn: { // 自定义点击购买按钮事件
  243. type: Boolean,
  244. default() {
  245. return false
  246. }
  247. },
  248. index: Number, // 下标
  249. showTag: { // 显示商品插件名
  250. type: [Number, Boolean],
  251. default() {
  252. return true
  253. }
  254. },
  255. padding: Number, // 边距
  256. extra: String, // 额外信息
  257. theme: Object, // 主题色
  258. goods: Object, // 商品
  259. buttonColor: String, // 按钮颜色
  260. listStyle: { // 列表样式
  261. type: Number,
  262. default() {
  263. return -1
  264. }
  265. },
  266. fill: { // 商品图片显示样式
  267. type: Number,
  268. default() {
  269. return 1
  270. }
  271. },
  272. goodsCoverProportion: { // 商品封面图比例
  273. type: String,
  274. default() {
  275. return '1:1'
  276. }
  277. },
  278. goodsStyle: { // 商品样式
  279. type: Number,
  280. default() {
  281. return -1
  282. }
  283. },
  284. showGoodsName: { // 是否显示商品名
  285. type: [Number, Boolean],
  286. default() {
  287. return true
  288. }
  289. },
  290. showGoodsPrice: { // 是否显示商品价格
  291. type: [Number, Boolean],
  292. default() {
  293. return true
  294. }
  295. },
  296. textStyle: { // 价格文字样式
  297. type: Number,
  298. default() {
  299. return 1
  300. }
  301. },
  302. showBuyBtn: { // 是否显示购买按钮
  303. type: [Number, Boolean],
  304. default() {
  305. return true
  306. }
  307. },
  308. showProgressBar: { // 是否显示进度条
  309. type: [Number, Boolean],
  310. default() {
  311. return false
  312. }
  313. },
  314. buyBtnStyle: { // 购买按钮样式
  315. type: Number,
  316. default() {
  317. return 1
  318. }
  319. },
  320. buyBtnImage: { // 购买按钮图片
  321. type: String,
  322. default() {
  323. return ''
  324. }
  325. },
  326. btnSize: Number, // 购买按钮图片尺寸
  327. buyBtnText: String, // 购买按钮文字
  328. showGoodsTag: { // 显示商品角标
  329. type: [Number, Boolean],
  330. default() {
  331. return false
  332. }
  333. },
  334. goodsTagPicUrl: String, // 商品角标图片
  335. isUnderLinePrice: { // 是否显示划线价
  336. type: [Number, Boolean],
  337. default() {
  338. return false
  339. }
  340. },
  341. no_extra: { // 是否没有额外信息
  342. type: [Number, Boolean],
  343. default() {
  344. return false
  345. }
  346. },
  347. buy: { // 是否激活购买事件
  348. type: [Number, Boolean],
  349. default() {
  350. return false
  351. }
  352. },
  353. c_border_top: Number, // 上圆角尺寸
  354. c_border_bottom: Number, // 下圆角尺寸
  355. scrollWidth: { // 滚动模式下元素宽度
  356. type: Number,
  357. default() {
  358. return 208
  359. }
  360. },
  361. show_time: { // 是否显示倒计时
  362. type: [Number, Boolean],
  363. default() {
  364. return true
  365. }
  366. },
  367. goodsEndColor: { // 商品背景颜色
  368. type: String,
  369. default() {
  370. return "#FFFFFF"
  371. }
  372. },
  373. goodsBorderColor: String, // 商品边框颜色
  374. show_sale: { // 是否显示销量
  375. type: [Number, Boolean],
  376. default() {
  377. return false
  378. }
  379. },
  380. isIndex: { // 是否为首页显示
  381. type: [Number, Boolean],
  382. default() {
  383. return false
  384. }
  385. },
  386. },
  387. created() {
  388. let that = this;
  389. that.activityImage = that.diyImg[that.goods.sign];
  390. if(that.goods.is_negotiable != 1) {
  391. that.price = that.handlePrice(that.price_extra ? that.goods.min_price : that.goods.price ? that.goods.price : that.goods.goods.price);
  392. }else {
  393. that.price = that.handlePrice(that.appSetting.negotiable_text);
  394. }
  395. if(that.showMemberPrice) {
  396. that.memberPrice = that.handlePrice(that.goods.level_price);
  397. }
  398. if(that.goods.sign == 'flash_sale') {
  399. that.rate = that.goods.sales / (+that.goods.sales + +that.goods.goods_stock) * 100 + '%'
  400. }
  401. if(that.goods.end_prepayment_time > 0) {
  402. that.time = that.goods.end_prepayment_time;
  403. }
  404. if(that.goods.end_prepayment_time < 0) {
  405. that.countTime();
  406. }
  407. if(that.time > 0 && !that.isIndex && that.show_time) {
  408. that.countdown = setInterval(function() {
  409. that.getTime(that.time);
  410. }, 1000)
  411. }
  412. },
  413. computed: {
  414. ...mapState({
  415. diyImg: state => state.mallConfig.__wxapp_img.diy,
  416. appImg: state => state.mallConfig.__wxapp_img.mall,
  417. appSetting: state => state.mallConfig.mall.setting,
  418. }),
  419. ...mapGetters('mallConfig', {
  420. getVideo: 'getVideo',
  421. getTheme: 'getTheme',
  422. }),
  423. themeColor() {
  424. return this.theme ? this.theme : this.getTheme
  425. },
  426. showMemberPrice() {
  427. return this.goods.is_level == 1 && this.goods.is_negotiable != 1 && (this.goods.level_price > 0 || this.goods.level_price == 0)
  428. },
  429. showBtn() {
  430. return this.showBuyBtn && this.goods.is_negotiable != 1 && this.textStyle == 1 && this.listStyle != 0 && this.listStyle != 3 && this.buyBtnImage == ''
  431. },
  432. extraMsg() {
  433. return (['weekly_buy','pintuan','advance', 'flash_sale', 'pick'].indexOf(this.goods.sign) !== -1 || this.listStyle != 1) && ['step','integral_mall'].indexOf(this.goods.sign) == -1
  434. },
  435. pluginTag() {
  436. if(this.goods.composition_id > 0) {
  437. return this.goods.tag
  438. }else if(this.goods.sign) {
  439. switch (this.goods.sign) {
  440. case 'pintuan':
  441. return '拼团';
  442. case 'miaosha':
  443. return '秒杀';
  444. case 'pick':
  445. return 'N元任选';
  446. case 'advance':
  447. return '预售';
  448. case 'bargain':
  449. return '砍价';
  450. case 'gift':
  451. return '社交送礼';
  452. case 'flash_sale':
  453. return '限时抢购';
  454. case 'exchange':
  455. return '礼品卡';
  456. case 'wholesale':
  457. return '商品批发';
  458. case 'step':
  459. return '步数宝';
  460. case 'weekly_buy':
  461. return '周期购';
  462. case 'lottery':
  463. return '抽奖';
  464. case 'booking':
  465. return '预约';
  466. }
  467. }else {
  468. return ''
  469. }
  470. },
  471. appGoodsClass() {
  472. let className = ''
  473. if(this.listStyle == -1) {
  474. className += 'list dir-left-nowrap cross-center'
  475. if(this.goods.sign == 'flash-sale' || this.goods.sign == 'advance') {
  476. className += ' long'
  477. }
  478. }else if(this.listStyle == 0) {
  479. className += 'scroll dir-top-nowrap'
  480. }else if(this.listStyle == 3) {
  481. className += 'third dir-top-nowrap'
  482. }else {
  483. className += 'dir-top-nowrap'
  484. }
  485. if(this.isIndex) {
  486. className += ' is-index'
  487. }
  488. return className
  489. },
  490. svipMargin() {
  491. let style = "0 0 0 20rpx"
  492. if((this.listStyle == 0 && this.scrollWidth < 304) || this.listStyle == 3 || this.goods.sign == 'flash_sale' && !this.isIndex) {
  493. style = style.replace('0', '8rpx');
  494. }
  495. if(!(this.textStyle == 2 && !(this.listStyle == 0 && this.scrollWidth < 304) && this.listStyle != 3 && (this.extra || ['weekly_buy','pintuan','advance', 'pick'].indexOf(this.goods.sign) !== -1 ))) {
  496. style = style.replace('20rpx', '0');
  497. }
  498. return style
  499. },
  500. listSvipMargin() {
  501. let style = "0 0 0 20rpx"
  502. if((this.listStyle == 0 && this.scrollWidth < 304) || this.listStyle == 3 || this.goods.sign == 'flash_sale') {
  503. style = style.replace('0', '8rpx');
  504. }
  505. if(!(this.textStyle == 2 && this.extraMsg )) {
  506. style = style.replace('20rpx', '0');
  507. }
  508. return style
  509. },
  510. btnImageStyle() {
  511. let style = `background-color: ${this.themeColor.background};width: ${this.btnSize}rpx;height: ${this.btnSize}rpx;flex-shrink:0;background-image: url(${this.buyBtnImage});background-repeat: no-repeat;background-size: 100% 100%;border: 0;border-radius: 50%;margin-left: 12rpx;`
  512. return style;
  513. },
  514. coverRadius() {
  515. let radius = '';
  516. if(this.isIndex) {
  517. radius = `${this.c_border_top}rpx`
  518. }else {
  519. radius = `${this.c_border_top}rpx ${this.c_border_top}rpx 0 0`
  520. if(this.listStyle == -1) {
  521. radius = `${this.c_border_top}rpx 0 0 ${this.c_border_bottom}rpx`
  522. }
  523. }
  524. return radius;
  525. },
  526. coverHeight() {
  527. let height = 256
  528. if(this.listStyle > 0) {
  529. if(this.goodsCoverProportion == '3-2' && this.listStyle == 1) {
  530. height = `${this.itemWidth / 3 * 2}`
  531. }else {
  532. height = `${this.itemWidth}`
  533. }
  534. }else {
  535. height = ['flash_sale','miaosha'].indexOf(this.goods.sign) !== -1 && this.showTag ? 276 : 256
  536. if(this.goodsStyle == 2) {
  537. height = height - 4;
  538. }
  539. }
  540. if(this.listStyle == 0) {
  541. height = `${this.scrollWidth}`
  542. }
  543. return height + 'rpx';
  544. },
  545. coverStyle() {
  546. let style = `height: ${this.coverHeight > 0 ? this.coverHeight : this.listStyle == 0 ? this.scrollWidth : this.listStyle == -1 ? 256 : this.itemWidth}rpx;border-radius: ${this.coverRadius}`;
  547. return style;
  548. },
  549. priceFlex() {
  550. if(this.price_extra) {
  551. return ''
  552. }else if(((this.listStyle == -1 && (this.goods.sign == 'advance' || this.goods.sign == 'flash_sale')) || (this.listStyle == 1 && this.textStyle == 1) || (this.listStyle == 0 && this.scrollWidth > 208 && !this.isIndex)) && this.goods.sign != 'integral_mall' && this.goods.sign != 'step') {
  553. return 'dir-left-nowrap cross-bottom'
  554. }else {
  555. return 'dir-bottom-nowrap'
  556. }
  557. },
  558. // },
  559. marginStyle() {
  560. // #ifdef MP-ALIPAY || H5
  561. let style = '';
  562. // #endif
  563. // #ifndef MP-ALIPAY || H5
  564. let style = 'height: 100%;';
  565. // #endif
  566. this.itemWidth = 750 - this.padding*2;
  567. if(this.listStyle != 0) {
  568. if(this.listStyle == 2) {
  569. this.itemWidth = (750 - this.padding*2 - 20) / 2;
  570. }else if(this.listStyle == 3) {
  571. this.itemWidth = (750 - this.padding*2 - 32) / 3;
  572. }
  573. if(this.index % 3 == 1 && this.listStyle == 3) {
  574. style += `padding: 20rpx 16rpx 0;width: ${this.itemWidth + 32}rpx;`
  575. }else {
  576. style += `width: ${this.itemWidth}rpx;`
  577. }
  578. }
  579. if(this.index == 0 || (this.index == 1 && this.listStyle != -1 && this.listStyle != 1) || (this.index == 2 && this.listStyle != -1 && this.listStyle != 1 && this.listStyle != 2) || this.listStyle == 0) {
  580. style += `padding-top: 0;`
  581. }else {
  582. style += `padding-top: 20rpx;`
  583. }
  584. return style
  585. },
  586. appGoodsStyle() {
  587. let style = `width: 100%;border-radius: ${this.c_border_top}rpx ${this.c_border_top}rpx ${this.c_border_bottom}rpx ${this.c_border_bottom}rpx;background-color: ${this.goodsEndColor};`;
  588. if(this.listStyle == 0) {
  589. style += `width: ${this.scrollWidth}rpx;`
  590. }
  591. if(this.goodsStyle == 2) {
  592. style += `border: 2rpx solid #CCCCCC;`
  593. }else if(this.goodsBorderColor){
  594. style += `border: 2rpx solid ${this.goodsBorderColor};`
  595. }
  596. if(this.index == 0 && this.listStyle == 0) {
  597. style += `margin-left: 0;`
  598. }
  599. if(this.listStyle == -1 && ['flash_sale','miaosha'].indexOf(this.goods.sign) !== -1) {
  600. style += `height: 276rpx;`
  601. }
  602. return style;
  603. },
  604. btnStyle() {
  605. let style = '';
  606. if(this.buyBtnStyle > 2) {
  607. style += `border-radius: 24rpx;`
  608. }
  609. if(this.buyBtnStyle % 2 == 1) {
  610. style += `background-color: ${this.buttonColor ? this.buttonColor : this.themeColor.color};`
  611. }else if(this.buyBtnStyle % 2 == 0) {
  612. style += `color: ${this.buttonColor};border: 2rpx solid ${this.buttonColor}`
  613. }
  614. return style;
  615. },
  616. compositionStyle() {
  617. let style = `border-top-left-radius: ${this.c_border_top}rpx;overflow: hidden;flex-shrink: 0;`;
  618. if(this.listStyle == -1) {
  619. style += `width: 342rpx;height: 100%;border-bottom-left-radius: ${this.c_border_bottom}rpx;`
  620. }else if(this.listStyle == 1) {
  621. style += `width: 100%;height: 452rpx;border-top-right-radius: ${this.c_border_top}rpx;`
  622. }else if(this.listStyle == 2) {
  623. style += `width: 100%;height: 220rpx;border-top-right-radius: ${this.c_border_top}rpx;`
  624. }
  625. return style;
  626. }
  627. },
  628. destroyed() {
  629. clearTimeout(this.countdown)
  630. },
  631. watch: {
  632. 'attrGoods.attrShow': {
  633. handler(nVal) {
  634. this.$emit('show',nVal)
  635. },
  636. deep: true,
  637. immediate: true
  638. }
  639. },
  640. methods: {
  641. getTime() {
  642. this.time = this.time - 1;
  643. let dd = 0;
  644. let hh = parseInt(this.time / 3600);
  645. let h = this.time % 3600;
  646. let mm = parseInt(h / 60);
  647. let ss = h % 60
  648. this.hour = hh < 10 ? '0' + hh.toString() : hh;
  649. this.min = mm < 10 ? '0' + mm.toString() : mm;
  650. this.sec = ss < 10 ? '0' + ss.toString() : ss;
  651. if(this.time < 1) {
  652. this.countTime();
  653. clearTimeout(this.countdown)
  654. }
  655. },
  656. countTime() {
  657. this.endHeight = 52
  658. if(this.listStyle == 2) {
  659. this.endImage = this.diyImg.small_end;
  660. this.endHeight = 56
  661. }else if(this.listStyle == 3) {
  662. this.endImage = this.diyImg.middle_end;
  663. }else if(this.listStyle == 0) {
  664. this.endImage = this.diyImg.big_end;
  665. }else {
  666. this.endImage = this.diyImg[this.goods.sign + '_end'];
  667. this.endHeight = 80
  668. }
  669. },
  670. route_go(e) {
  671. // #ifndef MP-BAIDU
  672. if (e.video_url && this.getVideo == 1) {
  673. // #ifdef MP
  674. uni.navigateTo({
  675. url: `/pages/goods/video?goods_id=${e.id}&sign=${e.sign}`
  676. });
  677. // #endif
  678. // #ifdef H5
  679. uni.navigateTo({
  680. url: e.page_url
  681. });
  682. // #endif
  683. } else {
  684. uni.navigateTo({
  685. url: e.page_url
  686. });
  687. }
  688. // #endif
  689. // #ifdef MP-BAIDU
  690. uni.navigateTo({
  691. url: e.page_url
  692. });
  693. // #endif
  694. },
  695. clickBtn() {
  696. if(this.buy) {
  697. this.specification(this.goods)
  698. this.$emit('click', this.goods)
  699. }else if(this.click_btn || this.click) {
  700. this.$emit('click', this.goods)
  701. }else {
  702. this.route_go(this.goods);
  703. }
  704. },
  705. toDetail() {
  706. if(this.click) {
  707. this.$emit('click', this.goods)
  708. }else {
  709. this.route_go(this.goods);
  710. }
  711. },
  712. specification(goods) {
  713. this.attrGoods.select = null;
  714. uni.showLoading({
  715. text: '',
  716. mask: true
  717. });
  718. this.$request({
  719. url: this.$api.goods.attr,
  720. data: {
  721. id: goods.id,
  722. mch_id: goods.mch_id
  723. }
  724. }).then(e => {
  725. uni.hideLoading();
  726. if (e.code === 0) {
  727. this.attrGoods.goods = Object.assign(goods, e.data);
  728. this.$emit('show',true)
  729. this.attrGoods.attrShow = true;
  730. } else {
  731. uni.showToast({
  732. title: e.msg,
  733. icon: 'none'
  734. })
  735. }
  736. })
  737. },
  738. checkClick({item}) {
  739. this.attrGoods.select = item;
  740. },
  741. cartResult({checked}) {
  742. this.$emit('cart',checked)
  743. }
  744. }
  745. }
  746. </script>
  747. <style scoped lang="scss">
  748. .app-goods {
  749. background-color: #FFFFFF;
  750. flex-shrink: 0;
  751. font-size: 28rpx;
  752. position: relative;
  753. height: 100%;
  754. .goods-tag {
  755. position: absolute;
  756. top: 0;
  757. left: 0;
  758. width: 64rpx;
  759. height: 64rpx;
  760. z-index: 10;
  761. }
  762. .out-dialog {
  763. background-color: rgba(0,0,0,.5);
  764. position: absolute;
  765. top: 0;
  766. left: 0;
  767. image {
  768. width: 100%;
  769. height: 100%;
  770. }
  771. }
  772. .timing {
  773. font-size: 32rpx;
  774. margin: 0 6rpx;
  775. padding: 0 8rpx;
  776. height: 52rpx;
  777. line-height: 52rpx;
  778. border-radius: 4rpx;
  779. background-color: #333333;
  780. color: #FFFFFF;
  781. font-weight: 500;
  782. }
  783. .countdown-info {
  784. background: linear-gradient(135deg, #FFE3D6 0%, #FFEADA 43%, #FFF0E8 61%, #FFD0B7 100%);
  785. &.big {
  786. height: 56rpx;
  787. font-size: 24rpx;
  788. padding: 0 20rpx;
  789. padding-right: 16rpx;
  790. .timing {
  791. padding: 0 4rpx;
  792. height: 40rpx;
  793. line-height: 40rpx;
  794. margin: 0 4rpx;
  795. font-size: 24rpx;
  796. }
  797. }
  798. &.small {
  799. height: 52rpx;
  800. font-size: 20rpx;
  801. padding: 0 10rpx;
  802. padding-right: 8rpx;
  803. .timing {
  804. padding: 0 2rpx;
  805. height: 32rpx;
  806. line-height: 32rpx;
  807. margin: 0 2rpx;
  808. font-size: 24rpx;
  809. font-weight: 400;
  810. }
  811. }
  812. }
  813. .activity-info {
  814. position: relative;
  815. height: 80rpx;
  816. width: 100%;
  817. image {
  818. width: 100%;
  819. height: 100%;
  820. }
  821. .countdown {
  822. position: absolute;
  823. right: 14rpx;
  824. top: 0;
  825. height: 80rpx;
  826. font-size: 28rpx;
  827. .title {
  828. margin-right: 14rpx;
  829. }
  830. }
  831. }
  832. .end-info {
  833. width: 100%;
  834. display: block;
  835. }
  836. &.list {
  837. width: 100%;
  838. height: 256rpx;
  839. &.long {
  840. height: 276rpx;
  841. }
  842. .goods-cover {
  843. flex-shrink: 0;
  844. height: 100%;
  845. width: 256rpx;
  846. }
  847. .goods-info {
  848. height: 100%;
  849. &.no-tag {
  850. padding: 16rpx 20rpx;
  851. }
  852. }
  853. }
  854. &.scroll {
  855. width: 208rpx;
  856. margin-left: 16rpx;
  857. &.is-index {
  858. margin-left: 20rpx;
  859. .goods-info {
  860. padding: 16rpx 10rpx;
  861. }
  862. }
  863. .goods-info {
  864. padding: 16rpx;
  865. .goods-top {
  866. .goods-extra {
  867. .sales-info {
  868. max-width: 100%;
  869. view {
  870. max-width: 100%;
  871. display: inline-block;
  872. }
  873. }
  874. }
  875. }
  876. }
  877. .goods-cover {
  878. height: 208rpx;
  879. }
  880. }
  881. &.third {
  882. .goods-info {
  883. padding: 16rpx;
  884. .goods-top {
  885. .goods-extra {
  886. .sales-info {
  887. max-width: 100%;
  888. view {
  889. display: inline-block;
  890. }
  891. }
  892. }
  893. }
  894. }
  895. }
  896. .goods-cover {
  897. width: 100%;
  898. display: block;
  899. }
  900. .goods-info {
  901. padding: 20rpx;
  902. &.main-center {
  903. text-align: center;
  904. }
  905. .goods-top {
  906. .list-countdown {
  907. height: 36rpx;
  908. color: #999999;
  909. font-size: 24rpx;
  910. margin-top: 10rpx;
  911. .timing {
  912. height: 36rpx;
  913. font-size: 24rpx;
  914. line-height: 36rpx;
  915. padding: 0 4rpx;
  916. background-color: #F3F3F3;
  917. color: #999999;
  918. font-weight: 400;
  919. }
  920. &+.goods-extra {
  921. padding: 10rpx 0 16rpx;
  922. }
  923. }
  924. .goods-name {
  925. word-break: break-all;
  926. text-align: left;
  927. line-height: 36rpx;
  928. }
  929. .tag {
  930. display: inline-block;
  931. margin-right: 20rpx;
  932. padding: 0 12rpx;
  933. height: 36rpx;
  934. line-height: 36rpx;
  935. border-radius: 8rpx;
  936. color: #FFFFFF;
  937. font-size: 24rpx;
  938. }
  939. .goods-extra {
  940. width: 100%;
  941. padding-top: 16rpx;
  942. font-size: 22rpx;
  943. color: #999999;
  944. &.small-margin{
  945. padding: 10rpx 0;
  946. }
  947. &.dir-top-nowrap {
  948. padding: 6rpx 0;
  949. }
  950. .extra-info {
  951. font-size: 24rpx;
  952. color: #999999;
  953. }
  954. .sales-info {
  955. max-width: 50%;
  956. &.all {
  957. max-width: 100%;
  958. }
  959. view {
  960. color: #FF812D;
  961. height: 30rpx;
  962. line-height: 26rpx;
  963. border-radius: 15rpx;
  964. border: 2rpx solid #FF812D;
  965. padding: 0 8rpx;
  966. font-size: 20rpx;
  967. white-space: nowrap;
  968. overflow: hidden;
  969. text-overflow: ellipsis;
  970. }
  971. }
  972. }
  973. }
  974. .price-content {
  975. .sales {
  976. margin-top: 6rpx;
  977. }
  978. .goods-price {
  979. font-family: Alibaba;
  980. &.overflow {
  981. text-overflow: ellipsis;
  982. overflow: hidden;
  983. white-space: nowrap;
  984. }
  985. .price-int {
  986. font-size: #{40rpx};
  987. font-weight: 500;
  988. white-space: nowrap;
  989. &.text {
  990. font-size: 34rpx;
  991. }
  992. &.list-style-string {
  993. font-size: 36rpx;
  994. }
  995. }
  996. .price-float {
  997. font-size: #{28rpx};
  998. white-space: nowrap;
  999. }
  1000. .price-string {
  1001. white-space: nowrap;
  1002. margin: 0 5rpx;
  1003. font-size: #{28rpx};
  1004. }
  1005. &.small {
  1006. .price-int {
  1007. font-size: #{32rpx};
  1008. &.text {
  1009. font-size: 30rpx;
  1010. }
  1011. &.list-style-string {
  1012. font-size: 28rpx;
  1013. }
  1014. }
  1015. .price-float {
  1016. font-size: #{24rpx};
  1017. }
  1018. .price-string {
  1019. margin: 0 3rpx;
  1020. font-size: #{20rpx};
  1021. }
  1022. }
  1023. }
  1024. .original-price {
  1025. font-size: 24rpx;
  1026. color: #999999;
  1027. text-decoration: line-through;
  1028. &.overflow {
  1029. overflow: hidden;
  1030. text-overflow: ellipsis;
  1031. white-space: nowrap;
  1032. }
  1033. }
  1034. .goods-member-price {
  1035. font-size: 20rpx;
  1036. height: 28rpx;
  1037. line-height: 28rpx;
  1038. padding: 0 10rpx;
  1039. background-color: rgba(255, 69, 68, 0.1);
  1040. margin-top: -3rpx;
  1041. border-radius: 4rpx;
  1042. display: inline-block;
  1043. white-space: nowrap;
  1044. text-overflow: ellipsis;
  1045. overflow: hidden;
  1046. max-width: 100%;
  1047. }
  1048. }
  1049. .sales {
  1050. color: #999999;
  1051. font-size: 24rpx;
  1052. }
  1053. .sales-progress {
  1054. flex-shrink: 0;
  1055. &.line {
  1056. width: 100%;
  1057. }
  1058. &.bottom {
  1059. margin: 20rpx 0;
  1060. }
  1061. i {
  1062. font-size: 22rpx;
  1063. margin-right: 6rpx;
  1064. }
  1065. .show-sale {
  1066. height: 28rpx;
  1067. font-size: 20rpx;
  1068. line-height: 26rpx;
  1069. color: #ff4544;
  1070. font-weight: 600;
  1071. }
  1072. .progress {
  1073. height: 28rpx;
  1074. line-height: 24rpx;
  1075. text-align: center;
  1076. border-radius: 14rpx;
  1077. border: 2rpx solid #ff4544;
  1078. font-size: 20rpx;
  1079. color: #ff4544;
  1080. position: relative;
  1081. &.short {
  1082. width: 284rpx;
  1083. }
  1084. .line {
  1085. height: 16rpx;
  1086. width: 2rpx;
  1087. background-color: #ff4544;
  1088. margin: 0 6rpx;
  1089. }
  1090. >view:first-of-type {
  1091. font-weight: 600;
  1092. }
  1093. .rate {
  1094. position: absolute;
  1095. top: 0;
  1096. left: 0;
  1097. min-width: 26rpx;
  1098. height: 26rpx;
  1099. background: #FF4544;
  1100. border-radius: 13rpx;
  1101. opacity: 0.25;
  1102. }
  1103. }
  1104. }
  1105. .buy-btn-class {
  1106. color: #FFFFFF;
  1107. height: 48rpx;
  1108. width: 120rpx;
  1109. border-radius: 4rpx;
  1110. font-size: 24rpx;
  1111. flex-shrink: 0;
  1112. &.big-btn {
  1113. width: 136rpx;
  1114. height: 52rpx;
  1115. font-size: 28rpx;
  1116. }
  1117. }
  1118. }
  1119. }
  1120. //#ifdef MP-BAIDU || MP-TOUTIAO
  1121. @font-face {
  1122. font-family: 'iconfont'; /* Project id 1819490 */
  1123. src: url('https://at.alicdn.com/t/font_1819490_uvhnc7hoy3.woff2?t=1628318185237') format('woff2'),
  1124. url('https://at.alicdn.com/t/font_1819490_uvhnc7hoy3.woff?t=1628318185237') format('woff'),
  1125. url('https://at.alicdn.com/t/font_1819490_uvhnc7hoy3.ttf?t=1628318185237') format('truetype');
  1126. }
  1127. .iconfont{
  1128. font-family:"iconfont" !important;
  1129. font-size:12px;font-style:normal;
  1130. -webkit-font-smoothing: antialiased;
  1131. -webkit-text-stroke-width: 0.2px;
  1132. -moz-osx-font-smoothing: grayscale;
  1133. }
  1134. //#endif
  1135. </style>