Merge branch 'lili_self'
|  | @ -3,8 +3,8 @@ var BASE = { | ||||||
|      * @description api请求基础路径 |      * @description api请求基础路径 | ||||||
|      */ |      */ | ||||||
|     API_DEV: { |     API_DEV: { | ||||||
|       common: "https://common-api.pickmall.cn", |       common: "http://192.168.0.113:8890", | ||||||
|       buyer: "https://buyer-api.pickmall.cn", |       buyer: "http://192.168.0.113:8898", | ||||||
|       seller: "https://store-api.pickmall.cn", |       seller: "https://store-api.pickmall.cn", | ||||||
|       manager: "https://admin-api.pickmall.cn" |       manager: "https://admin-api.pickmall.cn" | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
|  | @ -212,3 +212,52 @@ export function receiptSelect (params) { | ||||||
|     params |     params | ||||||
|   }); |   }); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * 获取全部配送方式 | ||||||
|  |  */ | ||||||
|  |  export function shippingMethodList(params) { | ||||||
|  |   return request({ | ||||||
|  |     url: `/buyer/trade/carts/shippingMethodList`, | ||||||
|  |     method: Method.GET, | ||||||
|  |     needToken: true, | ||||||
|  |     params: params, | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * 获取全部配送方式 | ||||||
|  |  */ | ||||||
|  |  export function storeAddressList(params) { | ||||||
|  |   return request({ | ||||||
|  |     url: `/buyer/store/address/shippingMethodList`, | ||||||
|  |     method: Method.GET, | ||||||
|  |     needToken: true, | ||||||
|  |     params: params, | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * 设置自提地址ID | ||||||
|  |  * @param addressId | ||||||
|  |  */ | ||||||
|  |  export function setStoreAddressId(storeAddressId,way) { | ||||||
|  |   return request({ | ||||||
|  |     url: `/buyer/trade/carts/storeAddress?storeAddressId=${storeAddressId}&way=${way}`, | ||||||
|  |     method: Method.GET, | ||||||
|  |     needToken: true, | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * 提交配送方式 | ||||||
|  |  * @param params | ||||||
|  |  */ | ||||||
|  |  export function setShipMethod(params) { | ||||||
|  |   return request({ | ||||||
|  |     url: "/buyer/trade/carts/shippingMethod", | ||||||
|  |     method: Method.PUT, | ||||||
|  |     needToken: true, | ||||||
|  |     params, | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -75,3 +75,15 @@ export function applyStatus () { | ||||||
|     method: Method.GET |     method: Method.GET | ||||||
|   }) |   }) | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * 获取自提点信息 | ||||||
|  |  * @param storeId | ||||||
|  |  */ | ||||||
|  |  export function getStoreAddress(storeId,params) { | ||||||
|  |   return request({ | ||||||
|  |     url: `/buyer/store/address/page/${storeId}`, | ||||||
|  |     method: Method.GET, | ||||||
|  |     params | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -8,12 +8,7 @@ | ||||||
|       <div class="shop-nav-container"> |       <div class="shop-nav-container"> | ||||||
|         <Breadcrumb> |         <Breadcrumb> | ||||||
|           <BreadcrumbItem to="/">首页</BreadcrumbItem> |           <BreadcrumbItem to="/">首页</BreadcrumbItem> | ||||||
|           <BreadcrumbItem |           <BreadcrumbItem v-for="(item, index) in categoryBar" :to="goGoodsList(index)" target="_blank" :key="index"> | ||||||
|             v-for="(item, index) in categoryBar" |  | ||||||
|             :to="goGoodsList(index)" |  | ||||||
|             target="_blank" |  | ||||||
|             :key="index" |  | ||||||
|           > |  | ||||||
|             {{ item.name }} |             {{ item.name }} | ||||||
|           </BreadcrumbItem> |           </BreadcrumbItem> | ||||||
|         </Breadcrumb> |         </Breadcrumb> | ||||||
|  | @ -21,13 +16,10 @@ | ||||||
|           <span class="mr_10" v-if="goodsMsg.data"> |           <span class="mr_10" v-if="goodsMsg.data"> | ||||||
|             <router-link :to="'Merchant?id=' + goodsMsg.data.storeId">{{ |             <router-link :to="'Merchant?id=' + goodsMsg.data.storeId">{{ | ||||||
|     goodsMsg.data.storeName |     goodsMsg.data.storeName | ||||||
|             }}</router-link> | }}</router-link> | ||||||
|           </span> |           </span> | ||||||
|           <span @click="collect"> |           <span @click="collect"> | ||||||
|             <Icon |             <Icon type="ios-heart" :color="storeCollected ? '#ed3f14' : '#666'" /> | ||||||
|               type="ios-heart" |  | ||||||
|               :color="storeCollected ? '#ed3f14' : '#666'" |  | ||||||
|             /> |  | ||||||
|             {{ storeCollected ? "已收藏店铺" : "收藏店铺" }} |             {{ storeCollected ? "已收藏店铺" : "收藏店铺" }} | ||||||
|           </span> |           </span> | ||||||
|           <span class="ml_10" @click="IMService()">联系客服</span> |           <span class="ml_10" @click="IMService()">联系客服</span> | ||||||
|  | @ -36,11 +28,7 @@ | ||||||
|     </div> |     </div> | ||||||
| 
 | 
 | ||||||
|     <!-- 商品信息展示 --> |     <!-- 商品信息展示 --> | ||||||
|     <ShowGoods |     <ShowGoods @handleClickSku="targetClickSku" v-if="goodsMsg.data" :detail="goodsMsg"></ShowGoods> | ||||||
|       @handleClickSku="targetClickSku" |  | ||||||
|       v-if="goodsMsg.data" |  | ||||||
|       :detail="goodsMsg" |  | ||||||
|     ></ShowGoods> |  | ||||||
|     <!-- 商品详细展示 --> |     <!-- 商品详细展示 --> | ||||||
|     <ShowGoodsDetail v-if="goodsMsg.data" :detail="goodsMsg"></ShowGoodsDetail> |     <ShowGoodsDetail v-if="goodsMsg.data" :detail="goodsMsg"></ShowGoodsDetail> | ||||||
| 
 | 
 | ||||||
|  | @ -66,14 +54,14 @@ import { getIMDetail } from "@/api/common"; | ||||||
| import Storage from "../plugins/storage"; | import Storage from "../plugins/storage"; | ||||||
| export default { | export default { | ||||||
|   name: "GoodsDetail", |   name: "GoodsDetail", | ||||||
|   beforeRouteEnter(to, from, next) { |   beforeRouteEnter (to, from, next) { | ||||||
|     window.scrollTo(0, 0); |     window.scrollTo(0, 0); | ||||||
|     next(); |     next(); | ||||||
|   }, |   }, | ||||||
|   created() { |   created () { | ||||||
|     this.getGoodsDetail(); |     this.getGoodsDetail(); | ||||||
|   }, |   }, | ||||||
|   data() { |   data () { | ||||||
|     return { |     return { | ||||||
|       goodsMsg: {}, // 商品信息 |       goodsMsg: {}, // 商品信息 | ||||||
|       isLoading: false, // 加载状态 |       isLoading: false, // 加载状态 | ||||||
|  | @ -85,7 +73,7 @@ export default { | ||||||
|   }, |   }, | ||||||
|   methods: { |   methods: { | ||||||
|     // 跳转im客服 |     // 跳转im客服 | ||||||
|     async IMService() { |     async IMService () { | ||||||
|       // 获取访问Token |       // 获取访问Token | ||||||
|       let accessToken = Storage.getItem("accessToken"); |       let accessToken = Storage.getItem("accessToken"); | ||||||
|       await this.getIMDetailMethods(); |       await this.getIMDetailMethods(); | ||||||
|  | @ -98,22 +86,26 @@ export default { | ||||||
|         "?token=" + |         "?token=" + | ||||||
|         accessToken + |         accessToken + | ||||||
|         "&id=" + |         "&id=" + | ||||||
|           this.goodsMsg.data.storeId |         this.goodsMsg.data.storeId + | ||||||
|  |         "&goodsId=" + | ||||||
|  |         this.goodsMsg.data.goodsId + | ||||||
|  |         "&skuId=" + | ||||||
|  |         this.goodsMsg.data.id | ||||||
|       ); |       ); | ||||||
|     }, |     }, | ||||||
|     // 获取im信息 |     // 获取im信息 | ||||||
|     async getIMDetailMethods() { |     async getIMDetailMethods () { | ||||||
|       let res = await getIMDetail(); |       let res = await getIMDetail(); | ||||||
|       if (res.success) { |       if (res.success) { | ||||||
|         this.IMLink = res.result; |         this.IMLink = res.result; | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     // 点击规格 |     // 点击规格 | ||||||
|     targetClickSku(val) { |     targetClickSku (val) { | ||||||
|       this.getGoodsDetail(val); |       this.getGoodsDetail(val); | ||||||
|     }, |     }, | ||||||
|     // 获取商品详情 |     // 获取商品详情 | ||||||
|     getGoodsDetail(val) { |     getGoodsDetail (val) { | ||||||
|       this.isLoading = true; |       this.isLoading = true; | ||||||
|       const params = val || this.$route.query; |       const params = val || this.$route.query; | ||||||
| 
 | 
 | ||||||
|  | @ -179,7 +171,7 @@ export default { | ||||||
|           this.$router.push("/"); |           this.$router.push("/"); | ||||||
|         }); |         }); | ||||||
|     }, |     }, | ||||||
|     goGoodsList(currIndex) { |     goGoodsList (currIndex) { | ||||||
|       // 跳转商品列表 |       // 跳转商品列表 | ||||||
|       const arr = []; |       const arr = []; | ||||||
|       this.categoryBar.forEach((e, index) => { |       this.categoryBar.forEach((e, index) => { | ||||||
|  | @ -189,7 +181,7 @@ export default { | ||||||
|       }); |       }); | ||||||
|       return location.origin + "/goodsList?categoryId=" + arr.toString(); |       return location.origin + "/goodsList?categoryId=" + arr.toString(); | ||||||
|     }, |     }, | ||||||
|     async collect() { |     async collect () { | ||||||
|       // 收藏店铺 |       // 收藏店铺 | ||||||
|       if (this.storeCollected) { |       if (this.storeCollected) { | ||||||
|         let cancel = await cancelCollect("STORE", this.goodsMsg.data.storeId); |         let cancel = await cancelCollect("STORE", this.goodsMsg.data.storeId); | ||||||
|  |  | ||||||
|  | @ -32,6 +32,10 @@ export const orderStatusList = [ | ||||||
|   { |   { | ||||||
|     name: '已取消', |     name: '已取消', | ||||||
|     status: 'CANCELLED' |     status: 'CANCELLED' | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     name: '待提货', | ||||||
|  |     status: 'STAY_PICKED_UP' | ||||||
|   } |   } | ||||||
| ] | ] | ||||||
| // 订单售后状态
 | // 订单售后状态
 | ||||||
|  |  | ||||||
|  | @ -53,7 +53,7 @@ | ||||||
|         ></Step> |         ></Step> | ||||||
|       </Steps> |       </Steps> | ||||||
|     </div> |     </div> | ||||||
|     <div class="order-card"> |     <div class="order-card" v-if="order.order.deliveryMethod == 'LOGISTICS'"> | ||||||
|       <h3>收货人信息</h3> |       <h3>收货人信息</h3> | ||||||
|       <p>收货人:{{ order.order.consigneeName }}</p> |       <p>收货人:{{ order.order.consigneeName }}</p> | ||||||
|       <p>手机号码:{{ order.order.consigneeMobile | secrecyMobile }}</p> |       <p>手机号码:{{ order.order.consigneeMobile | secrecyMobile }}</p> | ||||||
|  | @ -62,6 +62,11 @@ | ||||||
|         {{ order.order.consigneeDetail }} |         {{ order.order.consigneeDetail }} | ||||||
|       </p> |       </p> | ||||||
|     </div> |     </div> | ||||||
|  |     <div class="order-card" v-if="order.order.deliveryMethod == 'SELF_PICK_UP'"> | ||||||
|  |       <h3>自提点信息</h3> | ||||||
|  |       <p>自提点名称:{{ order.order.storeAddressPath }}</p> | ||||||
|  |       <p>联系方式:{{ order.order.storeAddressMobile }}</p> | ||||||
|  |     </div> | ||||||
|     <div class="order-card"> |     <div class="order-card"> | ||||||
|       <h3>付款信息</h3> |       <h3>付款信息</h3> | ||||||
|       <p>支付方式:{{ order.paymentMethodValue }}</p> |       <p>支付方式:{{ order.paymentMethodValue }}</p> | ||||||
|  | @ -70,7 +75,7 @@ | ||||||
|     <div class="order-card" v-if="!order.order.verificationCode"> |     <div class="order-card" v-if="!order.order.verificationCode"> | ||||||
|       <h3>配送信息</h3> |       <h3>配送信息</h3> | ||||||
|       <p>配送方式:{{ order.deliveryMethodValue }}</p> |       <p>配送方式:{{ order.deliveryMethodValue }}</p> | ||||||
|       <p>配送状态:{{ order.deliverStatusValue }}</p> |       <p v-if="order.order.deliveryMethod === 'LOGISTICS'">配送状态:{{ order.deliverStatusValue }}</p> | ||||||
|       <p v-if="logistics"> |       <p v-if="logistics"> | ||||||
|         物流信息:{{ logistics.shipper || "暂无物流信息" }} |         物流信息:{{ logistics.shipper || "暂无物流信息" }} | ||||||
|       </p> |       </p> | ||||||
|  | @ -270,6 +275,9 @@ export default { | ||||||
|         if (res.success) { |         if (res.success) { | ||||||
|           this.order = res.result; |           this.order = res.result; | ||||||
|           this.progressList = res.result.orderLogs; |           this.progressList = res.result.orderLogs; | ||||||
|  |           if (this.order.order.deliveryMethod === 'LOGISTICS') { | ||||||
|  |             this.traces(); | ||||||
|  |           } | ||||||
|         } |         } | ||||||
|       }); |       }); | ||||||
|     }, |     }, | ||||||
|  | @ -336,7 +344,6 @@ export default { | ||||||
|   }, |   }, | ||||||
|   mounted() { |   mounted() { | ||||||
|     this.getDetail(); |     this.getDetail(); | ||||||
|     this.traces(); |  | ||||||
|   }, |   }, | ||||||
| }; | }; | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
|  | @ -9,42 +9,29 @@ | ||||||
|       </div> |       </div> | ||||||
|       <div class="cart-steps"> |       <div class="cart-steps"> | ||||||
|         <span :class="stepIndex == 1 ? 'active' : ''">1.我的购物车</span> |         <span :class="stepIndex == 1 ? 'active' : ''">1.我的购物车</span> | ||||||
|         <Icon |         <Icon :class="stepIndex == 1 ? 'active-arrow' : ''" custom="icomoon icon-next"></Icon> | ||||||
|           :class="stepIndex == 1 ? 'active-arrow' : ''" |  | ||||||
|           custom="icomoon icon-next" |  | ||||||
|         ></Icon> |  | ||||||
|         <span :class="stepIndex == 1 ? 'active' : ''">2.填写订单信息</span> |         <span :class="stepIndex == 1 ? 'active' : ''">2.填写订单信息</span> | ||||||
|         <Icon |         <Icon :class="stepIndex == 1 ? 'active-arrow' : ''" custom="icomoon icon-next"></Icon> | ||||||
|           :class="stepIndex == 1 ? 'active-arrow' : ''" |  | ||||||
|           custom="icomoon icon-next" |  | ||||||
|         ></Icon> |  | ||||||
|         <span :class="stepIndex == 2 ? 'active' : ''">3.成功提交订单</span> |         <span :class="stepIndex == 2 ? 'active' : ''">3.成功提交订单</span> | ||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
|     <Divider /> |     <Divider /> | ||||||
|     <div class="content width_1200"> |     <div class="content width_1200"> | ||||||
|       <!-- 收货地址 --> |       <!-- 收货地址 --> | ||||||
|       <div class="address"> |       <div class="address" v-if="selectedDeliverMethod === 'LOGISTICS'"> | ||||||
|         <div class="card-head"> |         <div class="card-head"> | ||||||
|           <span>收货人信息</span> |           <span>收货人信息</span> | ||||||
|           <span @click="goAddressManage">管理收货人地址</span> |           <span @click="goAddressManage">管理收货人地址</span> | ||||||
|         </div> |         </div> | ||||||
|         <div class="address-manage"> |         <div class="address-manage"> | ||||||
|           <div |           <div class="address-item" v-show="moreAddr ? true : index < 3" | ||||||
|             class="address-item" |             :class="selectedAddress.id === item.id ? 'border-red' : ''" @mouseenter="showEditBtn = index" | ||||||
|             v-show="moreAddr ? true : index < 3" |             @mouseleave="showEditBtn = ''" @click="selectAddress(item)" v-for="(item, index) in addressList" | ||||||
|             :class="selectedAddress.id === item.id ? 'border-red' : ''" |             :key="index"> | ||||||
|             @mouseenter="showEditBtn = index" |  | ||||||
|             @mouseleave="showEditBtn = ''" |  | ||||||
|             @click="selectAddress(item)" |  | ||||||
|             v-for="(item, index) in addressList" |  | ||||||
|             :key="index" |  | ||||||
|           > |  | ||||||
|             <div> |             <div> | ||||||
|               <span>{{ item.name }}</span> |               <span>{{ item.name }}</span> | ||||||
|               <Tag class="ml_10" v-if="item.isDefault" color="red">默认</Tag> |               <Tag class="ml_10" v-if="item.isDefault" color="red">默认</Tag> | ||||||
|               <Tag class="ml_10" v-if="item.alias" color="warning" |               <Tag class="ml_10" v-if="item.alias" color="warning">{{ item.alias }} | ||||||
|                 >{{ item.alias }} |  | ||||||
|               </Tag> |               </Tag> | ||||||
|             </div> |             </div> | ||||||
|             <div>{{ item.mobile }}</div> |             <div>{{ item.mobile }}</div> | ||||||
|  | @ -53,12 +40,7 @@ | ||||||
|             </div> |             </div> | ||||||
|             <div class="edit-btn" v-show="showEditBtn === index"> |             <div class="edit-btn" v-show="showEditBtn === index"> | ||||||
|               <span @click.stop="editAddress(item.id)">修改</span> |               <span @click.stop="editAddress(item.id)">修改</span> | ||||||
|               <span |               <span class="ml_10" v-if="!item.isDefault" @click.stop="delAddress(item)">删除</span> | ||||||
|                 class="ml_10" |  | ||||||
|                 v-if="!item.isDefault" |  | ||||||
|                 @click.stop="delAddress(item)" |  | ||||||
|                 >删除</span |  | ||||||
|               > |  | ||||||
|             </div> |             </div> | ||||||
|             <div class="corner-icon" v-show="selectedAddress.id === item.id"> |             <div class="corner-icon" v-show="selectedAddress.id === item.id"> | ||||||
|               <div></div> |               <div></div> | ||||||
|  | @ -70,49 +52,81 @@ | ||||||
|             <div>添加新地址</div> |             <div>添加新地址</div> | ||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
| 
 |         <div class="more-addr" @click="moreAddr = !moreAddr" v-if="addressList.length > 3"> | ||||||
|         <div |  | ||||||
|           class="more-addr" |  | ||||||
|           @click="moreAddr = !moreAddr" |  | ||||||
|           v-if="addressList.length > 3" |  | ||||||
|         > |  | ||||||
|           {{ moreAddr ? "收起地址" : "更多地址" }} |           {{ moreAddr ? "收起地址" : "更多地址" }} | ||||||
|           <Icon v-show="!moreAddr" type="md-arrow-dropdown" /> |           <Icon v-show="!moreAddr" type="md-arrow-dropdown" /> | ||||||
|           <Icon v-show="moreAddr" type="md-arrow-dropup" /> |           <Icon v-show="moreAddr" type="md-arrow-dropup" /> | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|  | 
 | ||||||
|  |       <div class="address" v-if="selectedDeliverMethod === 'SELF_PICK_UP'"> | ||||||
|  |         <div class="card-head"> | ||||||
|  |           <span>自提点信息</span> | ||||||
|  |         </div> | ||||||
|  |         <div class="address-manage"> | ||||||
|  |           <div class="address-item" v-show="storeMoreAddr ? true : index < 3" | ||||||
|  |             :class="selectedAddress.id === item.id ? 'border-red' : ''" @mouseenter="showEditBtn = index" | ||||||
|  |             @mouseleave="showEditBtn = ''" @click="selectStoreAddress(item)" v-for="(item, index) in storeAddressList" | ||||||
|  |             :key="index"> | ||||||
|  |             <div> | ||||||
|  |               <span>{{ item.addressName }}</span> | ||||||
|  |             </div> | ||||||
|  |             <div>{{ item.mobile }}</div> | ||||||
|  |             <div> | ||||||
|  |               {{ item.address | unitAddress }} {{ item.detail }} | ||||||
|  |             </div> | ||||||
|  |             <div class="corner-icon" v-show="selectedStoreAddress.id === item.id"> | ||||||
|  |               <div></div> | ||||||
|  |               <Icon type="md-checkmark" /> | ||||||
|  |             </div> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |         <div class="more-addr" @click="storeMoreAddr = !storeMoreAddr" v-if="addressList.length > 3"> | ||||||
|  |           {{ storeMoreAddr ? "收起地址" : "更多地址" }} | ||||||
|  |           <Icon v-show="!storeMoreAddr" type="md-arrow-dropdown" /> | ||||||
|  |           <Icon v-show="storeMoreAddr" type="md-arrow-dropup" /> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |       <div> | ||||||
|  |       </div> | ||||||
|  |       <div class="goods-content"> | ||||||
|  |         <div class="card-head mt_20 mb_20"> | ||||||
|  |           <span>配送方式</span> | ||||||
|  |         </div> | ||||||
|  |         <div class="delivery-method"> | ||||||
|  | 
 | ||||||
|  |           <div class="method-item" v-show="moreAddr ? true : index < 3" | ||||||
|  |             :class="selectedDeliverMethod === item.value ? 'border-red' : ''" @mouseenter="showEditBtn = item.value" | ||||||
|  |             @mouseleave="showEditBtn = ''" @click="selectDeliverMethod(item)" v-for="(item, index) in shippingMethod" | ||||||
|  |             :key="index"> | ||||||
|  |             <div>{{ item.label }}</div> | ||||||
|  |             <div class="corner-icon" v-show="selectedDeliverMethod === item.value"> | ||||||
|  |               <div></div> | ||||||
|  |               <Icon type="md-checkmark" /> | ||||||
|  |             </div> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|       <!-- 商品信息 --> |       <!-- 商品信息 --> | ||||||
|       <div class="goods-content"> |       <div class="goods-content"> | ||||||
|         <div class="card-head mt_20 mb_20"> |         <div class="card-head mt_20 mb_20"> | ||||||
|           <span>商品信息</span> |           <span>商品信息</span> | ||||||
|           <span @click="$router.push('/cart')">返回购物车</span> |           <span @click="$router.push('/cart')">返回购物车</span> | ||||||
|         </div> |         </div> | ||||||
|         <div |         <div class="goods-msg" v-for="(shop, shopIndex) in goodsList" :key="shopIndex"> | ||||||
|           class="goods-msg" |  | ||||||
|           v-for="(shop, shopIndex) in goodsList" |  | ||||||
|           :key="shopIndex" |  | ||||||
|         > |  | ||||||
|           <div v-if="shop.checked"> |           <div v-if="shop.checked"> | ||||||
|             <div class="shop-name"> |             <div class="shop-name"> | ||||||
|               <span> |               <span> | ||||||
|                 <span class="hover-color" @click="goShopPage(shop.storeId)">{{ |                 <span class="hover-color" @click="goShopPage(shop.storeId)">{{ | ||||||
|                     shop.storeName |                     shop.storeName | ||||||
|                 }}</span |                 }}</span>   | ||||||
|                 >   |  | ||||||
|               </span> |               </span> | ||||||
|             </div> |             </div> | ||||||
|             <div class="goods-list"> |             <div class="goods-list"> | ||||||
|               <div |               <div class="goods-item" v-for="(goods, goodsIndex) in shop.checkedSkuList" :key="goodsIndex"> | ||||||
|                 class="goods-item" |                 <span class="hover-color" @click=" | ||||||
|                 v-for="(goods, goodsIndex) in shop.checkedSkuList" |  | ||||||
|                 :key="goodsIndex" |  | ||||||
|               > |  | ||||||
|                 <span |  | ||||||
|                   class="hover-color" |  | ||||||
|                   @click=" |  | ||||||
|                   goGoodsDetail(goods.goodsSku.id, goods.goodsSku.goodsId) |                   goGoodsDetail(goods.goodsSku.id, goods.goodsSku.goodsId) | ||||||
|                   " |                 "> | ||||||
|                 > |  | ||||||
|                   <img :src="goods.goodsSku.thumbnail" alt="" /> |                   <img :src="goods.goodsSku.thumbnail" alt="" /> | ||||||
|                   <span style="vertical-align: top">{{ |                   <span style="vertical-align: top">{{ | ||||||
|                       goods.goodsSku.goodsName |                       goods.goodsSku.goodsName | ||||||
|  | @ -129,16 +143,8 @@ | ||||||
|               </div> |               </div> | ||||||
|             </div> |             </div> | ||||||
|             <div class="order-mark"> |             <div class="order-mark"> | ||||||
|               <Input |               <Input type="textarea" maxlength="60" v-model="shop.remark" show-word-limit placeholder="订单备注" /> | ||||||
|                 type="textarea" |               <span style="font-size: 12px; color: #999">提示:请勿填写有关支付、收货、发票方面的信息</span> | ||||||
|                 maxlength="60" |  | ||||||
|                 v-model="shop.remark" |  | ||||||
|                 show-word-limit |  | ||||||
|                 placeholder="订单备注" |  | ||||||
|               /> |  | ||||||
|               <span style="font-size: 12px; color: #999" |  | ||||||
|                 >提示:请勿填写有关支付、收货、发票方面的信息</span |  | ||||||
|               > |  | ||||||
|             </div> |             </div> | ||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
|  | @ -146,13 +152,9 @@ | ||||||
|       <!-- 发票信息 --> |       <!-- 发票信息 --> | ||||||
|       <div class="invoice"> |       <div class="invoice"> | ||||||
|         <div class="card-head mt_20 mb_20"> |         <div class="card-head mt_20 mb_20"> | ||||||
|           <span class="relative" |           <span class="relative">发票信息<span class="inv-tips"> | ||||||
|             >发票信息<span class="inv-tips"> |               <Icon type="ios-alert-outline" />开企业抬头发票须填写纳税人识别号,以免影响报销 | ||||||
|               <Icon |             </span></span> | ||||||
|                 type="ios-alert-outline" |  | ||||||
|               />开企业抬头发票须填写纳税人识别号,以免影响报销 |  | ||||||
|             </span></span |  | ||||||
|           > |  | ||||||
|         </div> |         </div> | ||||||
|         <div class="inovice-content"> |         <div class="inovice-content"> | ||||||
|           <span>{{ invoiceData.receiptTitle }}</span> |           <span>{{ invoiceData.receiptTitle }}</span> | ||||||
|  | @ -167,47 +169,24 @@ | ||||||
|         </div> |         </div> | ||||||
|         <div v-if="couponList.length === 0">无可用优惠券</div> |         <div v-if="couponList.length === 0">无可用优惠券</div> | ||||||
|         <ul v-else class="coupon-list"> |         <ul v-else class="coupon-list"> | ||||||
|           <li |           <li v-for="(item, index) in couponList" class="coupon-item" :key="index"> | ||||||
|             v-for="(item, index) in couponList" |  | ||||||
|             class="coupon-item" |  | ||||||
|             :key="index" |  | ||||||
|           > |  | ||||||
|             <div class="c-left"> |             <div class="c-left"> | ||||||
|               <div> |               <div> | ||||||
|                 <span |                 <span v-if="item.couponType === 'PRICE'" class="fontsize_12 global_color">¥<span class="price">{{ | ||||||
|                   v-if="item.couponType === 'PRICE'" |  | ||||||
|                   class="fontsize_12 global_color" |  | ||||||
|                   >¥<span class="price">{{ |  | ||||||
|                     item.price | unitPrice |                     item.price | unitPrice | ||||||
|                   }}</span></span |                 }}</span></span> | ||||||
|                 > |                 <span v-if="item.couponType === 'DISCOUNT'" class="fontsize_12 global_color"><span class="price">{{ | ||||||
|                 <span |                     item.discount | ||||||
|                   v-if="item.couponType === 'DISCOUNT'" |                 }}</span>折</span> | ||||||
|                   class="fontsize_12 global_color" |                 <span class="describe">满{{ item.consumeThreshold }}元可用</span> | ||||||
|                   ><span class="price">{{ item.discount }}</span |  | ||||||
|                   >折</span |  | ||||||
|                 > |  | ||||||
|                 <span class="describe" |  | ||||||
|                   >满{{ item.consumeThreshold }}元可用</span |  | ||||||
|                 > |  | ||||||
|               </div> |               </div> | ||||||
|               <p>使用范围:{{ useScope(item.scopeType) }}</p> |               <p>使用范围:{{ useScope(item.scopeType) }}</p> | ||||||
|               <p>有效期:{{ item.endTime }}</p> |               <p>有效期:{{ item.endTime }}</p> | ||||||
|             </div> |             </div> | ||||||
|             <img |             <img class="used" v-if="usedCouponId.includes(item.id)" src="../../assets/images/geted.png" alt="" /> | ||||||
|               class="used" |  | ||||||
|               v-if="usedCouponId.includes(item.id)" |  | ||||||
|               src="../../assets/images/geted.png" |  | ||||||
|               alt="" |  | ||||||
|             /> |  | ||||||
|             <b></b> |             <b></b> | ||||||
|             <a class="c-right" @click="useCoupon(item.id, true)">立即使用</a> |             <a class="c-right" @click="useCoupon(item.id, true)">立即使用</a> | ||||||
|             <a |             <a class="c-right" v-if="usedCouponId.includes(item.id)" @click="useCoupon(item.id, false)">放弃优惠</a> | ||||||
|               class="c-right" |  | ||||||
|               v-if="usedCouponId.includes(item.id)" |  | ||||||
|               @click="useCoupon(item.id, false)" |  | ||||||
|               >放弃优惠</a |  | ||||||
|             > |  | ||||||
|             <i class="circle-top"></i> |             <i class="circle-top"></i> | ||||||
|             <i class="circle-bottom"></i> |             <i class="circle-bottom"></i> | ||||||
|           </li> |           </li> | ||||||
|  | @ -216,29 +195,23 @@ | ||||||
|       <!-- 订单价格 --> |       <!-- 订单价格 --> | ||||||
|       <div class="order-price"> |       <div class="order-price"> | ||||||
|         <div> |         <div> | ||||||
|           <span>{{ totalNum }}件商品,总商品金额:</span |           <span>{{ totalNum }}件商品,总商品金额:</span><span>{{ priceDetailDTO.goodsPrice | unitPrice("¥") }}</span> | ||||||
|           ><span>{{ priceDetailDTO.goodsPrice | unitPrice("¥") }}</span> |  | ||||||
|         </div> |         </div> | ||||||
|         <div v-if="priceDetailDTO.freightPrice > 0"> |         <div v-if="priceDetailDTO.freightPrice > 0"> | ||||||
|           <span>运费:</span |           <span>运费:</span><span>{{ priceDetailDTO.freightPrice | unitPrice("¥") }}</span> | ||||||
|           ><span>{{ priceDetailDTO.freightPrice | unitPrice("¥") }}</span> |  | ||||||
|         </div> |         </div> | ||||||
|         <div v-if="priceDetailDTO.discountPrice > 0"> |         <div v-if="priceDetailDTO.discountPrice > 0"> | ||||||
|           <span>优惠金额:</span |           <span>优惠金额:</span><span>-{{ priceDetailDTO.discountPrice | unitPrice("¥") }}</span> | ||||||
|           ><span>-{{ priceDetailDTO.discountPrice | unitPrice("¥") }}</span> |  | ||||||
|         </div> |         </div> | ||||||
|         <div v-if="priceDetailDTO.couponPrice > 0"> |         <div v-if="priceDetailDTO.couponPrice > 0"> | ||||||
|           <span>优惠券金额:</span |           <span>优惠券金额:</span><span>-{{ priceDetailDTO.couponPrice | unitPrice("¥") }}</span> | ||||||
|           ><span>-{{ priceDetailDTO.couponPrice | unitPrice("¥") }}</span> |  | ||||||
|         </div> |         </div> | ||||||
| 
 | 
 | ||||||
|         <div v-if="$route.query.way === 'POINTS'"> |         <div v-if="$route.query.way === 'POINTS'"> | ||||||
|           <span>应付积分:</span |           <span>应付积分:</span><span class="actrual-price">{{ priceDetailDTO.payPoint }}</span> | ||||||
|           ><span class="actrual-price">{{ priceDetailDTO.payPoint }}</span> |  | ||||||
|         </div> |         </div> | ||||||
|         <div v-else> |         <div v-else> | ||||||
|           <span>应付金额:</span |           <span>应付金额:</span><span class="actrual-price">{{ | ||||||
|           ><span class="actrual-price">{{ |  | ||||||
|               priceDetailDTO.flowPrice | unitPrice("¥") |               priceDetailDTO.flowPrice | unitPrice("¥") | ||||||
|           }}</span> |           }}</span> | ||||||
|         </div> |         </div> | ||||||
|  | @ -256,17 +229,9 @@ | ||||||
|     </div> |     </div> | ||||||
|     <BaseFooter></BaseFooter> |     <BaseFooter></BaseFooter> | ||||||
|     <!-- 添加发票模态框 --> |     <!-- 添加发票模态框 --> | ||||||
|     <invoice-modal |     <invoice-modal ref="invModal" :invoiceData="invoiceData" @change="getInvMsg" /> | ||||||
|       ref="invModal" |  | ||||||
|       :invoiceData="invoiceData" |  | ||||||
|       @change="getInvMsg" |  | ||||||
|     /> |  | ||||||
|     <!-- 选择地址模态框 --> |     <!-- 选择地址模态框 --> | ||||||
|     <address-manage |     <address-manage ref="address" :id="addrId" @change="addrChange"></address-manage> | ||||||
|       ref="address" |  | ||||||
|       :id="addrId" |  | ||||||
|       @change="addrChange" |  | ||||||
|     ></address-manage> |  | ||||||
|   </div> |   </div> | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
|  | @ -279,8 +244,12 @@ import { | ||||||
|   createTrade, |   createTrade, | ||||||
|   selectAddr, |   selectAddr, | ||||||
|   selectCoupon, |   selectCoupon, | ||||||
|  |   setShipMethod, | ||||||
|  |   setStoreAddressId, | ||||||
|  |   shippingMethodList, | ||||||
|   couponNum, |   couponNum, | ||||||
| } from "@/api/cart"; | } from "@/api/cart"; | ||||||
|  | import { getStoreAddress } from "@/api/shopentry.js" | ||||||
| import { canUseCouponList } from "@/api/member.js"; | import { canUseCouponList } from "@/api/member.js"; | ||||||
| 
 | 
 | ||||||
| export default { | export default { | ||||||
|  | @ -288,15 +257,35 @@ export default { | ||||||
|   components: { invoiceModal, addressManage }, |   components: { invoiceModal, addressManage }, | ||||||
|   data() { |   data() { | ||||||
|     return { |     return { | ||||||
|  |       selectedStoreAddress: 'm', | ||||||
|  |       selectMethod: '', | ||||||
|       stepIndex: 1, // 顶部步骤条状态 |       stepIndex: 1, // 顶部步骤条状态 | ||||||
|       invoiceAvailable: false, // 发票编辑按钮 |       invoiceAvailable: false, // 发票编辑按钮 | ||||||
|       showEditBtn: "", // 鼠标移入显示编辑按钮 |       showEditBtn: "", // 鼠标移入显示编辑按钮 | ||||||
|       orderMark: "", // 订单备注 |       orderMark: "", // 订单备注 | ||||||
|  |       storeMoreAddr: false, | ||||||
|       invoiceData: { |       invoiceData: { | ||||||
|         // 发票数据 |         // 发票数据 | ||||||
|         receiptTitle: "个人", |         receiptTitle: "个人", | ||||||
|         receiptContent: "不开发票", |         receiptContent: "不开发票", | ||||||
|       }, |       }, | ||||||
|  |       searchForm: { | ||||||
|  |         pageNumber: 1, | ||||||
|  |         pageSize: 100 | ||||||
|  |       }, | ||||||
|  |       shippingMethod: [], | ||||||
|  |       storeAddressList: [], | ||||||
|  |       shippingWay: [ | ||||||
|  |         { | ||||||
|  |           value: "LOGISTICS", | ||||||
|  |           label: "物流", | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           value: "SELF_PICK_UP", | ||||||
|  |           label: "自提", | ||||||
|  |         }, | ||||||
|  |       ], | ||||||
|  |       selectedDeliverMethod: 'LOGISTICS', | ||||||
|       addressList: [], // 地址列表 |       addressList: [], // 地址列表 | ||||||
|       selectedAddress: {}, // 所选地址 |       selectedAddress: {}, // 所选地址 | ||||||
|       goodsList: [], // 商品列表 |       goodsList: [], // 商品列表 | ||||||
|  | @ -308,6 +297,7 @@ export default { | ||||||
|       couponList: [], // 可用优惠券列表 |       couponList: [], // 可用优惠券列表 | ||||||
|       usedCouponId: [], // 已使用优惠券id |       usedCouponId: [], // 已使用优惠券id | ||||||
|       selectedCoupon: {}, // 已选优惠券对象 |       selectedCoupon: {}, // 已选优惠券对象 | ||||||
|  |       storeId: '', //店铺Id | ||||||
|     }; |     }; | ||||||
|   }, |   }, | ||||||
|   mounted() { |   mounted() { | ||||||
|  | @ -317,6 +307,7 @@ export default { | ||||||
|     // 初始化数据 |     // 初始化数据 | ||||||
|     init() { |     init() { | ||||||
|       this.getGoodsDetail(); |       this.getGoodsDetail(); | ||||||
|  |       this.getDistribution(); | ||||||
|     }, |     }, | ||||||
|     goAddressManage() { |     goAddressManage() { | ||||||
|       // 跳转地址管理页面 |       // 跳转地址管理页面 | ||||||
|  | @ -324,7 +315,7 @@ export default { | ||||||
|     }, |     }, | ||||||
|     getAddress() { |     getAddress() { | ||||||
|       // 获取收货地址列表 |       // 获取收货地址列表 | ||||||
|       memberAddress().then((res) => { |       memberAddress(this.searchForm).then((res) => { | ||||||
|         if (res.success) { |         if (res.success) { | ||||||
|           this.addressList = res.result.records; |           this.addressList = res.result.records; | ||||||
|           this.addressList.forEach((e, index) => { |           this.addressList.forEach((e, index) => { | ||||||
|  | @ -335,6 +326,38 @@ export default { | ||||||
|         } |         } | ||||||
|       }); |       }); | ||||||
|     }, |     }, | ||||||
|  |     // 获取配送方式列表 | ||||||
|  |     async getDistribution() { | ||||||
|  |       let shopRes = await shippingMethodList({ way: this.$route.query.way }); | ||||||
|  |       let shopList; | ||||||
|  |       if (shopRes.success) { | ||||||
|  |         shopList = shopRes.result; | ||||||
|  |         let way = []; | ||||||
|  |         console.log(shopList) | ||||||
|  |         this.shippingWay.forEach((item) => { | ||||||
|  |           shopList.forEach((child) => { | ||||||
|  |             if (item.value == child) { | ||||||
|  |               way.push(item); | ||||||
|  |             } | ||||||
|  |           }); | ||||||
|  |         }); | ||||||
|  |         this.shippingMethod = way; | ||||||
|  |         console.log(this.shippingMethod) | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     async getStoreAddressList() { | ||||||
|  |       getStoreAddress(this.storeId,this.searchForm).then(res => { | ||||||
|  |         if (res.success) { | ||||||
|  |           this.storeAddressList = res.result.records | ||||||
|  |           this.storeAddressList.forEach((e, index) => { | ||||||
|  |             if (e.id === this.selectedAddress.id && index > 2) { | ||||||
|  |               this.storeMoreAddr = true; | ||||||
|  |             } | ||||||
|  |           }); | ||||||
|  |         } | ||||||
|  |       }) | ||||||
|  |     }, | ||||||
|     getGoodsDetail() { |     getGoodsDetail() { | ||||||
|       // 订单商品详情 |       // 订单商品详情 | ||||||
|       this.$Spin.show(); |       this.$Spin.show(); | ||||||
|  | @ -364,7 +387,7 @@ export default { | ||||||
|             this.goodsList = res.result.cartList; |             this.goodsList = res.result.cartList; | ||||||
|             this.priceDetailDTO = res.result.priceDetailDTO; |             this.priceDetailDTO = res.result.priceDetailDTO; | ||||||
|             this.skuList = res.result.skuList; |             this.skuList = res.result.skuList; | ||||||
| 
 |             this.storeId = this.goodsList[0].storeId | ||||||
|             if (res.result.receiptVO) { |             if (res.result.receiptVO) { | ||||||
|               this.invoiceData = res.result.receiptVO; |               this.invoiceData = res.result.receiptVO; | ||||||
|             } |             } | ||||||
|  | @ -401,6 +424,7 @@ export default { | ||||||
|               this.selectedAddress = res.result.memberAddress; |               this.selectedAddress = res.result.memberAddress; | ||||||
|             } |             } | ||||||
|             this.getAddress(); |             this.getAddress(); | ||||||
|  |             this.getStoreAddressList(); | ||||||
|             this.totalNum = 0; |             this.totalNum = 0; | ||||||
|             for (let i = 0; i < this.skuList.length; i++) { |             for (let i = 0; i < this.skuList.length; i++) { | ||||||
|               this.totalNum += this.skuList[i].num; |               this.totalNum += this.skuList[i].num; | ||||||
|  | @ -475,8 +499,32 @@ export default { | ||||||
|       }; |       }; | ||||||
|       selectAddr(params).then((res) => { |       selectAddr(params).then((res) => { | ||||||
|         if (res.success) { |         if (res.success) { | ||||||
|           this.$Message.success("选择收货地址成功"); |           this.$Message.success("选择配送方式成功"); | ||||||
|           this.selectedAddress = item; |           this.selectMethod = item; | ||||||
|  |           this.getGoodsDetail(); | ||||||
|  |         } | ||||||
|  |       }); | ||||||
|  |     }, | ||||||
|  |     selectStoreAddress(item) { | ||||||
|  |       console.log(item.id) | ||||||
|  |       console.log(this.$route.query.way) | ||||||
|  |       // 选择自提地址 | ||||||
|  |       setStoreAddressId(item.id,this.$route.query.way).then((res) => { | ||||||
|  |         if (res.success) { | ||||||
|  |           this.$Message.success("选择自提地址成功"); | ||||||
|  |           this.selectedStoreAddress = item; | ||||||
|  |           this.getGoodsDetail(); | ||||||
|  |         } | ||||||
|  |       }); | ||||||
|  |     }, | ||||||
|  |     selectDeliverMethod(item) { | ||||||
|  |       let params = { | ||||||
|  |         way: this.$route.query.way, | ||||||
|  |         shippingMethod: item.value, | ||||||
|  |       }; | ||||||
|  |       setShipMethod(params).then((res) => { | ||||||
|  |         if (res.success) { | ||||||
|  |           this.selectedDeliverMethod = item.value; | ||||||
|           this.getGoodsDetail(); |           this.getGoodsDetail(); | ||||||
|         } |         } | ||||||
|       }); |       }); | ||||||
|  | @ -503,7 +551,7 @@ export default { | ||||||
|             } |             } | ||||||
|           }); |           }); | ||||||
|         }, |         }, | ||||||
|         onCancel: () => {}, |         onCancel: () => { }, | ||||||
|       }); |       }); | ||||||
|     }, |     }, | ||||||
|     goGoodsDetail(skuId, goodsId) { |     goGoodsDetail(skuId, goodsId) { | ||||||
|  | @ -605,9 +653,11 @@ export default { | ||||||
| 
 | 
 | ||||||
| <style scoped lang="scss"> | <style scoped lang="scss"> | ||||||
| @import "../../assets/styles/coupon.scss"; | @import "../../assets/styles/coupon.scss"; | ||||||
|  | 
 | ||||||
| .goods-msg { | .goods-msg { | ||||||
|   overflow: hidden; |   overflow: hidden; | ||||||
| } | } | ||||||
|  | 
 | ||||||
| /** logo start */ | /** logo start */ | ||||||
| .logo { | .logo { | ||||||
|   height: 40px; |   height: 40px; | ||||||
|  | @ -680,12 +730,82 @@ export default { | ||||||
|   padding: 15px 25px; |   padding: 15px 25px; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | .delivery-method { | ||||||
|  |   display: flex; | ||||||
|  |   flex-wrap: wrap; | ||||||
|  | 
 | ||||||
|  |   >div { | ||||||
|  |     border: 1px dotted #949494; | ||||||
|  |     width: 50px; | ||||||
|  |     height: 40px; | ||||||
|  |     margin: 20px 20px 0 0; | ||||||
|  |     padding: 10px; | ||||||
|  |     cursor: pointer; | ||||||
|  |     color: #999; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .method-item { | ||||||
|  |     position: relative; | ||||||
|  |     font-size: 12px; | ||||||
|  | 
 | ||||||
|  |     >div:nth-child(1) { | ||||||
|  |       margin-bottom: 10px; | ||||||
|  | 
 | ||||||
|  |       span { | ||||||
|  |         margin-right: 10px; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       >span:nth-child(1) { | ||||||
|  |         color: #000000; | ||||||
|  |         font-size: 14px; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .edit-btn { | ||||||
|  |       font-size: 12px; | ||||||
|  |       position: absolute; | ||||||
|  |       top: 15px; | ||||||
|  |       right: 20px; | ||||||
|  |       color: $theme_color; | ||||||
|  | 
 | ||||||
|  |       span:hover { | ||||||
|  |         border-bottom: 1px solid $theme_color; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .corner-icon { | ||||||
|  |       position: absolute; | ||||||
|  |       right: -1px; | ||||||
|  |       bottom: -1px; | ||||||
|  | 
 | ||||||
|  |       div { | ||||||
|  |         width: 0; | ||||||
|  |         border-top: 20px solid transparent; | ||||||
|  |         border-right: 20px solid $theme_color; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       .ivu-icon { | ||||||
|  |         font-size: 12px; | ||||||
|  |         position: absolute; | ||||||
|  |         bottom: 0; | ||||||
|  |         right: 1px; | ||||||
|  |         transform: rotate(-15deg); | ||||||
|  |         color: #fff; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .border-red { | ||||||
|  |     border-color: $theme_color; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /** 地址管理 */ | /** 地址管理 */ | ||||||
| .address-manage { | .address-manage { | ||||||
|   display: flex; |   display: flex; | ||||||
|   flex-wrap: wrap; |   flex-wrap: wrap; | ||||||
| 
 | 
 | ||||||
|   > div { |   >div { | ||||||
|     border: 1px dotted #949494; |     border: 1px dotted #949494; | ||||||
|     width: 265px; |     width: 265px; | ||||||
|     height: 120px; |     height: 120px; | ||||||
|  | @ -704,20 +824,21 @@ export default { | ||||||
|     .ivu-icon { |     .ivu-icon { | ||||||
|       font-size: 24px; |       font-size: 24px; | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   .address-item { |   .address-item { | ||||||
|     position: relative; |     position: relative; | ||||||
|     font-size: 12px; |     font-size: 12px; | ||||||
| 
 | 
 | ||||||
|     > div:nth-child(1) { |     >div:nth-child(1) { | ||||||
|       margin-bottom: 10px; |       margin-bottom: 10px; | ||||||
| 
 | 
 | ||||||
|       span { |       span { | ||||||
|         margin-right: 10px; |         margin-right: 10px; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       > span:nth-child(1) { |       >span:nth-child(1) { | ||||||
|         color: #000000; |         color: #000000; | ||||||
|         font-size: 14px; |         font-size: 14px; | ||||||
|       } |       } | ||||||
|  | @ -767,7 +888,7 @@ export default { | ||||||
|   display: flex; |   display: flex; | ||||||
|   justify-content: space-between; |   justify-content: space-between; | ||||||
| 
 | 
 | ||||||
|   > span:nth-child(1) { |   >span:nth-child(1) { | ||||||
|     font-weight: bold; |     font-weight: bold; | ||||||
| 
 | 
 | ||||||
|     .ivu-icon { |     .ivu-icon { | ||||||
|  | @ -779,7 +900,7 @@ export default { | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   > span:nth-child(2) { |   >span:nth-child(2) { | ||||||
|     color: #999; |     color: #999; | ||||||
|     position: relative; |     position: relative; | ||||||
|     display: flex; |     display: flex; | ||||||
|  | @ -833,22 +954,23 @@ export default { | ||||||
|       height: 48px; |       height: 48px; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     > span { |     >span { | ||||||
|       text-align: center; |       text-align: center; | ||||||
|       width: 100px; |       width: 100px; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     > span:nth-child(1) { |     >span:nth-child(1) { | ||||||
|       font-size: 12px; |       font-size: 12px; | ||||||
| 
 | 
 | ||||||
|       flex: 1; |       flex: 1; | ||||||
|       text-align: left; |       text-align: left; | ||||||
|       > span { | 
 | ||||||
|  |       >span { | ||||||
|         margin-left: 10px; |         margin-left: 10px; | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     > span:last-child { |     >span:last-child { | ||||||
|       color: $theme_color; |       color: $theme_color; | ||||||
|       font-weight: bold; |       font-weight: bold; | ||||||
|     } |     } | ||||||
|  | @ -895,11 +1017,11 @@ export default { | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   .inovice-content { |   .inovice-content { | ||||||
|     > span { |     >span { | ||||||
|       margin-right: 10px; |       margin-right: 10px; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     > span:last-child { |     >span:last-child { | ||||||
|       color: $theme_color; |       color: $theme_color; | ||||||
|       cursor: pointer; |       cursor: pointer; | ||||||
| 
 | 
 | ||||||
|  | @ -919,7 +1041,7 @@ export default { | ||||||
|   font-size: 16px; |   font-size: 16px; | ||||||
|   color: #999; |   color: #999; | ||||||
| 
 | 
 | ||||||
|   > div > span:nth-child(2) { |   >div>span:nth-child(2) { | ||||||
|     width: 130px; |     width: 130px; | ||||||
|     text-align: right; |     text-align: right; | ||||||
|     display: inline-block; |     display: inline-block; | ||||||
|  |  | ||||||
|  | @ -0,0 +1,7 @@ | ||||||
|  | NODE_ENV=production | ||||||
|  | VUE_APP_PREVIEW=false | ||||||
|  | VUE_APP_API_BASE_URL=https://im-api.pickmall.cn | ||||||
|  | VUE_APP_WEB_SOCKET_URL=wss://im-api.pickmall.cn/lili/webSocket | ||||||
|  | VUE_APP_COMMON=https://common-api.pickmall.cn | ||||||
|  | VUE_APP_WEBSITE_NAME="LiLi IM" | ||||||
|  | VUE_APP_PC_ORDER_URL=https://store-b2b2c.pickmall.cn | ||||||
|  | @ -0,0 +1,7 @@ | ||||||
|  | NODE_ENV=development | ||||||
|  | VUE_APP_PREVIEW=true | ||||||
|  | VUE_APP_API_BASE_URL=http://192.168.0.113:8885 | ||||||
|  | VUE_APP_WEB_SOCKET_URL=ws://192.168.0.113:8885/lili/webSocket | ||||||
|  | VUE_APP_COMMON=http://192.168.0.113:8890 | ||||||
|  | VUE_APP_PC_URL="http://192.168.0.113:10001" | ||||||
|  | VUE_APP_WEBSITE_NAME="LiLi IM" | ||||||
|  | @ -0,0 +1,23 @@ | ||||||
|  | .DS_Store | ||||||
|  | node_modules | ||||||
|  | /dist | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # local env files | ||||||
|  | .env.local | ||||||
|  | .env.*.local | ||||||
|  | 
 | ||||||
|  | # Log files | ||||||
|  | npm-debug.log* | ||||||
|  | yarn-debug.log* | ||||||
|  | yarn-error.log* | ||||||
|  | pnpm-debug.log* | ||||||
|  | 
 | ||||||
|  | # Editor directories and files | ||||||
|  | .idea | ||||||
|  | .vscode | ||||||
|  | *.suo | ||||||
|  | *.ntvs* | ||||||
|  | *.njsproj | ||||||
|  | *.sln | ||||||
|  | *.sw? | ||||||
|  | @ -0,0 +1,21 @@ | ||||||
|  | MIT License | ||||||
|  | 
 | ||||||
|  | Copyright (c) 2020 YuanDong | ||||||
|  | 
 | ||||||
|  | Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  | of this software and associated documentation files (the "Software"), to deal | ||||||
|  | in the Software without restriction, including without limitation the rights | ||||||
|  | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||||
|  | copies of the Software, and to permit persons to whom the Software is | ||||||
|  | furnished to do so, subject to the following conditions: | ||||||
|  | 
 | ||||||
|  | The above copyright notice and this permission notice shall be included in all | ||||||
|  | copies or substantial portions of the Software. | ||||||
|  | 
 | ||||||
|  | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
|  | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||||
|  | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||||
|  | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||||
|  | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||||
|  | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||||
|  | SOFTWARE. | ||||||
|  | @ -0,0 +1,71 @@ | ||||||
|  | # Springboot-websocket | ||||||
|  | 
 | ||||||
|  | 过渡阶段IM | ||||||
|  | 
 | ||||||
|  | 前端仓库:https://gitee.com/beijing_hongye_huicheng/im | ||||||
|  | 
 | ||||||
|  | 后端仓库:https://gitee.com/beijing_hongye_huicheng/springboot-websocket | ||||||
|  | 
 | ||||||
|  | 部署方式: | ||||||
|  | 1、导入数据库,配置resource目录下application.yml的数据库以及redis配置文件。 | ||||||
|  | 
 | ||||||
|  | 2、maven打包 | ||||||
|  | 
 | ||||||
|  | 3、启动jar包即可。 | ||||||
|  | 
 | ||||||
|  | 4、前端程序运行,测试环境需配置根目录 .env.development文件,正式环境需配置 .env 文件。 | ||||||
|  | 
 | ||||||
|  | # 参考项目 | ||||||
|  | 项目前端参考:https://gitee.com/gzydong/LumenIM.git  功能更丰富,大家可以去学习一波 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # IM体验 | ||||||
|  | 浏览器1打开地址:http://127.0.0.1:8000/message?token=eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyQ29udGV4dCI6IntcInVzZXJuYW1lXCI6XCIxMzAxMTExMTExMVwiLFwibmlja05hbWVcIjpcIuW8oOS4ieWTiOWTiOWTiFwiLFwiZmFjZVwiOlwiaHR0cHM6Ly9saWxpc2hvcC1vc3Mub3NzLWNuLWJlaWppbmcuYWxpeXVuY3MuY29tLzQ1ZDUyOGYwMjRjZTQyMzI4NzYxNjFhZmQxN2Y0ZWExLmpwZ1wiLFwiaWRcIjpcIjEzNzY0MTc2ODQxNDAzMjY5MTJcIixcImxvbmdUZXJtXCI6ZmFsc2UsXCJyb2xlXCI6XCJTVE9SRVwiLFwic3RvcmVJZFwiOlwiMTM3NjQzMzU2NTI0NzQ3MTYxNlwiLFwic3RvcmVOYW1lXCI6XCLlrrblrrbkuZBcIixcImlzU3VwZXJcIjpmYWxzZX0iLCJzdWIiOiIxMzAxMTExMTExMSIsImV4cCI6MTY0NTQ2OTk1M30.fXpCZ2YiFYqACpmxVvKjIpXovfPRDJavHjftpQzdEps | ||||||
|  | 
 | ||||||
|  | 浏览器2打开地址:http://127.0.0.1:8000/message?token=eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyQ29udGV4dCI6IntcInVzZXJuYW1lXCI6XCIxMzAxMTExMTExMVwiLFwibmlja05hbWVcIjpcIuW8oOS4ieWTiOWTiOWTiFwiLFwiZmFjZVwiOlwiaHR0cHM6Ly9saWxpc2hvcC1vc3Mub3NzLWNuLWJlaWppbmcuYWxpeXVuY3MuY29tLzk2N2UzMzU1Yzg0NTRiNGFhMTk1N2M1NTQ5ZTZiNzIwLnBuZ1wiLFwiaWRcIjpcIjEzNzY0MTc2ODQxNDAzMjY5MTJcIixcImxvbmdUZXJtXCI6ZmFsc2UsXCJyb2xlXCI6XCJNRU1CRVJcIixcImlzU3VwZXJcIjpmYWxzZX0iLCJzdWIiOiIxMzAxMTExMTExMSIsImV4cCI6MTY0NTQ2OTAyNn0.GEkGpKRKF3rqzHRhaPCFilPpWe37cIXTT4KnwWR4Bt0&id=1376433565247471616 | ||||||
|  | 
 | ||||||
|  | 即可进行聊天。 | ||||||
|  | 
 | ||||||
|  | # NGINX配置事例 | ||||||
|  | 线上NGINX配置在测试阶段出现问题,这里吧相关的配置贴出来供大家参考。 | ||||||
|  | ```` | ||||||
|  |     server { | ||||||
|  |         listen       443 ssl; | ||||||
|  |         ssl_certificate "/etc/nginx/ssl/pickmall.cn.pem"; | ||||||
|  |         ssl_certificate_key "/etc/nginx/ssl/pickmall.cn.key"; | ||||||
|  |         ssl_session_cache shared:SSL:1m; | ||||||
|  |         ssl_session_timeout  10m; | ||||||
|  |         ssl_ciphers HIGH:!aNULL:!MD5; | ||||||
|  |         ssl_prefer_server_ciphers on; | ||||||
|  |         include /etc/nginx/default.d/*.conf; | ||||||
|  | 
 | ||||||
|  |         proxy_http_version 1.1; | ||||||
|  |         proxy_set_header Upgrade $http_upgrade; | ||||||
|  |         proxy_set_header Connection "upgrade"; | ||||||
|  | 
 | ||||||
|  |         server_name  im-api.pickmall.cn; | ||||||
|  |         location / { | ||||||
|  |             proxy_pass http://127.0.0.1:8088; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     server { | ||||||
|  |         listen       443 ssl; | ||||||
|  |         ssl_certificate "/etc/nginx/ssl/pickmall.cn.pem"; | ||||||
|  |         ssl_certificate_key "/etc/nginx/ssl/pickmall.cn.key"; | ||||||
|  |         ssl_session_cache shared:SSL:1m; | ||||||
|  |         ssl_session_timeout  10m; | ||||||
|  |         ssl_ciphers HIGH:!aNULL:!MD5; | ||||||
|  |         ssl_prefer_server_ciphers on; | ||||||
|  |         include /etc/nginx/default.d/*.conf; | ||||||
|  | 
 | ||||||
|  |         proxy_set_header Host $host; | ||||||
|  |         proxy_set_header X-Real-IP $remote_addr; | ||||||
|  |         proxy_set_header REMOTE-HOST $remote_addr; | ||||||
|  |         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | ||||||
|  | 
 | ||||||
|  |             server_name  im.pickmall.cn; | ||||||
|  |         try_files $uri $uri/ /index.html; | ||||||
|  |         root         /home/im/im/dist; | ||||||
|  |     } | ||||||
|  | ```` | ||||||
|  | @ -0,0 +1,51 @@ | ||||||
|  | module.exports = { | ||||||
|  |   presets: [ | ||||||
|  |     '@vue/cli-plugin-babel/preset' | ||||||
|  |   ], | ||||||
|  |   plugins: [ | ||||||
|  |     [ | ||||||
|  |       "import", | ||||||
|  |       { | ||||||
|  |         "libraryName": "element-ui", | ||||||
|  |         "styleLibraryName": "theme-chalk" | ||||||
|  |       } | ||||||
|  |     ], | ||||||
|  |     [ | ||||||
|  |       "prismjs", | ||||||
|  |       { | ||||||
|  |         "languages": [ | ||||||
|  |           "html", | ||||||
|  |           "css", | ||||||
|  |           "less", | ||||||
|  |           "javascript", | ||||||
|  |           "typescript", | ||||||
|  |           "json", | ||||||
|  |           "xml", | ||||||
|  |           "bash", | ||||||
|  |           "nginx", | ||||||
|  |           "sql", | ||||||
|  |           "docker", | ||||||
|  |           "php", | ||||||
|  |           "java", | ||||||
|  |           "go", | ||||||
|  |           "python", | ||||||
|  |           "ruby", | ||||||
|  |           "rust", | ||||||
|  |           "objectivec", | ||||||
|  |           "c", | ||||||
|  |           "csharp", | ||||||
|  |           "cpp", | ||||||
|  |           "lua", | ||||||
|  |           "shell", | ||||||
|  |           "vim", | ||||||
|  |           "yaml", | ||||||
|  |           "yml", | ||||||
|  |           "md", | ||||||
|  |           "erlang", | ||||||
|  |           "ini" | ||||||
|  |         ], | ||||||
|  |         "theme": "okaidia" | ||||||
|  |       } | ||||||
|  |     ] | ||||||
|  |   ] | ||||||
|  | } | ||||||
|  | @ -0,0 +1,18 @@ | ||||||
|  | { | ||||||
|  |     "compilerOptions": { | ||||||
|  |         "target": "es6", | ||||||
|  |         "baseUrl": ".", | ||||||
|  |         "paths": { | ||||||
|  |             "@/*": [ | ||||||
|  |                 "src/*" | ||||||
|  |             ] | ||||||
|  |         } | ||||||
|  |     }, | ||||||
|  |     "exclude": [ | ||||||
|  |         "node_modules", | ||||||
|  |         "dist" | ||||||
|  |     ], | ||||||
|  |     "include": [ | ||||||
|  |         "src/**/*" | ||||||
|  |     ] | ||||||
|  | } | ||||||
|  | @ -0,0 +1,67 @@ | ||||||
|  | { | ||||||
|  |   "name": "LiLi-IM", | ||||||
|  |   "version": "0.1.0", | ||||||
|  |   "private": true, | ||||||
|  |   "scripts": { | ||||||
|  |     "dev": "vue-cli-service serve", | ||||||
|  |     "serve": "vue-cli-service serve", | ||||||
|  |     "build": "vue-cli-service build", | ||||||
|  |     "lint": "vue-cli-service lint" | ||||||
|  |   }, | ||||||
|  |   "dependencies": { | ||||||
|  |     "axios": "^0.21.4", | ||||||
|  |     "babel-plugin-prismjs": "^2.0.1", | ||||||
|  |     "core-js": "^3.6.5", | ||||||
|  |     "element-ui": "^2.14.1", | ||||||
|  |     "js-audio-recorder": "^1.0.6", | ||||||
|  |     "js-base64": "^2.5.1", | ||||||
|  |     "mavon-editor": "^2.10.4", | ||||||
|  |     "nprogress": "^0.2.0", | ||||||
|  |     "prismjs": "^1.29.0", | ||||||
|  |     "svg-sprite-loader": "^5.0.0", | ||||||
|  |     "vue": "^2.6.11", | ||||||
|  |     "vue-contextmenujs": "^1.3.13", | ||||||
|  |     "vue-cropper": "^0.5.5", | ||||||
|  |     "vue-prism-editor": "^0.5.1", | ||||||
|  |     "vue-router": "^3.4.9", | ||||||
|  |     "vuex": "^3.5.1" | ||||||
|  |   }, | ||||||
|  |   "devDependencies": { | ||||||
|  |     "@vue/cli-plugin-babel": "^5.0.8", | ||||||
|  |     "@vue/cli-plugin-eslint": "^5.0.8", | ||||||
|  |     "@vue/cli-service": "^5.0.8", | ||||||
|  |     "babel-eslint": "^10.1.0", | ||||||
|  |     "babel-plugin-import": "^1.13.1", | ||||||
|  |     "compression-webpack-plugin": "^5.0.0", | ||||||
|  |     "eslint": "^8.28.0", | ||||||
|  |     "eslint-plugin-vue": "^6.2.2", | ||||||
|  |     "less": "^3.0.4", | ||||||
|  |     "less-loader": "^5.0.0", | ||||||
|  |     "postcss": "^8.4.20", | ||||||
|  |     "style-resources-loader": "^1.4.1", | ||||||
|  |     "vue-cli-plugin-style-resources-loader": "^0.1.4", | ||||||
|  |     "vue-svg-component-runtime": "^1.0.1", | ||||||
|  |     "vue-svg-icon-loader": "^2.1.1", | ||||||
|  |     "vue-template-compiler": "^2.6.11", | ||||||
|  |     "webpack": "^5.75.0" | ||||||
|  |   }, | ||||||
|  |   "eslintConfig": { | ||||||
|  |     "root": true, | ||||||
|  |     "env": { | ||||||
|  |       "node": true | ||||||
|  |     }, | ||||||
|  |     "extends": [ | ||||||
|  |       "plugin:vue/essential", | ||||||
|  |       "eslint:recommended" | ||||||
|  |     ], | ||||||
|  |     "parserOptions": { | ||||||
|  |       "parser": "babel-eslint" | ||||||
|  |     }, | ||||||
|  |     "rules": {} | ||||||
|  |   }, | ||||||
|  |   "browserslist": [ | ||||||
|  |     "> 1%", | ||||||
|  |     "last 2 versions", | ||||||
|  |     "not dead" | ||||||
|  |   ] | ||||||
|  | } | ||||||
| After Width: | Height: | Size: 1.1 KiB | 
|  | @ -0,0 +1,34 @@ | ||||||
|  | <!DOCTYPE html> | ||||||
|  | <html lang="zh-cmn-Hans"> | ||||||
|  |   <head> | ||||||
|  |     <meta charset="utf-8"> | ||||||
|  |     <meta http-equiv="X-UA-Compatible" content="IE=edge"> | ||||||
|  |     <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover"> | ||||||
|  |     <link rel="icon" href="favicon.ico"> | ||||||
|  |     <title>LiLi IM</title> | ||||||
|  |     <style>.first-loading-wrp{display:flex;justify-content:center;align-items:center;flex-direction:column;min-height:420px;height:100%}.first-loading-wrp>h1{font-size:128px}.first-loading-wrp .loading-wrp{padding:98px;display:flex;justify-content:center;align-items:center}.dot{animation:antRotate 1.2s infinite linear;transform:rotate(45deg);position:relative;display:inline-block;font-size:32px;width:32px;height:32px;box-sizing:border-box}.dot i{width:14px;height:14px;position:absolute;display:block;background-color:#1890ff;border-radius:100%;transform:scale(.75);transform-origin:50% 50%;opacity:.3;animation:antSpinMove 1s infinite linear alternate}.dot i:nth-child(1){top:0;left:0}.dot i:nth-child(2){top:0;right:0;-webkit-animation-delay:.4s;animation-delay:.4s}.dot i:nth-child(3){right:0;bottom:0;-webkit-animation-delay:.8s;animation-delay:.8s}.dot i:nth-child(4){bottom:0;left:0;-webkit-animation-delay:1.2s;animation-delay:1.2s}@keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@-webkit-keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@keyframes antSpinMove{to{opacity:1}}@-webkit-keyframes antSpinMove{to{opacity:1}}</style> | ||||||
|  |     <!-- require cdn assets css --> | ||||||
|  |     <% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.css) { %> | ||||||
|  |     <link rel="stylesheet" href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" /> | ||||||
|  |     <% } %> | ||||||
|  |     <link rel="stylesheet" href="https://at.alicdn.com/t/font_1425251_3v0kq1by4iq.css"> | ||||||
|  |     <link rel="stylesheet" href="https://cdn.bootcss.com/animate.css/3.7.2/animate.css"> | ||||||
|  |   </head> | ||||||
|  |   <body> | ||||||
|  |     <noscript> | ||||||
|  |       <strong>We're sorry but vue-antd-pro doesn't work properly without JavaScript enabled. Please enable it to continue.</strong> | ||||||
|  |     </noscript> | ||||||
|  |     <div id="app"> | ||||||
|  |       <div class="first-loading-wrp"> | ||||||
|  |         <div class="loading-wrp"> | ||||||
|  |           <span class="dot dot-spin"><i></i><i></i><i></i><i></i></span> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |     <!-- require cdn assets js --> | ||||||
|  |     <% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %> | ||||||
|  |     <script type="text/javascript" src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script> | ||||||
|  |     <% } %> | ||||||
|  |     <!-- built files will be auto injected --> | ||||||
|  |   </body> | ||||||
|  | </html> | ||||||
|  | @ -0,0 +1,47 @@ | ||||||
|  | <template> | ||||||
|  |   <div id="app"> | ||||||
|  |     <router-view v-if="showView" /> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | <script> | ||||||
|  | export default { | ||||||
|  |   name: 'App', | ||||||
|  |   data () { | ||||||
|  |     return { | ||||||
|  |       // 用于点击当前页的router时,刷新当前页 | ||||||
|  |       showView: true, | ||||||
|  |       beforeUnload: '', | ||||||
|  |       Handler: '' | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     // 刷新当前路由方法 | ||||||
|  |     refreshView () { | ||||||
|  |       this.showView = false | ||||||
|  |       this.$nextTick(() => (this.showView = true)) | ||||||
|  |     }, | ||||||
|  |     beforeunloadHandler () { | ||||||
|  |       this.beforeUnload = new Date().getTime(); | ||||||
|  |     }, | ||||||
|  |     // 页面关闭后 重置btnHide | ||||||
|  |     unloadHandler (e) { | ||||||
|  |       this.Handler = new Date().getTime() - this.beforeUnload; | ||||||
|  |       if (this.Handler <= 5) { | ||||||
|  |         var storage = window.localStorage; | ||||||
|  |         storage.clear() | ||||||
|  |         localStorage.setItem('btnHide', 1) | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   mounted () { | ||||||
|  |     window.addEventListener('beforeunload', e => this.beforeunloadHandler(e)) | ||||||
|  |     window.addEventListener('unload', e => this.unloadHandler(e)) | ||||||
|  |   }, | ||||||
|  |   destroyed () { | ||||||
|  |     window.removeEventListener('beforeunload', e => this.beforeunloadHandler(e)) | ||||||
|  |     window.removeEventListener('unload', e => this.unloadHandler(e)) | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | @ -0,0 +1,109 @@ | ||||||
|  | import { post, get, upload, del } from "@/utils/request"; | ||||||
|  | 
 | ||||||
|  | // 获取聊天列表服务接口
 | ||||||
|  | export const ServeGetTalkList = (data) => { | ||||||
|  |   return get("/im/talk/list", data); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // 获取聊天列表服务接口
 | ||||||
|  | export const ServeGetStoreTalkList = (data) => { | ||||||
|  |   return get("/im/talk/store/list", data); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // 聊天列表创建服务接口
 | ||||||
|  | export const ServeCreateTalkList = (id) => { | ||||||
|  |   return get(`/im/talk/user/${id}`); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // 删除聊天列表服务接口
 | ||||||
|  | export const ServeDeleteTalkList = (data) => { | ||||||
|  |   return del("/im/talk", data); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // 对话列表置顶服务接口
 | ||||||
|  | export const ServeTopTalkList = (data) => { | ||||||
|  |   return post("/im/talk/top", data); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // 清除聊天消息未读数服务接口
 | ||||||
|  | export const ServeClearTalkUnreadNum = (data) => { | ||||||
|  |   return post("/im/talk/update-unread-num", data); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // 获取聊天记录服务接口
 | ||||||
|  | export const ServeTalkRecords = (data) => { | ||||||
|  |   return get("/im/message", data); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // 获取转发会话记录详情列表服务接口
 | ||||||
|  | export const ServeGetForwardRecords = (data) => { | ||||||
|  |   return get("/im/talk/get-forward-records", data); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // 对话列表置顶服务接口
 | ||||||
|  | export const ServeSetNotDisturb = (data) => { | ||||||
|  |   return post("/im/talk/disturb", data); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // 查找用户聊天记录服务接口
 | ||||||
|  | export const ServeFindTalkRecords = (data) => { | ||||||
|  |   return get("/im/talk/find-chat-records", data); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // 搜索用户聊天记录服务接口
 | ||||||
|  | export const ServeSearchTalkRecords = (data) => { | ||||||
|  |   return get("/im/talk/search-chat-records", data); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export const ServeGetRecordsContext = (data) => { | ||||||
|  |   return get("/im/talk/get-records-context", data); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // 发送代码块消息服务接口
 | ||||||
|  | export const ServeSendTalkCodeBlock = (data) => { | ||||||
|  |   return post("/im/talk/message/code", data); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // 发送聊天文件服务接口
 | ||||||
|  | export const ServeSendTalkFile = (data) => { | ||||||
|  |   return post("/im/talk/message/file", data); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // 发送聊天图片服务接口
 | ||||||
|  | export const ServeSendTalkImage = (data) => { | ||||||
|  |   return upload("/common/common/upload/file", data); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // 发送表情包服务接口
 | ||||||
|  | export const ServeSendEmoticon = (data) => { | ||||||
|  |   return post("/im/talk/message/emoticon", data); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // 转发消息服务接口
 | ||||||
|  | export const ServeForwardRecords = (data) => { | ||||||
|  |   return post("/im/talk/message/forward", data); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // 撤回消息服务接口
 | ||||||
|  | export const ServeRevokeRecords = (data) => { | ||||||
|  |   return post("/im/talk/message/revoke", data); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // 删除消息服务接口
 | ||||||
|  | export const ServeRemoveRecords = (data) => { | ||||||
|  |   return post("/im/talk/message/delete", data); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // 收藏表情包服务接口
 | ||||||
|  | export const ServeCollectEmoticon = (data) => { | ||||||
|  |   return post("/im/talk/message/collect", data); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | //投票
 | ||||||
|  | export const ServeSendVote = (data) => { | ||||||
|  |   return post("/im/talk/message/vote", data); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export const ServeConfirmVoteHandle = (data) => { | ||||||
|  |   return post("/im/talk/message/vote/handle", data); | ||||||
|  | }; | ||||||
|  | @ -0,0 +1,45 @@ | ||||||
|  | import { post, get } from '@/utils/request' | ||||||
|  | 
 | ||||||
|  | // 获取好友列表服务接口
 | ||||||
|  | export const ServeGetContacts = data => { | ||||||
|  |   return get('/contacts/list', data) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 解除好友关系服务接口
 | ||||||
|  | export const ServeDeleteContact = data => { | ||||||
|  |   return post('/contacts/delete', data) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 修改好友备注服务接口
 | ||||||
|  | export const ServeEditContactRemark = data => { | ||||||
|  |   return post('/contacts/edit-remark', data) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 搜索联系人
 | ||||||
|  | export const ServeSearchContact = data => { | ||||||
|  |   return get('/contacts/search', data) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 好友申请服务接口
 | ||||||
|  | export const ServeCreateContact = data => { | ||||||
|  |   return post('/contacts/apply/create', data) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 查询好友申请服务接口
 | ||||||
|  | export const ServeGetContactApplyRecords = data => { | ||||||
|  |   return get('/contacts/apply/records', data) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 处理好友申请服务接口
 | ||||||
|  | export const ServeApplyAccept = data => { | ||||||
|  |   return post('/contacts/apply/accept', data) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export const ServeApplyDecline = data => { | ||||||
|  |   return post('/contacts/apply/decline', data) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 查询好友申请未读数量服务接口
 | ||||||
|  | export const ServeFindFriendApplyNum = () => { | ||||||
|  |   return get('/contacts/apply-unread-num') | ||||||
|  | } | ||||||
|  | @ -0,0 +1,26 @@ | ||||||
|  | import { post, get, upload } from '@/utils/request' | ||||||
|  | 
 | ||||||
|  | // 查询用户表情包服务接口
 | ||||||
|  | export const ServeFindUserEmoticon = () => { | ||||||
|  |   return get('/emoticon/list') | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 查询系统表情包服务接口
 | ||||||
|  | export const ServeFindSysEmoticon = () => { | ||||||
|  |   return get('/emoticon/system') | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 设置用户表情包服务接口
 | ||||||
|  | export const ServeSetUserEmoticon = data => { | ||||||
|  |   return post('/emoticon/set-user-emoticon', data) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 移除收藏表情包服务接口
 | ||||||
|  | export const ServeDelCollectEmoticon = data => { | ||||||
|  |   return post('/emoticon/del-collect-emoticon', data) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 上传表情包服务接口
 | ||||||
|  | export const ServeUploadEmoticon = data => { | ||||||
|  |   return upload('/emoticon/upload-emoticon', data) | ||||||
|  | } | ||||||
|  | @ -0,0 +1,5 @@ | ||||||
|  | import { post, get, upload, del } from "@/utils/request"; | ||||||
|  | 
 | ||||||
|  | export const ServeGetGoodsDetail = (data) => { | ||||||
|  |     return get(`/im/goods/goods/sku/${data.goodsId}/${data.skuId}`); | ||||||
|  |   }; | ||||||
|  | @ -0,0 +1,66 @@ | ||||||
|  | import { post, get } from '@/utils/request' | ||||||
|  | 
 | ||||||
|  | // 查询用户群聊服务接口
 | ||||||
|  | export const ServeGetGroups = () => { | ||||||
|  |   return get('/group/list') | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 获取群信息服务接口
 | ||||||
|  | export const ServeGroupDetail = data => { | ||||||
|  |   return get('/group/detail', data) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 创建群聊服务接口
 | ||||||
|  | export const ServeCreateGroup = data => { | ||||||
|  |   return post('/group/create', data) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //  修改群信息
 | ||||||
|  | export const ServeEditGroup = data => { | ||||||
|  |   return post('/group/edit', data) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 邀请好友加入群聊服务接口
 | ||||||
|  | export const ServeInviteGroup = data => { | ||||||
|  |   return post('/group/invite', data) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 移除群聊成员服务接口
 | ||||||
|  | export const ServeRemoveMembersGroup = data => { | ||||||
|  |   return post('/group/remove-members', data) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 管理员解散群聊服务接口
 | ||||||
|  | export const ServeDismissGroup = data => { | ||||||
|  |   return post('/group/dismiss', data) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 用户退出群聊服务接口
 | ||||||
|  | export const ServeSecedeGroup = data => { | ||||||
|  |   return post('/group/secede', data) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 修改群聊名片服务接口
 | ||||||
|  | export const ServeUpdateGroupCard = data => { | ||||||
|  |   return post('/group/set-group-card', data) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 获取用户可邀请加入群组的好友列表
 | ||||||
|  | export const ServeGetInviteFriends = data => { | ||||||
|  |   return get('/group/invite-friends', data) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //  获取群组成员列表
 | ||||||
|  | export const ServeGetGroupMembers = data => { | ||||||
|  |   return get('/group/members', data) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //  获取群组公告列表
 | ||||||
|  | export const ServeGetGroupNotices = data => { | ||||||
|  |   return get('/group/notices', data) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //  编辑群公告
 | ||||||
|  | export const ServeEditGroupNotice = data => { | ||||||
|  |   return post('/group/edit-notice', data) | ||||||
|  | } | ||||||
|  | @ -0,0 +1,16 @@ | ||||||
|  | import { post, get, upload } from '@/utils/request' | ||||||
|  | 
 | ||||||
|  | // 上传头像裁剪图片服务接口
 | ||||||
|  | export const ServeUploadFileStream = data => { | ||||||
|  |   return post('/upload/file-stream', data) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 查询大文件拆分信息服务接口
 | ||||||
|  | export const ServeFindFileSplitInfo = (data = {}) => { | ||||||
|  |   return get('/upload/get-file-split-info', data) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 文件拆分上传服务接口
 | ||||||
|  | export const ServeFileSubareaUpload = (data = {}, options = {}) => { | ||||||
|  |   return upload('/upload/file-subarea-upload', data, options) | ||||||
|  | } | ||||||
|  | @ -0,0 +1,30 @@ | ||||||
|  | import { get } from "@/utils/request"; | ||||||
|  | 
 | ||||||
|  | // 获取用户相关设置信息
 | ||||||
|  | export const ServeGetUserSetting = () => { | ||||||
|  |   return get("/im/user"); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // 获取店铺相关设置信息
 | ||||||
|  | export const ServeGetStoreSetting = () => { | ||||||
|  |   return get("/im/user/store"); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // 获取用户相关设置信息
 | ||||||
|  | export const ServeGetUserDetail = (memberId) => { | ||||||
|  |   return get(`/im/user/${memberId}`); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // 获取店铺相关设置信息
 | ||||||
|  | export const ServeGetStoreDetail = (storeId) => { | ||||||
|  |   return get(`/im/user/store/${storeId}`); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // 获取店铺相关设置信息
 | ||||||
|  | export const ServeGetFootPrint = (params) => { | ||||||
|  |   return get(`/im/user/history`,params); | ||||||
|  | }; | ||||||
|  | // 获取订单列表信息
 | ||||||
|  | export const ServeGetOrderPrint = (params) => { | ||||||
|  |   return get(`/im/orders/orders`,params); | ||||||
|  | }; | ||||||
|  | @ -0,0 +1,238 @@ | ||||||
|  | @import './reset.css'; | ||||||
|  | 
 | ||||||
|  | .no-select { | ||||||
|  |   -webkit-user-select: none; | ||||||
|  |   -moz-user-select: none; | ||||||
|  |   -ms-user-select: none; | ||||||
|  |   user-select: none; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .no-padding { | ||||||
|  |   padding: 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .avatar-box { | ||||||
|  |   height: 35px; | ||||||
|  |   width: 35px; | ||||||
|  |   flex-shrink: 0; | ||||||
|  |   background-color: #508afe; | ||||||
|  |   border-radius: 50%; | ||||||
|  |   display: flex; | ||||||
|  |   justify-content: center; | ||||||
|  |   align-items: center; | ||||||
|  |   font-size: 14px; | ||||||
|  |   color: white; | ||||||
|  |   user-select: none; | ||||||
|  |   transition: ease 1s; | ||||||
|  |   position: relative; | ||||||
|  |   overflow: hidden; | ||||||
|  | 
 | ||||||
|  |   img { | ||||||
|  |     width: 100%; | ||||||
|  |     height: 100%; | ||||||
|  |     background-color: white; | ||||||
|  |     border-radius: 3px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .top-mask { | ||||||
|  |     width: 100%; | ||||||
|  |     height: 100%; | ||||||
|  |     background-color: rgba(22, 25, 29, 0.6); | ||||||
|  |     position: absolute; | ||||||
|  |     top: 0; | ||||||
|  |     left: 0; | ||||||
|  |     color: white; | ||||||
|  |     display: none; | ||||||
|  |     align-items: center; | ||||||
|  |     justify-content: center; | ||||||
|  |     font-weight: bold; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   &:hover .top-mask { | ||||||
|  |     display: flex; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .no-border { | ||||||
|  |   border: 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .pointer { | ||||||
|  |   cursor: pointer; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .border-radius0 { | ||||||
|  |   border-radius: 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .full-height { | ||||||
|  |   height: 100%; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .talk-height { | ||||||
|  |   height: 80%; | ||||||
|  |   width: 80%; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ov-hidden { | ||||||
|  |   overflow: hidden; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 滚动条样式 | ||||||
|  | .lum-scrollbar { | ||||||
|  |   &::-webkit-scrollbar { | ||||||
|  |     width: 3px; | ||||||
|  |     background-color: #e4e4e5; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   &::-webkit-scrollbar-thumb { | ||||||
|  |     border-radius: 3px; | ||||||
|  |     background-color: #c0bebc; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .larkc-tag { | ||||||
|  |   font-size: 12px; | ||||||
|  |   font-weight: 400; | ||||||
|  |   display: inline-flex; | ||||||
|  |   align-items: center; | ||||||
|  |   justify-content: center; | ||||||
|  |   padding: 0 6px; | ||||||
|  |   height: 20px; | ||||||
|  |   border-radius: 2px; | ||||||
|  |   cursor: default; | ||||||
|  |   user-select: none; | ||||||
|  |   background-color: #dee0e3; | ||||||
|  |   transform: scale(0.83); | ||||||
|  |   transform-origin: left; | ||||||
|  |   flex-shrink: 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .flex-center { | ||||||
|  |   display: flex; | ||||||
|  |   align-items: center; | ||||||
|  |   justify-content: center; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 自定义 dialog 样式 | ||||||
|  | // lum-dialog -- start | ||||||
|  | .lum-dialog-mask { | ||||||
|  |   position: fixed; | ||||||
|  |   top: 0; | ||||||
|  |   left: 0; | ||||||
|  |   z-index: 999; | ||||||
|  |   width: 100%; | ||||||
|  |   height: 100%; | ||||||
|  |   background-color: @maskBagColor; | ||||||
|  |   overflow: hidden; | ||||||
|  |   display: flex; | ||||||
|  |   align-items: center; | ||||||
|  |   justify-content: center; | ||||||
|  | 
 | ||||||
|  |   .lum-dialog-box { | ||||||
|  |     min-width: 200px; | ||||||
|  |     min-height: 200px; | ||||||
|  |     background-color: white; | ||||||
|  |     border-radius: 3px; | ||||||
|  |     overflow: hidden; | ||||||
|  |     box-shadow: 0 2px 8px 0 rgba(31, 35, 41, 0.2); | ||||||
|  |     margin: 0 10px; | ||||||
|  | 
 | ||||||
|  |     .container { | ||||||
|  |       height: 100%; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .header { | ||||||
|  |       padding: 0; | ||||||
|  |       display: flex; | ||||||
|  |       align-items: center; | ||||||
|  |       justify-content: space-between; | ||||||
|  |       border-bottom: 1px solid #f5eeee; | ||||||
|  | 
 | ||||||
|  |       >p:first-child { | ||||||
|  |         text-indent: 20px; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       .tools { | ||||||
|  |         height: 100%; | ||||||
|  |         width: 100px; | ||||||
|  |         display: flex; | ||||||
|  |         align-items: center; | ||||||
|  |         justify-content: flex-end; | ||||||
|  |         padding-right: 20px; | ||||||
|  | 
 | ||||||
|  |         i { | ||||||
|  |           font-size: 20px; | ||||||
|  |           cursor: pointer; | ||||||
|  |           margin-left: 8px; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .main { | ||||||
|  |       /deep/.el-input__inner { | ||||||
|  |         border-radius: 1px !important; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       .submit-btn { | ||||||
|  |         border-radius: 2px; | ||||||
|  |         font-weight: 400; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // lum-dialog -- end | ||||||
|  | 
 | ||||||
|  | .talk-notify { | ||||||
|  |   .el-notification__title { | ||||||
|  |     font-weight: 300; | ||||||
|  |     font-size: 16px; | ||||||
|  |     color: #f44336; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   p { | ||||||
|  |     max-height: 65px; | ||||||
|  |     overflow: hidden; | ||||||
|  |     overflow: hidden; | ||||||
|  |     text-overflow: ellipsis; | ||||||
|  |     display: -webkit-box; | ||||||
|  |     -webkit-line-clamp: 3; | ||||||
|  |     -webkit-box-orient: vertical; | ||||||
|  |     text-indent: -7px; | ||||||
|  |     word-break: break-all; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .im-notify { | ||||||
|  |   padding: 14px 26px 8px 0px; | ||||||
|  | 
 | ||||||
|  |   .el-notification__closeBtn { | ||||||
|  |     position: absolute; | ||||||
|  |     top: 12px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .el-notification__group { | ||||||
|  |     margin-left: 5px; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .flex { | ||||||
|  |   display: flex; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .flex-2 { | ||||||
|  |   flex: 2; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .flex-8 { | ||||||
|  |   flex: 8; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .flex-4 { | ||||||
|  |   flex: 4; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .flex-10 { | ||||||
|  |   flex: 10; | ||||||
|  | } | ||||||
|  | @ -0,0 +1,991 @@ | ||||||
|  | .markdown-body .octicon { | ||||||
|  |   display: inline-block; | ||||||
|  |   fill: currentColor; | ||||||
|  |   vertical-align: text-bottom; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .anchor { | ||||||
|  |   float: left; | ||||||
|  |   line-height: 1; | ||||||
|  |   margin-left: -20px; | ||||||
|  |   padding-right: 4px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .anchor:focus { | ||||||
|  |   outline: none; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body h1 .octicon-link, | ||||||
|  | .markdown-body h2 .octicon-link, | ||||||
|  | .markdown-body h3 .octicon-link, | ||||||
|  | .markdown-body h4 .octicon-link, | ||||||
|  | .markdown-body h5 .octicon-link, | ||||||
|  | .markdown-body h6 .octicon-link { | ||||||
|  |   color: #1b1f23; | ||||||
|  |   vertical-align: middle; | ||||||
|  |   visibility: hidden; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body h1:hover .anchor, | ||||||
|  | .markdown-body h2:hover .anchor, | ||||||
|  | .markdown-body h3:hover .anchor, | ||||||
|  | .markdown-body h4:hover .anchor, | ||||||
|  | .markdown-body h5:hover .anchor, | ||||||
|  | .markdown-body h6:hover .anchor { | ||||||
|  |   text-decoration: none; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body h1:hover .anchor .octicon-link, | ||||||
|  | .markdown-body h2:hover .anchor .octicon-link, | ||||||
|  | .markdown-body h3:hover .anchor .octicon-link, | ||||||
|  | .markdown-body h4:hover .anchor .octicon-link, | ||||||
|  | .markdown-body h5:hover .anchor .octicon-link, | ||||||
|  | .markdown-body h6:hover .anchor .octicon-link { | ||||||
|  |   visibility: visible; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body h1:hover .anchor .octicon-link:before, | ||||||
|  | .markdown-body h2:hover .anchor .octicon-link:before, | ||||||
|  | .markdown-body h3:hover .anchor .octicon-link:before, | ||||||
|  | .markdown-body h4:hover .anchor .octicon-link:before, | ||||||
|  | .markdown-body h5:hover .anchor .octicon-link:before, | ||||||
|  | .markdown-body h6:hover .anchor .octicon-link:before { | ||||||
|  |   width: 16px; | ||||||
|  |   height: 16px; | ||||||
|  |   content: ' '; | ||||||
|  |   display: inline-block; | ||||||
|  |   background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' version='1.1' width='16' height='16' aria-hidden='true'%3E%3Cpath fill-rule='evenodd' d='M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z'%3E%3C/path%3E%3C/svg%3E"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body { | ||||||
|  |   -ms-text-size-adjust: 100%; | ||||||
|  |   -webkit-text-size-adjust: 100%; | ||||||
|  |   font-size: 16px; | ||||||
|  |   word-wrap: break-word; | ||||||
|  |   font-family: Content-font, Roboto, sans-serif; | ||||||
|  |   font-weight: 400; | ||||||
|  |   color: #3B454E; | ||||||
|  |   line-height: 1.625; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body details { | ||||||
|  |   display: block; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body summary { | ||||||
|  |   display: list-item; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body a { | ||||||
|  |   background-color: initial; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body a:active, | ||||||
|  | .markdown-body a:hover { | ||||||
|  |   outline-width: 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body strong { | ||||||
|  |   font-weight: inherit; | ||||||
|  |   font-weight: bolder; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body h1 { | ||||||
|  |   font-size: 2em; | ||||||
|  |   margin: .67em 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body img { | ||||||
|  |   border-style: none; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body code, | ||||||
|  | .markdown-body kbd, | ||||||
|  | .markdown-body pre { | ||||||
|  |   font-family: monospace, monospace; | ||||||
|  |   font-size: 1em; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body hr { | ||||||
|  |   box-sizing: initial; | ||||||
|  |   height: 0; | ||||||
|  |   overflow: visible; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body input { | ||||||
|  |   font: inherit; | ||||||
|  |   margin: 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body input { | ||||||
|  |   overflow: visible; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body [type=checkbox] { | ||||||
|  |   box-sizing: border-box; | ||||||
|  |   padding: 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body * { | ||||||
|  |   box-sizing: border-box; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body input { | ||||||
|  |   font-family: inherit; | ||||||
|  |   font-size: inherit; | ||||||
|  |   line-height: inherit; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body a { | ||||||
|  |   color: #0366d6; | ||||||
|  |   text-decoration: none; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body a:hover { | ||||||
|  |   text-decoration: underline; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body strong { | ||||||
|  |   font-weight: 600; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body hr { | ||||||
|  |   height: 0; | ||||||
|  |   margin: 15px 0; | ||||||
|  |   overflow: hidden; | ||||||
|  |   background: transparent; | ||||||
|  |   border: 0; | ||||||
|  |   border-bottom: 1px solid #dfe2e5; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body hr:after, | ||||||
|  | .markdown-body hr:before { | ||||||
|  |   display: table; | ||||||
|  |   content: ""; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body hr:after { | ||||||
|  |   clear: both; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body table { | ||||||
|  |   border-spacing: 0; | ||||||
|  |   border-collapse: collapse; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body td, | ||||||
|  | .markdown-body th { | ||||||
|  |   padding: 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body details summary { | ||||||
|  |   cursor: pointer; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body kbd { | ||||||
|  |   display: inline-block; | ||||||
|  |   padding: 3px 5px; | ||||||
|  |   font: 11px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace; | ||||||
|  |   line-height: 10px; | ||||||
|  |   color: #444d56; | ||||||
|  |   vertical-align: middle; | ||||||
|  |   background-color: #fafbfc; | ||||||
|  |   border: 1px solid #d1d5da; | ||||||
|  |   border-radius: 3px; | ||||||
|  |   box-shadow: inset 0 -1px 0 #d1d5da; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body h1, | ||||||
|  | .markdown-body h2, | ||||||
|  | .markdown-body h3, | ||||||
|  | .markdown-body h4, | ||||||
|  | .markdown-body h5, | ||||||
|  | .markdown-body h6 { | ||||||
|  |   margin-top: 0; | ||||||
|  |   margin-bottom: 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body h1 { | ||||||
|  |   font-size: 32px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body h1, | ||||||
|  | .markdown-body h2 { | ||||||
|  |   font-weight: 600; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body h2 { | ||||||
|  |   font-size: 24px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body h3 { | ||||||
|  |   font-size: 20px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body h3, | ||||||
|  | .markdown-body h4 { | ||||||
|  |   font-weight: 600; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body h4 { | ||||||
|  |   font-size: 16px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body h5 { | ||||||
|  |   font-size: 14px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body h5, | ||||||
|  | .markdown-body h6 { | ||||||
|  |   font-weight: 600; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body h6 { | ||||||
|  |   font-size: 12px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body p { | ||||||
|  |   margin-top: 0; | ||||||
|  |   margin-bottom: 10px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body blockquote { | ||||||
|  |   margin: 20px 0 !important; | ||||||
|  |   background-color: #f5f8fc; | ||||||
|  |   padding: 1rem; | ||||||
|  |   color: #8796a8; | ||||||
|  |   border-left: none; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body ol, | ||||||
|  | .markdown-body ul { | ||||||
|  |   padding-left: 0; | ||||||
|  |   margin-top: 0; | ||||||
|  |   margin-bottom: 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body ol ol, | ||||||
|  | .markdown-body ul ol { | ||||||
|  |   list-style-type: lower-roman; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body ol ol ol, | ||||||
|  | .markdown-body ol ul ol, | ||||||
|  | .markdown-body ul ol ol, | ||||||
|  | .markdown-body ul ul ol { | ||||||
|  |   list-style-type: lower-alpha; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body dd { | ||||||
|  |   margin-left: 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body code, | ||||||
|  | .markdown-body pre { | ||||||
|  |   font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace; | ||||||
|  |   font-size: 12px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body pre { | ||||||
|  |   margin-top: 0; | ||||||
|  |   margin-bottom: 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body input::-webkit-inner-spin-button, | ||||||
|  | .markdown-body input::-webkit-outer-spin-button { | ||||||
|  |   margin: 0; | ||||||
|  |   -webkit-appearance: none; | ||||||
|  |   appearance: none; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body :checked+.radio-label { | ||||||
|  |   position: relative; | ||||||
|  |   z-index: 1; | ||||||
|  |   border-color: #0366d6; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .border { | ||||||
|  |   border: 1px solid #e1e4e8 !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .border-0 { | ||||||
|  |   border: 0 !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .border-bottom { | ||||||
|  |   border-bottom: 1px solid #e1e4e8 !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .rounded-1 { | ||||||
|  |   border-radius: 3px !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .bg-white { | ||||||
|  |   background-color: #fff !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .bg-gray-light { | ||||||
|  |   background-color: #fafbfc !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .text-gray-light { | ||||||
|  |   color: #6a737d !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .mb-0 { | ||||||
|  |   margin-bottom: 0 !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .my-2 { | ||||||
|  |   margin-top: 8px !important; | ||||||
|  |   margin-bottom: 8px !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .pl-0 { | ||||||
|  |   padding-left: 0 !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .py-0 { | ||||||
|  |   padding-top: 0 !important; | ||||||
|  |   padding-bottom: 0 !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .pl-1 { | ||||||
|  |   padding-left: 4px !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .pl-2 { | ||||||
|  |   padding-left: 8px !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .py-2 { | ||||||
|  |   padding-top: 8px !important; | ||||||
|  |   padding-bottom: 8px !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .pl-3, | ||||||
|  | .markdown-body .px-3 { | ||||||
|  |   padding-left: 16px !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .px-3 { | ||||||
|  |   padding-right: 16px !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .pl-4 { | ||||||
|  |   padding-left: 24px !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .pl-5 { | ||||||
|  |   padding-left: 32px !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .pl-6 { | ||||||
|  |   padding-left: 40px !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .f6 { | ||||||
|  |   font-size: 12px !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .lh-condensed { | ||||||
|  |   line-height: 1.25 !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .text-bold { | ||||||
|  |   font-weight: 600 !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .pl-c { | ||||||
|  |   color: #6a737d; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .pl-c1, | ||||||
|  | .markdown-body .pl-s .pl-v { | ||||||
|  |   color: #005cc5; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .pl-e, | ||||||
|  | .markdown-body .pl-en { | ||||||
|  |   color: #6f42c1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .pl-s .pl-s1, | ||||||
|  | .markdown-body .pl-smi { | ||||||
|  |   color: #24292e; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .pl-ent { | ||||||
|  |   color: #22863a; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .pl-k { | ||||||
|  |   color: #d73a49; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .pl-pds, | ||||||
|  | .markdown-body .pl-s, | ||||||
|  | .markdown-body .pl-s .pl-pse .pl-s1, | ||||||
|  | .markdown-body .pl-sr, | ||||||
|  | .markdown-body .pl-sr .pl-cce, | ||||||
|  | .markdown-body .pl-sr .pl-sra, | ||||||
|  | .markdown-body .pl-sr .pl-sre { | ||||||
|  |   color: #032f62; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .pl-smw, | ||||||
|  | .markdown-body .pl-v { | ||||||
|  |   color: #e36209; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .pl-bu { | ||||||
|  |   color: #b31d28; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .pl-ii { | ||||||
|  |   color: #fafbfc; | ||||||
|  |   background-color: #b31d28; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .pl-c2 { | ||||||
|  |   color: #fafbfc; | ||||||
|  |   background-color: #d73a49; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .pl-c2:before { | ||||||
|  |   content: "^M"; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .pl-sr .pl-cce { | ||||||
|  |   font-weight: 700; | ||||||
|  |   color: #22863a; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .pl-ml { | ||||||
|  |   color: #735c0f; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .pl-mh, | ||||||
|  | .markdown-body .pl-mh .pl-en, | ||||||
|  | .markdown-body .pl-ms { | ||||||
|  |   font-weight: 700; | ||||||
|  |   color: #005cc5; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .pl-mi { | ||||||
|  |   font-style: italic; | ||||||
|  |   color: #24292e; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .pl-mb { | ||||||
|  |   font-weight: 700; | ||||||
|  |   color: #24292e; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .pl-md { | ||||||
|  |   color: #b31d28; | ||||||
|  |   background-color: #ffeef0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .pl-mi1 { | ||||||
|  |   color: #22863a; | ||||||
|  |   background-color: #f0fff4; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .pl-mc { | ||||||
|  |   color: #e36209; | ||||||
|  |   background-color: #ffebda; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .pl-mi2 { | ||||||
|  |   color: #f6f8fa; | ||||||
|  |   background-color: #005cc5; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .pl-mdr { | ||||||
|  |   font-weight: 700; | ||||||
|  |   color: #6f42c1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .pl-ba { | ||||||
|  |   color: #586069; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .pl-sg { | ||||||
|  |   color: #959da5; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .pl-corl { | ||||||
|  |   text-decoration: underline; | ||||||
|  |   color: #032f62; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .mb-0 { | ||||||
|  |   margin-bottom: 0 !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .my-2 { | ||||||
|  |   margin-bottom: 8px !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .my-2 { | ||||||
|  |   margin-top: 8px !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .pl-0 { | ||||||
|  |   padding-left: 0 !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .py-0 { | ||||||
|  |   padding-top: 0 !important; | ||||||
|  |   padding-bottom: 0 !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .pl-1 { | ||||||
|  |   padding-left: 4px !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .pl-2 { | ||||||
|  |   padding-left: 8px !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .py-2 { | ||||||
|  |   padding-top: 8px !important; | ||||||
|  |   padding-bottom: 8px !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .pl-3 { | ||||||
|  |   padding-left: 16px !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .pl-4 { | ||||||
|  |   padding-left: 24px !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .pl-5 { | ||||||
|  |   padding-left: 32px !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .pl-6 { | ||||||
|  |   padding-left: 40px !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .pl-7 { | ||||||
|  |   padding-left: 48px !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .pl-8 { | ||||||
|  |   padding-left: 64px !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .pl-9 { | ||||||
|  |   padding-left: 80px !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .pl-10 { | ||||||
|  |   padding-left: 96px !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .pl-11 { | ||||||
|  |   padding-left: 112px !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .pl-12 { | ||||||
|  |   padding-left: 128px !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body hr { | ||||||
|  |   border-bottom-color: #eee; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body kbd { | ||||||
|  |   display: inline-block; | ||||||
|  |   padding: 3px 5px; | ||||||
|  |   font: 11px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace; | ||||||
|  |   line-height: 10px; | ||||||
|  |   color: #444d56; | ||||||
|  |   vertical-align: middle; | ||||||
|  |   background-color: #fafbfc; | ||||||
|  |   border: 1px solid #d1d5da; | ||||||
|  |   border-radius: 3px; | ||||||
|  |   box-shadow: inset 0 -1px 0 #d1d5da; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body:after, | ||||||
|  | .markdown-body:before { | ||||||
|  |   display: table; | ||||||
|  |   content: ""; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body:after { | ||||||
|  |   clear: both; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body>:first-child { | ||||||
|  |   margin-top: 0 !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body>:last-child { | ||||||
|  |   margin-bottom: 0 !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body a:not([href]) { | ||||||
|  |   color: inherit; | ||||||
|  |   text-decoration: none; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body blockquote, | ||||||
|  | .markdown-body details, | ||||||
|  | .markdown-body dl, | ||||||
|  | .markdown-body ol, | ||||||
|  | .markdown-body p, | ||||||
|  | .markdown-body pre, | ||||||
|  | .markdown-body table, | ||||||
|  | .markdown-body ul { | ||||||
|  |   margin-top: 0; | ||||||
|  |   margin-bottom: 16px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body hr { | ||||||
|  |   height: .25em; | ||||||
|  |   padding: 0; | ||||||
|  |   margin: 24px 0; | ||||||
|  |   background-color: #e1e4e8; | ||||||
|  |   border: 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body blockquote { | ||||||
|  |   margin: 20px 0 !important; | ||||||
|  |   background-color: #f5f8fc; | ||||||
|  |   padding: 1rem; | ||||||
|  |   color: #8796a8; | ||||||
|  |   border-left: 3px solid #03A9F4; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body blockquote>:first-child { | ||||||
|  |   margin-top: 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body blockquote>:last-child { | ||||||
|  |   margin-bottom: 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body h1, | ||||||
|  | .markdown-body h2, | ||||||
|  | .markdown-body h3, | ||||||
|  | .markdown-body h4, | ||||||
|  | .markdown-body h5, | ||||||
|  | .markdown-body h6 { | ||||||
|  |   margin-top: 24px; | ||||||
|  |   margin-bottom: 16px; | ||||||
|  |   font-weight: 600; | ||||||
|  |   line-height: 1.25; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body h1 { | ||||||
|  |   font-size: 2em; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body h1, | ||||||
|  | .markdown-body h2 { | ||||||
|  |   padding-bottom: .3em; | ||||||
|  |   /* border-bottom: 1px solid #eaecef; */ | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body h2 { | ||||||
|  |   font-size: 1.5em; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body h3 { | ||||||
|  |   font-size: 1.25em; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body h4 { | ||||||
|  |   font-size: 1em; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body h5 { | ||||||
|  |   font-size: .875em; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body h6 { | ||||||
|  |   font-size: .85em; | ||||||
|  |   color: #6a737d; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body ol, | ||||||
|  | .markdown-body ul { | ||||||
|  |   padding-left: 2em; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body ol ol, | ||||||
|  | .markdown-body ol ul, | ||||||
|  | .markdown-body ul ol, | ||||||
|  | .markdown-body ul ul { | ||||||
|  |   margin-top: 0; | ||||||
|  |   margin-bottom: 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body li { | ||||||
|  |   word-wrap: break-all; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body li>p { | ||||||
|  |   margin-top: 16px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body li+li { | ||||||
|  |   margin-top: .25em; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body dl { | ||||||
|  |   padding: 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body dl dt { | ||||||
|  |   padding: 0; | ||||||
|  |   margin-top: 16px; | ||||||
|  |   font-size: 1em; | ||||||
|  |   font-style: italic; | ||||||
|  |   font-weight: 600; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body dl dd { | ||||||
|  |   padding: 0 16px; | ||||||
|  |   margin-bottom: 16px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body table { | ||||||
|  |   display: block; | ||||||
|  |   width: 100%; | ||||||
|  |   overflow: auto; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body table th { | ||||||
|  |   font-weight: 600; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body table td, | ||||||
|  | .markdown-body table th { | ||||||
|  |   padding: 6px 13px; | ||||||
|  |   border: 1px solid #dfe2e5; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body table tr { | ||||||
|  |   background-color: #fff; | ||||||
|  |   border-top: 1px solid #c6cbd1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body table tr:nth-child(2n) { | ||||||
|  |   background-color: #f6f8fa; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body img { | ||||||
|  |   max-width: 100%; | ||||||
|  |   box-sizing: initial; | ||||||
|  |   background-color: #fff; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body img[align=right] { | ||||||
|  |   padding-left: 20px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body img[align=left] { | ||||||
|  |   padding-right: 20px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body code { | ||||||
|  |   padding: .2em .4em; | ||||||
|  |   margin: 0; | ||||||
|  |   font-size: 85%; | ||||||
|  |   background-color: rgba(27, 31, 35, .05); | ||||||
|  |   border-radius: 3px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body pre { | ||||||
|  |   word-wrap: normal; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body pre>code { | ||||||
|  |   padding: 0; | ||||||
|  |   margin: 0; | ||||||
|  |   font-size: 100%; | ||||||
|  |   word-break: normal; | ||||||
|  |   white-space: pre; | ||||||
|  |   background: transparent; | ||||||
|  |   border: 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .highlight { | ||||||
|  |   margin-bottom: 16px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .highlight pre { | ||||||
|  |   margin-bottom: 0; | ||||||
|  |   word-break: normal; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .highlight pre, | ||||||
|  | .markdown-body pre { | ||||||
|  |   padding: 16px; | ||||||
|  |   overflow: auto; | ||||||
|  |   font-size: 85%; | ||||||
|  |   line-height: 1.45; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body pre code { | ||||||
|  |   display: inline; | ||||||
|  |   max-width: auto; | ||||||
|  |   padding: 0; | ||||||
|  |   margin: 0; | ||||||
|  |   overflow: visible; | ||||||
|  |   line-height: inherit; | ||||||
|  |   word-wrap: normal; | ||||||
|  |   background-color: initial; | ||||||
|  |   border: 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .commit-tease-sha { | ||||||
|  |   display: inline-block; | ||||||
|  |   font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace; | ||||||
|  |   font-size: 90%; | ||||||
|  |   color: #444d56; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .full-commit .btn-outline:not(:disabled):hover { | ||||||
|  |   color: #005cc5; | ||||||
|  |   border-color: #005cc5; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .blob-wrapper { | ||||||
|  |   overflow-x: auto; | ||||||
|  |   overflow-y: hidden; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .blob-wrapper-embedded { | ||||||
|  |   max-height: 240px; | ||||||
|  |   overflow-y: auto; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .blob-num { | ||||||
|  |   width: 1%; | ||||||
|  |   min-width: 50px; | ||||||
|  |   padding-right: 10px; | ||||||
|  |   padding-left: 10px; | ||||||
|  |   font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace; | ||||||
|  |   font-size: 12px; | ||||||
|  |   line-height: 20px; | ||||||
|  |   color: rgba(27, 31, 35, .3); | ||||||
|  |   text-align: right; | ||||||
|  |   white-space: nowrap; | ||||||
|  |   vertical-align: top; | ||||||
|  |   cursor: pointer; | ||||||
|  |   -webkit-user-select: none; | ||||||
|  |   -moz-user-select: none; | ||||||
|  |   -ms-user-select: none; | ||||||
|  |   user-select: none; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .blob-num:hover { | ||||||
|  |   color: rgba(27, 31, 35, .6); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .blob-num:before { | ||||||
|  |   content: attr(data-line-number); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .blob-code { | ||||||
|  |   position: relative; | ||||||
|  |   padding-right: 10px; | ||||||
|  |   padding-left: 10px; | ||||||
|  |   line-height: 20px; | ||||||
|  |   vertical-align: top; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .blob-code-inner { | ||||||
|  |   overflow: visible; | ||||||
|  |   font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace; | ||||||
|  |   font-size: 12px; | ||||||
|  |   color: #24292e; | ||||||
|  |   word-wrap: normal; | ||||||
|  |   white-space: pre; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .pl-token.active, | ||||||
|  | .markdown-body .pl-token:hover { | ||||||
|  |   cursor: pointer; | ||||||
|  |   background: #ffea7f; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .tab-size[data-tab-size="1"] { | ||||||
|  |   -moz-tab-size: 1; | ||||||
|  |   tab-size: 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .tab-size[data-tab-size="2"] { | ||||||
|  |   -moz-tab-size: 2; | ||||||
|  |   tab-size: 2; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .tab-size[data-tab-size="3"] { | ||||||
|  |   -moz-tab-size: 3; | ||||||
|  |   tab-size: 3; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .tab-size[data-tab-size="4"] { | ||||||
|  |   -moz-tab-size: 4; | ||||||
|  |   tab-size: 4; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .tab-size[data-tab-size="5"] { | ||||||
|  |   -moz-tab-size: 5; | ||||||
|  |   tab-size: 5; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .tab-size[data-tab-size="6"] { | ||||||
|  |   -moz-tab-size: 6; | ||||||
|  |   tab-size: 6; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .tab-size[data-tab-size="7"] { | ||||||
|  |   -moz-tab-size: 7; | ||||||
|  |   tab-size: 7; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .tab-size[data-tab-size="8"] { | ||||||
|  |   -moz-tab-size: 8; | ||||||
|  |   tab-size: 8; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .tab-size[data-tab-size="9"] { | ||||||
|  |   -moz-tab-size: 9; | ||||||
|  |   tab-size: 9; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .tab-size[data-tab-size="10"] { | ||||||
|  |   -moz-tab-size: 10; | ||||||
|  |   tab-size: 10; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .tab-size[data-tab-size="11"] { | ||||||
|  |   -moz-tab-size: 11; | ||||||
|  |   tab-size: 11; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .tab-size[data-tab-size="12"] { | ||||||
|  |   -moz-tab-size: 12; | ||||||
|  |   tab-size: 12; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .task-list-item { | ||||||
|  |   list-style-type: none; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .task-list-item+.task-list-item { | ||||||
|  |   margin-top: 3px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .markdown-body .task-list-item input { | ||||||
|  |   margin: 0 .2em .25em -1.6em; | ||||||
|  |   vertical-align: middle; | ||||||
|  | } | ||||||
|  | @ -0,0 +1,281 @@ | ||||||
|  | .aside-box { | ||||||
|  |   position: relative; | ||||||
|  |   background-color: white; | ||||||
|  |   border-right: 1px solid rgb(245, 245, 245); | ||||||
|  |   overflow: hidden; | ||||||
|  |   padding: 0; | ||||||
|  | 
 | ||||||
|  |   .header { | ||||||
|  |     display: flex; | ||||||
|  |     flex-direction: row; | ||||||
|  |     align-items: center; | ||||||
|  |     padding: 0 15px; | ||||||
|  | 
 | ||||||
|  |     .from { | ||||||
|  |       flex: 1 1; | ||||||
|  |       flex-shrink: 0; | ||||||
|  |       height: 40px; | ||||||
|  | 
 | ||||||
|  |       /deep/.el-input .el-input__inner { | ||||||
|  |         border-radius: 20px; | ||||||
|  |         width: 170px; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .tools { | ||||||
|  |       flex-basis: 32px; | ||||||
|  |       flex-shrink: 0; | ||||||
|  |       height: 32px; | ||||||
|  |       margin-bottom: 8px; | ||||||
|  |       cursor: pointer; | ||||||
|  |       line-height: 32px; | ||||||
|  |       text-align: center; | ||||||
|  |       position: relative; | ||||||
|  |       user-select: none; | ||||||
|  | 
 | ||||||
|  |       .tools-menu { | ||||||
|  |         position: absolute; | ||||||
|  |         right: 0; | ||||||
|  |         top: 38px; | ||||||
|  |         width: 100px; | ||||||
|  |         min-height: 80px; | ||||||
|  |         box-sizing: border-box; | ||||||
|  |         background-color: rgba(31, 35, 41, 0.9); | ||||||
|  |         border-radius: 5px; | ||||||
|  |         z-index: 1; | ||||||
|  |         padding: 3px 0; | ||||||
|  | 
 | ||||||
|  |         .menu1-item { | ||||||
|  |           height: 40px; | ||||||
|  |           line-height: 40px; | ||||||
|  |           color: white; | ||||||
|  |           font-size: 14px; | ||||||
|  | 
 | ||||||
|  |           &:hover { | ||||||
|  |             background-color: rgba(70, 72, 73, 0.9); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 右侧面板 | ||||||
|  | .panel { | ||||||
|  |   position: relative; | ||||||
|  |   width: 100%; | ||||||
|  |   height: 100%; | ||||||
|  |   box-sizing: border-box; | ||||||
|  | 
 | ||||||
|  |   .header { | ||||||
|  |     display: flex; | ||||||
|  |     align-items: center; | ||||||
|  |     justify-content: space-between; | ||||||
|  |     &.border{ | ||||||
|  |       border-bottom: 1px solid #f5f5f5; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .subheader { | ||||||
|  |     display: flex; | ||||||
|  |     align-items: center; | ||||||
|  |     justify-content: flex-start; | ||||||
|  |     border-top: 1px solid rgb(92, 156, 230); | ||||||
|  |     border-bottom: 1px solid rgb(92, 156, 230); | ||||||
|  | 
 | ||||||
|  |     p { | ||||||
|  |       padding: 0 10px; | ||||||
|  |       cursor: pointer; | ||||||
|  |       font-size: 13px; | ||||||
|  | 
 | ||||||
|  |       &:first-child { | ||||||
|  |         padding-left: 0; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       &.active { | ||||||
|  |         color: #508afe; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .panel-body { | ||||||
|  |     overflow: auto; | ||||||
|  |     width: 100%; | ||||||
|  |     height: 100%; | ||||||
|  |     box-sizing: border-box; | ||||||
|  | 
 | ||||||
|  |     .preloading { | ||||||
|  |       height: 100%; | ||||||
|  |       width: 100%; | ||||||
|  |       display: flex; | ||||||
|  |       justify-content: center; | ||||||
|  |       align-items: center; | ||||||
|  |       flex-direction: column; | ||||||
|  |       user-select: none; | ||||||
|  | 
 | ||||||
|  |       p { | ||||||
|  |         margin-top: 20px; | ||||||
|  |         color: #afacac; | ||||||
|  |         font-size: 14px; | ||||||
|  |         font-weight: 300; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .data-item { | ||||||
|  |       display: flex; | ||||||
|  |       flex-direction: row; | ||||||
|  |       align-items: center; | ||||||
|  |       height: 60px; | ||||||
|  |       cursor: pointer; | ||||||
|  |       padding: 5px 15px; | ||||||
|  |       position: relative; | ||||||
|  |       overflow: hidden; | ||||||
|  |       border-bottom: 1px solid #f1ebeb; | ||||||
|  |       margin-bottom: 2px; | ||||||
|  | 
 | ||||||
|  |       .avatar { | ||||||
|  |         height: 35px; | ||||||
|  |         width: 35px; | ||||||
|  |         flex-basis: 35px; | ||||||
|  |         flex-shrink: 0; | ||||||
|  |         background-color: #508afe; | ||||||
|  |         border-radius: 50%; | ||||||
|  |         overflow: hidden; | ||||||
|  |         display: flex; | ||||||
|  |         justify-content: center; | ||||||
|  |         align-items: center; | ||||||
|  |         font-size: 14px; | ||||||
|  |         color: white; | ||||||
|  |         user-select: none; | ||||||
|  |         transition: ease 1s; | ||||||
|  |         position: relative; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       .card { | ||||||
|  |         height: 40px; | ||||||
|  |         display: flex; | ||||||
|  |         align-content: center; | ||||||
|  |         flex-direction: column; | ||||||
|  |         flex: 1 1; | ||||||
|  |         margin-left: 10px; | ||||||
|  |         overflow: hidden; | ||||||
|  | 
 | ||||||
|  |         .title { | ||||||
|  |           width: 100%; | ||||||
|  |           height: 20px; | ||||||
|  |           display: flex; | ||||||
|  |           align-items: center; | ||||||
|  | 
 | ||||||
|  |           .name { | ||||||
|  |             margin-right: 15px; | ||||||
|  |             color: #1f2329; | ||||||
|  |           } | ||||||
|  | 
 | ||||||
|  |           .larkc-tag { | ||||||
|  |             font-size: 12px; | ||||||
|  |             font-weight: 400; | ||||||
|  |             display: inline-flex; | ||||||
|  |             align-items: center; | ||||||
|  |             justify-content: center; | ||||||
|  |             padding: 0 6px; | ||||||
|  |             height: 20px; | ||||||
|  |             border-radius: 2px; | ||||||
|  |             cursor: default; | ||||||
|  |             user-select: none; | ||||||
|  |             background-color: #dee0e3; | ||||||
|  |             transform: scale(0.8); | ||||||
|  |             transform-origin: left; | ||||||
|  |             flex-shrink: 0; | ||||||
|  |           } | ||||||
|  | 
 | ||||||
|  |           .wait { | ||||||
|  |             background: #ffb445; | ||||||
|  |             color: white; | ||||||
|  |           } | ||||||
|  | 
 | ||||||
|  |           .agree { | ||||||
|  |             background: #53bd53; | ||||||
|  |             color: white; | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         .content { | ||||||
|  |           font-size: 10px; | ||||||
|  |           line-height: 18px; | ||||||
|  |           color: #8f959e; | ||||||
|  |           overflow: hidden; | ||||||
|  |           margin-top: 3px; | ||||||
|  |           font-weight: 300; | ||||||
|  |           white-space: nowrap; | ||||||
|  |           text-overflow: ellipsis; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       .apply-from { | ||||||
|  |         display: flex; | ||||||
|  |         justify-content: center; | ||||||
|  |         align-items: center; | ||||||
|  |         flex-direction: column; | ||||||
|  |         position: relative; | ||||||
|  |         right: -110px; | ||||||
|  |         top: 0px; | ||||||
|  |         height: 60px; | ||||||
|  |         width: 100px; | ||||||
|  |         transition: ease 0.5s 0.3s; | ||||||
|  |         background-color: white; | ||||||
|  |         opacity: 0; | ||||||
|  |         button { | ||||||
|  |           margin: 2px; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       &:hover { | ||||||
|  |         box-shadow: 0 0 8px 4px #f1f1f1; | ||||||
|  | 
 | ||||||
|  |         .avatar { | ||||||
|  |           border-radius: 2px; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         .apply-from { | ||||||
|  |           opacity: 1; | ||||||
|  |           right: 0px; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .broadside-box { | ||||||
|  |   position: absolute; | ||||||
|  |   width: 350px; | ||||||
|  |   height: 100%; | ||||||
|  |   top: 0; | ||||||
|  |   right: 0; | ||||||
|  |   z-index: 2; | ||||||
|  |   animation: showBox 0.5s ease-in-out; | ||||||
|  |   -webkit-animation: showBox 0.5s ease-in-out; | ||||||
|  |   -moz-animation: showBox 0.5s ease-in-out; | ||||||
|  |   -webkit-box-direction: normal; | ||||||
|  |   background: white; | ||||||
|  |   box-shadow: 0 0 14px #cccccc70; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @keyframes showBox { | ||||||
|  |   0% { | ||||||
|  |     transform: translateX(350px); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   to { | ||||||
|  |     transform: translateX(0); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @-webkit-keyframes showBox { | ||||||
|  |   0% { | ||||||
|  |     -webkit-transform: translateX(350px); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   to { | ||||||
|  |     -webkit-transform: translateX(0); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,198 @@ | ||||||
|  | /deep/.el-input__inner { | ||||||
|  |   border-radius: 1px !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #auth-container { | ||||||
|  |   position: fixed; | ||||||
|  |   top: 0; | ||||||
|  |   left: 0; | ||||||
|  |   height: 100%; | ||||||
|  |   width: 100%; | ||||||
|  |   background-color: #f6f8fb; | ||||||
|  | 
 | ||||||
|  |   #logo-name { | ||||||
|  |     width: 200px; | ||||||
|  |     height: 38px; | ||||||
|  |     font-size: 34px; | ||||||
|  |     font-family: Times New Roman, Georgia, Serif; | ||||||
|  |     color: #2196f3; | ||||||
|  |     margin-left: 20px; | ||||||
|  |     margin-top: 20px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   #login-box { | ||||||
|  |     position: absolute; | ||||||
|  |     width: 350px; | ||||||
|  |     min-height: 480px; | ||||||
|  |     top: 50%; | ||||||
|  |     left: 50%; | ||||||
|  |     transform: translate(-50%, -50%); | ||||||
|  |     background-color: white; | ||||||
|  |     border-radius: 5px; | ||||||
|  |     box-shadow: 0 0 0 #ccc; | ||||||
|  |     box-shadow: 0 4px 14px 0 rgba(206, 207, 209, 0.5); | ||||||
|  |     padding: 10px 20px; | ||||||
|  | 
 | ||||||
|  |     .header { | ||||||
|  |       width: 100%; | ||||||
|  |       height: 38px; | ||||||
|  |       font-size: 22px; | ||||||
|  |       margin: 25px 0 20px 0; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .main { | ||||||
|  |       width: 100%; | ||||||
|  | 
 | ||||||
|  |       .links { | ||||||
|  |         display: flex; | ||||||
|  |         justify-content: space-between; | ||||||
|  |         align-items: center; | ||||||
|  | 
 | ||||||
|  |         a { | ||||||
|  |           font-weight: normal !important; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       .send-code-btn { | ||||||
|  |         width: 140px; | ||||||
|  |         height: 40px; | ||||||
|  |         line-height: 40px; | ||||||
|  |         display: inline-block; | ||||||
|  |         background: #f3ecec; | ||||||
|  |         text-align: center; | ||||||
|  |         color: #777373; | ||||||
|  |         cursor: pointer; | ||||||
|  |         user-select: none; | ||||||
|  |         margin-left: 5px; | ||||||
|  | 
 | ||||||
|  |         &:active { | ||||||
|  |           background: #e4dbdb; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       .send-sms-disable { | ||||||
|  |         cursor: not-allowed !important; | ||||||
|  |         background: #f7f7f7 !important; | ||||||
|  |         color: silver !important; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       .submit-btn { | ||||||
|  |         width: 100%; | ||||||
|  |         border-radius: 2px; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .preview-account { | ||||||
|  |   text-align: center; | ||||||
|  | 
 | ||||||
|  |   p { | ||||||
|  |     height: 25px; | ||||||
|  |     line-height: 25px; | ||||||
|  |     color: rgb(45, 44, 44); | ||||||
|  |     font-weight: 100; | ||||||
|  |     font-size: 12px; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .copyright { | ||||||
|  |   position: absolute; | ||||||
|  |   bottom: 30px; | ||||||
|  |   left: 0; | ||||||
|  |   right: 0; | ||||||
|  |   width: 70%; | ||||||
|  |   text-align: center; | ||||||
|  |   margin: 0 auto; | ||||||
|  |   font-size: 12px; | ||||||
|  |   color: #b1a0a0; | ||||||
|  | 
 | ||||||
|  |   a { | ||||||
|  |     color: #777272; | ||||||
|  |     font-weight: 400; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @media screen and (max-height: 500px) { | ||||||
|  |   .copyright { | ||||||
|  |     display: none; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .fly-box { | ||||||
|  |   .fly { | ||||||
|  |     pointer-events: none; | ||||||
|  |     position: fixed; | ||||||
|  |     z-index: 100; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .bg-fly-circle1 { | ||||||
|  |     left: 40px; | ||||||
|  |     top: 100px; | ||||||
|  |     width: 100px; | ||||||
|  |     height: 100px; | ||||||
|  |     border-radius: 50%; | ||||||
|  |     background: linear-gradient( | ||||||
|  |       to right, | ||||||
|  |       rgba(100, 84, 239, 0.07) 0%, | ||||||
|  |       rgba(48, 33, 236, 0.04) 100% | ||||||
|  |     ); | ||||||
|  |     animation: move 2.5s linear infinite; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .bg-fly-circle2 { | ||||||
|  |     left: 3%; | ||||||
|  |     top: 60%; | ||||||
|  |     width: 150px; | ||||||
|  |     height: 150px; | ||||||
|  |     border-radius: 50%; | ||||||
|  |     background: linear-gradient( | ||||||
|  |       to right, | ||||||
|  |       rgba(100, 84, 239, 0.08) 0%, | ||||||
|  |       rgba(48, 33, 236, 0.04) 100% | ||||||
|  |     ); | ||||||
|  |     animation: move 3s linear infinite; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .bg-fly-circle3 { | ||||||
|  |     right: 2%; | ||||||
|  |     top: 140px; | ||||||
|  |     width: 145px; | ||||||
|  |     height: 145px; | ||||||
|  |     border-radius: 50%; | ||||||
|  |     background: linear-gradient( | ||||||
|  |       to right, | ||||||
|  |       rgba(100, 84, 239, 0.1) 0%, | ||||||
|  |       rgba(48, 33, 236, 0.04) 100% | ||||||
|  |     ); | ||||||
|  |     animation: move 2.5s linear infinite; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .bg-fly-circle4 { | ||||||
|  |     right: 5%; | ||||||
|  |     top: 60%; | ||||||
|  |     width: 160px; | ||||||
|  |     height: 160px; | ||||||
|  |     border-radius: 50%; | ||||||
|  |     background: linear-gradient( | ||||||
|  |       to right, | ||||||
|  |       rgba(100, 84, 239, 0.02) 0%, | ||||||
|  |       rgba(48, 33, 236, 0.04) 100% | ||||||
|  |     ); | ||||||
|  |     animation: move 3.5s linear infinite; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @keyframes move { | ||||||
|  |   0% { | ||||||
|  |     transform: translateY(0px); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   50% { | ||||||
|  |     transform: translateY(25px); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   100% { | ||||||
|  |     transform: translateY(0px); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,62 @@ | ||||||
|  | * { | ||||||
|  |     margin: 0; | ||||||
|  |     padding: 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | body, | ||||||
|  | html { | ||||||
|  |     height: 100%; | ||||||
|  |     min-width: 500px; | ||||||
|  |     font-family: "Microsoft YaHei"; | ||||||
|  |     font-size: 16px; | ||||||
|  |     color: #333; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | button, | ||||||
|  | input, | ||||||
|  | select, | ||||||
|  | textarea { | ||||||
|  |     font-size: 100%; | ||||||
|  |     margin: 0; | ||||||
|  |     padding: 0; | ||||||
|  |     border: none; | ||||||
|  |     outline: none; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | img { | ||||||
|  |     border: 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | a, | ||||||
|  | img { | ||||||
|  |     -webkit-touch-callout: none | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | a { | ||||||
|  |     text-decoration: none; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | textarea { | ||||||
|  |     resize: none; | ||||||
|  |     outline: 0; | ||||||
|  |     white-space: pre-wrap; | ||||||
|  |     word-wrap: break-word; | ||||||
|  |     border: none; | ||||||
|  |     background: #fff; | ||||||
|  |     font-family: "Microsoft YaHei"; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | :focus { | ||||||
|  |     outline: none; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .clearfix { | ||||||
|  |     clear: both; | ||||||
|  |     content: ""; | ||||||
|  |     display: block; | ||||||
|  |     overflow: hidden | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .clear { | ||||||
|  |     clear: both; | ||||||
|  | } | ||||||
|  | @ -0,0 +1,50 @@ | ||||||
|  | .message-group { | ||||||
|  |   min-height: 30px; | ||||||
|  |   display: flex; | ||||||
|  |   margin-bottom: 5px; | ||||||
|  |   flex-direction: row; | ||||||
|  |   padding: 3px 12px 3px 0; | ||||||
|  | 
 | ||||||
|  |   &:first-child { | ||||||
|  |     margin-top: 10px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .left-box { | ||||||
|  |     width: 50px; | ||||||
|  |     flex-shrink: 0; | ||||||
|  |     display: flex; | ||||||
|  |     justify-content: center; | ||||||
|  |     user-select: none; | ||||||
|  |     padding-top: 8px; | ||||||
|  | 
 | ||||||
|  |     img { | ||||||
|  |       height: 30px; | ||||||
|  |       width: 30px; | ||||||
|  |       border-radius: 3px; | ||||||
|  |       cursor: pointer; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .right-box { | ||||||
|  |     flex: auto; | ||||||
|  |     overflow-x: auto; | ||||||
|  |     padding: 0px 5px 15px 5px; | ||||||
|  | 
 | ||||||
|  |     .msg-header { | ||||||
|  |       height: 30px; | ||||||
|  |       line-height: 30px; | ||||||
|  |       font-size: 12px; | ||||||
|  |       color: #a09a9a; | ||||||
|  |       position: relative; | ||||||
|  |       user-select: none; | ||||||
|  | 
 | ||||||
|  |       .name { | ||||||
|  |         color: #333; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /deep/.text-message { | ||||||
|  |       border-radius: 0; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,10 @@ | ||||||
|  | //主题皮肤 - 预留功能 | ||||||
|  | :root { | ||||||
|  |     --themeBagColor: red; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ------- 定义 Less 变量 ------- | ||||||
|  | @themeBagColor: var(--themeBagColor); | ||||||
|  | 
 | ||||||
|  | // 遮罩层背景颜色 | ||||||
|  | @maskBagColor: rgba(31, 35, 41, .3); | ||||||
| After Width: | Height: | Size: 78 KiB | 
| After Width: | Height: | Size: 117 KiB | 
| After Width: | Height: | Size: 830 KiB | 
| After Width: | Height: | Size: 1.3 MiB | 
| After Width: | Height: | Size: 789 KiB | 
| After Width: | Height: | Size: 114 KiB | 
| After Width: | Height: | Size: 671 KiB | 
| After Width: | Height: | Size: 5.5 KiB | 
| After Width: | Height: | Size: 151 KiB | 
| After Width: | Height: | Size: 192 KiB | 
| After Width: | Height: | Size: 2.9 KiB | 
| After Width: | Height: | Size: 11 KiB | 
| After Width: | Height: | Size: 12 KiB | 
| After Width: | Height: | Size: 758 B | 
| After Width: | Height: | Size: 882 B | 
| After Width: | Height: | Size: 13 KiB | 
|  | @ -0,0 +1,313 @@ | ||||||
|  | <template> | ||||||
|  |   <div class="lum-dialog-mask"> | ||||||
|  |     <div class="container animated bounceInDown" :class="{ 'full-screen': isFullScreen }"> | ||||||
|  |       <el-container class="full-height"> | ||||||
|  |         <el-header class="header no-padding" height="50px"> | ||||||
|  |           <div class="tools"> | ||||||
|  |             <span>选择编程语言:  </span> | ||||||
|  |             <el-select v-model="language" size="mini" filterable placeholder="语言类型" :disabled="!editMode"> | ||||||
|  |               <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" /> | ||||||
|  |             </el-select> | ||||||
|  |           </div> | ||||||
|  |           <i class="el-icon-close close-btn" @click="close" /> | ||||||
|  |           <i class="iconfont icon-full-screen" :class="{ | ||||||
|  |               'icon-tuichuquanping': isFullScreen, | ||||||
|  |               'icon-quanping ': !isFullScreen, | ||||||
|  |             }" :title="isFullScreen ? '关闭全屏模式' : '打开全屏模式'" @click="isFullScreen = !isFullScreen" /> | ||||||
|  |         </el-header> | ||||||
|  | 
 | ||||||
|  |         <el-main class="main no-padding"> | ||||||
|  |           <PrismEditor class="peditor" style="border-radius: 0" :code="code" :language="language" :line-numbers="true" | ||||||
|  |             @change="codeChanged" /> | ||||||
|  |         </el-main> | ||||||
|  | 
 | ||||||
|  |         <el-footer class="footer no-padding" height="50px"> | ||||||
|  |           <div class="code-num"> | ||||||
|  |             <span>代码字数:{{ code.length }}字</span> | ||||||
|  |             <span v-show="code.length > 10000 && editMode" class="code-warning"> | ||||||
|  |               (字数不能超过10000字) | ||||||
|  |             </span> | ||||||
|  |           </div> | ||||||
|  | 
 | ||||||
|  |           <div class="buttom-group"> | ||||||
|  |             <el-button size="small" plain @click="close"> | ||||||
|  |               {{ editMode ? '取消编辑' : '关闭预览' }} | ||||||
|  |             </el-button> | ||||||
|  | 
 | ||||||
|  |             <el-button v-show="editMode" type="primary" size="small" @click="submit">发送代码 | ||||||
|  |             </el-button> | ||||||
|  |           </div> | ||||||
|  |         </el-footer> | ||||||
|  |       </el-container> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | <script> | ||||||
|  | import PrismEditor from "vue-prism-editor"; | ||||||
|  | import "vue-prism-editor/dist/VuePrismEditor.css"; | ||||||
|  | import "prismjs/themes/prism-okaidia.css"; | ||||||
|  | import Vue from "vue"; | ||||||
|  | import { Select, Option } from "element-ui"; | ||||||
|  | Vue.use(Select); | ||||||
|  | Vue.use(Option); | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   name: "TalkCodeBlock", | ||||||
|  |   components: { | ||||||
|  |     PrismEditor, | ||||||
|  |   }, | ||||||
|  |   props: { | ||||||
|  |     loadCode: { | ||||||
|  |       type: String, | ||||||
|  |       default: "", | ||||||
|  |     }, | ||||||
|  |     loadLang: { | ||||||
|  |       type: String, | ||||||
|  |       default: "", | ||||||
|  |     }, | ||||||
|  |     editMode: { | ||||||
|  |       type: Boolean, | ||||||
|  |       default: false, | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       language: "", | ||||||
|  |       code: "", | ||||||
|  |       options: [ | ||||||
|  |         { | ||||||
|  |           value: "css", | ||||||
|  |           label: "css", | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           value: "less", | ||||||
|  |           label: "less", | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           value: "javascript", | ||||||
|  |           label: "javascript", | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           value: "json", | ||||||
|  |           label: "json", | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           value: "bash", | ||||||
|  |           label: "bash", | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           value: "c", | ||||||
|  |           label: "c", | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           value: "cil", | ||||||
|  |           label: "cil", | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           value: "docker", | ||||||
|  |           label: "docker", | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           value: "git", | ||||||
|  |           label: "git", | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           value: "go", | ||||||
|  |           label: "go", | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           value: "java", | ||||||
|  |           label: "java", | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           value: "lua", | ||||||
|  |           label: "lua", | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           value: "nginx", | ||||||
|  |           label: "nginx", | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           value: "objectivec", | ||||||
|  |           label: "objectivec", | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           value: "php", | ||||||
|  |           label: "php", | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           value: "python", | ||||||
|  |           label: "python", | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           value: "ruby", | ||||||
|  |           label: "ruby", | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           value: "rust", | ||||||
|  |           label: "rust", | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           value: "sql", | ||||||
|  |           label: "sql", | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           value: "swift", | ||||||
|  |           label: "swift", | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           value: "vim", | ||||||
|  |           label: "vim", | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           value: "visual-basic", | ||||||
|  |           label: "visual-basic", | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           value: "shell", | ||||||
|  |           label: "shell", | ||||||
|  |         }, | ||||||
|  |       ], | ||||||
|  |       isFullScreen: false, | ||||||
|  |     }; | ||||||
|  |   }, | ||||||
|  |   watch: { | ||||||
|  |     loadCode(value) { | ||||||
|  |       this.code = value; | ||||||
|  |     }, | ||||||
|  |     loadLang(value) { | ||||||
|  |       this.language = value; | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   created() { | ||||||
|  |     this.code = this.loadCode; | ||||||
|  |     this.language = this.loadLang; | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     submit() { | ||||||
|  |       if (!this.code) { | ||||||
|  |         this.$message.error("代码块不能为空..."); | ||||||
|  |         return false; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       if (this.language == "") { | ||||||
|  |         this.$message.error("请选择语言"); | ||||||
|  |         return false; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       if (this.code.length > 10000) { | ||||||
|  |         this.$message.error("代码字数不能超过10000字!!!"); | ||||||
|  |         return false; | ||||||
|  |       } | ||||||
|  |       this.$emit("confirm", { | ||||||
|  |         language: this.language, | ||||||
|  |         code: this.code, | ||||||
|  |       }); | ||||||
|  |     }, | ||||||
|  |     close() { | ||||||
|  |       this.$emit("close"); | ||||||
|  |     }, | ||||||
|  |     codeChanged(code) { | ||||||
|  |       this.code = code; | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | }; | ||||||
|  | </script> | ||||||
|  | <style lang="less" scoped> | ||||||
|  | .container { | ||||||
|  |   width: 80%; | ||||||
|  |   max-width: 800px; | ||||||
|  |   height: 600px; | ||||||
|  |   overflow: hidden; | ||||||
|  |   box-shadow: 0 2px 8px 0 rgba(31, 35, 41, 0.2); | ||||||
|  |   transition: 0.5s ease; | ||||||
|  |   background: #2d2d2d; | ||||||
|  | 
 | ||||||
|  |   .header { | ||||||
|  |     position: relative; | ||||||
|  |     background-color: white; | ||||||
|  | 
 | ||||||
|  |     .close-btn { | ||||||
|  |       position: absolute; | ||||||
|  |       right: 12px; | ||||||
|  |       top: 13px; | ||||||
|  |       font-size: 24px; | ||||||
|  |       cursor: pointer; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .icon-full-screen { | ||||||
|  |       position: absolute; | ||||||
|  |       right: 45px; | ||||||
|  |       top: 13px; | ||||||
|  |       font-size: 20px; | ||||||
|  |       cursor: pointer; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .tools { | ||||||
|  |       line-height: 50px; | ||||||
|  |       padding-left: 10px; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .footer { | ||||||
|  |     background-color: #3c3c3c; | ||||||
|  |     padding-right: 20px; | ||||||
|  |     line-height: 50px; | ||||||
|  | 
 | ||||||
|  |     .code-num { | ||||||
|  |       float: left; | ||||||
|  |       color: white; | ||||||
|  |       padding-left: 10px; | ||||||
|  |       font-size: 14px; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .code-warning { | ||||||
|  |       color: red; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .buttom-group { | ||||||
|  |       float: right; | ||||||
|  |       height: 100%; | ||||||
|  |       line-height: 50px; | ||||||
|  |       text-align: right; | ||||||
|  | 
 | ||||||
|  |       button { | ||||||
|  |         border-radius: 0; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .full-screen { | ||||||
|  |   width: 100%; | ||||||
|  |   height: 100%; | ||||||
|  |   max-width: 100%; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /deep/ .el-input__inner { | ||||||
|  |   border-radius: 0; | ||||||
|  |   width: 130px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /deep/ pre { | ||||||
|  |   border-radius: 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /deep/ .prism-editor-wrapper pre::-webkit-scrollbar { | ||||||
|  |   background-color: #272822; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /deep/ .prism-editor-wrapper pre::-webkit-scrollbar-thumb { | ||||||
|  |   background-color: #41413f; | ||||||
|  |   cursor: pointer; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /deep/ .prism-editor-wrapper::-webkit-scrollbar { | ||||||
|  |   background-color: #272822; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /deep/ .prism-editor-wrapper::-webkit-scrollbar-thumb { | ||||||
|  |   background-color: rgb(114, 112, 112); | ||||||
|  |   cursor: pointer; | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,139 @@ | ||||||
|  | <template> | ||||||
|  |   <div class="lum-dialog-mask" v-show="isShow"> | ||||||
|  |     <el-container class="lum-dialog-box" v-outside="close"> | ||||||
|  |       <el-header class="no-padding header" height="60px"> | ||||||
|  |         <p>会话记录 ({{ records.length }})</p> | ||||||
|  |         <p class="tools"> | ||||||
|  |           <i class="el-icon-close" @click="close" /> | ||||||
|  |         </p> | ||||||
|  |       </el-header> | ||||||
|  |       <el-main class="no-padding main" v-loading="loading"> | ||||||
|  |         <el-scrollbar class="full-height" tag="section" :native="false"> | ||||||
|  |           <div v-for="record in records" :key="record.id" class="message-group"> | ||||||
|  |             <div class="left-box"> | ||||||
|  |               <el-avatar | ||||||
|  |                 fit="contain" | ||||||
|  |                 shape="square" | ||||||
|  |                 :size="30" | ||||||
|  |                 :src="record.avatar" | ||||||
|  |               /> | ||||||
|  |             </div> | ||||||
|  | 
 | ||||||
|  |             <div class="right-box"> | ||||||
|  |               <div class="msg-header"> | ||||||
|  |                 <span class="name"> | ||||||
|  |                   {{ | ||||||
|  |                     record.nickname_remarks | ||||||
|  |                       ? record.nickname_remarks | ||||||
|  |                       : record.nickname | ||||||
|  |                   }} | ||||||
|  |                 </span> | ||||||
|  |                 <el-divider direction="vertical" /> | ||||||
|  |                 <span class="time">{{ record.created_at }}</span> | ||||||
|  |               </div> | ||||||
|  | 
 | ||||||
|  |               <!-- 文本消息 --> | ||||||
|  |               <text-message | ||||||
|  |                 v-if="record.msg_type == 1" | ||||||
|  |                 :content="record.content" | ||||||
|  |               /> | ||||||
|  | 
 | ||||||
|  |               <!-- 文件 - 图片消息 --> | ||||||
|  |               <image-message | ||||||
|  |                 v-else-if="record.msg_type == 2 && record.file.file_type == 1" | ||||||
|  |                 :src="record.file.file_url" | ||||||
|  |               /> | ||||||
|  | 
 | ||||||
|  |               <!-- 文件 - 音频消息 --> | ||||||
|  |               <audio-message | ||||||
|  |                 v-else-if="record.msg_type == 2 && record.file.file_type == 2" | ||||||
|  |                 :src="record.file.file_url" | ||||||
|  |               /> | ||||||
|  | 
 | ||||||
|  |               <!-- 文件 - 视频消息 --> | ||||||
|  |               <video-message | ||||||
|  |                 v-else-if="record.msg_type == 2 && record.file.file_type == 3" | ||||||
|  |               /> | ||||||
|  | 
 | ||||||
|  |               <!-- 文件 - 其它格式文件 --> | ||||||
|  |               <file-message | ||||||
|  |                 v-else-if="record.msg_type == 2 && record.file.file_type == 4" | ||||||
|  |                 :file="record.file" | ||||||
|  |                 :record_id="record.id" | ||||||
|  |               /> | ||||||
|  | 
 | ||||||
|  |               <!-- 代码块消息 --> | ||||||
|  |               <code-message | ||||||
|  |                 v-else-if="record.msg_type == 4" | ||||||
|  |                 :code="record.code_block.code" | ||||||
|  |                 :lang="record.code_block.code_lang" | ||||||
|  |               /> | ||||||
|  | 
 | ||||||
|  |               <div v-else class="other-message">未知消息类型</div> | ||||||
|  |             </div> | ||||||
|  |           </div> | ||||||
|  |         </el-scrollbar> | ||||||
|  |       </el-main> | ||||||
|  |     </el-container> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | <script> | ||||||
|  | import { ServeGetForwardRecords } from '@/api/chat' | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   name: 'TalkForwardRecord', | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       record_id: 0, | ||||||
|  |       records: [], | ||||||
|  |       loading: false, | ||||||
|  |       isShow: false, | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     open(record_id) { | ||||||
|  |       if (record_id !== this.record_id) { | ||||||
|  |         this.record_id = record_id | ||||||
|  |         this.records = [] | ||||||
|  |         this.loadRecords() | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       this.isShow = true | ||||||
|  |     }, | ||||||
|  |     close() { | ||||||
|  |       this.isShow = false | ||||||
|  |     }, | ||||||
|  |     loadRecords() { | ||||||
|  |       this.loading = true | ||||||
|  |       ServeGetForwardRecords({ | ||||||
|  |         record_id: this.record_id, | ||||||
|  |       }) | ||||||
|  |         .then(res => { | ||||||
|  |           if (res.code == 200) { | ||||||
|  |             this.records = res.data.rows | ||||||
|  |           } | ||||||
|  |         }) | ||||||
|  |         .finally(() => { | ||||||
|  |           this.loading = false | ||||||
|  |         }) | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | <style lang="less" scoped> | ||||||
|  | .lum-dialog-mask { | ||||||
|  |   z-index: 99999; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .lum-dialog-box { | ||||||
|  |   width: 500px; | ||||||
|  |   max-width: 500px; | ||||||
|  |   height: 600px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /deep/.el-scrollbar__wrap { | ||||||
|  |   overflow-x: hidden; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @import '~@/assets/css/talk/talk-records.less'; | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,602 @@ | ||||||
|  | <template> | ||||||
|  |   <div class="lum-dialog-mask"> | ||||||
|  |     <el-container class="lum-dialog-box" :class="{ 'full-screen': fullscreen }"> | ||||||
|  |       <el-header height="60px" class="header"> | ||||||
|  |         <p>消息管理器</p> | ||||||
|  |         <p class="title"> | ||||||
|  |           <span>{{ query.talk_type == 1 ? "好友" : "群" }}【{{ title }}】</span> | ||||||
|  |         </p> | ||||||
|  |         <p class="tools"> | ||||||
|  |           <i | ||||||
|  |             class="iconfont" | ||||||
|  |             style="transform: scale(0.85)" | ||||||
|  |             :class="fullscreen ? 'icon-tuichuquanping' : 'icon-quanping'" | ||||||
|  |             @click="fullscreen = !fullscreen" | ||||||
|  |           /> | ||||||
|  |           <i class="el-icon-close" @click="$emit('close')" /> | ||||||
|  |         </p> | ||||||
|  |       </el-header> | ||||||
|  | 
 | ||||||
|  |       <el-header height="38px" class="sub-header"> | ||||||
|  |         <i | ||||||
|  |           class="iconfont pointer" | ||||||
|  |           :class="{ 'icon-shouqi2': broadside, 'icon-zhankai': !broadside }" | ||||||
|  |           @click="triggerBroadside" | ||||||
|  |         /> | ||||||
|  |         <div class="search-box no-select"> | ||||||
|  |           <i class="el-icon-search" /> | ||||||
|  |           <input | ||||||
|  |             v-model="search.keyword" | ||||||
|  |             type="text" | ||||||
|  |             maxlength="30" | ||||||
|  |             placeholder="关键字搜索" | ||||||
|  |             @keyup.enter="searchText($event)" | ||||||
|  |           /> | ||||||
|  |         </div> | ||||||
|  |       </el-header> | ||||||
|  | 
 | ||||||
|  |       <el-container class="full-height ov-hidden"> | ||||||
|  |         <el-aside width="200px" class="broadside" v-show="broadside"> | ||||||
|  |           <el-container class="full-height"> | ||||||
|  |             <el-header height="40px" class="aside-header"> | ||||||
|  |               <div | ||||||
|  |                 class="item" | ||||||
|  |                 :class="{ selected: contacts.show == 'friends' }" | ||||||
|  |                 @click="contacts.show = 'friends'" | ||||||
|  |               > | ||||||
|  |                 我的好友({{ contacts.friends.length }}) | ||||||
|  |               </div> | ||||||
|  |               <div class="item-shuxian">|</div> | ||||||
|  |               <div | ||||||
|  |                 class="item" | ||||||
|  |                 :class="{ selected: contacts.show == 'groups' }" | ||||||
|  |                 @click="contacts.show = 'groups'" | ||||||
|  |               > | ||||||
|  |                 我的群组({{ contacts.groups.length }}) | ||||||
|  |               </div> | ||||||
|  |             </el-header> | ||||||
|  |             <el-main class="no-padding"> | ||||||
|  |               <el-scrollbar class="full-height" tag="section" :native="false"> | ||||||
|  |                 <div | ||||||
|  |                   v-for="item in contacts[contacts.show]" | ||||||
|  |                   class="contacts-item pointer" | ||||||
|  |                   :class="{ | ||||||
|  |                     selected: | ||||||
|  |                       query.talk_type == item.type && | ||||||
|  |                       query.receiver_id == item.id, | ||||||
|  |                   }" | ||||||
|  |                   :key="item.id" | ||||||
|  |                   @click="triggerMenuItem(item)" | ||||||
|  |                 > | ||||||
|  |                   <div class="avatar"> | ||||||
|  |                     <el-avatar :size="20" :src="item.avatar"> | ||||||
|  |                       <img src="~@/assets/image/detault-avatar.jpg" /> | ||||||
|  |                     </el-avatar> | ||||||
|  |                   </div> | ||||||
|  |                   <div class="content" v-text="item.name"></div> | ||||||
|  |                 </div> | ||||||
|  |               </el-scrollbar> | ||||||
|  |             </el-main> | ||||||
|  |           </el-container> | ||||||
|  |         </el-aside> | ||||||
|  | 
 | ||||||
|  |         <!-- 聊天记录阅览 --> | ||||||
|  |         <el-main v-show="showBox == 0" class="no-padding"> | ||||||
|  |           <el-container class="full-height"> | ||||||
|  |             <el-header height="40px" class="type-items"> | ||||||
|  |               <span | ||||||
|  |                 v-for="tab in tabType" | ||||||
|  |                 :class="{ active: query.msg_type == tab.type }" | ||||||
|  |                 @click="triggerLoadType(tab.type)" | ||||||
|  |                 >{{ tab.name }} | ||||||
|  |               </span> | ||||||
|  |             </el-header> | ||||||
|  | 
 | ||||||
|  |             <el-main | ||||||
|  |               v-if="records.isEmpty" | ||||||
|  |               class="history-record animated fadeIn" | ||||||
|  |             > | ||||||
|  |               <div class="empty-records"> | ||||||
|  |                 <img src="~@/assets/image/chat-search-no-message.png" /> | ||||||
|  |                 <p>暂无聊天记录</p> | ||||||
|  |               </div> | ||||||
|  |             </el-main> | ||||||
|  | 
 | ||||||
|  |             <el-main v-else class="history-record"> | ||||||
|  |               <el-scrollbar class="full-height" tag="section" :native="false"> | ||||||
|  |                 <div | ||||||
|  |                   v-for="record in records.items" | ||||||
|  |                   :key="record.id" | ||||||
|  |                   class="message-group" | ||||||
|  |                 > | ||||||
|  |                   <div class="left-box"> | ||||||
|  |                     <el-avatar | ||||||
|  |                       shape="square" | ||||||
|  |                       fit="contain" | ||||||
|  |                       :size="30" | ||||||
|  |                       :src="record.avatar" | ||||||
|  |                     /> | ||||||
|  |                   </div> | ||||||
|  | 
 | ||||||
|  |                   <div class="right-box"> | ||||||
|  |                     <div class="msg-header"> | ||||||
|  |                       <span class="name"> | ||||||
|  |                         {{ | ||||||
|  |                           record.nickname_remarks | ||||||
|  |                             ? record.nickname_remarks | ||||||
|  |                             : record.nickname | ||||||
|  |                         }} | ||||||
|  |                       </span> | ||||||
|  |                       <el-divider direction="vertical" /> | ||||||
|  |                       <span class="time">{{ record.created_at }}</span> | ||||||
|  |                     </div> | ||||||
|  | 
 | ||||||
|  |                     <!-- 文本消息 --> | ||||||
|  |                     <text-message | ||||||
|  |                       v-if="record.msg_type == 1" | ||||||
|  |                       :content="record.content" | ||||||
|  |                     /> | ||||||
|  | 
 | ||||||
|  |                     <!-- 文件 - 图片消息 --> | ||||||
|  |                     <image-message | ||||||
|  |                       v-else-if=" | ||||||
|  |                         record.msg_type == 2 && record.file.file_type == 1 | ||||||
|  |                       " | ||||||
|  |                       :src="record.file.file_url" | ||||||
|  |                     /> | ||||||
|  | 
 | ||||||
|  |                     <!-- 文件 - 音频消息 --> | ||||||
|  |                     <audio-message | ||||||
|  |                       v-else-if=" | ||||||
|  |                         record.msg_type == 2 && record.file.file_type == 2 | ||||||
|  |                       " | ||||||
|  |                       :src="record.file.file_url" | ||||||
|  |                     /> | ||||||
|  | 
 | ||||||
|  |                     <!-- 文件 - 视频消息 --> | ||||||
|  |                     <video-message | ||||||
|  |                       v-else-if=" | ||||||
|  |                         record.msg_type == 2 && record.file.file_type == 3 | ||||||
|  |                       " | ||||||
|  |                     /> | ||||||
|  | 
 | ||||||
|  |                     <!-- 文件 - 其它格式文件 --> | ||||||
|  |                     <file-message | ||||||
|  |                       v-else-if=" | ||||||
|  |                         record.msg_type == 2 && record.file.file_type == 4 | ||||||
|  |                       " | ||||||
|  |                       :file="record.file" | ||||||
|  |                       :record_id="record.id" | ||||||
|  |                     /> | ||||||
|  | 
 | ||||||
|  |                     <!-- 会话记录消息 --> | ||||||
|  |                     <forward-message | ||||||
|  |                       v-else-if="record.msg_type == 3" | ||||||
|  |                       :forward="record.forward" | ||||||
|  |                       :record_id="record.id" | ||||||
|  |                     /> | ||||||
|  | 
 | ||||||
|  |                     <!-- 代码块消息 --> | ||||||
|  |                     <code-message | ||||||
|  |                       v-else-if="record.msg_type == 4" | ||||||
|  |                       :code="record.code_block.code" | ||||||
|  |                       :lang="record.code_block.code_lang" | ||||||
|  |                     /> | ||||||
|  | 
 | ||||||
|  |                     <!-- 投票消息 --> | ||||||
|  |                     <vote-message | ||||||
|  |                       v-else-if="record.msg_type == 5" | ||||||
|  |                       :record_id="record.id" | ||||||
|  |                       :vote="record.vote" | ||||||
|  |                     /> | ||||||
|  | 
 | ||||||
|  |                     <div v-else class="other-message">未知消息类型</div> | ||||||
|  |                   </div> | ||||||
|  |                 </div> | ||||||
|  | 
 | ||||||
|  |                 <!-- 数据加载栏 --> | ||||||
|  |                 <div v-show="records.loadStatus == 1" class="load-button blue"> | ||||||
|  |                   <i class="el-icon-loading" /> | ||||||
|  |                   <span>加载数据中...</span> | ||||||
|  |                 </div> | ||||||
|  |                 <div v-show="records.loadStatus == 0" class="load-button"> | ||||||
|  |                   <i class="el-icon-arrow-down" /> | ||||||
|  |                   <span @click="loadChatRecord">加载更多...</span> | ||||||
|  |                 </div> | ||||||
|  |               </el-scrollbar> | ||||||
|  |             </el-main> | ||||||
|  |           </el-container> | ||||||
|  |         </el-main> | ||||||
|  |       </el-container> | ||||||
|  |     </el-container> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | <script> | ||||||
|  | import { ServeGetContacts } from "@/api/contacts"; | ||||||
|  | import { ServeFindTalkRecords } from "@/api/chat"; | ||||||
|  | import { formatSize as renderSize, download, imgZoom } from "@/utils/functions"; | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   name: "TalkSearchRecord", | ||||||
|  |   props: { | ||||||
|  |     params: { | ||||||
|  |       type: Object, | ||||||
|  |       default: () => { | ||||||
|  |         return { | ||||||
|  |           talk_type: 0, | ||||||
|  |           receiver_id: 0, | ||||||
|  |           title: "", | ||||||
|  |         }; | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       fullscreen: false, | ||||||
|  | 
 | ||||||
|  |       user_id: this.$store.state.user.id, | ||||||
|  |       title: "", | ||||||
|  | 
 | ||||||
|  |       // 侧边栏相关信息 | ||||||
|  |       broadside: false, | ||||||
|  |       contacts: { | ||||||
|  |         show: "friends", | ||||||
|  |         friends: [], | ||||||
|  |         groups: [], | ||||||
|  |       }, | ||||||
|  | 
 | ||||||
|  |       query: { | ||||||
|  |         talk_type: 0, | ||||||
|  |         receiver_id: 0, | ||||||
|  |         msg_type: 0, | ||||||
|  |       }, | ||||||
|  | 
 | ||||||
|  |       // 用户聊天记录 | ||||||
|  |       records: { | ||||||
|  |         record_id: 0, | ||||||
|  |         items: [], | ||||||
|  |         isEmpty: false, | ||||||
|  |         loadStatus: 0, | ||||||
|  |       }, | ||||||
|  | 
 | ||||||
|  |       showBox: 0, | ||||||
|  | 
 | ||||||
|  |       tabType: [ | ||||||
|  |         { name: "全部", type: 0 }, | ||||||
|  |         { name: "文件", type: 2 }, | ||||||
|  |         { name: "会话记录", type: 3 }, | ||||||
|  |         { name: "代码块", type: 4 }, | ||||||
|  |         { name: "群投票", type: 5 }, | ||||||
|  |       ], | ||||||
|  | 
 | ||||||
|  |       search: { | ||||||
|  |         keyword: "", // 关键字查询 | ||||||
|  |         date: "", // 时间查询 | ||||||
|  |         page: 1, // 当前分页 | ||||||
|  |         totalPage: 50, // 总分页 | ||||||
|  |         items: [], // 数据列表 | ||||||
|  |         isShowDate: false, | ||||||
|  |       }, | ||||||
|  |     }; | ||||||
|  |   }, | ||||||
|  |   mounted() { | ||||||
|  |     this.title = this.params.title; | ||||||
|  |     this.query = { | ||||||
|  |       talk_type: this.params.talk_type, | ||||||
|  |       receiver_id: this.params.receiver_id, | ||||||
|  |       msg_type: 0, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     this.loadChatRecord(0); | ||||||
|  |   }, | ||||||
|  |   created() { | ||||||
|  |     this.loadFriends(); | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     download, | ||||||
|  |     renderSize, | ||||||
|  | 
 | ||||||
|  |     // 获取图片信息 | ||||||
|  |     getImgStyle(url) { | ||||||
|  |       return imgZoom(url, 200); | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     // 获取会话记录消息名称 | ||||||
|  |     getForwardTitle(item) { | ||||||
|  |       let arr = [...new Set(item.map((v) => v.nickname))]; | ||||||
|  |       return arr.join("、") + "的会话记录"; | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     // 获取好友列表 | ||||||
|  |     loadFriends() { | ||||||
|  |       ServeGetContacts().then(({ code, data }) => { | ||||||
|  |         if (code == 200) { | ||||||
|  |           this.contacts.friends = data.map((item) => { | ||||||
|  |             return { | ||||||
|  |               id: item.id, | ||||||
|  |               type: 1, | ||||||
|  |               avatar: item.avatar, | ||||||
|  |               name: item.friend_remark ? item.friend_remark : item.nickname, | ||||||
|  |             }; | ||||||
|  |           }); | ||||||
|  |         } | ||||||
|  |       }); | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     // 左侧联系人菜单点击事件 | ||||||
|  |     triggerMenuItem(item) { | ||||||
|  |       this.title = item.name; | ||||||
|  |       this.query.talk_type = item.type; | ||||||
|  |       this.query.receiver_id = item.id; | ||||||
|  |       this.showBox = 0; | ||||||
|  | 
 | ||||||
|  |       this.triggerLoadType(0); | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     // 加载历史记录 | ||||||
|  |     loadChatRecord() { | ||||||
|  |       let data = { | ||||||
|  |         talk_type: this.query.talk_type, | ||||||
|  |         receiver_id: this.query.receiver_id, | ||||||
|  |         record_id: this.records.record_id, | ||||||
|  |         msg_type: this.query.msg_type, | ||||||
|  |       }; | ||||||
|  | 
 | ||||||
|  |       if (this.records.loadStatus == 1) return; | ||||||
|  | 
 | ||||||
|  |       this.records.loadStatus = 1; | ||||||
|  |       ServeFindTalkRecords(data) | ||||||
|  |         .then((res) => { | ||||||
|  |           if (res.code != 200) return; | ||||||
|  | 
 | ||||||
|  |           let records = data.record_id == 0 ? [] : this.records.items; | ||||||
|  | 
 | ||||||
|  |           records.push(...res.data.rows); | ||||||
|  | 
 | ||||||
|  |           this.records.items = records; | ||||||
|  |           this.records.loadStatus = | ||||||
|  |             res.data.rows.length < res.data.limit ? 2 : 0; | ||||||
|  | 
 | ||||||
|  |           if (this.records.items.length == 0) { | ||||||
|  |             this.records.isEmpty = true; | ||||||
|  |           } else { | ||||||
|  |             this.records.record_id = | ||||||
|  |               this.records.items[this.records.items.length - 1].id; | ||||||
|  |           } | ||||||
|  |         }) | ||||||
|  |         .catch(() => { | ||||||
|  |           this.records.loadStatus = 0; | ||||||
|  |         }); | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     triggerLoadType(type) { | ||||||
|  |       this.records.record_id = 0; | ||||||
|  |       this.query.msg_type = type; | ||||||
|  |       this.records.isEmpty = false; | ||||||
|  |       this.records.items = []; | ||||||
|  | 
 | ||||||
|  |       this.loadChatRecord(); | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     searchText() { | ||||||
|  |       if (this.search.keyword == "") { | ||||||
|  |         this.showBox = 0; | ||||||
|  |         return false; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       this.$notify.info({ | ||||||
|  |         title: "消息", | ||||||
|  |         message: "查询功能正在开发中...", | ||||||
|  |       }); | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     triggerBroadside() { | ||||||
|  |       this.broadside = !this.broadside; | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | }; | ||||||
|  | </script> | ||||||
|  | <style lang="less" scoped> | ||||||
|  | /deep/.el-scrollbar__wrap { | ||||||
|  |   overflow-x: hidden; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .lum-dialog-mask { | ||||||
|  |   z-index: 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .lum-dialog-box { | ||||||
|  |   width: 100%; | ||||||
|  |   height: 600px; | ||||||
|  |   max-width: 800px; | ||||||
|  |   transition: 1s ease; | ||||||
|  | 
 | ||||||
|  |   &.full-screen { | ||||||
|  |     width: 100%; | ||||||
|  |     height: 100%; | ||||||
|  |     max-width: unset; | ||||||
|  |     margin: 0; | ||||||
|  |     border-radius: 0px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .sub-header { | ||||||
|  |     height: 38px; | ||||||
|  |     line-height: 38px; | ||||||
|  |     font-size: 12px; | ||||||
|  |     border-bottom: 1px solid #f9f4f4; | ||||||
|  |     margin-top: 10px; | ||||||
|  |     padding: 0 10px; | ||||||
|  |     position: relative; | ||||||
|  | 
 | ||||||
|  |     i { | ||||||
|  |       font-size: 22px; | ||||||
|  |       color: #6f6a6a; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .search-box { | ||||||
|  |       position: absolute; | ||||||
|  |       width: 230px; | ||||||
|  |       height: 32px; | ||||||
|  |       top: 2px; | ||||||
|  |       right: 10px; | ||||||
|  |       background: #f9f4f4; | ||||||
|  |       border-radius: 5px; | ||||||
|  | 
 | ||||||
|  |       i { | ||||||
|  |         position: absolute; | ||||||
|  |         left: 10px; | ||||||
|  |         top: 8px; | ||||||
|  |         font-size: 16px; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       input { | ||||||
|  |         position: absolute; | ||||||
|  |         left: 35px; | ||||||
|  |         top: 3px; | ||||||
|  |         height: 25px; | ||||||
|  |         width: 184px; | ||||||
|  |         color: #7d7171; | ||||||
|  |         background: #f9f4f4; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .broadside { | ||||||
|  |     @border: 1px solid #f9f9f9; | ||||||
|  |     border-right: @border; | ||||||
|  |     user-select: none; | ||||||
|  |     transition: 3s ease; | ||||||
|  | 
 | ||||||
|  |     .aside-header { | ||||||
|  |       display: flex; | ||||||
|  |       flex-direction: row; | ||||||
|  |       height: 100%; | ||||||
|  |       border-bottom: @border; | ||||||
|  |       padding: 0; | ||||||
|  | 
 | ||||||
|  |       > div { | ||||||
|  |         text-align: center; | ||||||
|  |         line-height: 40px; | ||||||
|  |         font-size: 13px; | ||||||
|  |         font-weight: 400; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       .item { | ||||||
|  |         flex: 1; | ||||||
|  |         cursor: pointer; | ||||||
|  | 
 | ||||||
|  |         &.selected { | ||||||
|  |           color: #66b1ff; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       .item-shuxian { | ||||||
|  |         flex-basis: 1px; | ||||||
|  |         flex-shrink: 0; | ||||||
|  |         color: rgb(232 224 224); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .contacts-item { | ||||||
|  |       height: 35px; | ||||||
|  |       display: flex; | ||||||
|  |       flex-direction: row; | ||||||
|  |       align-items: center; | ||||||
|  |       justify-content: center; | ||||||
|  |       padding-left: 10px; | ||||||
|  |       position: relative; | ||||||
|  | 
 | ||||||
|  |       .avatar { | ||||||
|  |         flex-basis: 40px; | ||||||
|  |         flex-shrink: 0; | ||||||
|  |         height: 100%; | ||||||
|  |         display: flex; | ||||||
|  |         justify-content: center; | ||||||
|  |         align-items: center; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       .content { | ||||||
|  |         flex: 1 1; | ||||||
|  |         height: 100%; | ||||||
|  |         line-height: 35px; | ||||||
|  |         white-space: nowrap; | ||||||
|  |         overflow: hidden; | ||||||
|  |         text-overflow: ellipsis; | ||||||
|  |         font-size: 13px; | ||||||
|  |         padding-right: 10px; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       &:hover, | ||||||
|  |       &.selected { | ||||||
|  |         background-color: #f5f5f5; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* first box */ | ||||||
|  | .type-items { | ||||||
|  |   padding: 0 0 0 10px; | ||||||
|  |   line-height: 40px; | ||||||
|  |   user-select: none; | ||||||
|  |   border-bottom: 1px solid #f9f4f4; | ||||||
|  | 
 | ||||||
|  |   .active { | ||||||
|  |     color: #03a9f4; | ||||||
|  |     font-weight: 500; | ||||||
|  |     font-size: 13px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   span { | ||||||
|  |     height: 40px; | ||||||
|  |     width: 45px; | ||||||
|  |     text-align: center; | ||||||
|  |     cursor: pointer; | ||||||
|  |     margin: 0 10px; | ||||||
|  |     font-size: 12px; | ||||||
|  |     font-weight: 400; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .history-record { | ||||||
|  |   padding: 10px 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .load-button { | ||||||
|  |   width: 100%; | ||||||
|  |   height: 35px; | ||||||
|  |   display: flex; | ||||||
|  |   justify-content: center; | ||||||
|  |   align-items: center; | ||||||
|  |   flex-direction: row; | ||||||
|  | 
 | ||||||
|  |   &.blue { | ||||||
|  |     color: #51b2ff; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   span { | ||||||
|  |     margin-left: 5px; | ||||||
|  |     font-size: 13px; | ||||||
|  |     cursor: pointer; | ||||||
|  |     user-select: none; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .empty-records { | ||||||
|  |   width: 100%; | ||||||
|  |   height: 100%; | ||||||
|  |   display: flex; | ||||||
|  |   justify-content: center; | ||||||
|  |   align-items: center; | ||||||
|  |   flex-direction: column; | ||||||
|  |   color: #cccccc; | ||||||
|  |   font-weight: 300; | ||||||
|  |   font-size: 14px; | ||||||
|  | 
 | ||||||
|  |   img { | ||||||
|  |     width: 100px; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @import "~@/assets/css/talk/talk-records.less"; | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,193 @@ | ||||||
|  | <template> | ||||||
|  |   <div class="audio-message"> | ||||||
|  |     <div class="videodisc"> | ||||||
|  |       <div class="disc" :class="{ play: isPlay }" @click="toPlay"> | ||||||
|  |         <i v-if="loading" class="el-icon-loading" /> | ||||||
|  |         <i v-else-if="isPlay" class="el-icon-video-pause" /> | ||||||
|  |         <i v-else class="el-icon-video-play" /> | ||||||
|  |         <audio | ||||||
|  |           ref="audio" | ||||||
|  |           type="audio/mp3" | ||||||
|  |           :src="src" | ||||||
|  |           @timeupdate="timeupdate" | ||||||
|  |           @ended="ended" | ||||||
|  |           @canplay="canplay" | ||||||
|  |         ></audio> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |     <div class="detail"> | ||||||
|  |       <div class="text"> | ||||||
|  |         <i class="el-icon-service" /> | ||||||
|  |         <span>{{ getCurrDuration }} / {{ getTotalDuration }}</span> | ||||||
|  |       </div> | ||||||
|  |       <div class="process"> | ||||||
|  |         <el-progress :percentage="progress" :show-text="false" /> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | function formatSeconds(value) { | ||||||
|  |   var theTime = parseInt(value) // 秒 | ||||||
|  |   var theTime1 = 0 // 分 | ||||||
|  |   var theTime2 = 0 // 小时 | ||||||
|  |   if (theTime > 60) { | ||||||
|  |     theTime1 = parseInt(theTime / 60) | ||||||
|  |     theTime = parseInt(theTime % 60) | ||||||
|  |     if (theTime1 > 60) { | ||||||
|  |       theTime2 = parseInt(theTime1 / 60) | ||||||
|  |       theTime1 = parseInt(theTime1 % 60) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   var result = '' + parseInt(theTime) //秒 | ||||||
|  |   if (10 > theTime > 0) { | ||||||
|  |     result = '0' + parseInt(theTime) //秒 | ||||||
|  |   } else { | ||||||
|  |     result = '' + parseInt(theTime) //秒 | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (10 > theTime1 > 0) { | ||||||
|  |     result = '0' + parseInt(theTime1) + ':' + result //分,不足两位数,首位补充0, | ||||||
|  |   } else { | ||||||
|  |     result = '' + parseInt(theTime1) + ':' + result //分 | ||||||
|  |   } | ||||||
|  |   if (theTime2 > 0) { | ||||||
|  |     result = '' + parseInt(theTime2) + ':' + result //时 | ||||||
|  |   } | ||||||
|  |   return result | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   name: 'AudioMessage', | ||||||
|  |   props: { | ||||||
|  |     src: { | ||||||
|  |       type: String, | ||||||
|  |       default: '', | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       loading: true, | ||||||
|  |       isPlay: false, | ||||||
|  |       duration: 0, | ||||||
|  |       currentTime: 0, | ||||||
|  |       progress: 0, | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   computed: { | ||||||
|  |     getTotalDuration() { | ||||||
|  |       return formatSeconds(this.duration) | ||||||
|  |     }, | ||||||
|  |     getCurrDuration() { | ||||||
|  |       return formatSeconds(this.currentTime) | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     toPlay() { | ||||||
|  |       if (this.loading) { | ||||||
|  |         return | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       let audio = this.$refs.audio | ||||||
|  |       if (this.isPlay) { | ||||||
|  |         audio.pause() | ||||||
|  |       } else { | ||||||
|  |         audio.play() | ||||||
|  |       } | ||||||
|  |       this.isPlay = !this.isPlay | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     // 当目前的播放位置已更改时 | ||||||
|  |     timeupdate() { | ||||||
|  |       let audio = this.$refs.audio | ||||||
|  |       this.currentTime = audio.currentTime | ||||||
|  |       this.progress = (audio.currentTime / audio.duration) * 100 | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     // 当浏览器可以播放音频/视频时 | ||||||
|  |     canplay() { | ||||||
|  |       this.duration = this.$refs.audio.duration | ||||||
|  |       this.loading = false | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     // 当目前的播放列表已结束时 | ||||||
|  |     ended() { | ||||||
|  |       this.isPlay = false | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | <style scoped lang="less"> | ||||||
|  | .audio-message { | ||||||
|  |   width: 200px; | ||||||
|  |   height: 60px; | ||||||
|  |   border-radius: 5px; | ||||||
|  |   background: #ffffff; | ||||||
|  |   display: flex; | ||||||
|  |   align-items: center; | ||||||
|  |   border: 1px solid #03a9f4; | ||||||
|  |   overflow: hidden; | ||||||
|  | 
 | ||||||
|  |   > div { | ||||||
|  |     height: 100%; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .videodisc { | ||||||
|  |     flex-basis: 60px; | ||||||
|  |     flex-shrink: 0; | ||||||
|  |     display: flex; | ||||||
|  |     justify-content: center; | ||||||
|  |     align-items: center; | ||||||
|  |     .disc { | ||||||
|  |       width: 42px; | ||||||
|  |       height: 42px; | ||||||
|  |       background: #e9e5e5; | ||||||
|  |       border-radius: 50%; | ||||||
|  |       display: flex; | ||||||
|  |       align-items: center; | ||||||
|  |       justify-content: center; | ||||||
|  |       color: white; | ||||||
|  |       cursor: pointer; | ||||||
|  |       transition: ease 0.5; | ||||||
|  | 
 | ||||||
|  |       &.play { | ||||||
|  |         background: #ff5722; | ||||||
|  |         box-shadow: 0 0 4px 0px #f76a3e; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       i { | ||||||
|  |         font-size: 24px; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       &:active i { | ||||||
|  |         transform: scale(1.1); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .detail { | ||||||
|  |     flex: 1 1; | ||||||
|  |     display: flex; | ||||||
|  |     flex-direction: column; | ||||||
|  |     justify-content: center; | ||||||
|  |     align-items: center; | ||||||
|  |     padding-top: 10px; | ||||||
|  | 
 | ||||||
|  |     .text { | ||||||
|  |       width: 90%; | ||||||
|  |       font-size: 12px; | ||||||
|  |       i { | ||||||
|  |         margin-right: 5px; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .process { | ||||||
|  |       padding-top: 10px; | ||||||
|  |       height: 20px; | ||||||
|  |       width: 90%; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,148 @@ | ||||||
|  | <template> | ||||||
|  |   <div | ||||||
|  |     class="code-message" | ||||||
|  |     :class="{ | ||||||
|  |       'max-height': lineNumber > 6, | ||||||
|  |       'max-width': maxwidth, | ||||||
|  |       'full-screen': fullscreen, | ||||||
|  |     }" | ||||||
|  |   > | ||||||
|  |     <i | ||||||
|  |       :class=" | ||||||
|  |         fullscreen ? 'el-icon-close' : 'iconfont icon-tubiao_chakangongyi' | ||||||
|  |       " | ||||||
|  |       @click="fullscreen = !fullscreen" | ||||||
|  |     /> | ||||||
|  |     <pre class="lum-scrollbar" v-html="formatCode(code, lang)" /> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | <script> | ||||||
|  | import Prism from 'prismjs' | ||||||
|  | import 'prismjs/themes/prism-okaidia.css' | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   name: 'CodeMessage', | ||||||
|  |   props: { | ||||||
|  |     code: { | ||||||
|  |       type: [String, Number], | ||||||
|  |       default: '', | ||||||
|  |     }, | ||||||
|  |     lang: { | ||||||
|  |       type: String, | ||||||
|  |       default: '', | ||||||
|  |     }, | ||||||
|  |     maxwidth: { | ||||||
|  |       type: Boolean, | ||||||
|  |       default: false, | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       fullscreen: false, | ||||||
|  |       lineNumber: 0, | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   created() { | ||||||
|  |     this.lineNumber = this.code.split(/\n/).length | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     formatCode(code, lang) { | ||||||
|  |       try { | ||||||
|  |         return Prism.highlight(code, Prism.languages[lang], lang) + '<br/>' | ||||||
|  |       } catch (error) { | ||||||
|  |         return code | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | <style lang="less" scoped> | ||||||
|  | .code-message { | ||||||
|  |   position: relative; | ||||||
|  |   overflow: hidden; | ||||||
|  |   border-radius: 5px; | ||||||
|  |   box-sizing: border-box; | ||||||
|  | 
 | ||||||
|  |   &.max-width { | ||||||
|  |     max-width: 500px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   &.max-height { | ||||||
|  |     height: 208px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   i { | ||||||
|  |     position: absolute; | ||||||
|  |     right: 0px; | ||||||
|  |     top: 0px; | ||||||
|  |     font-size: 16px; | ||||||
|  |     cursor: pointer; | ||||||
|  |     color: white; | ||||||
|  |     display: inline-block; | ||||||
|  |     opacity: 0; | ||||||
|  |     width: 50px; | ||||||
|  |     height: 30px; | ||||||
|  |     background: #171616; | ||||||
|  |     text-align: center; | ||||||
|  |     line-height: 30px; | ||||||
|  |     border-radius: 0 0 0px 8px; | ||||||
|  |     transition: 1s ease; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   &:hover { | ||||||
|  |     i { | ||||||
|  |       opacity: 1; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   pre { | ||||||
|  |     box-sizing: border-box; | ||||||
|  |     height: 100%; | ||||||
|  |     width: 100%; | ||||||
|  |     overflow: auto; | ||||||
|  |     padding: 10px; | ||||||
|  |     line-height: 24px; | ||||||
|  |     background: #272822; | ||||||
|  |     color: #d5d4d4; | ||||||
|  |     font-family: SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', | ||||||
|  |       'Courier New', monospace; | ||||||
|  |     font-size: 85%; | ||||||
|  | 
 | ||||||
|  |     &.lum-scrollbar { | ||||||
|  |       &::-webkit-scrollbar { | ||||||
|  |         background-color: black; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   &.full-screen { | ||||||
|  |     position: fixed; | ||||||
|  |     top: 0; | ||||||
|  |     left: 0; | ||||||
|  |     height: 100%; | ||||||
|  |     width: 100%; | ||||||
|  |     max-width: unset; | ||||||
|  |     max-height: unset; | ||||||
|  |     border-radius: 0px; | ||||||
|  |     background: #272822; | ||||||
|  |     z-index: 99999999; | ||||||
|  | 
 | ||||||
|  |     i { | ||||||
|  |       position: fixed; | ||||||
|  |       top: 15px; | ||||||
|  |       right: 15px; | ||||||
|  |       width: 50px; | ||||||
|  |       height: 50px; | ||||||
|  |       display: flex; | ||||||
|  |       align-items: center; | ||||||
|  |       justify-content: center; | ||||||
|  |       border-radius: 50%; | ||||||
|  |       font-size: 24px; | ||||||
|  | 
 | ||||||
|  |       &:active { | ||||||
|  |         box-shadow: 0 0 5px 0px #ccc; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,145 @@ | ||||||
|  | <template> | ||||||
|  |   <div class="file-message"> | ||||||
|  |     <div class="main"> | ||||||
|  |       <div class="ext">{{ ext }}</div> | ||||||
|  |       <div class="file-box"> | ||||||
|  |         <p class="info"> | ||||||
|  |           <span class="name">{{ fileName }}</span> | ||||||
|  |           <span class="size">({{ fileSize }})</span> | ||||||
|  |         </p> | ||||||
|  |         <p class="notice">文件已成功发送, 文件助手永久保存</p> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |     <div class="footer"> | ||||||
|  |       <a @click="download(record_id)">下载</a> | ||||||
|  |       <a>在线预览</a> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | <script> | ||||||
|  | import { formatSize, download } from '@/utils/functions' | ||||||
|  | export default { | ||||||
|  |   name: 'FileMessage', | ||||||
|  |   props: { | ||||||
|  |     file: { | ||||||
|  |       type: Object, | ||||||
|  |       required: true, | ||||||
|  |     }, | ||||||
|  |     record_id: { | ||||||
|  |       type: Number, | ||||||
|  |       required: true, | ||||||
|  |       default: 0, | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       file_id: 0, | ||||||
|  |       ext: '', | ||||||
|  |       fileName: '', | ||||||
|  |       fileSize: '', | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   created() { | ||||||
|  |     this.file_id = this.file.id | ||||||
|  |     this.ext = this.file.file_suffix.toUpperCase() | ||||||
|  |     this.fileName = this.file.original_name | ||||||
|  |     this.fileSize = formatSize(this.file.file_size) | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     download, | ||||||
|  |   }, | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | <style lang="less" scoped> | ||||||
|  | .file-message { | ||||||
|  |   width: 250px; | ||||||
|  |   height: 85px; | ||||||
|  |   background: white; | ||||||
|  |   box-shadow: 0 0 5px 0px #e8e4e4; | ||||||
|  |   padding: 10px; | ||||||
|  |   border-radius: 3px; | ||||||
|  |   transition: all 0.5s; | ||||||
|  | 
 | ||||||
|  |   &:hover { | ||||||
|  |     box-shadow: 0 0 5px 0px #cac6c6; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .main { | ||||||
|  |     height: 45px; | ||||||
|  |     display: flex; | ||||||
|  |     flex-direction: row; | ||||||
|  | 
 | ||||||
|  |     .ext { | ||||||
|  |       display: flex; | ||||||
|  |       justify-content: center; | ||||||
|  |       align-items: center; | ||||||
|  |       width: 45px; | ||||||
|  |       height: 45px; | ||||||
|  |       flex-shrink: 0; | ||||||
|  |       color: #ffffff; | ||||||
|  |       background: #49a4ff; | ||||||
|  |       border-radius: 5px; | ||||||
|  |       font-size: 12px; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .file-box { | ||||||
|  |       flex: 1 1; | ||||||
|  |       height: 45px; | ||||||
|  |       margin-left: 10px; | ||||||
|  |       overflow: hidden; | ||||||
|  | 
 | ||||||
|  |       .info { | ||||||
|  |         display: flex; | ||||||
|  |         justify-content: space-between; | ||||||
|  |         align-items: center; | ||||||
|  |         overflow: hidden; | ||||||
|  |         height: 24px; | ||||||
|  |         color: rgb(76, 76, 76); | ||||||
|  |         font-size: 14px; | ||||||
|  | 
 | ||||||
|  |         .name { | ||||||
|  |           flex: 1 auto; | ||||||
|  |           white-space: nowrap; | ||||||
|  |           overflow: hidden; | ||||||
|  |           text-overflow: ellipsis; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         .size { | ||||||
|  |           font-size: 12px; | ||||||
|  |           color: #cac6c6; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       .notice { | ||||||
|  |         height: 25px; | ||||||
|  |         line-height: 25px; | ||||||
|  |         font-size: 12px; | ||||||
|  |         color: #929191; | ||||||
|  |         white-space: nowrap; | ||||||
|  |         overflow: hidden; | ||||||
|  |         text-overflow: ellipsis; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .footer { | ||||||
|  |     height: 30px; | ||||||
|  |     line-height: 37px; | ||||||
|  |     color: #409eff; | ||||||
|  |     text-align: right; | ||||||
|  |     font-size: 12px; | ||||||
|  |     border-top: 1px solid #eff7ef; | ||||||
|  |     margin-top: 10px; | ||||||
|  | 
 | ||||||
|  |     a { | ||||||
|  |       margin: 0 3px; | ||||||
|  |       user-select: none; | ||||||
|  |       cursor: pointer; | ||||||
|  | 
 | ||||||
|  |       &:hover { | ||||||
|  |         color: royalblue; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,116 @@ | ||||||
|  | <template> | ||||||
|  |   <div> | ||||||
|  |     <div class="forward-message" @click="catForwardRecords"> | ||||||
|  |       <div class="title">{{ title }}</div> | ||||||
|  |       <div v-for="(record, index) in records" :key="index" class="lists"> | ||||||
|  |         <p> | ||||||
|  |           <span>{{ record.nickname }}:</span> | ||||||
|  |           <span>{{ record.text }}</span> | ||||||
|  |         </p> | ||||||
|  |       </div> | ||||||
|  |       <div class="footer"> | ||||||
|  |         <span>转发:聊天会话记录 ({{ num }}条)</span> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  | 
 | ||||||
|  |     <!-- 会话记录查看器 --> | ||||||
|  |     <talk-forward-record ref="forwardRecordsRef" /> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | <script> | ||||||
|  | import TalkForwardRecord from '@/components/chat/TalkForwardRecord' | ||||||
|  | export default { | ||||||
|  |   name: 'ForwardMessage', | ||||||
|  |   components: { | ||||||
|  |     TalkForwardRecord, | ||||||
|  |   }, | ||||||
|  |   props: { | ||||||
|  |     forward: { | ||||||
|  |       type: Object, | ||||||
|  |       required: true, | ||||||
|  |     }, | ||||||
|  |     record_id: { | ||||||
|  |       type: Number, | ||||||
|  |       required: true, | ||||||
|  |       default: 0, | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       title: '', | ||||||
|  |       records: [], | ||||||
|  |       num: 0, | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     catForwardRecords() { | ||||||
|  |       this.$refs.forwardRecordsRef.open(this.record_id) | ||||||
|  |     }, | ||||||
|  |     getForwardTitle(list) { | ||||||
|  |       let arr = [...new Set(list.map(v => v.nickname))] | ||||||
|  |       return arr.join('、') + '的会话记录' | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   created() { | ||||||
|  |     let forward = this.forward | ||||||
|  | 
 | ||||||
|  |     this.num = forward.num | ||||||
|  |     this.records = forward.list | ||||||
|  |     this.title = this.getForwardTitle(this.records) | ||||||
|  |   }, | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | <style lang="less" scoped> | ||||||
|  | /* 会话记录消息 */ | ||||||
|  | .forward-message { | ||||||
|  |   width: 250px; | ||||||
|  |   min-height: 95px; | ||||||
|  |   max-height: 150px; | ||||||
|  |   border-radius: 3px; | ||||||
|  |   background-color: white; | ||||||
|  |   padding: 3px 10px; | ||||||
|  |   cursor: pointer; | ||||||
|  |   box-shadow: 0 0 5px 0px #e8e4e4; | ||||||
|  |   text-align: left; | ||||||
|  |   user-select: none; | ||||||
|  | 
 | ||||||
|  |   .title { | ||||||
|  |     height: 30px; | ||||||
|  |     line-height: 30px; | ||||||
|  |     font-size: 14px; | ||||||
|  |     color: #565353; | ||||||
|  |     overflow: hidden; | ||||||
|  |     text-overflow: ellipsis; | ||||||
|  |     white-space: nowrap; | ||||||
|  |     font-weight: 400; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .lists p { | ||||||
|  |     height: 18px; | ||||||
|  |     line-height: 18px; | ||||||
|  |     font-size: 10px; | ||||||
|  |     color: #aaa9a9; | ||||||
|  |     overflow: hidden; | ||||||
|  |     text-overflow: ellipsis; | ||||||
|  |     white-space: nowrap; | ||||||
|  |     font-weight: 100; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .footer { | ||||||
|  |     height: 32px; | ||||||
|  |     line-height: 35px; | ||||||
|  |     color: #858282; | ||||||
|  |     border-top: 1px solid #f1ebeb; | ||||||
|  |     font-size: 12px; | ||||||
|  |     margin-top: 12px; | ||||||
|  |     font-weight: 300; | ||||||
|  |     overflow: hidden; | ||||||
|  |     text-overflow: ellipsis; | ||||||
|  |     white-space: nowrap; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   &:hover { | ||||||
|  |     box-shadow: 0 0 5px 0px #cac6c6; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,150 @@ | ||||||
|  | <template> | ||||||
|  |   <!-- 好友申请消息 --> | ||||||
|  |   <div class="apply-card"> | ||||||
|  |     <div class="card-header"> | ||||||
|  |       <img class="avatar" :src="avatar" /> | ||||||
|  |       <div class="nickname">No. {{ nickname }}</div> | ||||||
|  |       <div class="datetime">{{ datetime }}</div> | ||||||
|  |       <div class="remarks"> | ||||||
|  |         <span>备注信息:{{ remarks }}</span> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  | 
 | ||||||
|  |     <div class="card-footer"> | ||||||
|  |       <div class="mini-button" @click="handle(1)">同意</div> | ||||||
|  |       <el-divider direction="vertical"></el-divider> | ||||||
|  |       <div class="mini-button" @click="handle(2)">拒绝</div> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | export default { | ||||||
|  |   name: 'FriendApplyMessage', | ||||||
|  |   props: { | ||||||
|  |     data: { | ||||||
|  |       type: Object, | ||||||
|  |       default() { | ||||||
|  |         return {} | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       avatar: | ||||||
|  |         'http://im-img.gzydong.club/media/images/avatar/20210602/60b6f03598ed0104301.png', | ||||||
|  |       nickname: '独特态度', | ||||||
|  |       datetime: '05/09 12:13 分', | ||||||
|  |       remarks: '编辑个签,展示我的独特态度 展示我的独特态度。', | ||||||
|  |       apply_id: 0, | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   created() {}, | ||||||
|  |   methods: { | ||||||
|  |     handle(type) { | ||||||
|  |       alert(type) | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | <style lang="less" scoped> | ||||||
|  | .apply-card { | ||||||
|  |   position: relative; | ||||||
|  |   width: 170px; | ||||||
|  |   min-height: 180px; | ||||||
|  |   border-radius: 15px; | ||||||
|  |   overflow: hidden; | ||||||
|  |   transition: all 0.5s; | ||||||
|  |   box-sizing: border-box; | ||||||
|  |   background-image: linear-gradient(-84deg, #1ab6ff 0, #1ab6ff 0, #82c1f3 100%); | ||||||
|  |   // #028fff | ||||||
|  | 
 | ||||||
|  |   &:hover { | ||||||
|  |     transform: scale(1.02); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .card-header { | ||||||
|  |     position: relative; | ||||||
|  |     width: 100%; | ||||||
|  |     height: 135px; | ||||||
|  | 
 | ||||||
|  |     .avatar { | ||||||
|  |       position: absolute; | ||||||
|  |       top: 18px; | ||||||
|  |       left: 8px; | ||||||
|  |       width: 40px; | ||||||
|  |       height: 40px; | ||||||
|  |       border-radius: 50%; | ||||||
|  |       background: white; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .nickname { | ||||||
|  |       position: absolute; | ||||||
|  |       top: 15px; | ||||||
|  |       right: 8px; | ||||||
|  |       width: 90px; | ||||||
|  |       height: 25px; | ||||||
|  |       font-size: 10px; | ||||||
|  |       text-align: center; | ||||||
|  |       line-height: 25px; | ||||||
|  |       color: white; | ||||||
|  |       border-bottom: 1px dashed white; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .datetime { | ||||||
|  |       position: absolute; | ||||||
|  |       top: 42px; | ||||||
|  |       right: 11.5px; | ||||||
|  |       color: white; | ||||||
|  |       font-size: 10px; | ||||||
|  |       transform: scale(0.9); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .remarks { | ||||||
|  |       position: absolute; | ||||||
|  |       bottom: 5px; | ||||||
|  |       color: white; | ||||||
|  |       font-size: 10px; | ||||||
|  |       padding: 3px 5px; | ||||||
|  |       display: -webkit-box; | ||||||
|  |       -webkit-box-orient: vertical; | ||||||
|  |       -webkit-line-clamp: 3; | ||||||
|  |       overflow: hidden; | ||||||
|  |       transform: scale(0.95); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .card-footer { | ||||||
|  |     position: absolute; | ||||||
|  |     bottom: 0; | ||||||
|  |     width: 100%; | ||||||
|  |     height: 40px; | ||||||
|  |     border-top: 1px solid white; | ||||||
|  |     display: flex; | ||||||
|  |     align-items: center; | ||||||
|  |     justify-content: center; | ||||||
|  |     box-sizing: border-box; | ||||||
|  | 
 | ||||||
|  |     /deep/.el-divider { | ||||||
|  |       background: white; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .mini-button { | ||||||
|  |       display: flex; | ||||||
|  |       width: 50px; | ||||||
|  |       height: 25px; | ||||||
|  |       margin: 0 10px; | ||||||
|  |       text-align: center; | ||||||
|  |       align-items: center; | ||||||
|  |       justify-content: center; | ||||||
|  |       font-size: 13px; | ||||||
|  |       color: white; | ||||||
|  |       cursor: pointer; | ||||||
|  | 
 | ||||||
|  |       &:hover { | ||||||
|  |         font-size: 14px; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,51 @@ | ||||||
|  | <template> | ||||||
|  |   <div class="image-message no-select"> | ||||||
|  |     <el-image | ||||||
|  |       fit="cover" | ||||||
|  |       :src="src" | ||||||
|  |       :lazy="true" | ||||||
|  |       :style="getImgStyle(src)" | ||||||
|  |       :preview-src-list="[src]" | ||||||
|  |     > | ||||||
|  |       <div slot="error" class="image-slot">图片加载失败...</div> | ||||||
|  |       <div slot="placeholder" class="image-slot">图片加载中...</div> | ||||||
|  |     </el-image> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | <script> | ||||||
|  | import { imgZoom } from '@/utils/functions' | ||||||
|  | export default { | ||||||
|  |   name: 'ImageMessage', | ||||||
|  |   props: { | ||||||
|  |     src: { | ||||||
|  |       type: String, | ||||||
|  |       default: '', | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     getImgStyle(url) { | ||||||
|  |       return imgZoom(url, 200) | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | <style lang="less" scoped> | ||||||
|  | .image-message { | ||||||
|  |   /deep/.el-image { | ||||||
|  |     border-radius: 5px; | ||||||
|  |     cursor: pointer; | ||||||
|  |     background: #f1efef; | ||||||
|  | 
 | ||||||
|  |     .image-slot { | ||||||
|  |       display: flex; | ||||||
|  |       justify-content: center; | ||||||
|  |       align-items: center; | ||||||
|  |       width: 100%; | ||||||
|  |       height: 100%; | ||||||
|  |       font-size: 13px; | ||||||
|  |       color: #908686; | ||||||
|  |       background: #efeaea; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,74 @@ | ||||||
|  | <template> | ||||||
|  |   <div class="invite-message"> | ||||||
|  |     <div v-if="invite.type == 1 || invite.type == 3" class="system-msg"> | ||||||
|  |       <a @click="toUser(invite.operate_user.id)"> | ||||||
|  |         {{ invite.operate_user.nickname }} | ||||||
|  |       </a> | ||||||
|  |       <span>{{ invite.type == 1 ? '邀请了' : '将' }}</span> | ||||||
|  |       <template v-for="(user, uidx) in invite.users"> | ||||||
|  |         <a @click="toUser(user.id)">{{ user.nickname }}</a> | ||||||
|  |         <em v-show="uidx < invite.users.length - 1">、</em> | ||||||
|  |       </template> | ||||||
|  |       <span>{{ invite.type == 1 ? '加入了群聊' : '踢出了群聊' }}</span> | ||||||
|  |     </div> | ||||||
|  | 
 | ||||||
|  |     <div v-else-if="invite.type == 2" class="system-msg"> | ||||||
|  |       <a @click="toUser(invite.operate_user.id)"> | ||||||
|  |         {{ invite.operate_user.nickname }} | ||||||
|  |       </a> | ||||||
|  |       <span>退出了群聊</span> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | <script> | ||||||
|  | export default { | ||||||
|  |   name: 'InviteMessage', | ||||||
|  |   props: { | ||||||
|  |     invite: { | ||||||
|  |       type: Object, | ||||||
|  |       required: true, | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     toUser(user_id) { | ||||||
|  |       this.$emit('cat', user_id) | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | <style lang="less" scoped> | ||||||
|  | .invite-message { | ||||||
|  |   display: flex; | ||||||
|  |   justify-content: center; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .system-msg { | ||||||
|  |   margin: 10px auto; | ||||||
|  |   background-color: #f5f5f5; | ||||||
|  |   font-size: 11px; | ||||||
|  |   line-height: 30px; | ||||||
|  |   padding: 0 8px; | ||||||
|  |   word-break: break-all; | ||||||
|  |   word-wrap: break-word; | ||||||
|  |   color: #979191; | ||||||
|  |   user-select: none; | ||||||
|  |   font-weight: 300; | ||||||
|  |   display: inline-block; | ||||||
|  |   border-radius: 3px; | ||||||
|  | 
 | ||||||
|  |   span { | ||||||
|  |     margin: 0 5px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   a { | ||||||
|  |     color: #939596; | ||||||
|  |     cursor: pointer; | ||||||
|  |     font-size: 12px; | ||||||
|  |     font-weight: 400; | ||||||
|  | 
 | ||||||
|  |     &:hover { | ||||||
|  |       color: black; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,101 @@ | ||||||
|  | <template> | ||||||
|  |   <!-- 登录消息 --> | ||||||
|  |   <div class="login-message"> | ||||||
|  |     <h4>登录操作通知</h4> | ||||||
|  |     <p>登录时间:{{ datetime }} (CST)</p> | ||||||
|  |     <p>IP地址:{{ ip }}</p> | ||||||
|  |     <p>登录地点:{{ address }}</p> | ||||||
|  |     <p>登录设备:{{ platform }}</p> | ||||||
|  |     <p>异常原因:{{ reason }}</p> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | import { parseTime } from '@/utils/functions' | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   name: 'LoginMessage', | ||||||
|  |   props: { | ||||||
|  |     detail: { | ||||||
|  |       type: Object, | ||||||
|  |       required: true, | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       datetime: '', | ||||||
|  |       ip: '', | ||||||
|  |       address: '', | ||||||
|  |       platform: '', | ||||||
|  |       reason: '常用设备登录', | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   created() { | ||||||
|  |     this.ip = this.detail.ip | ||||||
|  |     this.datetime = parseTime( | ||||||
|  |       this.detail.created_at, | ||||||
|  |       '{y}年{m}月{d}日 {h}:{i}:{s}' | ||||||
|  |     ) | ||||||
|  |     this.address = this.detail.address | ||||||
|  |     this.reason = this.detail.reason | ||||||
|  | 
 | ||||||
|  |     this.platform = | ||||||
|  |       this.getExploreName(this.detail.agent) + | ||||||
|  |       ' / ' + | ||||||
|  |       this.getExploreOs(this.detail.agent) | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     getExploreName(userAgent = '') { | ||||||
|  |       if (userAgent.indexOf('Opera') > -1 || userAgent.indexOf('OPR') > -1) { | ||||||
|  |         return 'Opera' | ||||||
|  |       } else if ( | ||||||
|  |         userAgent.indexOf('compatible') > -1 && | ||||||
|  |         userAgent.indexOf('MSIE') > -1 | ||||||
|  |       ) { | ||||||
|  |         return 'IE' | ||||||
|  |       } else if (userAgent.indexOf('Edge') > -1) { | ||||||
|  |         return 'Edge' | ||||||
|  |       } else if (userAgent.indexOf('Firefox') > -1) { | ||||||
|  |         return 'Firefox' | ||||||
|  |       } else if ( | ||||||
|  |         userAgent.indexOf('Safari') > -1 && | ||||||
|  |         userAgent.indexOf('Chrome') == -1 | ||||||
|  |       ) { | ||||||
|  |         return 'Safari' | ||||||
|  |       } else if ( | ||||||
|  |         userAgent.indexOf('Chrome') > -1 && | ||||||
|  |         userAgent.indexOf('Safari') > -1 | ||||||
|  |       ) { | ||||||
|  |         return 'Chrome' | ||||||
|  |       } else { | ||||||
|  |         return 'Unkonwn' | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     getExploreOs(userAgent = '') { | ||||||
|  |       if (userAgent.indexOf('Mac OS') > -1) { | ||||||
|  |         return 'Mac OS' | ||||||
|  |       } else { | ||||||
|  |         return 'Windows' | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | <style lang="less" scoped> | ||||||
|  | .login-message { | ||||||
|  |   width: 300px; | ||||||
|  |   min-height: 50px; | ||||||
|  |   background: #f7f7f7; | ||||||
|  |   border-radius: 5px; | ||||||
|  |   padding: 15px; | ||||||
|  | 
 | ||||||
|  |   p { | ||||||
|  |     font-size: 13px; | ||||||
|  |     margin: 10px 0; | ||||||
|  |     &:last-child { | ||||||
|  |       margin-bottom: 0; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,26 @@ | ||||||
|  | <template> | ||||||
|  |   <div class="reply-message">这是回复的消息[预留]</div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | export default { | ||||||
|  |   name: 'ReplyMessage', | ||||||
|  |   data() { | ||||||
|  |     return {} | ||||||
|  |   }, | ||||||
|  |   created() {}, | ||||||
|  |   methods: {}, | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | <style lang="less" scoped> | ||||||
|  | .reply-message { | ||||||
|  |   margin-top: 5px; | ||||||
|  |   min-height: 28px; | ||||||
|  |   background: #f7f1f1; | ||||||
|  |   line-height: 28px; | ||||||
|  |   font-size: 12px; | ||||||
|  |   padding: 0 10px; | ||||||
|  |   border-radius: 3px; | ||||||
|  |   color: #a7a2a2; | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,66 @@ | ||||||
|  | <template> | ||||||
|  |   <div class="revoke-message"> | ||||||
|  |     <div class="content"> | ||||||
|  |       <span v-if="$store.state.user.id == item.user_id"> | ||||||
|  |         你撤回了一条消息 | {{ sendTime(item.created_at) }} | ||||||
|  |       </span> | ||||||
|  |       <span v-else-if="item.talk_type == 1"> | ||||||
|  |         对方撤回了一条消息 | {{ sendTime(item.created_at) }} | ||||||
|  |       </span> | ||||||
|  |       <span v-else> | ||||||
|  |         "{{ item.nickname }}" 撤回了一条消息 | {{ sendTime(item.created_at) }} | ||||||
|  |       </span> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | <script> | ||||||
|  | import { formatTime as sendTime } from "@/utils/functions"; | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   name: "RevokeMessage", | ||||||
|  |   props: { | ||||||
|  |     item: { | ||||||
|  |       type: Object, | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     sendTime, | ||||||
|  |   }, | ||||||
|  | }; | ||||||
|  | </script> | ||||||
|  | <style lang="less" scoped> | ||||||
|  | .revoke-message { | ||||||
|  |   display: flex; | ||||||
|  |   justify-content: center; | ||||||
|  | 
 | ||||||
|  |   .content { | ||||||
|  |     margin: 10px auto; | ||||||
|  |     background-color: #f5f5f5; | ||||||
|  |     font-size: 11px; | ||||||
|  |     line-height: 30px; | ||||||
|  |     padding: 0 8px; | ||||||
|  |     word-break: break-all; | ||||||
|  |     word-wrap: break-word; | ||||||
|  |     color: #979191; | ||||||
|  |     user-select: none; | ||||||
|  |     font-weight: 300; | ||||||
|  |     display: inline-block; | ||||||
|  |     border-radius: 3px; | ||||||
|  | 
 | ||||||
|  |     span { | ||||||
|  |       margin: 0 5px; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     a { | ||||||
|  |       color: #939596; | ||||||
|  |       cursor: pointer; | ||||||
|  |       font-size: 12px; | ||||||
|  |       font-weight: 400; | ||||||
|  | 
 | ||||||
|  |       &:hover { | ||||||
|  |         color: black; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,39 @@ | ||||||
|  | <template> | ||||||
|  |   <div class="system-text-message"> | ||||||
|  |     <div class="content">{{ content }}</div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | <script> | ||||||
|  | import { formatTime as sendTime } from '@/utils/functions' | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   name: 'SystemTextMessage', | ||||||
|  |   props: { | ||||||
|  |     content: String, | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     sendTime, | ||||||
|  |   }, | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | <style lang="less" scoped> | ||||||
|  | .system-text-message { | ||||||
|  |   display: flex; | ||||||
|  |   justify-content: center; | ||||||
|  | 
 | ||||||
|  |   .content { | ||||||
|  |     margin: 10px auto; | ||||||
|  |     background-color: #f5f5f5; | ||||||
|  |     font-size: 11px; | ||||||
|  |     line-height: 30px; | ||||||
|  |     padding: 0 8px; | ||||||
|  |     word-break: break-all; | ||||||
|  |     word-wrap: break-word; | ||||||
|  |     color: #979191; | ||||||
|  |     user-select: none; | ||||||
|  |     font-weight: 300; | ||||||
|  |     display: inline-block; | ||||||
|  |     border-radius: 3px; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,109 @@ | ||||||
|  | <template> | ||||||
|  |   <div | ||||||
|  |     class="text-message" | ||||||
|  |     :class="{ | ||||||
|  |       left: float == 'left', | ||||||
|  |       right: float == 'right', | ||||||
|  |       'max-width': !fullWidth, | ||||||
|  |     }" | ||||||
|  |   > | ||||||
|  |     <div v-if="arrow" class="arrow"></div> | ||||||
|  |     <pre v-html="html" /> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | <script> | ||||||
|  | import { textReplaceLink } from "@/utils/functions"; | ||||||
|  | import { textReplaceEmoji } from "@/utils/emojis"; | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   name: "TextMessage", | ||||||
|  |   props: { | ||||||
|  |     content: { | ||||||
|  |       type: [String, Number], | ||||||
|  |       default: "", | ||||||
|  |     }, | ||||||
|  |     float: { | ||||||
|  |       type: String, | ||||||
|  |       default: "left", | ||||||
|  |     }, | ||||||
|  |     fullWidth: { | ||||||
|  |       type: Boolean, | ||||||
|  |       default: true, | ||||||
|  |     }, | ||||||
|  |     arrow: { | ||||||
|  |       type: Boolean, | ||||||
|  |       default: false, | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       html: "", | ||||||
|  |     }; | ||||||
|  |   }, | ||||||
|  |   created() { | ||||||
|  |     const text = textReplaceLink( | ||||||
|  |       this.content, | ||||||
|  |       this.float == "right" ? "#ffffff" : "rgb(9 149 208)" | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     this.html = textReplaceEmoji(text); | ||||||
|  |   }, | ||||||
|  | }; | ||||||
|  | </script> | ||||||
|  | <style lang="less" scoped> | ||||||
|  | @bg-left-color: #f5f5f5; | ||||||
|  | @bg-right-color: #1ebafc; | ||||||
|  | 
 | ||||||
|  | .text-message { | ||||||
|  |   position: relative; | ||||||
|  |   min-width: 30px; | ||||||
|  |   min-height: 30px; | ||||||
|  |   border-radius: 5px; | ||||||
|  |   padding: 5px; | ||||||
|  | 
 | ||||||
|  |   .arrow { | ||||||
|  |     position: absolute; | ||||||
|  |     width: 0; | ||||||
|  |     height: 0; | ||||||
|  |     font-size: 0; | ||||||
|  |     border: 5px solid; | ||||||
|  |     top: 6px; | ||||||
|  |     left: -10px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   &.max-width { | ||||||
|  |     max-width: calc(100% - 50px); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   &.left { | ||||||
|  |     color: #3a3a3a; | ||||||
|  |     background: @bg-left-color; | ||||||
|  | 
 | ||||||
|  |     .arrow { | ||||||
|  |       border-color: transparent @bg-left-color transparent transparent; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   &.right { | ||||||
|  |     color: #fff; | ||||||
|  |     background: @bg-right-color; | ||||||
|  | 
 | ||||||
|  |     .arrow { | ||||||
|  |       right: -10px; | ||||||
|  |       left: unset; | ||||||
|  |       border-color: transparent transparent transparent @bg-right-color; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   pre { | ||||||
|  |     white-space: pre-wrap; | ||||||
|  |     overflow: hidden; | ||||||
|  |     word-break: break-word; | ||||||
|  |     word-wrap: break-word; | ||||||
|  |     font-size: 15px; | ||||||
|  |     padding: 3px 10px; | ||||||
|  |     font-family: "Microsoft YaHei"; | ||||||
|  |     line-height: 25px; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,19 @@ | ||||||
|  | <template> | ||||||
|  |   <!-- 用户卡片消息 - 预留 --> | ||||||
|  |   <div></div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | export default { | ||||||
|  |   name: 'UserCardMessage', | ||||||
|  |   components: {}, | ||||||
|  |   data() { | ||||||
|  |     return {} | ||||||
|  |   }, | ||||||
|  |   computed: {}, | ||||||
|  |   watch: {}, | ||||||
|  |   methods: {}, | ||||||
|  |   created() {}, | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | <style lang="less" scoped></style> | ||||||
|  | @ -0,0 +1,18 @@ | ||||||
|  | <template> | ||||||
|  |   <div class="video-message"> | ||||||
|  |     视频消息 | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | export default { | ||||||
|  |   name: 'VideoMessage', | ||||||
|  |   components: {}, | ||||||
|  |   data() { | ||||||
|  |     return {} | ||||||
|  |   }, | ||||||
|  |   methods: {}, | ||||||
|  |   created() {}, | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | <style lang="less" scoped></style> | ||||||
|  | @ -0,0 +1,134 @@ | ||||||
|  | <template> | ||||||
|  |   <div class="visit-card-message"> | ||||||
|  |     <div class="user flex-center"> | ||||||
|  |       <div class="avatar flex-center"> | ||||||
|  |         <el-avatar :size="28" :src="avatar" /> | ||||||
|  |       </div> | ||||||
|  |       <div class="content flex-center"> | ||||||
|  |         <p class="ellipsis">{{ nickname }}</p> | ||||||
|  |       </div> | ||||||
|  |       <div class="tools flex-center"> | ||||||
|  |         <span class="flex-center pointer"> | ||||||
|  |           <i class="el-icon-plus" /> 加好友 | ||||||
|  |         </span> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |     <div class="sign"><span>个性签名 : </span>{{ sign }}</div> | ||||||
|  |     <div class="share no-select ellipsis"> | ||||||
|  |       <a class="pointer" @click="openVisitCard(friendId)">你是谁?</a> | ||||||
|  |       分享了用户名片,可点击添加好友 ... | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | <script> | ||||||
|  | export default { | ||||||
|  |   name: 'VisitCardMessage', | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       userId: 0, | ||||||
|  |       friendId: 0, | ||||||
|  |       avatar: | ||||||
|  |         'http://im-serve0.gzydong.club/static/image/sys-head/2019012107542668696.jpg', | ||||||
|  |       sign: | ||||||
|  |         '这个社会,是赢家通吃,输者一无所有,社会,永远都是只以成败论英雄。', | ||||||
|  |       nickname: | ||||||
|  |         '氨基酸纳氨基酸纳氨基酸纳氨基酸纳氨基酸纳氨基酸纳氨基酸纳氨基酸纳', | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   created() {}, | ||||||
|  |   methods: { | ||||||
|  |     openVisitCard(user_id) { | ||||||
|  |       this.$emit('openVisitCard', user_id) | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | <style lang="less" scoped> | ||||||
|  | .ellipsis { | ||||||
|  |   white-space: nowrap; | ||||||
|  |   overflow: hidden; | ||||||
|  |   text-overflow: ellipsis; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .visit-card-message { | ||||||
|  |   min-height: 130px; | ||||||
|  |   min-width: 100px; | ||||||
|  |   max-width: 300px; | ||||||
|  |   border-radius: 5px; | ||||||
|  |   padding: 10px; | ||||||
|  |   box-sizing: border-box; | ||||||
|  |   border: 1px solid #ece5e5; | ||||||
|  |   transition: all 0.5s; | ||||||
|  | 
 | ||||||
|  |   &:hover { | ||||||
|  |     box-shadow: 0 0 8px #e2d3d3; | ||||||
|  |     transform: scale(1.01); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .user { | ||||||
|  |     height: 40px; | ||||||
|  |     overflow: hidden; | ||||||
|  |     box-sizing: border-box; | ||||||
|  |     > div { | ||||||
|  |       height: inherit; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .avatar { | ||||||
|  |       flex-basis: 30px; | ||||||
|  |       flex-shrink: 0; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .content { | ||||||
|  |       flex: 1 1; | ||||||
|  |       margin: 0 10px; | ||||||
|  |       font-size: 14px; | ||||||
|  |       justify-content: flex-start; | ||||||
|  |       overflow: hidden; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .tools { | ||||||
|  |       flex-basis: 60px; | ||||||
|  |       flex-shrink: 0; | ||||||
|  | 
 | ||||||
|  |       span { | ||||||
|  |         width: 65px; | ||||||
|  |         height: 30px; | ||||||
|  |         background: #409eff; | ||||||
|  |         color: white; | ||||||
|  |         font-size: 13px; | ||||||
|  |         border-radius: 20px; | ||||||
|  |         padding: 0 8px; | ||||||
|  |         transform: scale(0.8); | ||||||
|  |         user-select: none; | ||||||
|  | 
 | ||||||
|  |         &:active { | ||||||
|  |           background: #83b0f3; | ||||||
|  |           transform: scale(0.83); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .sign { | ||||||
|  |     min-height: 22px; | ||||||
|  |     line-height: 22px; | ||||||
|  |     border-radius: 3px; | ||||||
|  |     padding: 5px 8px; | ||||||
|  |     background: #f3f5f7; | ||||||
|  |     color: #7d7d7d; | ||||||
|  |     font-size: 12px; | ||||||
|  |     margin: 10px 0; | ||||||
|  |     span { | ||||||
|  |       font-weight: bold; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .share { | ||||||
|  |     font-size: 12px; | ||||||
|  |     color: #7d7d7d; | ||||||
|  |     a { | ||||||
|  |       color: #4cabf7; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,16 @@ | ||||||
|  | <template> | ||||||
|  |   <div class="voice-message"></div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | export default { | ||||||
|  |   name: 'VoiceMessage', | ||||||
|  |   components: {}, | ||||||
|  |   data() { | ||||||
|  |     return {} | ||||||
|  |   }, | ||||||
|  |   methods: {}, | ||||||
|  |   created() {}, | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | <style lang="less" scoped></style> | ||||||
|  | @ -0,0 +1,299 @@ | ||||||
|  | <template> | ||||||
|  |   <div> | ||||||
|  |     <div class="vote-message"> | ||||||
|  |       <div class="vote-from"> | ||||||
|  |         <div class="vheader"> | ||||||
|  |           <p> | ||||||
|  |             {{ answer_mode == 1 ? "[多选投票]" : "[单选投票]" }} | ||||||
|  |             <i | ||||||
|  |               v-show="is_vote" | ||||||
|  |               class="pointer" | ||||||
|  |               :class="{ | ||||||
|  |                 'el-icon-loading': refresh, | ||||||
|  |                 'el-icon-refresh': !refresh, | ||||||
|  |               }" | ||||||
|  |               title="刷新投票结果" | ||||||
|  |               @click="loadRefresh" | ||||||
|  |             ></i> | ||||||
|  |           </p> | ||||||
|  |           <p>{{ title }}</p> | ||||||
|  |         </div> | ||||||
|  | 
 | ||||||
|  |         <template v-if="is_vote"> | ||||||
|  |           <div class="vbody"> | ||||||
|  |             <div class="vote-view" v-for="(option, index) in options"> | ||||||
|  |               <p class="vote-option">{{ option.value }}. {{ option.text }}</p> | ||||||
|  |               <p class="vote-census"> | ||||||
|  |                 {{ option.num }} 票 {{ option.progress }}% | ||||||
|  |               </p> | ||||||
|  |               <p class="vote-progress"> | ||||||
|  |                 <el-progress | ||||||
|  |                   :show-text="false" | ||||||
|  |                   :percentage="parseInt(option.progress)" | ||||||
|  |                 /> | ||||||
|  |               </p> | ||||||
|  |             </div> | ||||||
|  |           </div> | ||||||
|  |           <div class="vfooter vote-view"> | ||||||
|  |             <p>应参与人数:{{ answer_num }} 人</p> | ||||||
|  |             <p>实际参与人数:{{ answered_num }} 人</p> | ||||||
|  |           </div> | ||||||
|  |         </template> | ||||||
|  |         <template v-else> | ||||||
|  |           <div class="vbody"> | ||||||
|  |             <p class="option" v-for="(option, index) in options"> | ||||||
|  |               <el-checkbox | ||||||
|  |                 v-model="option.is_checked" | ||||||
|  |                 @change="toSelect2(option)" | ||||||
|  |               /> | ||||||
|  |               <span @click="toSelect(option, index)" style="margin-left: 10px"> | ||||||
|  |                 {{ option.value }} 、{{ option.text }} | ||||||
|  |               </span> | ||||||
|  |             </p> | ||||||
|  |           </div> | ||||||
|  |           <div class="vfooter"> | ||||||
|  |             <el-button plain round @click="toVote"> | ||||||
|  |               {{ isUserVote ? "立即投票" : "请选择进行投票" }} | ||||||
|  |             </el-button> | ||||||
|  |           </div> | ||||||
|  |         </template> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | import { ServeConfirmVoteHandle } from "@/api/chat"; | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   name: "VoteMessage", | ||||||
|  |   props: { | ||||||
|  |     vote: { | ||||||
|  |       type: Object, | ||||||
|  |       required: true, | ||||||
|  |     }, | ||||||
|  |     record_id: { | ||||||
|  |       type: Number, | ||||||
|  |       required: true, | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       answer_mode: 0, | ||||||
|  |       title: "啊谁叫你打开你卡沙发那,那就是看、卡收纳是你", | ||||||
|  |       radio_value: "", | ||||||
|  |       options: [], | ||||||
|  |       is_vote: false, | ||||||
|  |       answer_num: 0, | ||||||
|  |       answered_num: 0, | ||||||
|  |       refresh: false, | ||||||
|  |     }; | ||||||
|  |   }, | ||||||
|  |   computed: { | ||||||
|  |     isUserVote() { | ||||||
|  |       return this.options.some((iten) => { | ||||||
|  |         return iten.is_checked; | ||||||
|  |       }); | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   created() { | ||||||
|  |     let user_id = this.$store.state.user.id; | ||||||
|  |     let { detail, statistics, vote_users } = this.vote; | ||||||
|  | 
 | ||||||
|  |     this.answer_mode = detail.answer_mode; | ||||||
|  |     this.answer_num = detail.answer_num; | ||||||
|  |     this.answered_num = detail.answered_num; | ||||||
|  | 
 | ||||||
|  |     detail.answer_option.forEach((item) => { | ||||||
|  |       this.options.push({ | ||||||
|  |         value: item.key, | ||||||
|  |         text: item.value, | ||||||
|  |         is_checked: false, | ||||||
|  |         num: 0, | ||||||
|  |         progress: "00.0", | ||||||
|  |       }); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     this.is_vote = vote_users.some((value) => { | ||||||
|  |       return value == user_id; | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     this.updateStatistics(statistics); | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     loadRefresh() { | ||||||
|  |       this.refresh = true; | ||||||
|  | 
 | ||||||
|  |       setTimeout(() => { | ||||||
|  |         this.refresh = false; | ||||||
|  |       }, 500); | ||||||
|  |     }, | ||||||
|  |     updateStatistics(data) { | ||||||
|  |       let count = data.count; | ||||||
|  |       this.options.forEach((option) => { | ||||||
|  |         option.num = data.options[option.value]; | ||||||
|  | 
 | ||||||
|  |         if (count > 0) { | ||||||
|  |           option.progress = (data.options[option.value] / count) * 100; | ||||||
|  |         } | ||||||
|  |       }); | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     toSelect(option, index) { | ||||||
|  |       if (this.answer_mode == 0) { | ||||||
|  |         this.options.forEach((option) => { | ||||||
|  |           option.is_checked = false; | ||||||
|  |         }); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       this.options[index].is_checked = !option.is_checked; | ||||||
|  |     }, | ||||||
|  |     toSelect2(option) { | ||||||
|  |       if (this.answer_mode == 0) { | ||||||
|  |         this.options.forEach((item) => { | ||||||
|  |           if (option.value == item.value) { | ||||||
|  |             item.is_checked = option.is_checked; | ||||||
|  |           } else { | ||||||
|  |             item.is_checked = false; | ||||||
|  |           } | ||||||
|  |         }); | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     toVote() { | ||||||
|  |       if (this.isUserVote == false) { | ||||||
|  |         return false; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       let items = []; | ||||||
|  |       this.options.forEach((item) => { | ||||||
|  |         if (item.is_checked) { | ||||||
|  |           items.push(item.value); | ||||||
|  |         } | ||||||
|  |       }); | ||||||
|  | 
 | ||||||
|  |       ServeConfirmVoteHandle({ | ||||||
|  |         record_id: this.record_id, | ||||||
|  |         options: items.join(","), | ||||||
|  |       }).then((res) => { | ||||||
|  |         if (res.code == 200) { | ||||||
|  |           this.is_vote = true; | ||||||
|  |           this.updateStatistics(res.data); | ||||||
|  |         } | ||||||
|  |       }); | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | }; | ||||||
|  | </script> | ||||||
|  | <style lang="less" scoped> | ||||||
|  | .vote-message { | ||||||
|  |   width: 300px; | ||||||
|  |   min-height: 150px; | ||||||
|  |   border: 1px solid #eceff1; | ||||||
|  |   box-sizing: border-box; | ||||||
|  |   border-radius: 5px; | ||||||
|  |   overflow: hidden; | ||||||
|  | 
 | ||||||
|  |   .vote-from { | ||||||
|  |     width: 100%; | ||||||
|  | 
 | ||||||
|  |     .vheader { | ||||||
|  |       min-height: 50px; | ||||||
|  |       background: #4e83fd; | ||||||
|  |       padding: 8px; | ||||||
|  |       position: relative; | ||||||
|  |       p { | ||||||
|  |         margin: 3px 0; | ||||||
|  |         &:first-child { | ||||||
|  |           color: rgb(245, 237, 237); | ||||||
|  |           font-size: 13px; | ||||||
|  |           margin-bottom: 8px; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         &:last-child { | ||||||
|  |           color: white; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       &::before { | ||||||
|  |         content: "投票"; | ||||||
|  |         position: absolute; | ||||||
|  |         font-size: 60px; | ||||||
|  |         color: white; | ||||||
|  |         opacity: 0.1; | ||||||
|  |         top: -5px; | ||||||
|  |         right: 10px; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .vbody { | ||||||
|  |       min-height: 80px; | ||||||
|  |       width: 100%; | ||||||
|  |       padding: 5px 15px; | ||||||
|  |       box-sizing: border-box; | ||||||
|  |       .option { | ||||||
|  |         margin: 14px 0px; | ||||||
|  |         font-size: 13px; | ||||||
|  |         span { | ||||||
|  |           cursor: pointer; | ||||||
|  |           user-select: none; | ||||||
|  |           line-height: 22px; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         .el-radio { | ||||||
|  |           margin-right: 0; | ||||||
|  |           .el-radio__label { | ||||||
|  |             padding-left: 5px; | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       margin-bottom: 10px; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .vfooter { | ||||||
|  |       height: 55px; | ||||||
|  |       text-align: center; | ||||||
|  |       box-sizing: border-box; | ||||||
|  | 
 | ||||||
|  |       .el-button { | ||||||
|  |         width: 80%; | ||||||
|  |         font-weight: 400; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       &.vote-view { | ||||||
|  |         display: flex; | ||||||
|  |         flex-direction: column; | ||||||
|  |         justify-content: flex-start; | ||||||
|  |         align-items: flex-start; | ||||||
|  |         padding-left: 15px; | ||||||
|  | 
 | ||||||
|  |         p { | ||||||
|  |           border-left: 2px solid #2196f3; | ||||||
|  |           padding-left: 5px; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .vote-view { | ||||||
|  |     width: 100%; | ||||||
|  |     min-height: 30px; | ||||||
|  |     margin: 15px 0; | ||||||
|  |     box-sizing: border-box; | ||||||
|  | 
 | ||||||
|  |     > p { | ||||||
|  |       margin: 6px 0px; | ||||||
|  |       font-size: 13px; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .vote-option { | ||||||
|  |       min-height: 20px; | ||||||
|  |       line-height: 20px; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .vote-census { | ||||||
|  |       height: 20px; | ||||||
|  |       line-height: 20px; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,33 @@ | ||||||
|  | import AudioMessage from './AudioMessage.vue'; | ||||||
|  | import CodeMessage from './CodeMessage.vue'; | ||||||
|  | import ForwardMessage from './ForwardMessage.vue'; | ||||||
|  | import ImageMessage from './ImageMessage.vue'; | ||||||
|  | import TextMessage from './TextMessage.vue'; | ||||||
|  | import VideoMessage from './VideoMessage.vue'; | ||||||
|  | import VoiceMessage from './VoiceMessage.vue'; | ||||||
|  | import SystemTextMessage from './SystemTextMessage.vue'; | ||||||
|  | import FileMessage from './FileMessage.vue'; | ||||||
|  | import InviteMessage from './InviteMessage.vue'; | ||||||
|  | import RevokeMessage from './RevokeMessage.vue'; | ||||||
|  | import VisitCardMessage from './VisitCardMessage.vue'; | ||||||
|  | import ReplyMessage from './ReplyMessage.vue'; | ||||||
|  | import VoteMessage from './VoteMessage.vue'; | ||||||
|  | import LoginMessage from './LoginMessage.vue'; | ||||||
|  | 
 | ||||||
|  | export { | ||||||
|  |     AudioMessage, | ||||||
|  |     CodeMessage, | ||||||
|  |     ForwardMessage, | ||||||
|  |     ImageMessage, | ||||||
|  |     TextMessage, | ||||||
|  |     VideoMessage, | ||||||
|  |     VoiceMessage, | ||||||
|  |     SystemTextMessage, | ||||||
|  |     FileMessage, | ||||||
|  |     InviteMessage, | ||||||
|  |     RevokeMessage, | ||||||
|  |     VisitCardMessage, | ||||||
|  |     ReplyMessage, | ||||||
|  |     VoteMessage, | ||||||
|  |     LoginMessage | ||||||
|  | } | ||||||
|  | @ -0,0 +1,226 @@ | ||||||
|  | <template> | ||||||
|  |   <el-tabs v-model="activeName" @tab-click="handleClick" type="card" :stretch=true> | ||||||
|  |     <el-tab-pane :label="toUser.storeFlag ? '正在咨询' : '他的足迹'" name="history"> | ||||||
|  |       <div style="margin-left: 12px;" v-if="toUser.storeFlag"> | ||||||
|  |         <GoodsLink :goodsDetail="goodsDetail" v-if="toUser.userId === goodsDetail.storeId" | ||||||
|  |           @sendMessage="submitSendMessage" /> | ||||||
|  |         <FootPrint :list="footPrintList" @loadMore="loadMoreFootPrint()" :orderList="orderPrintList" | ||||||
|  |           @sendMessage="submitSendMessage" /> | ||||||
|  |       </div> | ||||||
|  |     </el-tab-pane> | ||||||
|  |     <el-tab-pane label="店铺信息" name="UserInfo" v-if="toUser.storeFlag"> | ||||||
|  |       <div v-if="toUser.storeFlag"> | ||||||
|  |         <StoreDetail :storeInfo="storeInfo" /> | ||||||
|  |       </div> | ||||||
|  |     </el-tab-pane> | ||||||
|  |   </el-tabs> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | import { Tabs, TabPane } from 'element-ui' | ||||||
|  | import { ServeGetStoreDetail, ServeGetUserDetail, ServeGetFootPrint, ServeGetOrderPrint } from '@/api/user' | ||||||
|  | import { ServeGetGoodsDetail } from '@/api/goods' | ||||||
|  | import StoreDetail from "@/components/chat/panel/template/storeDetail.vue"; | ||||||
|  | import FootPrint from "@/components/chat/panel/template/footPrint.vue"; | ||||||
|  | import GoodsLink from "@/components/chat/panel/template/goodsLink.vue"; | ||||||
|  | import SocketInstance from "@/im-server/socket-instance"; | ||||||
|  | import { mapState, mapGetters } from "vuex"; | ||||||
|  | export default { | ||||||
|  |   components: { | ||||||
|  |     "el-tabs": Tabs, | ||||||
|  |     "el-tab-pane": TabPane, | ||||||
|  |     StoreDetail, | ||||||
|  |     FootPrint, | ||||||
|  |     GoodsLink | ||||||
|  |   }, | ||||||
|  |   props: { | ||||||
|  |     toUser: { | ||||||
|  |       type: Object, | ||||||
|  |       default: null, | ||||||
|  |     }, | ||||||
|  |     id: { | ||||||
|  |       type: String, | ||||||
|  |       default: '', | ||||||
|  |     }, | ||||||
|  |     goodsParams: { | ||||||
|  |       type: Object, | ||||||
|  |       default: null, | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   computed: { | ||||||
|  |     ...mapGetters(["talkItems"]), | ||||||
|  |     ...mapState({ | ||||||
|  |       index_name: (state) => state.dialogue.index_name, | ||||||
|  |     }), | ||||||
|  |   }, | ||||||
|  |   data () { | ||||||
|  |     return { | ||||||
|  |       activeName: 'history', | ||||||
|  |       storeInfo: {}, //店铺信息 | ||||||
|  |       memberInfo: {}, //会员信息 | ||||||
|  |       footPrintParams: { | ||||||
|  |         pageSize: 10, | ||||||
|  |         pageNumber: 1, | ||||||
|  |         memberId: '', | ||||||
|  |         storeId: '', | ||||||
|  |       }, | ||||||
|  |       goodsDetail: {}, | ||||||
|  |       footPrintList: [], // 商品 | ||||||
|  |       orderPrintList: []// 订单 | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   mounted () { | ||||||
|  |     if (this.toUser.storeFlag) { | ||||||
|  |       this.getStoreDetail() | ||||||
|  |     } else { | ||||||
|  |       this.getMemberDetail() | ||||||
|  |     } | ||||||
|  |     this.getFootPrint() | ||||||
|  |     if (this.goodsParams) { | ||||||
|  |       this.getGoodsDetail() | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     getStoreDetail () { | ||||||
|  |       ServeGetStoreDetail(this.toUser.userId).then(res => { | ||||||
|  |         if (res.success) { | ||||||
|  |           this.storeInfo = res.result | ||||||
|  |         } | ||||||
|  |       }) | ||||||
|  |     }, | ||||||
|  |     loadMoreFootPrint (e) { | ||||||
|  |       //触底再次调接口 | ||||||
|  |       this.footPrintParams.pageNumber++ | ||||||
|  |       this.getFootPrint() | ||||||
|  |     }, | ||||||
|  |     handleClick () { }, | ||||||
|  |     getMemberDetail () { | ||||||
|  |       ServeGetUserDetail(this.toUser.userId).then(res => { | ||||||
|  |         if (res.success) { | ||||||
|  |           this.memberInfo = res.result | ||||||
|  |         } | ||||||
|  |       }) | ||||||
|  |     }, | ||||||
|  |     getGoodsDetail () { | ||||||
|  |       ServeGetGoodsDetail(this.goodsParams).then(res => { | ||||||
|  |         if (res.success) { | ||||||
|  |           this.goodsDetail = res.result.data | ||||||
|  |         } | ||||||
|  |       }) | ||||||
|  |     }, | ||||||
|  |     getFootPrint () { | ||||||
|  |       if (this.toUser.storeFlag) { | ||||||
|  |         this.footPrintParams.memberId = this.id | ||||||
|  |         this.footPrintParams.storeId = this.toUser.userId | ||||||
|  |       } else { | ||||||
|  |         this.footPrintParams.memberId = this.toUser.userId | ||||||
|  |         this.footPrintParams.storeId = this.id | ||||||
|  |       } | ||||||
|  |       ServeGetFootPrint(this.footPrintParams).then(res => { | ||||||
|  |         res.result.records.forEach((item, index) => { | ||||||
|  |           if (localStorage.getItem(item.goodsId)) { | ||||||
|  |             item.btnHide = 0 | ||||||
|  |           } else { | ||||||
|  |             item.btnHide = 1 | ||||||
|  |           } | ||||||
|  |           if (item.goodsId === this.goodsParams.goodsId) { | ||||||
|  |             res.result.records.splice(index, 1) | ||||||
|  |           } | ||||||
|  |         }); | ||||||
|  |         this.footPrintList.push(...res.result.records) | ||||||
|  |       }) | ||||||
|  |       // 订单列表 | ||||||
|  |       ServeGetOrderPrint(this.footPrintParams).then((res) => { | ||||||
|  |         if (res.code == 200) { | ||||||
|  |           res.result.records.forEach((item) => { | ||||||
|  |             this.orderPrintList.push({ | ||||||
|  |               ...item, | ||||||
|  |               btnHide: 1 | ||||||
|  |             }) | ||||||
|  |           }) | ||||||
|  |           // this.orderPrintList.push(...res.result.records) | ||||||
|  |         } | ||||||
|  |       }) | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     // 发送消息回调事件 | ||||||
|  |     submitSendMessage (record, context, messageType) { | ||||||
|  |       SocketInstance.emit("event_talk", record); | ||||||
|  |       this.$store.commit("UPDATE_TALK_ITEM", { | ||||||
|  |         index_name: this.index_name, | ||||||
|  |         draft_text: "", | ||||||
|  |       }); | ||||||
|  |       /** | ||||||
|  |        * 插入数据 | ||||||
|  |        */ | ||||||
|  |       const insterChat = { | ||||||
|  |         createTime: this.formateDateAndTimeToString(new Date()), | ||||||
|  |         fromUser: this.id, | ||||||
|  |         toUser: record.to, | ||||||
|  |         isRead: false, | ||||||
|  |         messageType: messageType, | ||||||
|  |         text: context, | ||||||
|  |         float: "right", | ||||||
|  |       }; | ||||||
|  | 
 | ||||||
|  |       // 插入对话记录 | ||||||
|  |       this.$store.commit("PUSH_DIALOGUE", insterChat); | ||||||
|  |       // 获取聊天面板元素节点 | ||||||
|  |       let el = document.getElementById("lumenChatPanel"); | ||||||
|  |       // 判断的滚动条是否在底部 | ||||||
|  |       let isBottom = | ||||||
|  |         Math.ceil(el.scrollTop) + el.clientHeight >= el.scrollHeight; | ||||||
|  | 
 | ||||||
|  |       if (isBottom || record.to == this.id) { | ||||||
|  |         this.$nextTick(() => { | ||||||
|  |           el.scrollTop = el.scrollHeight; | ||||||
|  |         }); | ||||||
|  |       } else { | ||||||
|  |         this.$store.commit("SET_TLAK_UNREAD_MESSAGE", { | ||||||
|  |           content: content, | ||||||
|  |           nickname: record.name, | ||||||
|  |         }); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     formateDateAndTimeToString (date) { | ||||||
|  |       var hours = date.getHours(); | ||||||
|  |       var mins = date.getMinutes(); | ||||||
|  |       var secs = date.getSeconds(); | ||||||
|  |       var msecs = date.getMilliseconds(); | ||||||
|  |       if (hours < 10) hours = "0" + hours; | ||||||
|  |       if (mins < 10) mins = "0" + mins; | ||||||
|  |       if (secs < 10) secs = "0" + secs; | ||||||
|  |       if (msecs < 10) secs = "0" + msecs; | ||||||
|  |       return ( | ||||||
|  |         this.formatDateToString(date) + " " + hours + ":" + mins + ":" + secs | ||||||
|  |       ); | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     formatDateToString (date) { | ||||||
|  |       var year = date.getFullYear(); | ||||||
|  |       var month = date.getMonth() + 1; | ||||||
|  |       var day = date.getDate(); | ||||||
|  |       if (month < 10) month = "0" + month; | ||||||
|  |       if (day < 10) day = "0" + day; | ||||||
|  |       return year + "-" + month + "-" + day; | ||||||
|  |     }, | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style scoped lang="less"> | ||||||
|  | // /deep/ .el-tabs__nav.is-top { | ||||||
|  | // } | ||||||
|  | 
 | ||||||
|  | /deep/ .el-tabs__nav { | ||||||
|  |   height: 60px; | ||||||
|  |   line-height: 60px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /deep/ .el-tab-pane { | ||||||
|  |   margin-left: 12px; | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,278 @@ | ||||||
|  | <template> | ||||||
|  |   <el-header id="panel-header"> | ||||||
|  |     <div class="module left-module"> | ||||||
|  |       <span | ||||||
|  |         class="icon-badge" | ||||||
|  |         v-show="params.is_robot == 0" | ||||||
|  |         :class="{ 'red-color': params.talk_type == 1 }" | ||||||
|  |       > | ||||||
|  |         {{ params.talk_type == 1 ? '好友' : '群组' }} | ||||||
|  |       </span> | ||||||
|  |       <span class="nickname">{{ params.nickname }}</span> | ||||||
|  |       <span v-show="params.talk_type == 2" class="num">({{ groupNum }})</span> | ||||||
|  |     </div> | ||||||
|  | 
 | ||||||
|  |     <div v-show="params.talk_type == 1 && params.is_robot == 0" class="module center-module"> | ||||||
|  |       <p class="online"> | ||||||
|  |         <span v-show="isOnline" class="online-status"></span> | ||||||
|  |         <span>{{ isOnline ? '在线' : '离线' }}</span> | ||||||
|  |       </p> | ||||||
|  |       <p class="keyboard-status" v-show="isKeyboard">对方正在输入 ...</p> | ||||||
|  |     </div> | ||||||
|  | 
 | ||||||
|  |     <div class="module right-module" > | ||||||
|  |       <el-tooltip content="历史消息" placement="top"> | ||||||
|  |         <p v-show="params.is_robot == 0"> | ||||||
|  |           <i class="el-icon-time" @click="triggerEvent('history')" /> | ||||||
|  |         </p> | ||||||
|  |       </el-tooltip> | ||||||
|  |       <el-tooltip content="群公告" placement="top"> | ||||||
|  |         <p v-show="params.talk_type == 2"> | ||||||
|  |           <i class="iconfont icon-gonggao2" @click="triggerEvent('notice')" /> | ||||||
|  |         </p> | ||||||
|  |       </el-tooltip> | ||||||
|  |       <el-tooltip content="群设置" placement="top"> | ||||||
|  |         <p v-show="params.talk_type == 2"> | ||||||
|  |           <i class="el-icon-setting" @click="triggerEvent('setting')" /> | ||||||
|  |         </p> | ||||||
|  |       </el-tooltip> | ||||||
|  |     </div> | ||||||
|  |   </el-header> | ||||||
|  | </template> | ||||||
|  | <script> | ||||||
|  | export default { | ||||||
|  |   props: { | ||||||
|  |     data: { | ||||||
|  |       type: Object, | ||||||
|  |       default: () => { | ||||||
|  |         return { | ||||||
|  |           talk_type: 0, | ||||||
|  |           receiver_id: 0, | ||||||
|  |           params: 0, | ||||||
|  |           nickname: '', | ||||||
|  |         } | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     online: { | ||||||
|  |       type: Boolean, | ||||||
|  |       default: false, | ||||||
|  |     }, | ||||||
|  |     keyboard: { | ||||||
|  |       type: [Boolean, Number], | ||||||
|  |       default: false, | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       params: { | ||||||
|  |         talk_type: 0, | ||||||
|  |         receiver_id: 0, | ||||||
|  |         params: 0, | ||||||
|  |         nickname: '', | ||||||
|  |       }, | ||||||
|  |       isOnline: false, | ||||||
|  |       isKeyboard: false, | ||||||
|  |       groupNum: 0, | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   created() { | ||||||
|  |     this.setParamsData(this.data) | ||||||
|  |     this.setOnlineStatus(this.online) | ||||||
|  |   }, | ||||||
|  |   watch: { | ||||||
|  |     data(value) { | ||||||
|  |       this.setParamsData(value) | ||||||
|  |     }, | ||||||
|  |     online(value) { | ||||||
|  |       this.setOnlineStatus(value) | ||||||
|  |     }, | ||||||
|  |     keyboard(value) { | ||||||
|  |       this.isKeyboard = value | ||||||
|  |       setTimeout(() => { | ||||||
|  |         this.isKeyboard = false | ||||||
|  |       }, 2000) | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     setOnlineStatus(value) { | ||||||
|  |       this.isOnline = value | ||||||
|  |     }, | ||||||
|  |     setParamsData(object) { | ||||||
|  |       Object.assign(this.params, object) | ||||||
|  |     }, | ||||||
|  |     setGroupNum(value) { | ||||||
|  |       this.groupNum = value | ||||||
|  |     }, | ||||||
|  |     triggerEvent(event_name) { | ||||||
|  |       this.$emit('event', event_name) | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | <style lang="less" scoped> | ||||||
|  | #panel-header { | ||||||
|  |   display: flex; | ||||||
|  |   align-items: center; | ||||||
|  |   justify-content: space-between; | ||||||
|  |   box-sizing: border-box; | ||||||
|  |   border-bottom: 1px solid #f5eeee; | ||||||
|  | 
 | ||||||
|  |   .module { | ||||||
|  |     width: 100%/3; | ||||||
|  |     height: 100%; | ||||||
|  |     display: flex; | ||||||
|  |     align-items: center; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .left-module { | ||||||
|  |     padding-right: 5px; | ||||||
|  | 
 | ||||||
|  |     .icon-badge { | ||||||
|  |       background: rgb(81 139 254); | ||||||
|  |       height: 18px; | ||||||
|  |       line-height: 18px; | ||||||
|  |       padding: 1px 3px; | ||||||
|  |       font-size: 10px; | ||||||
|  |       color: white; | ||||||
|  |       border-radius: 3px; | ||||||
|  |       margin-right: 8px; | ||||||
|  |       flex-shrink: 0; | ||||||
|  | 
 | ||||||
|  |       &.red-color { | ||||||
|  |         background: #f97348; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .nickname { | ||||||
|  |       overflow: hidden; | ||||||
|  |       white-space: nowrap; | ||||||
|  |       text-overflow: ellipsis; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .center-module { | ||||||
|  |     flex-direction: column; | ||||||
|  |     justify-content: center; | ||||||
|  | 
 | ||||||
|  |     .online { | ||||||
|  |       color: #cccccc; | ||||||
|  |       font-weight: 300; | ||||||
|  |       font-size: 15px; | ||||||
|  | 
 | ||||||
|  |       &.color { | ||||||
|  |         color: #1890ff; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       .online-status { | ||||||
|  |         position: relative; | ||||||
|  |         top: -1px; | ||||||
|  |         display: inline-block; | ||||||
|  |         width: 6px; | ||||||
|  |         height: 6px; | ||||||
|  |         vertical-align: middle; | ||||||
|  |         border-radius: 50%; | ||||||
|  |         position: relative; | ||||||
|  |         background-color: #1890ff; | ||||||
|  |         margin-right: 5px; | ||||||
|  | 
 | ||||||
|  |         &:after { | ||||||
|  |           position: absolute; | ||||||
|  |           top: -1px; | ||||||
|  |           left: -1px; | ||||||
|  |           width: 100%; | ||||||
|  |           height: 100%; | ||||||
|  |           border: 1px solid #1890ff; | ||||||
|  |           border-radius: 50%; | ||||||
|  |           -webkit-animation: antStatusProcessing 1.2s ease-in-out infinite; | ||||||
|  |           animation: antStatusProcessing 1.2s ease-in-out infinite; | ||||||
|  |           content: ''; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .keyboard-status { | ||||||
|  |       height: 20px; | ||||||
|  |       line-height: 18px; | ||||||
|  |       font-size: 10px; | ||||||
|  |       animation: inputfade 600ms infinite; | ||||||
|  |       -webkit-animation: inputfade 600ms infinite; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .right-module { | ||||||
|  |     display: flex; | ||||||
|  |     justify-content: flex-end; | ||||||
|  |     align-items: center; | ||||||
|  | 
 | ||||||
|  |     p { | ||||||
|  |       cursor: pointer; | ||||||
|  |       margin: 0 8px; | ||||||
|  |       font-size: 20px; | ||||||
|  |       color: #828f95; | ||||||
|  |       &:active i { | ||||||
|  |         font-size: 26px; | ||||||
|  |         transform: scale(1.3); | ||||||
|  |         transition: ease 0.5s; | ||||||
|  |         color: red; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* css 动画 */ | ||||||
|  | @keyframes inputfade { | ||||||
|  |   from { | ||||||
|  |     opacity: 1; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   50% { | ||||||
|  |     opacity: 0.4; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   to { | ||||||
|  |     opacity: 1; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @-webkit-keyframes inputfade { | ||||||
|  |   from { | ||||||
|  |     opacity: 1; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   50% { | ||||||
|  |     opacity: 0.4; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   to { | ||||||
|  |     opacity: 1; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @-webkit-keyframes antStatusProcessing { | ||||||
|  |   0% { | ||||||
|  |     -webkit-transform: scale(0.8); | ||||||
|  |     transform: scale(0.8); | ||||||
|  |     opacity: 0.5; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   to { | ||||||
|  |     -webkit-transform: scale(2.4); | ||||||
|  |     transform: scale(2.4); | ||||||
|  |     opacity: 0; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @keyframes antStatusProcessing { | ||||||
|  |   0% { | ||||||
|  |     -webkit-transform: scale(0.8); | ||||||
|  |     transform: scale(0.8); | ||||||
|  |     opacity: 0.5; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   to { | ||||||
|  |     -webkit-transform: scale(2.4); | ||||||
|  |     transform: scale(2.4); | ||||||
|  |     opacity: 0; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,101 @@ | ||||||
|  | <template> | ||||||
|  |   <div class="multi-select"> | ||||||
|  |     <div class="multi-title"> | ||||||
|  |       <span>已选中:{{ value }} 条消息</span> | ||||||
|  |     </div> | ||||||
|  |     <div class="multi-main"> | ||||||
|  |       <div class="btn-group"> | ||||||
|  |         <div | ||||||
|  |           class="multi-icon pointer" | ||||||
|  |           @click="$emit('event', 'merge_forward')" | ||||||
|  |         > | ||||||
|  |           <i class="el-icon-position" /> | ||||||
|  |         </div> | ||||||
|  |         <p>合并转发</p> | ||||||
|  |       </div> | ||||||
|  |       <div class="btn-group"> | ||||||
|  |         <div class="multi-icon pointer" @click="$emit('event', 'forward')"> | ||||||
|  |           <i class="el-icon-position" /> | ||||||
|  |         </div> | ||||||
|  |         <p>逐条转发</p> | ||||||
|  |       </div> | ||||||
|  |       <div class="btn-group"> | ||||||
|  |         <div class="multi-icon pointer" @click="$emit('event', 'delete')"> | ||||||
|  |           <i class="el-icon-delete" /> | ||||||
|  |         </div> | ||||||
|  |         <p>批量删除</p> | ||||||
|  |       </div> | ||||||
|  |       <div class="btn-group"> | ||||||
|  |         <div class="multi-icon pointer" @click="$emit('event', 'close')"> | ||||||
|  |           <i class="el-icon-close" /> | ||||||
|  |         </div> | ||||||
|  |         <p>关闭</p> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | export default { | ||||||
|  |   props: { | ||||||
|  |     value: { | ||||||
|  |       default: 0, | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | <style lang="less" scoped> | ||||||
|  | .multi-select { | ||||||
|  |   width: 100%; | ||||||
|  |   height: 100%; | ||||||
|  |   box-sizing: border-box; | ||||||
|  |   display: flex; | ||||||
|  |   align-items: center; | ||||||
|  |   flex-direction: column; | ||||||
|  |   justify-content: center; | ||||||
|  | 
 | ||||||
|  |   .multi-title { | ||||||
|  |     width: 100%; | ||||||
|  |     height: 50px; | ||||||
|  |     line-height: 50px; | ||||||
|  |     text-align: center; | ||||||
|  |     color: #878484; | ||||||
|  |     font-weight: 300; | ||||||
|  |     font-size: 14px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .multi-main { | ||||||
|  |     .btn-group { | ||||||
|  |       display: inline-block; | ||||||
|  |       width: 70px; | ||||||
|  |       height: 70px; | ||||||
|  |       margin-right: 15px; | ||||||
|  | 
 | ||||||
|  |       .multi-icon { | ||||||
|  |         width: 45px; | ||||||
|  |         height: 45px; | ||||||
|  |         display: flex; | ||||||
|  |         align-items: center; | ||||||
|  |         justify-content: center; | ||||||
|  |         background-color: #f5f5f5; | ||||||
|  |         border-radius: 50%; | ||||||
|  |         margin: 0 auto; | ||||||
|  |         border: 1px solid transparent; | ||||||
|  | 
 | ||||||
|  |         &:hover { | ||||||
|  |           color: red; | ||||||
|  |           border-color: red; | ||||||
|  |           background: transparent; | ||||||
|  |           font-size: 18px; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       p { | ||||||
|  |         font-size: 12px; | ||||||
|  |         margin-top: 8px; | ||||||
|  |         text-align: center; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,239 @@ | ||||||
|  | <template> | ||||||
|  |   <div class="box" @scroll="scrollBottom"> | ||||||
|  |     <div class="tab"> | ||||||
|  |       <el-tabs v-model="activeName" @tab-click="handleClick" :stretch=true> | ||||||
|  |         <el-tab-pane label="最近浏览" name="goods"> | ||||||
|  |           <dl> | ||||||
|  |             <dd v-for="item in list" v-infinite-scroll="loadMore"> | ||||||
|  |               <div class="base"> | ||||||
|  |                 <div> | ||||||
|  |                   <img :src="item.thumbnail" class="image" /> | ||||||
|  |                 </div> | ||||||
|  |                 <div style="margin-left: 13px"> | ||||||
|  |                   <a class="goods_name" @click="linkToGoods(item.goodsId, item.id)">{{ item.goodsName }}</a> | ||||||
|  |                   <div style="margin-top: 8px;"> | ||||||
|  |                     <span style="color: red;">¥{{ item.price }}</span> | ||||||
|  |                   </div> | ||||||
|  |                   <div> | ||||||
|  |                     <el-button class="store-button" type="danger" v-if="item.btnHide == 1" size="mini" | ||||||
|  |                       @click="submitSendGoodsMessage(item)" plain>发送</el-button> | ||||||
|  |                   </div> | ||||||
|  |                 </div> | ||||||
|  |               </div> | ||||||
|  |             </dd> | ||||||
|  |           </dl> | ||||||
|  |         </el-tab-pane> | ||||||
|  |         <el-tab-pane label="订单列表" name="orders"> | ||||||
|  |           <dl> | ||||||
|  |             <dd v-for="(item, index) in orderList" v-infinite-scroll="loadMore" :key="index"> | ||||||
|  |               <div style="margin-bottom: 20px;"> | ||||||
|  |                 <span class="orderSn">订单号:{{ item.sn }}</span> | ||||||
|  |                 <img :src="item.groupImages" alt="" | ||||||
|  |                   style="height: 100px; width: 100px;margin-top: 10px; vertical-align: middle; "> | ||||||
|  |                 <span class="orderGoodsName" @click="linkToOrders(item.sn)"> {{ item.groupName }}</span> | ||||||
|  |                 <span style="margin-left: 10px; color: red;">{{ item.paymentTime }}</span> | ||||||
|  |                 <div class="orderBtn"> | ||||||
|  |                   <el-button type="danger" class="store-button" v-if="item.btnHide == 1" size="mini" | ||||||
|  |                     @click="submitSendOrderMessage(item, index)" plain>发送</el-button> | ||||||
|  |                 </div> | ||||||
|  |               </div> | ||||||
|  | 
 | ||||||
|  |             </dd> | ||||||
|  |           </dl> | ||||||
|  |         </el-tab-pane> | ||||||
|  |       </el-tabs> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | import { Tag, button, Tabs, TabPane, InfiniteScroll } from 'element-ui' | ||||||
|  | import { mapState, mapGetters } from "vuex"; | ||||||
|  | export default { | ||||||
|  |   directives: { | ||||||
|  |     "infinite-scroll": InfiniteScroll, | ||||||
|  |   }, | ||||||
|  |   data () { | ||||||
|  |     return { | ||||||
|  |       activeName: 'goods', | ||||||
|  |       btnHide: undefined, | ||||||
|  |       hide: true | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   computed: { | ||||||
|  |     ...mapGetters(["talkItems"]), | ||||||
|  |     ...mapState({ | ||||||
|  |       id: (state) => state.user.id, | ||||||
|  | 
 | ||||||
|  |       toUser: (state) => state.user.toUser, | ||||||
|  |     }), | ||||||
|  |   }, | ||||||
|  |   components: { | ||||||
|  |     "el-tag": Tag, | ||||||
|  |     "el-button": button, | ||||||
|  |     "el-tabs": Tabs, | ||||||
|  |     "el-tab-pane": TabPane, | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     //跳转订单列表 | ||||||
|  | 
 | ||||||
|  |     scrollBottom (e) { | ||||||
|  |       const { scrollTop, scrollHeight, clientHeight } = e.srcElement | ||||||
|  |       if (scrollTop + clientHeight >= scrollHeight) { | ||||||
|  |         this.$emit('loadMore') | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     loadMore () { | ||||||
|  | 
 | ||||||
|  |     }, | ||||||
|  |     // 发送消息回调事件 | ||||||
|  |     submitSendGoodsMessage (item) { | ||||||
|  |       const context = { | ||||||
|  |         id: item.id, | ||||||
|  |         goodsId: item.goodsId, | ||||||
|  |         thumbnail: item.thumbnail, | ||||||
|  |         price: item.price, | ||||||
|  |         goodsName: item.goodsName | ||||||
|  |       } | ||||||
|  |       const record = { | ||||||
|  |         operation_type: "MESSAGE", | ||||||
|  |         to: this.toUser.userId, | ||||||
|  |         from: this.id, | ||||||
|  |         message_type: "GOODS", | ||||||
|  |         context: context, | ||||||
|  |         talk_id: this.toUser.id, | ||||||
|  |       }; | ||||||
|  |       this.$emit('sendMessage', record, context, 'GOODS'); | ||||||
|  |       localStorage.setItem(item.goodsId, 0) | ||||||
|  |       item.btnHide = 0 | ||||||
|  |     }, | ||||||
|  |     // 发送订单列表 | ||||||
|  |     submitSendOrderMessage (item, index) { | ||||||
|  |       const context = { | ||||||
|  |         sn: item.sn, | ||||||
|  |         groupImages: item.groupImages, | ||||||
|  |         paymentTime: item.paymentTime, | ||||||
|  |         groupName: item.groupName, | ||||||
|  |       } | ||||||
|  |       const record = { | ||||||
|  |         operation_type: "MESSAGE", | ||||||
|  |         to: this.toUser.userId, | ||||||
|  |         from: this.id, | ||||||
|  |         message_type: "ORDER", | ||||||
|  |         context: context, | ||||||
|  |         talk_id: this.toUser.id, | ||||||
|  |       }; | ||||||
|  |       this.$emit('sendMessage', record, context, 'ORDER'); | ||||||
|  |       localStorage.setItem(item.goodsId, 0) | ||||||
|  |       item.btnHide = 0 | ||||||
|  |     }, | ||||||
|  |     handleClick (tab, event) { | ||||||
|  |       console.log(tab, event); | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   props: { | ||||||
|  |     list: { | ||||||
|  |       type: Array, | ||||||
|  |       default: [], | ||||||
|  |     }, | ||||||
|  |     orderList: { | ||||||
|  |       type: Array, | ||||||
|  |       default: [] | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   mounted () { | ||||||
|  |     console.log(this.orderList, 'orderList'); | ||||||
|  |     this.btnHide = localStorage.getItem('btnHide') | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style scoped lang="less"> | ||||||
|  | .orderGoodsName { | ||||||
|  |   width: 200px; | ||||||
|  |   overflow: hidden; | ||||||
|  |   text-overflow: ellipsis; | ||||||
|  |   white-space: nowrap; | ||||||
|  |   position: absolute; | ||||||
|  |   margin-left: 10px; | ||||||
|  |   margin-top: 10px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .orderSn { | ||||||
|  |   overflow: hidden; | ||||||
|  |   text-overflow: ellipsis; | ||||||
|  |   white-space: nowrap | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .orderBtn { | ||||||
|  |   display: flex; | ||||||
|  |   justify-content: flex-end; | ||||||
|  |   margin-right: 15px; | ||||||
|  |   position: relative; | ||||||
|  |   bottom: 30px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .goods_name { | ||||||
|  |   text-overflow: -o-ellipsis-lastline; | ||||||
|  |   overflow: hidden; | ||||||
|  |   text-overflow: ellipsis; | ||||||
|  |   display: -webkit-box; | ||||||
|  |   -webkit-line-clamp: 2; | ||||||
|  |   line-clamp: 2; | ||||||
|  |   -webkit-box-orient: vertical; | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /deep/ .el-tabs__item.is-top:last-child { | ||||||
|  |   color: black; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /deep/.el-tabs__active-bar { | ||||||
|  |   background-color: #f23030; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /deep/ .el-tabs__nav.is-top { | ||||||
|  |   height: 50px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .tab { | ||||||
|  |   display: flex; | ||||||
|  |   justify-content: center; | ||||||
|  |   align-items: center; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .box { | ||||||
|  |   height: 500px; | ||||||
|  |   overflow: auto; | ||||||
|  |   width: 350px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .store-button { | ||||||
|  |   background-color: white; | ||||||
|  |   border-color: #F56C6C; | ||||||
|  |   margin-top: 10px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .base { | ||||||
|  |   margin-top: 5px; | ||||||
|  |   height: 120px; | ||||||
|  |   display: flex; | ||||||
|  | 
 | ||||||
|  |   .price { | ||||||
|  |     color: red; | ||||||
|  |     margin-top: 15px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .image { | ||||||
|  |     height: 100px; | ||||||
|  |     margin-top: 3px; | ||||||
|  |     width: 100px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .separate { | ||||||
|  |   margin-top: 8px; | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,186 @@ | ||||||
|  | <template> | ||||||
|  |   <div style="width: 350px;"> | ||||||
|  |     当前浏览 | ||||||
|  |     <div class="base"> | ||||||
|  |       <div> | ||||||
|  |         <img :src="goodsDetail.thumbnail" class="image" /> | ||||||
|  |       </div> | ||||||
|  |       <div style="margin-left: 13px"> | ||||||
|  |         <a @click="linkToGoods(goodsDetail.goodsId, goodsDetail.id)"> {{ goodsDetail.goodsName }} </a> | ||||||
|  |         <div> | ||||||
|  |           <span style="color: red;">¥{{ goodsDetail.price }}</span> | ||||||
|  |         </div> | ||||||
|  |         <div v-if="hide"> | ||||||
|  |           <el-button class="store-button" type="danger" v-if="!sendFlag && btnHide == 1" size="mini" | ||||||
|  |             @click="submitSendMessage()" plain>发送</el-button> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |     <hr class="separate" /> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | import { Tag, button } from 'element-ui' | ||||||
|  | import { mapState, mapGetters } from "vuex"; | ||||||
|  | import SocketInstance from "@/im-server/socket-instance"; | ||||||
|  | export default { | ||||||
|  |   data () { | ||||||
|  |     return { | ||||||
|  |       sendFlag: false, | ||||||
|  |       btnHide: undefined, | ||||||
|  |       hide: true | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   computed: { | ||||||
|  |     ...mapGetters(["talkItems"]), | ||||||
|  |     ...mapState({ | ||||||
|  |       id: (state) => state.user.id, | ||||||
|  |       index_name: (state) => state.dialogue.index_name, | ||||||
|  |       toUser: (state) => state.user.toUser, | ||||||
|  |     }), | ||||||
|  |   }, | ||||||
|  |   mounted () { | ||||||
|  |     this.btnHide = localStorage.getItem('btnHide') | ||||||
|  |   }, | ||||||
|  |   components: { | ||||||
|  |     "el-tag": Tag, | ||||||
|  |     "el-button": button, | ||||||
|  |     Storage | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     toGoods () { | ||||||
|  |       alert("toGoods") | ||||||
|  |     }, | ||||||
|  |     toMessage () { | ||||||
|  |       alert(JSON.stringify(this.toUser)) | ||||||
|  |       alert("toMessage") | ||||||
|  |     }, | ||||||
|  |     // 回车键发送消息回调事件 | ||||||
|  |     submitSendMessage () { | ||||||
|  |       console.log("发送"); | ||||||
|  |       const context = this.goodsDetail | ||||||
|  |       const record = { | ||||||
|  |         operation_type: "MESSAGE", | ||||||
|  |         to: this.toUser.userId, | ||||||
|  |         from: this.id, | ||||||
|  |         message_type: "GOODS", | ||||||
|  |         context: context, | ||||||
|  |         talk_id: this.toUser.id, | ||||||
|  |       }; | ||||||
|  |       SocketInstance.emit("event_talk", record); | ||||||
|  | 
 | ||||||
|  |       this.$store.commit("UPDATE_TALK_ITEM", { | ||||||
|  |         index_name: this.index_name, | ||||||
|  |         draft_text: "", | ||||||
|  |       }); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |       /** | ||||||
|  |        * 插入数据 | ||||||
|  |        */ | ||||||
|  |       const insterChat = { | ||||||
|  |         createTime: this.formateDateAndTimeToString(new Date()), | ||||||
|  |         fromUser: this.id, | ||||||
|  |         toUser: record.to, | ||||||
|  |         isRead: false, | ||||||
|  |         messageType: "GOODS", | ||||||
|  |         text: context, | ||||||
|  |         float: "right", | ||||||
|  |       }; | ||||||
|  | 
 | ||||||
|  |       console.log("insterChat", insterChat); | ||||||
|  |       // console.log("插入对话记录",'') | ||||||
|  |       // 插入对话记录 | ||||||
|  |       this.$store.commit("PUSH_DIALOGUE", insterChat); | ||||||
|  |       // 获取聊天面板元素节点 | ||||||
|  |       let el = document.getElementById("lumenChatPanel"); | ||||||
|  |       // 判断的滚动条是否在底部 | ||||||
|  |       let isBottom = | ||||||
|  |         Math.ceil(el.scrollTop) + el.clientHeight >= el.scrollHeight; | ||||||
|  | 
 | ||||||
|  |       if (isBottom || record.to == this.id) { | ||||||
|  |         this.$nextTick(() => { | ||||||
|  |           el.scrollTop = el.scrollHeight; | ||||||
|  |         }); | ||||||
|  |       } else { | ||||||
|  |         this.$store.commit("SET_TLAK_UNREAD_MESSAGE", { | ||||||
|  |           content: content, | ||||||
|  |           nickname: record.name, | ||||||
|  |         }); | ||||||
|  |       } | ||||||
|  |       // 发送后隐藏按钮  0:隐藏 1:显示 | ||||||
|  |       localStorage.setItem('btnHide', 0) | ||||||
|  |       this.hide = false | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     formateDateAndTimeToString (date) { | ||||||
|  |       var hours = date.getHours(); | ||||||
|  |       var mins = date.getMinutes(); | ||||||
|  |       var secs = date.getSeconds(); | ||||||
|  |       var msecs = date.getMilliseconds(); | ||||||
|  |       if (hours < 10) hours = "0" + hours; | ||||||
|  |       if (mins < 10) mins = "0" + mins; | ||||||
|  |       if (secs < 10) secs = "0" + secs; | ||||||
|  |       if (msecs < 10) secs = "0" + msecs; | ||||||
|  |       return ( | ||||||
|  |         this.formatDateToString(date) + " " + hours + ":" + mins + ":" + secs | ||||||
|  |       ); | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     formatDateToString (date) { | ||||||
|  |       var year = date.getFullYear(); | ||||||
|  |       var month = date.getMonth() + 1; | ||||||
|  |       var day = date.getDate(); | ||||||
|  |       if (month < 10) month = "0" + month; | ||||||
|  |       if (day < 10) day = "0" + day; | ||||||
|  |       return year + "-" + month + "-" + day; | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   props: { | ||||||
|  |     goodsDetail: { | ||||||
|  |       type: Object, | ||||||
|  |       default: null, | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style scoped lang="less"> | ||||||
|  | .store-button { | ||||||
|  |   background-color: white; | ||||||
|  |   border-color: #F56C6C; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .base { | ||||||
|  |   margin-top: 5px; | ||||||
|  |   height: 120px; | ||||||
|  |   display: flex; | ||||||
|  | 
 | ||||||
|  |   div { | ||||||
|  |     overflow: hidden; | ||||||
|  |     text-overflow: ellipsis; | ||||||
|  |     margin-top: 8px; | ||||||
|  |     white-space: nowrap; | ||||||
|  |     margin-top: 8px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .image { | ||||||
|  |     height: 100px; | ||||||
|  |     margin-top: 3px; | ||||||
|  |     width: 100px | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .click-button { | ||||||
|  |   margin-top: 8px; | ||||||
|  |   background-color: white; | ||||||
|  |   border-color: #F56C6C; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .separate { | ||||||
|  |   margin-top: 8px; | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,77 @@ | ||||||
|  | <template> | ||||||
|  |   <div style="width: 362px;"> | ||||||
|  |     <div class="base"> | ||||||
|  |       <div> | ||||||
|  |         <img :src="storeInfo.storeLogo" class="image" /> | ||||||
|  |       </div> | ||||||
|  |       <div style="margin-left: 13px"> | ||||||
|  |         <div class="div-zoom"> | ||||||
|  |           {{ storeInfo.storeName }} | ||||||
|  |           <el-tag type="danger" v-if="storeInfo.selfOperated" size="mini">自营</el-tag> | ||||||
|  |         </div> | ||||||
|  |         <div> | ||||||
|  |           联系方式: {{ storeInfo.memberName }} | ||||||
|  |         </div> | ||||||
|  |         <div> | ||||||
|  |           <el-button class="store-button" type="danger" @click="linkToStore(storeInfo.id)" size="mini" | ||||||
|  |             plain>进入店铺</el-button> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |     <hr class="separate" /> | ||||||
|  |     <div class="separate">店铺评分: <span>{{ storeInfo.serviceScore }}</span></div> | ||||||
|  |     <div class="separate">服务评分: <span>{{ storeInfo.descriptionScore }}</span></div> | ||||||
|  |     <div class="separate">物流评分: <span>{{ storeInfo.deliveryScore }}</span></div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | import { Tag, button } from 'element-ui' | ||||||
|  | export default { | ||||||
|  |   data () { | ||||||
|  |     return { | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   components: { | ||||||
|  |     "el-tag": Tag, | ||||||
|  |     "el-button": button, | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |   }, | ||||||
|  |   props: { | ||||||
|  |     storeInfo: { | ||||||
|  |       type: Object, | ||||||
|  |       default: null, | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style scoped lang="less"> | ||||||
|  | .store-button { | ||||||
|  |   background-color: white; | ||||||
|  |   border-color: #F56C6C; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .base { | ||||||
|  |   margin-top: 5px; | ||||||
|  |   height: 120px; | ||||||
|  |   display: flex; | ||||||
|  | 
 | ||||||
|  |   div { | ||||||
|  |     margin-top: 8px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .image { | ||||||
|  |     height: 100px; | ||||||
|  |     margin-top: 3px; | ||||||
|  |     width: 100px | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .separate { | ||||||
|  |   margin-top: 8px; | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,506 @@ | ||||||
|  | <template> | ||||||
|  |   <div> | ||||||
|  |     <el-container class="editor-container"> | ||||||
|  |       <el-header class="no-padding toolbar" height="35px"> | ||||||
|  |         <ul> | ||||||
|  |           <li v-popover:popoverEmoticon> | ||||||
|  |             <i class="iconfont icon-icon_im_face" style="font-size: 15px" /> | ||||||
|  |             <p class="tip-title">表情符号</p> | ||||||
|  |           </li> | ||||||
|  |           <!-- <li @click="codeBlock.isShow = true"> | ||||||
|  |             <i class="iconfont icon-daima" /> | ||||||
|  |             <p class="tip-title">代码片段</p> | ||||||
|  |           </li> | ||||||
|  |           <li @click="recorder = true"> | ||||||
|  |             <i class="el-icon-headset" /> | ||||||
|  |             <p class="tip-title">语音消息</p> | ||||||
|  |           </li> --> | ||||||
|  |           <!-- #TODO 发图片功能暂时隐藏 此处会涉及到token过期 --> | ||||||
|  |           <!-- <li @click="$refs.restFile.click()"> | ||||||
|  |             <i class="el-icon-picture-outline-round" /> | ||||||
|  |             <p class="tip-title">图片</p> | ||||||
|  |           </li> --> | ||||||
|  |           <!-- <li @click="$refs.restFile2.click()"> | ||||||
|  |             <i class="el-icon-folder" /> | ||||||
|  |             <p class="tip-title">附件</p> | ||||||
|  |           </li> | ||||||
|  |           <li @click="filesManager.isShow = true"> | ||||||
|  |             <i class="el-icon-folder-opened" /> | ||||||
|  |             <p class="tip-title">上传管理</p> | ||||||
|  |           </li> | ||||||
|  |           <li v-show="isGroupTalk" @click="vote.isShow = true"> | ||||||
|  |             <i class="el-icon-s-data" /> | ||||||
|  |             <p class="tip-title">发起投票</p> | ||||||
|  |           </li> --> | ||||||
|  | 
 | ||||||
|  | <!--          <p class="text-tips no-select">--> | ||||||
|  | <!--            <span>按Enter发送 / Shift+Enter 换行</span>--> | ||||||
|  | <!--            <el-popover placement="top-end" width="600" trigger="click">--> | ||||||
|  | <!--              <div class="editor-books">--> | ||||||
|  | <!--                <div class="books-title">编辑说明:</div>--> | ||||||
|  | <!--                <p>--> | ||||||
|  | <!--                  1.--> | ||||||
|  | <!--                  支持上传QQ及微信截图,在QQ或微信中截图后使用Ctrl+v上传图片。--> | ||||||
|  | <!--                </p>--> | ||||||
|  | <!--                <p>--> | ||||||
|  | <!--                  2.--> | ||||||
|  | <!--                  支持浏览器及Word文档中的图片复制上传、复制后使用Ctrl+v上传图片。--> | ||||||
|  | <!--                </p>--> | ||||||
|  | <!--                <p>3. 支持图片拖拽上传。</p>--> | ||||||
|  | <!--                <p>4. 支持文件上传 ( 文件小于100M ) 。</p>--> | ||||||
|  | <!--                <p>5. 按Enter发送 / Shift+Enter 换行。</p>--> | ||||||
|  | <!--                <p>--> | ||||||
|  | <!--                  6.--> | ||||||
|  | <!--                  注意:当文件正在上传时,请勿关闭网页或离开当前对话框,否则将导致文件停止上传或上传失败。--> | ||||||
|  | <!--                </p>--> | ||||||
|  | <!--              </div>--> | ||||||
|  | <!--              <i class="el-icon-info" slot="reference" />--> | ||||||
|  | <!--            </el-popover>--> | ||||||
|  | <!--          </p>--> | ||||||
|  |         </ul> | ||||||
|  | 
 | ||||||
|  |         <el-popover | ||||||
|  |           ref="popoverEmoticon" | ||||||
|  |           placement="top-start" | ||||||
|  |           trigger="click" | ||||||
|  |           width="300" | ||||||
|  |           popper-class="no-padding el-popover-em" | ||||||
|  |         > | ||||||
|  |           <MeEditorEmoticon ref="editorEmoticon" @selected="selecteEmoticon" /> | ||||||
|  |         </el-popover> | ||||||
|  | 
 | ||||||
|  |         <form | ||||||
|  |           enctype="multipart/form-data" | ||||||
|  |           style="display: none" | ||||||
|  |           ref="fileFrom" | ||||||
|  |         > | ||||||
|  |           <input | ||||||
|  |             type="file" | ||||||
|  |             ref="restFile" | ||||||
|  |             accept="image/*" | ||||||
|  |             @change="uploadImageChange" | ||||||
|  |           /> | ||||||
|  |           <input type="file" ref="restFile2" @change="uploadFileChange" /> | ||||||
|  |         </form> | ||||||
|  |       </el-header> | ||||||
|  |       <el-main class="no-padding textarea"> | ||||||
|  |         <textarea | ||||||
|  |           ref="textarea" | ||||||
|  |           v-paste="pasteImage" | ||||||
|  |           v-drag="dragPasteImage" | ||||||
|  |           v-model.trim="editorText" | ||||||
|  |           rows="6" | ||||||
|  |           placeholder="你想要的聊点什么呢 ..." | ||||||
|  |           @keydown="keydownEvent($event)" | ||||||
|  |           @input="inputEvent($event)" | ||||||
|  |         /> | ||||||
|  |       </el-main> | ||||||
|  |     </el-container> | ||||||
|  | 
 | ||||||
|  |     <!-- 图片查看器 --> | ||||||
|  |     <MeEditorImageView | ||||||
|  |       ref="imageViewer" | ||||||
|  |       v-model="imageViewer.isShow" | ||||||
|  |       :file="imageViewer.file" | ||||||
|  |       @confirm="confirmUploadImage" | ||||||
|  |     /> | ||||||
|  | 
 | ||||||
|  |     <MeEditorRecorder v-if="recorder" @close="recorder = false" /> | ||||||
|  | 
 | ||||||
|  |     <!-- 代码块编辑器 --> | ||||||
|  |     <TalkCodeBlock | ||||||
|  |       v-if="codeBlock.isShow" | ||||||
|  |       :edit-mode="codeBlock.editMode" | ||||||
|  |       @close="codeBlock.isShow = false" | ||||||
|  |       @confirm="confirmCodeBlock" | ||||||
|  |     /> | ||||||
|  | 
 | ||||||
|  |     <!-- 文件上传管理器 --> | ||||||
|  |     <MeEditorFileManage ref="filesManager" v-model="filesManager.isShow" /> | ||||||
|  | 
 | ||||||
|  |     <MeEditorVote | ||||||
|  |       v-if="vote.isShow" | ||||||
|  |       @close=" | ||||||
|  |         () => { | ||||||
|  |           this.vote.isShow = false; | ||||||
|  |         } | ||||||
|  |       " | ||||||
|  |     /> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | import MeEditorEmoticon from "./MeEditorEmoticon"; | ||||||
|  | import MeEditorFileManage from "./MeEditorFileManage"; | ||||||
|  | import MeEditorImageView from "./MeEditorImageView"; | ||||||
|  | import MeEditorRecorder from "./MeEditorRecorder"; | ||||||
|  | import MeEditorVote from "./MeEditorVote"; | ||||||
|  | import TalkCodeBlock from "@/components/chat/TalkCodeBlock"; | ||||||
|  | import { getPasteImgs, getDragPasteImg } from "@/utils/editor"; | ||||||
|  | import { findTalk } from "@/utils/talk"; | ||||||
|  | 
 | ||||||
|  | import { | ||||||
|  |   ServeSendTalkCodeBlock, | ||||||
|  |   ServeSendTalkImage, | ||||||
|  |   ServeSendEmoticon, | ||||||
|  | } from "@/api/chat"; | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   name: "MeEditor", | ||||||
|  |   components: { | ||||||
|  |     MeEditorEmoticon, | ||||||
|  |     MeEditorFileManage, | ||||||
|  |     MeEditorImageView, | ||||||
|  |     TalkCodeBlock, | ||||||
|  |     MeEditorRecorder, | ||||||
|  |     MeEditorVote, | ||||||
|  |   }, | ||||||
|  |   computed: { | ||||||
|  |     talkUser() { | ||||||
|  |       return this.$store.state.dialogue.index_name; | ||||||
|  |     }, | ||||||
|  |     isGroupTalk() { | ||||||
|  |       return this.$store.state.dialogue.talk_type == 2; | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   watch: { | ||||||
|  |     talkUser(n_index_name) { | ||||||
|  |       this.$refs.filesManager.clear(); | ||||||
|  |       this.editorText = this.getDraftText(n_index_name); | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       // 当前编辑的内容 | ||||||
|  |       editorText: "", | ||||||
|  | 
 | ||||||
|  |       // 图片查看器相关信息 | ||||||
|  |       imageViewer: { | ||||||
|  |         isShow: false, | ||||||
|  |         file: null, | ||||||
|  |       }, | ||||||
|  | 
 | ||||||
|  |       codeBlock: { | ||||||
|  |         isShow: false, | ||||||
|  |         editMode: true, | ||||||
|  |       }, | ||||||
|  | 
 | ||||||
|  |       filesManager: { | ||||||
|  |         isShow: false, | ||||||
|  |       }, | ||||||
|  | 
 | ||||||
|  |       vote: { | ||||||
|  |         isShow: false, | ||||||
|  |       }, | ||||||
|  | 
 | ||||||
|  |       // 录音器 | ||||||
|  |       recorder: false, | ||||||
|  | 
 | ||||||
|  |       // 上次发送消息的时间 | ||||||
|  |       sendtime: 0, | ||||||
|  | 
 | ||||||
|  |       // 发送间隔时间(默认1秒) | ||||||
|  |       interval: 1000, | ||||||
|  |     }; | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     // 读取对话编辑草稿信息 并赋值给当前富文本 | ||||||
|  |     getDraftText(index_name) { | ||||||
|  |       console.log("findTalk(index_name)", findTalk(index_name)); | ||||||
|  |       return findTalk(index_name)?.draft_text || ""; | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     //复制粘贴图片回调方法 | ||||||
|  |     pasteImage(e) { | ||||||
|  |       let files = getPasteImgs(e); | ||||||
|  |       if (files.length == 0) return; | ||||||
|  | 
 | ||||||
|  |       this.openImageViewer(files[0]); | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     //拖拽上传图片回调方法 | ||||||
|  |     dragPasteImage(e) { | ||||||
|  |       let files = getDragPasteImg(e); | ||||||
|  |       if (files.length == 0) return; | ||||||
|  | 
 | ||||||
|  |       this.openImageViewer(files[0]); | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     inputEvent(e) { | ||||||
|  |       this.$emit("keyboard-event", e.target.value); | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     // 键盘按下监听事件 | ||||||
|  |     keydownEvent(e) { | ||||||
|  |       if (e.keyCode == 13 && this.editorText == "") { | ||||||
|  |         e.preventDefault(); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       // 回车发送消息 | ||||||
|  |       if (e.keyCode == 13 && e.shiftKey == false && this.editorText != "") { | ||||||
|  |         let currentTime = new Date().getTime(); | ||||||
|  | 
 | ||||||
|  |         if (this.sendtime > 0) { | ||||||
|  |           // 判断 1秒内只能发送一条消息 | ||||||
|  |           if (currentTime - this.sendtime < this.interval) { | ||||||
|  |             e.preventDefault(); | ||||||
|  |             return false; | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         this.$emit("send", this.editorText); | ||||||
|  |         this.editorText = ""; | ||||||
|  |         this.sendtime = currentTime; | ||||||
|  |         e.preventDefault(); | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     // 选择图片文件后回调方法 | ||||||
|  |     uploadImageChange(e) { | ||||||
|  |       this.openImageViewer(e.target.files[0]); | ||||||
|  |       this.$refs.restFile.value = null; | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     // 选择文件回调事件 | ||||||
|  |     uploadFileChange(e) { | ||||||
|  |       let maxsize = 100 * 1024 * 1024; | ||||||
|  |       if (e.target.files.length == 0) { | ||||||
|  |         return false; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       let file = e.target.files[0]; | ||||||
|  |       if (/\.(gif|jpg|jpeg|png|webp|GIF|JPG|PNG|WEBP)$/.test(file.name)) { | ||||||
|  |         this.openImageViewer(file); | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       if (file.size > maxsize) { | ||||||
|  |         this.$notify.info({ | ||||||
|  |           title: "消息", | ||||||
|  |           message: "上传文件不能大于100M", | ||||||
|  |         }); | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       this.filesManager.isShow = true; | ||||||
|  |       this.$refs.restFile2.value = null; | ||||||
|  |       this.$refs.filesManager.upload(file); | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     // 打开图片查看器 | ||||||
|  |     openImageViewer(file) { | ||||||
|  |       this.imageViewer.isShow = true; | ||||||
|  |       this.imageViewer.file = file; | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     // 代码块编辑器确认完成回调事件 | ||||||
|  |     confirmCodeBlock(data) { | ||||||
|  |       const { talk_type, receiver_id } = this.$store.state.dialogue; | ||||||
|  |       ServeSendTalkCodeBlock({ | ||||||
|  |         talk_type, | ||||||
|  |         receiver_id, | ||||||
|  |         code: data.code, | ||||||
|  |         lang: data.language, | ||||||
|  |       }).then((res) => { | ||||||
|  |         if (res.code == 200) { | ||||||
|  |           this.codeBlock.isShow = false; | ||||||
|  |         } else { | ||||||
|  |           this.$notify({ | ||||||
|  |             title: "友情提示", | ||||||
|  |             message: res.message, | ||||||
|  |             type: "warning", | ||||||
|  |           }); | ||||||
|  |         } | ||||||
|  |       }); | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     // 确认上传图片消息回调事件 | ||||||
|  |     confirmUploadImage() { | ||||||
|  |       let fileData = new FormData(); | ||||||
|  |       fileData.append("file", this.imageViewer.file); | ||||||
|  | 
 | ||||||
|  |       let ref = this.$refs.imageViewer; | ||||||
|  | 
 | ||||||
|  |       ServeSendTalkImage(fileData) | ||||||
|  |         .then((res) => { | ||||||
|  |           ref.loading = false; | ||||||
|  |           if (res.code == 200) { | ||||||
|  |             ref.closeBox(); | ||||||
|  |           } else { | ||||||
|  |             this.$notify({ | ||||||
|  |               title: "友情提示", | ||||||
|  |               message: res.message, | ||||||
|  |               type: "warning", | ||||||
|  |             }); | ||||||
|  |           } | ||||||
|  |         }) | ||||||
|  |         .finally(() => { | ||||||
|  |           ref.loading = false; | ||||||
|  |         }); | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     // 选中表情包回调事件 | ||||||
|  |     selecteEmoticon(data) { | ||||||
|  |       if (data.type == 1) { | ||||||
|  |         let value = this.editorText; | ||||||
|  |         let el = this.$refs.textarea; | ||||||
|  |         let startPos = el.selectionStart; | ||||||
|  |         let endPos = el.selectionEnd; | ||||||
|  |         let newValue = | ||||||
|  |           value.substring(0, startPos) + | ||||||
|  |           data.value + | ||||||
|  |           value.substring(endPos, value.length); | ||||||
|  | 
 | ||||||
|  |         this.editorText = newValue; | ||||||
|  | 
 | ||||||
|  |         if (el.setSelectionRange) { | ||||||
|  |           setTimeout(() => { | ||||||
|  |             let index = startPos + data.value.length; | ||||||
|  |             el.setSelectionRange(index, index); | ||||||
|  |             el.focus(); | ||||||
|  |           }, 0); | ||||||
|  |         } | ||||||
|  |       } else { | ||||||
|  |         const { talk_type, receiver_id } = this.$store.state.dialogue; | ||||||
|  |         ServeSendEmoticon({ | ||||||
|  |           talk_type, | ||||||
|  |           receiver_id, | ||||||
|  |           emoticon_id: data.value, | ||||||
|  |         }); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       this.$refs.popoverEmoticon.doClose(); | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | }; | ||||||
|  | </script> | ||||||
|  | <style scoped lang="less"> | ||||||
|  | .editor-container { | ||||||
|  |   height: 160px; | ||||||
|  |   width: 100%; | ||||||
|  |   background-color: white; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .editor-container .toolbar { | ||||||
|  |   line-height: 35px; | ||||||
|  |   border-bottom: 1px solid #f5f0f0; | ||||||
|  |   border-top: 1px solid #f5f0f0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .editor-container .toolbar li { | ||||||
|  |   list-style: none; | ||||||
|  |   float: left; | ||||||
|  |   width: 35px; | ||||||
|  |   margin-left: 3px; | ||||||
|  |   cursor: pointer; | ||||||
|  |   text-align: center; | ||||||
|  |   line-height: 35px; | ||||||
|  |   position: relative; | ||||||
|  |   color: #8d8d8d; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .editor-container .toolbar li .tip-title { | ||||||
|  |   display: none; | ||||||
|  |   position: absolute; | ||||||
|  |   top: 38px; | ||||||
|  |   left: 0px; | ||||||
|  |   height: 26px; | ||||||
|  |   line-height: 26px; | ||||||
|  |   background-color: rgba(31, 35, 41, 0.9); | ||||||
|  |   color: white; | ||||||
|  |   min-width: 30px; | ||||||
|  |   font-size: 10px; | ||||||
|  |   padding: 0 5px; | ||||||
|  |   border-radius: 2px; | ||||||
|  |   white-space: pre; | ||||||
|  |   text-align: center; | ||||||
|  |   user-select: none; | ||||||
|  |   z-index: 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .editor-container .toolbar li:hover .tip-title { | ||||||
|  |   display: block; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .editor-container .toolbar li:hover { | ||||||
|  |   background-color: #f7f5f5; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .editor-container .toolbar .text-tips { | ||||||
|  |   float: right; | ||||||
|  |   margin-right: 15px; | ||||||
|  |   font-size: 12px; | ||||||
|  |   color: #ccc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .editor-container .toolbar .text-tips i { | ||||||
|  |   font-size: 14px; | ||||||
|  |   cursor: pointer; | ||||||
|  |   margin-left: 5px; | ||||||
|  |   color: rgb(255, 181, 111); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .editor-container .textarea { | ||||||
|  |   overflow: hidden; | ||||||
|  |   position: relative; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | textarea { | ||||||
|  |   width: calc(100% - 10px); | ||||||
|  |   width: -moz-calc(100% - 10px); | ||||||
|  |   width: -webkit-calc(100% - 10px); | ||||||
|  |   height: calc(100% - 10px); | ||||||
|  |   height: -moz-calc(100% - 10px); | ||||||
|  |   height: -webkit-calc(100% - 10px); | ||||||
|  |   border: 0 none; | ||||||
|  |   outline: none; | ||||||
|  |   resize: none; | ||||||
|  |   font-size: 15px; | ||||||
|  |   overflow-y: auto; | ||||||
|  |   color: #464545; | ||||||
|  |   padding: 5px; | ||||||
|  |   position: relative; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | textarea::-webkit-scrollbar { | ||||||
|  |   width: 4px; | ||||||
|  |   height: 1px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | textarea::-webkit-scrollbar-thumb { | ||||||
|  |   background: #d5cfcf; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | textarea::-webkit-scrollbar-track { | ||||||
|  |   background: #ededed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | textarea::-webkit-input-placeholder { | ||||||
|  |   color: #dccdcd; | ||||||
|  |   font-size: 12px; | ||||||
|  |   font-weight: 400; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* 编辑器文档说明 --- start */ | ||||||
|  | .editor-books .books-title { | ||||||
|  |   font-size: 16px; | ||||||
|  |   height: 30px; | ||||||
|  |   line-height: 22px; | ||||||
|  |   margin-top: 10px; | ||||||
|  |   margin-bottom: 10px; | ||||||
|  |   border-bottom: 1px solid #cbcbcb; | ||||||
|  |   color: #726f6f; | ||||||
|  |   font-weight: 400; | ||||||
|  |   margin-left: 11px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .editor-books p { | ||||||
|  |   text-indent: 10px; | ||||||
|  |   font-size: 12px; | ||||||
|  |   height: 30px; | ||||||
|  |   line-height: 30px; | ||||||
|  |   color: #7f7c7c; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* 编辑器文档说明 --- end */ | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,345 @@ | ||||||
|  | <template> | ||||||
|  |   <div> | ||||||
|  |     <el-container class="container"> | ||||||
|  |       <el-main class="no-padding main lum-scrollbar"> | ||||||
|  |         <input | ||||||
|  |           type="file" | ||||||
|  |           ref="fileCustomEmoji" | ||||||
|  |           accept="image/*" | ||||||
|  |           style="display: none" | ||||||
|  |           @change="customUploadEmoji" | ||||||
|  |         /> | ||||||
|  | 
 | ||||||
|  |         <div v-show="showEmoticonId == -1" class="emoticon"> | ||||||
|  |           <div class="title">QQ表情</div> | ||||||
|  |           <div | ||||||
|  |             v-for="(elImg, text) in emoji.emojis" | ||||||
|  |             v-html="elImg" | ||||||
|  |             :key="text" | ||||||
|  |             class="emoticon-item" | ||||||
|  |             @click="clickEmoticon(text)" | ||||||
|  |           ></div> | ||||||
|  |           <div class="clear"></div> | ||||||
|  |           <div class="title">符号表情</div> | ||||||
|  |           <div | ||||||
|  |             v-for="(item, i) in emoji.symbol" | ||||||
|  |             :key="i" | ||||||
|  |             class="emoticon-item symbol" | ||||||
|  |             @click="clickEmoticon(item)" | ||||||
|  |           > | ||||||
|  |             {{ item }} | ||||||
|  |           </div> | ||||||
|  |           <div class="clear"></div> | ||||||
|  |         </div> | ||||||
|  | 
 | ||||||
|  |         <div | ||||||
|  |           v-for="item in emojiItem.slice(1)" | ||||||
|  |           v-show="item.emoticon_id == showEmoticonId" | ||||||
|  |           :key="item.emoticon_id" | ||||||
|  |           class="emoji-box" | ||||||
|  |         > | ||||||
|  |           <div | ||||||
|  |             v-if="item.emoticon_id == 0" | ||||||
|  |             class="emoji-item custom-emoji" | ||||||
|  |             @click="$refs.fileCustomEmoji.click()" | ||||||
|  |           > | ||||||
|  |             <i class="el-icon-picture" /> | ||||||
|  |             <span>自定义</span> | ||||||
|  |           </div> | ||||||
|  |           <div | ||||||
|  |             v-for="subitem in item.list" | ||||||
|  |             :key="subitem.src" | ||||||
|  |             class="emoji-item" | ||||||
|  |             @click="clickImageEmoticon(subitem)" | ||||||
|  |           > | ||||||
|  |             <el-image :src="subitem.src" fit="cover" /> | ||||||
|  |           </div> | ||||||
|  |           <div class="clear"></div> | ||||||
|  |         </div> | ||||||
|  |       </el-main> | ||||||
|  |       <!-- <el-footer height="40px" class="no-padding footer"> | ||||||
|  |         <div class="toolbar-items"> | ||||||
|  |           <div | ||||||
|  |             v-show="emojiItem.length > 13" | ||||||
|  |             class="toolbar-item prev-page" | ||||||
|  |             @click="turnPage(1)" | ||||||
|  |           > | ||||||
|  |             <i class="el-icon-caret-left" /> | ||||||
|  |           </div> | ||||||
|  |           <div | ||||||
|  |             v-for="(item, index) in showItems" | ||||||
|  |             :key="index" | ||||||
|  |             class="toolbar-item" | ||||||
|  |             @click="triggerItem(item)" | ||||||
|  |           > | ||||||
|  |             <img :src="item.url" /> | ||||||
|  |             <p class="title">{{ item.name }}</p> | ||||||
|  |           </div> | ||||||
|  |           <div | ||||||
|  |             v-show="emojiItem.length > 13 && showItems.length == 13" | ||||||
|  |             class="toolbar-item next-page" | ||||||
|  |             @click="turnPage(2)" | ||||||
|  |           > | ||||||
|  |             <i class="el-icon-caret-right" /> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |       </el-footer> --> | ||||||
|  |     </el-container> | ||||||
|  | 
 | ||||||
|  |     <MeEditorSystemEmoticon | ||||||
|  |       v-if="systemEmojiBox" | ||||||
|  |       @close="systemEmojiBox = false" | ||||||
|  |     /> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | import MeEditorSystemEmoticon from "@/components/editor/MeEditorSystemEmoticon"; | ||||||
|  | import { emojiList as emoji } from "@/utils/emojis"; | ||||||
|  | import { mapState } from "vuex"; | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   name: "MeEditorEmoticon", | ||||||
|  |   components: { | ||||||
|  |     MeEditorSystemEmoticon, | ||||||
|  |   }, | ||||||
|  |   computed: { | ||||||
|  |     ...mapState({ | ||||||
|  |       emojiItem: (state) => state.emoticon.items, | ||||||
|  |     }), | ||||||
|  |     showItems() { | ||||||
|  |       let start = (this.page - 1) * this.pageSize; | ||||||
|  |       let end = start + this.pageSize; | ||||||
|  |       return this.emojiItem.slice(start, end); | ||||||
|  |     }, | ||||||
|  |     pageTotal() { | ||||||
|  |       return this.emojiItem.length / this.pageSize; | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       emoji, | ||||||
|  | 
 | ||||||
|  |       // 系统表情包套弹出窗 | ||||||
|  |       systemEmojiBox: false, | ||||||
|  | 
 | ||||||
|  |       showEmoticonId: -1, | ||||||
|  |       showTitle: "QQ表情/符号表情", | ||||||
|  | 
 | ||||||
|  |       page: 1, | ||||||
|  |       pageSize: 13, | ||||||
|  |     }; | ||||||
|  |   }, | ||||||
|  |   created() { | ||||||
|  |     this.$store.commit("LOAD_USER_EMOTICON"); | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     // 表情包导航翻页 | ||||||
|  |     turnPage(type) { | ||||||
|  |       if (type == 1) { | ||||||
|  |         if (this.page == 1) return false; | ||||||
|  |         this.page--; | ||||||
|  |       } else { | ||||||
|  |         if (this.page >= this.pageTotal) return false; | ||||||
|  |         this.page++; | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     // 点击表情包导航 | ||||||
|  |     triggerItem(item) { | ||||||
|  |       this.showEmoticonId = item.emoticon_id; | ||||||
|  |       this.showTitle = item.name; | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     // 选中表情 | ||||||
|  |     clickEmoticon(emoji) { | ||||||
|  |       this.callback({ | ||||||
|  |         type: 1, | ||||||
|  |         value: emoji, | ||||||
|  |       }); | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     // 发送图片表情包 | ||||||
|  |     clickImageEmoticon(item) { | ||||||
|  |       this.callback({ | ||||||
|  |         type: 2, | ||||||
|  |         value: item.media_id, | ||||||
|  |       }); | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     callback(data) { | ||||||
|  |       this.$emit("selected", data); | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     // 自定义上传表情 | ||||||
|  |     customUploadEmoji(e) { | ||||||
|  |       if (e.target.files.length == 0) { | ||||||
|  |         return false; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       this.$store.commit("UPLOAD_USER_EMOTICON", { | ||||||
|  |         file: e.target.files[0], | ||||||
|  |       }); | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | }; | ||||||
|  | </script> | ||||||
|  | <style lang="less" scoped> | ||||||
|  | .container { | ||||||
|  |   height: 300px; | ||||||
|  |   max-width: 500px; | ||||||
|  |   background-color: white; | ||||||
|  | 
 | ||||||
|  |   .header { | ||||||
|  |     line-height: 30px; | ||||||
|  |     font-size: 13px; | ||||||
|  |     font-weight: 400; | ||||||
|  |     padding-left: 5px; | ||||||
|  |     user-select: none; | ||||||
|  |     position: relative; | ||||||
|  |     border-bottom: 1px solid #fbf5f5; | ||||||
|  | 
 | ||||||
|  |     .addbtn { | ||||||
|  |       position: absolute; | ||||||
|  |       right: 10px; | ||||||
|  |       top: 1px; | ||||||
|  |       color: #409eff; | ||||||
|  |       cursor: pointer; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .footer { | ||||||
|  |     background-color: #eff1f7; | ||||||
|  | 
 | ||||||
|  |     .toolbar-items { | ||||||
|  |       width: 100%; | ||||||
|  |       height: 40px; | ||||||
|  |       line-height: 40px; | ||||||
|  |       display: flex; | ||||||
|  |       flex-direction: row; | ||||||
|  |       align-items: center; | ||||||
|  | 
 | ||||||
|  |       .toolbar-item { | ||||||
|  |         height: 30px; | ||||||
|  |         width: 30px; | ||||||
|  |         margin: 0 2px; | ||||||
|  |         background-color: #fff; | ||||||
|  |         display: inline-block; | ||||||
|  |         cursor: pointer; | ||||||
|  |         display: flex; | ||||||
|  |         justify-content: center; | ||||||
|  |         align-items: center; | ||||||
|  |         position: relative; | ||||||
|  | 
 | ||||||
|  |         img { | ||||||
|  |           width: 20px; | ||||||
|  |           height: 20px; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         .title { | ||||||
|  |           display: none; | ||||||
|  |           position: absolute; | ||||||
|  |           top: -25px; | ||||||
|  |           left: 0px; | ||||||
|  |           height: 20px; | ||||||
|  |           line-height: 20px; | ||||||
|  |           background: #353434; | ||||||
|  |           color: white; | ||||||
|  |           min-width: 30px; | ||||||
|  |           font-size: 10px; | ||||||
|  |           padding-left: 5px; | ||||||
|  |           padding-right: 5px; | ||||||
|  |           border-radius: 2px; | ||||||
|  |           white-space: pre; | ||||||
|  |           text-align: center; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         &:hover .title { | ||||||
|  |           display: block; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .container .footer .toolbar-items .prev-page:active i, | ||||||
|  | .container .footer .toolbar-items .next-page:active i { | ||||||
|  |   transform: scale(1.2); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .emoji-box, | ||||||
|  | .emoticon { | ||||||
|  |   width: 100%; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .emoticon { | ||||||
|  |   .title { | ||||||
|  |     width: 50%; | ||||||
|  |     height: 25px; | ||||||
|  |     line-height: 25px; | ||||||
|  |     color: #ccc; | ||||||
|  |     font-weight: 400; | ||||||
|  |     padding-left: 3px; | ||||||
|  |     font-size: 12px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .emoticon-item { | ||||||
|  |     width: 30px; | ||||||
|  |     height: 30px; | ||||||
|  |     margin: 2px; | ||||||
|  |     float: left; | ||||||
|  |     cursor: pointer; | ||||||
|  |     display: flex; | ||||||
|  |     justify-content: center; | ||||||
|  |     align-items: center; | ||||||
|  | 
 | ||||||
|  |     &:hover { | ||||||
|  |       transform: scale(1.3); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .symbol { | ||||||
|  |     font-size: 22px; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .emoji-box { | ||||||
|  |   .emoji-item { | ||||||
|  |     width: 67px; | ||||||
|  |     height: 67px; | ||||||
|  |     margin: 2px; | ||||||
|  |     background-color: #eff1f7; | ||||||
|  |     float: left; | ||||||
|  |     cursor: pointer; | ||||||
|  |     transition: ease-in 0.3s; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .custom-emoji { | ||||||
|  |     display: flex; | ||||||
|  |     justify-content: center; | ||||||
|  |     align-items: center; | ||||||
|  |     flex-direction: column; | ||||||
|  |     font-size: 10px; | ||||||
|  | 
 | ||||||
|  |     i { | ||||||
|  |       font-size: 30px; | ||||||
|  |       margin-bottom: 3px; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     &:active { | ||||||
|  |       color: #409eff; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /deep/ .el-image { | ||||||
|  |   width: 100%; | ||||||
|  |   height: 100%; | ||||||
|  |   transition: ease-in 0.3s; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .emoji-box .emoji-item:hover .el-image, | ||||||
|  | .emoji-box .emoji-item:hover { | ||||||
|  |   border-radius: 10px; | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,440 @@ | ||||||
|  | <template> | ||||||
|  |   <el-container | ||||||
|  |     class="container animated bounceInUp" | ||||||
|  |     v-outside="closeBox" | ||||||
|  |     v-if="show" | ||||||
|  |   > | ||||||
|  |     <el-header class="no-padding header" height="50px"> | ||||||
|  |       <p> | ||||||
|  |         上传管理 <span v-show="total">({{ successNum }}/{{ total }})</span> | ||||||
|  |       </p> | ||||||
|  |       <i class="close-btn el-icon-close" @click="closeBox" /> | ||||||
|  |     </el-header> | ||||||
|  | 
 | ||||||
|  |     <el-main class="no-padding mian lum-scrollbar"> | ||||||
|  |       <div class="empty-data" v-show="total == 0"> | ||||||
|  |         <SvgNotData /> | ||||||
|  |         <p>暂无上传文件</p> | ||||||
|  |       </div> | ||||||
|  | 
 | ||||||
|  |       <div | ||||||
|  |         v-for="file in items" | ||||||
|  |         v-show="!file.isDelete" | ||||||
|  |         :key="file.hashName" | ||||||
|  |         class="file-item" | ||||||
|  |       > | ||||||
|  |         <div class="file-header"> | ||||||
|  |           <div class="type-icon">{{ file.ext }}</div> | ||||||
|  |           <el-tooltip :content="file.filename" placement="top-start"> | ||||||
|  |             <div class="filename">{{ file.filename }}</div> | ||||||
|  |           </el-tooltip> | ||||||
|  | 
 | ||||||
|  |           <div class="status"> | ||||||
|  |             <span v-if="file.status == 0">等待上传</span> | ||||||
|  |             <span v-else-if="file.status == 1" style="color: #66b1ff"> | ||||||
|  |               正在上传... | ||||||
|  |             </span> | ||||||
|  |             <span v-else-if="file.status == 2" style="color: #67c23a"> | ||||||
|  |               已完成 | ||||||
|  |             </span> | ||||||
|  |             <span v-else style="color: red">网络异常</span> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |         <div class="file-mian"> | ||||||
|  |           <div class="progress"> | ||||||
|  |             <el-progress | ||||||
|  |               type="dashboard" | ||||||
|  |               :percentage="file.progress" | ||||||
|  |               :width="50" | ||||||
|  |               :color="colors" | ||||||
|  |             /> | ||||||
|  |             <span class="name">上传进度</span> | ||||||
|  |           </div> | ||||||
|  |           <div class="detail"> | ||||||
|  |             <p> | ||||||
|  |               文件类型:<span>{{ file.filetype }}</span> | ||||||
|  |             </p> | ||||||
|  |             <p> | ||||||
|  |               文件大小:<span>{{ file.filesize }}</span> | ||||||
|  |             </p> | ||||||
|  |             <p> | ||||||
|  |               上传时间:<span>{{ file.datetime }}</span> | ||||||
|  |             </p> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |         <div v-show="file.status == 2 || file.status == 3" class="file-means"> | ||||||
|  |           <div class="btns" @click="removeFile(file.hashName)">删除</div> | ||||||
|  |           <div | ||||||
|  |             v-show="file.status == 3" | ||||||
|  |             class="btns" | ||||||
|  |             @click="triggerUpload(file.hashName)" | ||||||
|  |           > | ||||||
|  |             继续上传 | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |     </el-main> | ||||||
|  |   </el-container> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | import Vue from 'vue' | ||||||
|  | import { SvgNotData } from '@/core/icons' | ||||||
|  | import { Progress } from 'element-ui' | ||||||
|  | Vue.use(Progress) | ||||||
|  | 
 | ||||||
|  | import { ServeFindFileSplitInfo, ServeFileSubareaUpload } from '@/api/upload' | ||||||
|  | import { formatSize, getFileExt, parseTime } from '@/utils/functions' | ||||||
|  | import { ServeSendTalkFile } from '@/api/chat' | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   name: 'MeEditorFileManage', | ||||||
|  |   model: { | ||||||
|  |     prop: 'show', | ||||||
|  |     event: 'close', | ||||||
|  |   }, | ||||||
|  |   props: { | ||||||
|  |     show: Boolean, | ||||||
|  |   }, | ||||||
|  |   components: { | ||||||
|  |     SvgNotData, | ||||||
|  |   }, | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       colors: [ | ||||||
|  |         { | ||||||
|  |           color: '#f56c6c', | ||||||
|  |           percentage: 20, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           color: '#e6a23c', | ||||||
|  |           percentage: 40, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           color: '#5cb87a', | ||||||
|  |           percentage: 60, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           color: '#1989fa', | ||||||
|  |           percentage: 80, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           color: '#11ce65', | ||||||
|  |           percentage: 100, | ||||||
|  |         }, | ||||||
|  |       ], | ||||||
|  | 
 | ||||||
|  |       items: [], | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   computed: { | ||||||
|  |     total() { | ||||||
|  |       return this.items.filter(item => { | ||||||
|  |         return item.isDelete === false | ||||||
|  |       }).length | ||||||
|  |     }, | ||||||
|  |     successNum() { | ||||||
|  |       return this.items.filter(item => { | ||||||
|  |         return item.isDelete === false && item.status == 2 | ||||||
|  |       }).length | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     closeBox() { | ||||||
|  |       this.$emit('close', false) | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     upload(file) { | ||||||
|  |       ServeFindFileSplitInfo({ | ||||||
|  |         file_name: file.name, | ||||||
|  |         file_size: file.size, | ||||||
|  |       }).then(res => { | ||||||
|  |         if (res.code == 200) { | ||||||
|  |           const { hash_name, split_size } = res.data | ||||||
|  | 
 | ||||||
|  |           this.items.unshift({ | ||||||
|  |             hashName: hash_name, | ||||||
|  |             originalFile: file, | ||||||
|  |             filename: file.name, | ||||||
|  |             status: 0, // 文件上传状态 0:等待上传 1:上传中 2:上传完成 3:网络异常 | ||||||
|  |             progress: 0, | ||||||
|  |             filesize: formatSize(file.size), | ||||||
|  |             filetype: file.type || '未知', | ||||||
|  |             datetime: parseTime(new Date(), '{m}-{d} {h}:{i}'), | ||||||
|  |             ext: getFileExt(file.name), | ||||||
|  |             forms: this.fileSlice(file, hash_name, split_size), | ||||||
|  |             successNum: 0, | ||||||
|  |             isDelete: false, | ||||||
|  |           }) | ||||||
|  | 
 | ||||||
|  |           this.triggerUpload(hash_name) | ||||||
|  |         } | ||||||
|  |       }) | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     // 处理拆分上传文件 | ||||||
|  |     fileSlice(file, hash, eachSize) { | ||||||
|  |       const ext = getFileExt(file.name) | ||||||
|  |       const splitNum = Math.ceil(file.size / eachSize) // 分片总数 | ||||||
|  |       const forms = [] | ||||||
|  | 
 | ||||||
|  |       // 处理每个分片的上传操作 | ||||||
|  |       for (let i = 0; i < splitNum; i++) { | ||||||
|  |         let start = i * eachSize | ||||||
|  |         let end = Math.min(file.size, start + eachSize) | ||||||
|  | 
 | ||||||
|  |         // 构建表单 | ||||||
|  |         const form = new FormData() | ||||||
|  |         form.append('file', file.slice(start, end)) | ||||||
|  |         form.append('name', file.name) | ||||||
|  |         form.append('hash', hash) | ||||||
|  |         form.append('ext', ext) | ||||||
|  |         form.append('size', file.size) | ||||||
|  |         form.append('split_index', i) | ||||||
|  |         form.append('split_num', splitNum) | ||||||
|  |         forms.push(form) | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       return forms | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     // 触发上传文件 | ||||||
|  |     triggerUpload(hashName) { | ||||||
|  |       let $index = this.getFileIndex(hashName) | ||||||
|  |       if ($index < 0 || this.items[$index].isDelte) { | ||||||
|  |         return | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       let i = this.items[$index].successNum | ||||||
|  |       let form = this.items[$index].forms[i] | ||||||
|  |       let length = this.items[$index].forms.length | ||||||
|  |       this.items[$index].status = 1 | ||||||
|  |       ServeFileSubareaUpload(form) | ||||||
|  |         .then(res => { | ||||||
|  |           if (res.code == 200) { | ||||||
|  |             $index = this.getFileIndex(hashName) | ||||||
|  |             this.items[$index].successNum++ | ||||||
|  |             this.items[$index].progress = Math.floor( | ||||||
|  |               (this.items[$index].successNum / length) * 100 | ||||||
|  |             ) | ||||||
|  |             if (this.items[$index].successNum == length) { | ||||||
|  |               this.items[$index].status = 2 | ||||||
|  |               if (res.data.is_file_merge) { | ||||||
|  |                 ServeSendTalkFile({ | ||||||
|  |                   hash_name: res.data.hash, | ||||||
|  |                   receiver_id: this.$store.state.dialogue.receiver_id, | ||||||
|  |                   talk_type: this.$store.state.dialogue.talk_type, | ||||||
|  |                 }) | ||||||
|  |               } | ||||||
|  |             } else { | ||||||
|  |               this.triggerUpload(hashName) | ||||||
|  |             } | ||||||
|  |           } else { | ||||||
|  |             this.items[$index].status = 3 | ||||||
|  |           } | ||||||
|  |         }) | ||||||
|  |         .catch(() => { | ||||||
|  |           $index = this.getFileIndex(hashName) | ||||||
|  |           this.items[$index].status = 3 | ||||||
|  |         }) | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     // 获取分片文件数组索引 | ||||||
|  |     getFileIndex(hashName) { | ||||||
|  |       return this.items.findIndex(item => { | ||||||
|  |         return item.hashName === hashName | ||||||
|  |       }) | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     removeFile(hashName) { | ||||||
|  |       let index = this.getFileIndex(hashName) | ||||||
|  |       this.items[index].isDelete = true | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     clear() { | ||||||
|  |       this.items = [] | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | <style lang="less" scoped> | ||||||
|  | .container { | ||||||
|  |   position: fixed; | ||||||
|  |   right: 0; | ||||||
|  |   bottom: 0; | ||||||
|  |   width: 400px; | ||||||
|  |   height: 600px; | ||||||
|  |   background-color: white; | ||||||
|  |   box-shadow: 0 0 5px #eae5e5; | ||||||
|  |   border: 1px solid #eae5e5; | ||||||
|  |   overflow: hidden; | ||||||
|  |   border-radius: 3px 3px 0 0; | ||||||
|  | 
 | ||||||
|  |   .header { | ||||||
|  |     height: 50px; | ||||||
|  |     line-height: 50px; | ||||||
|  |     position: relative; | ||||||
|  |     text-indent: 20px; | ||||||
|  |     border-bottom: 1px solid #f5eeee; | ||||||
|  | 
 | ||||||
|  |     i { | ||||||
|  |       position: absolute; | ||||||
|  |       right: 20px; | ||||||
|  |       top: 15px; | ||||||
|  |       font-size: 20px; | ||||||
|  |       cursor: pointer; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .mian { | ||||||
|  |     .empty-data { | ||||||
|  |       width: 100%; | ||||||
|  |       height: 80px; | ||||||
|  |       display: flex; | ||||||
|  |       justify-content: center; | ||||||
|  |       align-items: center; | ||||||
|  |       flex-direction: column; | ||||||
|  |       margin-top: 50%; | ||||||
|  | 
 | ||||||
|  |       svg { | ||||||
|  |         font-size: 70px; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       p { | ||||||
|  |         margin-top: 30px; | ||||||
|  |         color: #cccccc; | ||||||
|  |         font-size: 10px; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .file-item { | ||||||
|  |   width: 95%; | ||||||
|  |   min-height: 100px; | ||||||
|  |   background-color: white; | ||||||
|  |   display: flex; | ||||||
|  |   flex-direction: column; | ||||||
|  |   border-radius: 5px; | ||||||
|  |   margin: 15px auto; | ||||||
|  |   box-shadow: 0 0 5px #eae5e5; | ||||||
|  |   overflow: hidden; | ||||||
|  | 
 | ||||||
|  |   .file-header { | ||||||
|  |     height: 45px; | ||||||
|  |     display: flex; | ||||||
|  |     flex-direction: row; | ||||||
|  |     align-items: center; | ||||||
|  |     position: relative; | ||||||
|  |     border-bottom: 1px solid #f7f4f4; | ||||||
|  | 
 | ||||||
|  |     .type-icon { | ||||||
|  |       height: 30px; | ||||||
|  |       width: 30px; | ||||||
|  |       background-color: #66b1ff; | ||||||
|  |       border-radius: 50%; | ||||||
|  |       margin-left: 5px; | ||||||
|  |       font-size: 10px; | ||||||
|  |       font-weight: 200; | ||||||
|  |       display: flex; | ||||||
|  |       justify-content: center; | ||||||
|  |       align-items: center; | ||||||
|  |       overflow: hidden; | ||||||
|  |       color: white; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .filename { | ||||||
|  |       margin-left: 10px; | ||||||
|  |       font-size: 14px; | ||||||
|  |       width: 65%; | ||||||
|  |       overflow: hidden; | ||||||
|  |       text-overflow: ellipsis; | ||||||
|  |       white-space: nowrap; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .status { | ||||||
|  |       position: absolute; | ||||||
|  |       right: 14px; | ||||||
|  |       top: 12px; | ||||||
|  |       font-size: 13px; | ||||||
|  |       color: #6b6868; | ||||||
|  |       font-weight: 200; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .file-mian { | ||||||
|  |     padding: 8px; | ||||||
|  |     display: flex; | ||||||
|  |     flex-direction: row; | ||||||
|  | 
 | ||||||
|  |     .progress { | ||||||
|  |       width: 80px; | ||||||
|  |       height: 80px; | ||||||
|  |       flex-shrink: 0; | ||||||
|  |       background: #f9f6f6; | ||||||
|  |       display: flex; | ||||||
|  |       justify-content: center; | ||||||
|  |       align-items: center; | ||||||
|  |       flex-direction: column; | ||||||
|  |       cursor: pointer; | ||||||
|  | 
 | ||||||
|  |       .name { | ||||||
|  |         font-size: 12px; | ||||||
|  |         color: #ada8a8; | ||||||
|  |         font-weight: 300; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .detail { | ||||||
|  |       flex: auto; | ||||||
|  |       flex-shrink: 0; | ||||||
|  |       display: flex; | ||||||
|  |       flex-direction: column; | ||||||
|  |       padding-left: 20px; | ||||||
|  |       justify-content: center; | ||||||
|  |       align-items: flex-start; | ||||||
|  |       font-size: 13px; | ||||||
|  | 
 | ||||||
|  |       p { | ||||||
|  |         margin: 3px; | ||||||
|  |         color: #ada8a8; | ||||||
|  | 
 | ||||||
|  |         span { | ||||||
|  |           color: #595a5a; | ||||||
|  |           font-weight: 500; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .file-means { | ||||||
|  |     width: 96.5%; | ||||||
|  |     height: 35px; | ||||||
|  |     border-top: 1px dashed rgb(234, 227, 227); | ||||||
|  |     margin: 3px auto; | ||||||
|  |     padding-top: 5px; | ||||||
|  |     display: flex; | ||||||
|  |     justify-content: flex-end; | ||||||
|  |     align-items: center; | ||||||
|  | 
 | ||||||
|  |     .btns { | ||||||
|  |       width: 80px; | ||||||
|  |       height: 25px; | ||||||
|  |       border: 1px solid #e6e1e1; | ||||||
|  |       display: flex; | ||||||
|  |       justify-content: center; | ||||||
|  |       align-items: center; | ||||||
|  |       margin: 3px; | ||||||
|  |       border-radius: 15px; | ||||||
|  |       font-size: 12px; | ||||||
|  |       color: #635f5f; | ||||||
|  |       cursor: pointer; | ||||||
|  | 
 | ||||||
|  |       &:active { | ||||||
|  |         box-shadow: 0 0 5px #eae5e5; | ||||||
|  |         font-size: 13px; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,133 @@ | ||||||
|  | <template> | ||||||
|  |   <div v-if="show" class="lum-dialog-mask animated fadeIn"> | ||||||
|  |     <el-container class="lum-dialog-box" v-outside="closeBox"> | ||||||
|  |       <el-header class="no-padding header" height="50px"> | ||||||
|  |         <p>发送图片</p> | ||||||
|  |         <p class="tools"> | ||||||
|  |           <i class="el-icon-close" @click="closeBox" /> | ||||||
|  |         </p> | ||||||
|  |       </el-header> | ||||||
|  | 
 | ||||||
|  |       <el-main class="no-padding mian"> | ||||||
|  |         <img v-show="src" :src="src" /> | ||||||
|  |         <div v-show="src"> | ||||||
|  |           <span class="filename">{{ fileName }}</span> | ||||||
|  |           <br /> | ||||||
|  |           <span class="size">{{ fileSize }} KB</span> | ||||||
|  |         </div> | ||||||
|  |       </el-main> | ||||||
|  | 
 | ||||||
|  |       <el-footer class="footer" height="50px"> | ||||||
|  |         <el-button | ||||||
|  |           class="btn" | ||||||
|  |           type="primary" | ||||||
|  |           size="medium" | ||||||
|  |           :loading="loading" | ||||||
|  |           @click="uploadImage" | ||||||
|  |           >立即发送 | ||||||
|  |         </el-button> | ||||||
|  |       </el-footer> | ||||||
|  |     </el-container> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | <script> | ||||||
|  | export default { | ||||||
|  |   name: 'MeEditorImageView', | ||||||
|  |   model: { | ||||||
|  |     prop: 'show', | ||||||
|  |     event: 'close', | ||||||
|  |   }, | ||||||
|  |   props: { | ||||||
|  |     show: Boolean, | ||||||
|  |     file: File, | ||||||
|  |   }, | ||||||
|  |   watch: { | ||||||
|  |     file(file) { | ||||||
|  |       this.loadFile(file) | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       src: '', | ||||||
|  |       fileSize: '', | ||||||
|  |       fileName: '', | ||||||
|  |       loading: false, | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     closeBox() { | ||||||
|  |       if (this.loading) { | ||||||
|  |         return false | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       this.$emit('close', false) | ||||||
|  |     }, | ||||||
|  |     loadFile(file) { | ||||||
|  |       let reader = new FileReader() | ||||||
|  |       this.fileSize = Math.ceil(file.size / 1024) | ||||||
|  |       this.fileName = file.name | ||||||
|  |       reader.onload = () => { | ||||||
|  |         this.src = reader.result | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       reader.readAsDataURL(file) | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     // 确认按钮事件 | ||||||
|  |     uploadImage() { | ||||||
|  |       this.loading = true | ||||||
|  |       this.$emit('confirm') | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | <style lang="less" scoped> | ||||||
|  | .lum-dialog-box { | ||||||
|  |   width: 500px; | ||||||
|  |   max-width: 500px; | ||||||
|  |   height: 450px; | ||||||
|  | 
 | ||||||
|  |   .mian { | ||||||
|  |     display: flex; | ||||||
|  |     justify-content: center; | ||||||
|  |     align-items: center; | ||||||
|  |     flex-direction: column; | ||||||
|  | 
 | ||||||
|  |     img { | ||||||
|  |       max-width: 80%; | ||||||
|  |       max-height: 80%; | ||||||
|  |       border-radius: 3px; | ||||||
|  |       cursor: pointer; | ||||||
|  |       box-shadow: 0 0 8px #e0dbdb; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     div { | ||||||
|  |       margin-top: 10px; | ||||||
|  |       text-align: center; | ||||||
|  |       overflow: hidden; | ||||||
|  |       max-width: 80%; | ||||||
|  | 
 | ||||||
|  |       .filename { | ||||||
|  |         font-weight: 400; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       .size { | ||||||
|  |         color: rgb(148, 140, 140); | ||||||
|  |         font-size: 12px; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .footer { | ||||||
|  |     height: 50px; | ||||||
|  |     background: rgba(247, 245, 245, 0.66); | ||||||
|  |     text-align: center; | ||||||
|  |     line-height: 50px; | ||||||
|  | 
 | ||||||
|  |     .btn { | ||||||
|  |       width: 150px; | ||||||
|  |       border-radius: 2px; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,517 @@ | ||||||
|  | <template> | ||||||
|  |   <div class="lum-dialog-mask animated fadeIn"> | ||||||
|  |     <el-container class="lum-dialog-box"> | ||||||
|  |       <el-header class="no-padding header no-select" height="50px"> | ||||||
|  |         <p>语音消息</p> | ||||||
|  |         <p class="tools"><i class="el-icon-close" @click="closeBox" /></p> | ||||||
|  |       </el-header> | ||||||
|  | 
 | ||||||
|  |       <el-main class="no-padding mian"> | ||||||
|  |         <div class="music"> | ||||||
|  |           <span class="line line1" :class="{ 'line-ani': animation }"></span> | ||||||
|  |           <span class="line line2" :class="{ 'line-ani': animation }"></span> | ||||||
|  |           <span class="line line3" :class="{ 'line-ani': animation }"></span> | ||||||
|  |           <span class="line line4" :class="{ 'line-ani': animation }"></span> | ||||||
|  |           <span class="line line5" :class="{ 'line-ani': animation }"></span> | ||||||
|  |         </div> | ||||||
|  |         <div style="margin-top: 35px; color: #676262; font-weight: 300"> | ||||||
|  |           <template v-if="recorderStatus == 0"> | ||||||
|  |             <p style="font-size: 13px; margin-top: 5px"> | ||||||
|  |               <span>语音消息,让聊天更简单方便 ...</span> | ||||||
|  |             </p> | ||||||
|  |           </template> | ||||||
|  |           <template | ||||||
|  |             v-else-if=" | ||||||
|  |               recorderStatus == 1 || recorderStatus == 2 || recorderStatus == 3 | ||||||
|  |             " | ||||||
|  |           > | ||||||
|  |             <p>{{ datetime }}</p> | ||||||
|  |             <p style="font-size: 13px; margin-top: 5px"> | ||||||
|  |               <span v-if="recorderStatus == 1">正在录音</span> | ||||||
|  |               <span v-else-if="recorderStatus == 2">已暂停录音</span> | ||||||
|  |               <span v-else-if="recorderStatus == 3">录音时长</span> | ||||||
|  |             </p> | ||||||
|  |           </template> | ||||||
|  |           <template | ||||||
|  |             v-else-if=" | ||||||
|  |               recorderStatus == 4 || recorderStatus == 5 || recorderStatus == 6 | ||||||
|  |             " | ||||||
|  |           > | ||||||
|  |             <p>{{ formatPlayTime }}</p> | ||||||
|  |             <p style="font-size: 13px; margin-top: 5px"> | ||||||
|  |               <span v-if="recorderStatus == 4">正在播放</span> | ||||||
|  |               <span v-else-if="recorderStatus == 5">已暂停播放</span> | ||||||
|  |               <span v-else-if="recorderStatus == 6">播放已结束</span> | ||||||
|  |             </p> | ||||||
|  |           </template> | ||||||
|  |         </div> | ||||||
|  |       </el-main> | ||||||
|  | 
 | ||||||
|  |       <el-footer class="footer" height="50px"> | ||||||
|  |         <!-- 0:未开始录音 1:正在录音 2:暂停录音 3:结束录音 4:播放录音 5:停止播放 --> | ||||||
|  |         <el-button | ||||||
|  |           v-show="recorderStatus == 0" | ||||||
|  |           type="primary" | ||||||
|  |           size="mini" | ||||||
|  |           round | ||||||
|  |           icon="el-icon-microphone" | ||||||
|  |           @click="startRecorder" | ||||||
|  |           >开始录音 | ||||||
|  |         </el-button> | ||||||
|  |         <el-button | ||||||
|  |           v-show="recorderStatus == 1" | ||||||
|  |           type="primary" | ||||||
|  |           size="mini" | ||||||
|  |           round | ||||||
|  |           icon="el-icon-video-pause" | ||||||
|  |           @click="pauseRecorder" | ||||||
|  |           >暂停录音 | ||||||
|  |         </el-button> | ||||||
|  |         <el-button | ||||||
|  |           v-show="recorderStatus == 2" | ||||||
|  |           type="primary" | ||||||
|  |           size="mini" | ||||||
|  |           round | ||||||
|  |           icon="el-icon-microphone" | ||||||
|  |           @click="resumeRecorder" | ||||||
|  |           >继续录音 | ||||||
|  |         </el-button> | ||||||
|  |         <el-button | ||||||
|  |           v-show="recorderStatus == 2" | ||||||
|  |           type="primary" | ||||||
|  |           size="mini" | ||||||
|  |           round | ||||||
|  |           icon="el-icon-microphone" | ||||||
|  |           @click="stopRecorder" | ||||||
|  |           >结束录音 | ||||||
|  |         </el-button> | ||||||
|  |         <el-button | ||||||
|  |           v-show="recorderStatus == 3 || recorderStatus == 6" | ||||||
|  |           type="primary" | ||||||
|  |           size="mini" | ||||||
|  |           round | ||||||
|  |           icon="el-icon-video-play" | ||||||
|  |           @click="playRecorder" | ||||||
|  |           >播放录音 | ||||||
|  |         </el-button> | ||||||
|  | 
 | ||||||
|  |         <el-button | ||||||
|  |           v-show=" | ||||||
|  |             recorderStatus == 3 || recorderStatus == 5 || recorderStatus == 6 | ||||||
|  |           " | ||||||
|  |           type="primary" | ||||||
|  |           size="mini" | ||||||
|  |           round | ||||||
|  |           icon="el-icon-video-play" | ||||||
|  |           @click="startRecorder" | ||||||
|  |           >重新录音 | ||||||
|  |         </el-button> | ||||||
|  | 
 | ||||||
|  |         <el-button | ||||||
|  |           v-show="recorderStatus == 4" | ||||||
|  |           type="primary" | ||||||
|  |           size="mini" | ||||||
|  |           round | ||||||
|  |           icon="el-icon-video-pause" | ||||||
|  |           @click="pausePlayRecorder" | ||||||
|  |           >暂停播放 | ||||||
|  |         </el-button> | ||||||
|  |         <el-button | ||||||
|  |           v-show="recorderStatus == 5" | ||||||
|  |           type="primary" | ||||||
|  |           size="mini" | ||||||
|  |           round | ||||||
|  |           icon="el-icon-video-play" | ||||||
|  |           @click="resumePlayRecorder" | ||||||
|  |           >继续播放 | ||||||
|  |         </el-button> | ||||||
|  | 
 | ||||||
|  |         <el-button | ||||||
|  |           v-show=" | ||||||
|  |             recorderStatus == 3 || recorderStatus == 5 || recorderStatus == 6 | ||||||
|  |           " | ||||||
|  |           type="primary" | ||||||
|  |           size="mini" | ||||||
|  |           round | ||||||
|  |           @click="submit" | ||||||
|  |           >立即发送 | ||||||
|  |         </el-button> | ||||||
|  |       </el-footer> | ||||||
|  |     </el-container> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | <script> | ||||||
|  | import Recorder from 'js-audio-recorder' | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   name: 'MeEditorRecorder', | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       // 录音实例 | ||||||
|  |       recorder: null, | ||||||
|  | 
 | ||||||
|  |       // 录音时长 | ||||||
|  |       duration: 0, | ||||||
|  | 
 | ||||||
|  |       // 播放时长 | ||||||
|  |       playTime: 0, | ||||||
|  | 
 | ||||||
|  |       animation: false, | ||||||
|  | 
 | ||||||
|  |       // 当前状态 | ||||||
|  |       recorderStatus: 0, //0:未开始录音 1:正在录音 2:暂停录音 3:结束录音 4:播放录音 5:停止播放 6:播放结束 | ||||||
|  | 
 | ||||||
|  |       playTimeout: null, | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   computed: { | ||||||
|  |     datetime() { | ||||||
|  |       let hour = parseInt((this.duration / 60 / 60) % 24) //小时 | ||||||
|  |       let minute = parseInt((this.duration / 60) % 60) //分钟 | ||||||
|  |       let seconds = parseInt(this.duration % 60) //秒 | ||||||
|  | 
 | ||||||
|  |       if (hour < 10) hour = `0${hour}` | ||||||
|  |       if (minute < 10) minute = `0${minute}` | ||||||
|  |       if (seconds < 10) seconds = `0${seconds}` | ||||||
|  | 
 | ||||||
|  |       return `${hour}:${minute}:${seconds}` | ||||||
|  |     }, | ||||||
|  |     formatPlayTime() { | ||||||
|  |       let hour = parseInt((this.playTime / 60 / 60) % 24) //小时 | ||||||
|  |       let minute = parseInt((this.playTime / 60) % 60) //分钟 | ||||||
|  |       let seconds = parseInt(this.playTime % 60) //秒 | ||||||
|  | 
 | ||||||
|  |       if (hour < 10) hour = `0${hour}` | ||||||
|  |       if (minute < 10) minute = `0${minute}` | ||||||
|  |       if (seconds < 10) seconds = `0${seconds}` | ||||||
|  | 
 | ||||||
|  |       return `${hour}:${minute}:${seconds}` | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   destroyed() { | ||||||
|  |     if (this.recorder) { | ||||||
|  |       this.destroyRecorder() | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     closeBox() { | ||||||
|  |       if (this.recorder == null) { | ||||||
|  |         this.$emit('close', false) | ||||||
|  |         return | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       if (this.recorderStatus == 1) { | ||||||
|  |         this.stopRecorder() | ||||||
|  |       } else if (this.recorderStatus == 4) { | ||||||
|  |         this.pausePlayRecorder() | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       // 销毁录音后关闭窗口 | ||||||
|  |       this.destroyRecorder(() => { | ||||||
|  |         this.$emit('close', false) | ||||||
|  |       }) | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     // 开始录音 | ||||||
|  |     startRecorder() { | ||||||
|  |       let _this = this | ||||||
|  |       // http://recorder.api.zhuyuntao.cn/Recorder/event.html | ||||||
|  |       // https://blog.csdn.net/qq_41619796/article/details/107865602 | ||||||
|  |       this.recorder = new Recorder() | ||||||
|  |       this.recorder.onprocess = duration => { | ||||||
|  |         duration = parseInt(duration) | ||||||
|  |         _this.duration = duration | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       this.recorder.start().then( | ||||||
|  |         () => { | ||||||
|  |           this.recorderStatus = 1 | ||||||
|  |           this.animation = true | ||||||
|  |         }, | ||||||
|  |         error => { | ||||||
|  |           console.log(`${error.name} : ${error.message}`) | ||||||
|  |         } | ||||||
|  |       ) | ||||||
|  |     }, | ||||||
|  |     // 暂停录音 | ||||||
|  |     pauseRecorder() { | ||||||
|  |       this.recorder.pause() | ||||||
|  |       this.recorderStatus = 2 | ||||||
|  |       this.animation = false | ||||||
|  |     }, | ||||||
|  |     // 继续录音 | ||||||
|  |     resumeRecorder() { | ||||||
|  |       this.recorderStatus = 1 | ||||||
|  |       this.recorder.resume() | ||||||
|  |       this.animation = true | ||||||
|  |     }, | ||||||
|  |     // 结束录音 | ||||||
|  |     stopRecorder() { | ||||||
|  |       this.recorderStatus = 3 | ||||||
|  |       this.recorder.stop() | ||||||
|  |       this.animation = false | ||||||
|  |     }, | ||||||
|  |     // 录音播放 | ||||||
|  |     playRecorder() { | ||||||
|  |       this.recorderStatus = 4 | ||||||
|  |       this.recorder.play() | ||||||
|  |       this.playTimeouts() | ||||||
|  |       this.animation = true | ||||||
|  |     }, | ||||||
|  |     // 暂停录音播放 | ||||||
|  |     pausePlayRecorder() { | ||||||
|  |       this.recorderStatus = 5 | ||||||
|  |       this.recorder.pausePlay() | ||||||
|  |       clearInterval(this.playTimeout) | ||||||
|  |       this.animation = false | ||||||
|  |     }, | ||||||
|  |     // 恢复录音播放 | ||||||
|  |     resumePlayRecorder() { | ||||||
|  |       this.recorderStatus = 4 | ||||||
|  |       this.recorder.resumePlay() | ||||||
|  |       this.playTimeouts() | ||||||
|  |       this.animation = true | ||||||
|  |     }, | ||||||
|  |     // 销毁录音 | ||||||
|  |     destroyRecorder(callBack) { | ||||||
|  |       this.recorder.destroy().then(() => { | ||||||
|  |         this.recorder = null | ||||||
|  |         if (callBack) { | ||||||
|  |           callBack() | ||||||
|  |         } | ||||||
|  |       }) | ||||||
|  |     }, | ||||||
|  |     // 获取录音文件大小(单位:字节) | ||||||
|  |     recorderSize() { | ||||||
|  |       return this.recorder.fileSize | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     playTimeouts() { | ||||||
|  |       this.playTimeout = setInterval(() => { | ||||||
|  |         let time = parseInt(this.recorder.getPlayTime()) | ||||||
|  |         this.playTime = time | ||||||
|  |         if (time == this.duration) { | ||||||
|  |           clearInterval(this.playTimeout) | ||||||
|  |           this.animation = false | ||||||
|  |           this.recorderStatus = 6 | ||||||
|  |         } | ||||||
|  |       }, 100) | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     submit() { | ||||||
|  |       alert('功能研发中,敬请期待...') | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | <style lang="less" scoped> | ||||||
|  | .lum-dialog-box { | ||||||
|  |   width: 500px; | ||||||
|  |   max-width: 500px; | ||||||
|  |   height: 450px; | ||||||
|  | 
 | ||||||
|  |   .mian { | ||||||
|  |     display: flex; | ||||||
|  |     justify-content: center; | ||||||
|  |     align-items: center; | ||||||
|  |     flex-direction: column; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .footer { | ||||||
|  |     height: 50px; | ||||||
|  |     text-align: center; | ||||||
|  |     line-height: 50px; | ||||||
|  |     border-top: 1px solid #f7f3f3; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .music { | ||||||
|  |   position: relative; | ||||||
|  |   width: 180px; | ||||||
|  |   height: 160px; | ||||||
|  |   border: 8px solid #bebebe; | ||||||
|  |   border-bottom: 0px; | ||||||
|  |   border-top-left-radius: 110px; | ||||||
|  |   border-top-right-radius: 110px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .music:before, | ||||||
|  | .music:after { | ||||||
|  |   content: ''; | ||||||
|  |   position: absolute; | ||||||
|  |   bottom: -20px; | ||||||
|  |   width: 40px; | ||||||
|  |   height: 82px; | ||||||
|  |   background-color: #bebebe; | ||||||
|  |   border-radius: 15px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .music:before { | ||||||
|  |   right: -25px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .music:after { | ||||||
|  |   left: -25px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .line { | ||||||
|  |   position: absolute; | ||||||
|  |   width: 6px; | ||||||
|  |   min-height: 30px; | ||||||
|  |   transition: 0.5s; | ||||||
|  | 
 | ||||||
|  |   vertical-align: middle; | ||||||
|  |   bottom: 0 !important; | ||||||
|  |   box-shadow: inset 0px 0px 16px -2px rgba(0, 0, 0, 0.15); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .line-ani { | ||||||
|  |   animation: equalize 4s 0s infinite; | ||||||
|  |   animation-timing-function: linear; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .line1 { | ||||||
|  |   left: 30%; | ||||||
|  |   bottom: 0px; | ||||||
|  |   animation-delay: -1.9s; | ||||||
|  |   background-color: #ff5e50; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .line2 { | ||||||
|  |   left: 40%; | ||||||
|  |   height: 60px; | ||||||
|  |   bottom: -15px; | ||||||
|  |   animation-delay: -2.9s; | ||||||
|  |   background-color: #a64de6; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .line3 { | ||||||
|  |   left: 50%; | ||||||
|  |   height: 30px; | ||||||
|  |   bottom: -1.5px; | ||||||
|  |   animation-delay: -3.9s; | ||||||
|  |   background-color: #5968dc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .line4 { | ||||||
|  |   left: 60%; | ||||||
|  |   height: 65px; | ||||||
|  |   bottom: -16px; | ||||||
|  |   animation-delay: -4.9s; | ||||||
|  |   background-color: #27c8f8; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .line5 { | ||||||
|  |   left: 70%; | ||||||
|  |   height: 60px; | ||||||
|  |   bottom: -12px; | ||||||
|  |   animation-delay: -5.9s; | ||||||
|  |   background-color: #cc60b5; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @keyframes equalize { | ||||||
|  |   0% { | ||||||
|  |     height: 48px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   4% { | ||||||
|  |     height: 42px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   8% { | ||||||
|  |     height: 40px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   12% { | ||||||
|  |     height: 30px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   16% { | ||||||
|  |     height: 20px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   20% { | ||||||
|  |     height: 30px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   24% { | ||||||
|  |     height: 40px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   28% { | ||||||
|  |     height: 10px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   32% { | ||||||
|  |     height: 40px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   36% { | ||||||
|  |     height: 48px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   40% { | ||||||
|  |     height: 20px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   44% { | ||||||
|  |     height: 40px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   48% { | ||||||
|  |     height: 48px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   52% { | ||||||
|  |     height: 30px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   56% { | ||||||
|  |     height: 10px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   60% { | ||||||
|  |     height: 30px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   64% { | ||||||
|  |     height: 48px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   68% { | ||||||
|  |     height: 30px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   72% { | ||||||
|  |     height: 48px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   76% { | ||||||
|  |     height: 20px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   80% { | ||||||
|  |     height: 48px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   84% { | ||||||
|  |     height: 38px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   88% { | ||||||
|  |     height: 48px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   92% { | ||||||
|  |     height: 20px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   96% { | ||||||
|  |     height: 48px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   100% { | ||||||
|  |     height: 48px; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,169 @@ | ||||||
|  | <template> | ||||||
|  |   <div class="lum-dialog-mask"> | ||||||
|  |     <el-container class="lum-dialog-box" v-outside="closeBox"> | ||||||
|  |       <el-header class="no-padding header" height="50px"> | ||||||
|  |         <p>系统表情</p> | ||||||
|  |         <p class="tools"> | ||||||
|  |           <i class="el-icon-close" @click="closeBox" /> | ||||||
|  |         </p> | ||||||
|  |       </el-header> | ||||||
|  | 
 | ||||||
|  |       <el-main class="no-padding mian lum-scrollbar"> | ||||||
|  |         <ul> | ||||||
|  |           <li v-for="(item, i) in items" :key="item.id" class="no-select"> | ||||||
|  |             <div class="pkg-avatar"> | ||||||
|  |               <el-image :src="item.icon" fit="cover" :lazy="true" /> | ||||||
|  |             </div> | ||||||
|  |             <div class="pkg-info" v-text="item.name"></div> | ||||||
|  |             <div class="pkg-status"> | ||||||
|  |               <button | ||||||
|  |                 :class="{ | ||||||
|  |                   'add-emoji': item.status == 0, | ||||||
|  |                   'remove-emoji': item.status != 0, | ||||||
|  |                 }" | ||||||
|  |                 @click="setEmoticon(i, item, item.status == 0 ? 1 : 2)" | ||||||
|  |               > | ||||||
|  |                 {{ item.status == 0 ? '添加' : '移除' }} | ||||||
|  |               </button> | ||||||
|  |             </div> | ||||||
|  |           </li> | ||||||
|  |         </ul> | ||||||
|  |       </el-main> | ||||||
|  | 
 | ||||||
|  |       <el-footer class="footer" height="50px"> | ||||||
|  |         <el-button type="primary" size="medium" class="btn" @click="closeBox"> | ||||||
|  |           关闭窗口 | ||||||
|  |         </el-button> | ||||||
|  |       </el-footer> | ||||||
|  |     </el-container> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | import { ServeFindSysEmoticon, ServeSetUserEmoticon } from '@/api/emoticon' | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   name: 'MeEditorSystemEmoticon', | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       items: [], | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   created() { | ||||||
|  |     this.loadSysEmoticon() | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     closeBox() { | ||||||
|  |       this.$emit('close') | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     // 获取系统表情包列表 | ||||||
|  |     loadSysEmoticon() { | ||||||
|  |       ServeFindSysEmoticon().then(res => { | ||||||
|  |         if (res.code == 200) { | ||||||
|  |           this.items = res.data | ||||||
|  |         } | ||||||
|  |       }) | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     setEmoticon(index, item, type) { | ||||||
|  |       ServeSetUserEmoticon({ | ||||||
|  |         emoticon_id: item.id, | ||||||
|  |         type: type, | ||||||
|  |       }).then(res => { | ||||||
|  |         if (res.code == 200) { | ||||||
|  |           if (type == 1) { | ||||||
|  |             this.items[index].status = 1 | ||||||
|  |             this.$store.commit('APPEND_SYS_EMOTICON', res.data) | ||||||
|  |           } else { | ||||||
|  |             this.items[index].status = 0 | ||||||
|  |             this.$store.commit('REMOVE_SYS_EMOTICON', { | ||||||
|  |               emoticon_id: item.id, | ||||||
|  |             }) | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       }) | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | <style lang="less" scoped> | ||||||
|  | .lum-dialog-box { | ||||||
|  |   width: 350px; | ||||||
|  |   max-width: 350px; | ||||||
|  |   height: 500px; | ||||||
|  | 
 | ||||||
|  |   .mian { | ||||||
|  |     height: 480px; | ||||||
|  |     overflow-y: auto; | ||||||
|  | 
 | ||||||
|  |     li { | ||||||
|  |       display: flex; | ||||||
|  |       cursor: pointer; | ||||||
|  |       height: 68px; | ||||||
|  |       align-items: center; | ||||||
|  |       border-bottom: 3px solid #fbf2fb; | ||||||
|  |       padding-left: 5px; | ||||||
|  | 
 | ||||||
|  |       .pkg-avatar { | ||||||
|  |         flex-shrink: 0; | ||||||
|  | 
 | ||||||
|  |         .el-image { | ||||||
|  |           width: 50px; | ||||||
|  |           height: 50px; | ||||||
|  |           border-radius: 3px; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       .pkg-info { | ||||||
|  |         display: flex; | ||||||
|  |         flex-direction: column; | ||||||
|  |         justify-content: center; | ||||||
|  |         margin-left: 14px; | ||||||
|  |         margin-right: 14px; | ||||||
|  |         overflow: hidden; | ||||||
|  |         display: -webkit-box; | ||||||
|  |         -webkit-box-orient: vertical; | ||||||
|  |         -webkit-line-clamp: 2; | ||||||
|  |         width: 200px; | ||||||
|  |         color: #615d5d; | ||||||
|  |         font-size: 13px; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       .pkg-status { | ||||||
|  |         flex-shrink: 0; | ||||||
|  | 
 | ||||||
|  |         button { | ||||||
|  |           font-size: 12px; | ||||||
|  |           text-align: center; | ||||||
|  |           line-height: 28px; | ||||||
|  |           border-radius: 20px; | ||||||
|  |           width: 50px; | ||||||
|  |           cursor: pointer; | ||||||
|  |           color: white; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         .add-emoji { | ||||||
|  |           background-color: #38adff; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         .remove-emoji { | ||||||
|  |           background-color: #ff5722; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .footer { | ||||||
|  |     height: 50px; | ||||||
|  |     background: rgba(247, 245, 245, 0.66); | ||||||
|  |     text-align: center; | ||||||
|  |     line-height: 50px; | ||||||
|  | 
 | ||||||
|  |     .btn { | ||||||
|  |       width: 150px; | ||||||
|  |       border-radius: 2px; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,226 @@ | ||||||
|  | <template> | ||||||
|  |   <div class="lum-dialog-mask animated fadeIn"> | ||||||
|  |     <el-container class="lum-dialog-box"> | ||||||
|  |       <el-header class="header no-select" height="60px"> | ||||||
|  |         <p>发起投票</p> | ||||||
|  |         <p class="tools"> | ||||||
|  |           <i class="el-icon-close" @click="$emit('close')" /> | ||||||
|  |         </p> | ||||||
|  |       </el-header> | ||||||
|  |       <el-main class="main no-padding vote-from"> | ||||||
|  |         <div class="vote-title">投票方式</div> | ||||||
|  |         <div> | ||||||
|  |           <el-radio-group v-model="mode"> | ||||||
|  |             <el-radio :label="0">单选</el-radio> | ||||||
|  |             <el-radio :label="1">多选</el-radio> | ||||||
|  |           </el-radio-group> | ||||||
|  |         </div> | ||||||
|  |         <div class="vote-title">投票主题</div> | ||||||
|  |         <div> | ||||||
|  |           <el-input | ||||||
|  |             size="medium" | ||||||
|  |             clear="vote-input" | ||||||
|  |             v-model.trim="title" | ||||||
|  |             placeholder="请输入投票主题,最多50字" | ||||||
|  |             :maxlength="50" | ||||||
|  |           /> | ||||||
|  |         </div> | ||||||
|  | 
 | ||||||
|  |         <div class="vote-title">投票选项</div> | ||||||
|  |         <div> | ||||||
|  |           <div class="vote-options" v-for="(option, index) in options"> | ||||||
|  |             <div class="lbox"> | ||||||
|  |               <el-input | ||||||
|  |                 size="medium" | ||||||
|  |                 clear="vote-input" | ||||||
|  |                 v-model.trim="option.value" | ||||||
|  |                 placeholder="请输入选项内容" | ||||||
|  |                 :maxlength="120" | ||||||
|  |               > | ||||||
|  |                 <span | ||||||
|  |                   slot="prefix" | ||||||
|  |                   style="margin-left:7px;" | ||||||
|  |                   v-text="String.fromCharCode(65 + index)" | ||||||
|  |                 /> | ||||||
|  |               </el-input> | ||||||
|  |             </div> | ||||||
|  |             <div class="rbox"> | ||||||
|  |               <i class="el-icon-close" @click="removeOption(index)"></i> | ||||||
|  |             </div> | ||||||
|  |           </div> | ||||||
|  | 
 | ||||||
|  |           <h6 class="pointer add-option" @click="addOption"> | ||||||
|  |             <i class="el-icon-plus"></i> 添加选项 | ||||||
|  |           </h6> | ||||||
|  |         </div> | ||||||
|  |       </el-main> | ||||||
|  |       <el-footer class="footer"> | ||||||
|  |         <el-button plain size="small">取消</el-button> | ||||||
|  |         <el-button | ||||||
|  |           type="primary" | ||||||
|  |           size="small" | ||||||
|  |           :disabled="isCheck" | ||||||
|  |           :loading="loading" | ||||||
|  |           @click="submit" | ||||||
|  |           >发起投票</el-button | ||||||
|  |         > | ||||||
|  |       </el-footer> | ||||||
|  |     </el-container> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | <script> | ||||||
|  | import { ServeSendVote } from '@/api/chat' | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   name: 'MeEditorVote', | ||||||
|  |   props: { | ||||||
|  |     group_id: { | ||||||
|  |       type: [String, Number], | ||||||
|  |       default: 0, | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       loading: false, | ||||||
|  |       mode: 0, | ||||||
|  |       title: '', | ||||||
|  |       options: [ | ||||||
|  |         { | ||||||
|  |           value: '', | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           value: '', | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           value: '', | ||||||
|  |         }, | ||||||
|  |       ], | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   computed: { | ||||||
|  |     isCheck() { | ||||||
|  |       if (this.title == '') return true | ||||||
|  | 
 | ||||||
|  |       return this.options.some(option => option.value == '') | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     submit() { | ||||||
|  |       let items = [] | ||||||
|  |       const { receiver_id } = this.$store.state.dialogue | ||||||
|  |       this.options.forEach(option => { | ||||||
|  |         items.push(option.value) | ||||||
|  |       }) | ||||||
|  | 
 | ||||||
|  |       this.loading = true | ||||||
|  | 
 | ||||||
|  |       ServeSendVote({ | ||||||
|  |         receiver_id, | ||||||
|  |         mode: this.mode, | ||||||
|  |         title: this.title, | ||||||
|  |         options: items, | ||||||
|  |       }) | ||||||
|  |         .then(res => { | ||||||
|  |           if (res.code == 200) { | ||||||
|  |             this.$emit('close') | ||||||
|  |             this.$notify({ | ||||||
|  |               title: '友情提示', | ||||||
|  |               message: '发起投票成功!', | ||||||
|  |               type: 'success', | ||||||
|  |             }) | ||||||
|  |           } else { | ||||||
|  |             this.$notify({ | ||||||
|  |               title: '友情提示', | ||||||
|  |               message: res.message, | ||||||
|  |               type: 'warning', | ||||||
|  |             }) | ||||||
|  |           } | ||||||
|  |         }) | ||||||
|  |         .catch(() => { | ||||||
|  |           this.loading = false | ||||||
|  |         }) | ||||||
|  |     }, | ||||||
|  |     addOption() { | ||||||
|  |       if (this.options.length >= 6) { | ||||||
|  |         return false | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       this.options.push({ | ||||||
|  |         value: '', | ||||||
|  |       }) | ||||||
|  |     }, | ||||||
|  |     removeOption(index) { | ||||||
|  |       if (this.options.length <= 2) { | ||||||
|  |         return false | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       this.$delete(this.options, index) | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | <style lang="less" scoped> | ||||||
|  | .lum-dialog-box { | ||||||
|  |   height: 600px; | ||||||
|  |   max-width: 450px; | ||||||
|  | 
 | ||||||
|  |   .vote-from { | ||||||
|  |     box-sizing: border-box; | ||||||
|  |     padding: 15px 25px; | ||||||
|  |     overflow: hidden; | ||||||
|  | 
 | ||||||
|  |     .vote-title { | ||||||
|  |       margin: 20px 0 10px; | ||||||
|  |       &:first-child { | ||||||
|  |         margin-top: 0; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .vote-options { | ||||||
|  |       display: flex; | ||||||
|  |       min-height: 30px; | ||||||
|  |       margin: 10px 0; | ||||||
|  | 
 | ||||||
|  |       .lbox { | ||||||
|  |         width: 100%; | ||||||
|  | 
 | ||||||
|  |         /deep/.el-input__prefix { | ||||||
|  |           height: 36px; | ||||||
|  |           line-height: 36px; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       .rbox { | ||||||
|  |         flex-basis: 50px; | ||||||
|  |         display: flex; | ||||||
|  |         justify-content: center; | ||||||
|  |         align-items: center; | ||||||
|  |         i { | ||||||
|  |           font-size: 18px; | ||||||
|  |           cursor: pointer; | ||||||
|  |           &:hover { | ||||||
|  |             color: red; | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .add-option { | ||||||
|  |       margin-top: 5px; | ||||||
|  |       font-weight: 400; | ||||||
|  |       color: #3370ff; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .footer { | ||||||
|  |     display: flex; | ||||||
|  |     align-items: center; | ||||||
|  |     justify-content: flex-end; | ||||||
|  |     padding-right: 32px; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /deep/.el-radio__input.is-checked + .el-radio__label { | ||||||
|  |   color: #606266; | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,18 @@ | ||||||
|  | <template> | ||||||
|  |     <img :src="'https://avatars.dicebear.com/api/initials/'+text+'.svg?fontSize=38'" alt=""/> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  |     export default { | ||||||
|  |         props:{ | ||||||
|  |             text:{ | ||||||
|  |                 type:null, | ||||||
|  |                 default:'' | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style lang="scss" scoped> | ||||||
|  | 
 | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,18 @@ | ||||||
|  | <template> | ||||||
|  |     <img :src="text" alt=""/> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  |     export default { | ||||||
|  |         props:{ | ||||||
|  |             text:{ | ||||||
|  |                 type:null, | ||||||
|  |                 default:'' | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style lang="scss" scoped> | ||||||
|  | 
 | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,49 @@ | ||||||
|  | <template> | ||||||
|  |   <div class="empty-content"> | ||||||
|  |     <div class="image"> | ||||||
|  |       <img :src="src" /> | ||||||
|  |     </div> | ||||||
|  |     <div class="text" v-text="text" /> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | export default { | ||||||
|  |   name: 'Empty', | ||||||
|  |   props: { | ||||||
|  |     text: { | ||||||
|  |       type: String, | ||||||
|  |       default: '数据为空...', | ||||||
|  |     }, | ||||||
|  |     src: { | ||||||
|  |       type: String, | ||||||
|  |       default: require('@/assets/image/no-oncall.6b776fcf.png'), | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   data() { | ||||||
|  |     return {} | ||||||
|  |   }, | ||||||
|  |   created() {}, | ||||||
|  |   methods: {}, | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | <style lang="less" scoped> | ||||||
|  | .empty-content { | ||||||
|  |   width: 100%; | ||||||
|  |   height: 60%; | ||||||
|  |   display: flex; | ||||||
|  |   flex-direction: column; | ||||||
|  |   align-items: center; | ||||||
|  |   justify-content: center; | ||||||
|  |   font-size: 13px; | ||||||
|  | 
 | ||||||
|  |   .image { | ||||||
|  |     width: 200px; | ||||||
|  |     height: 200px; | ||||||
|  | 
 | ||||||
|  |     img { | ||||||
|  |       width: 100%; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,313 @@ | ||||||
|  | <template> | ||||||
|  |   <div class="loading-content"> | ||||||
|  |     <div class="ant-spin ant-spin-lg ant-spin-spinning"> | ||||||
|  |       <span class="ant-spin-dot ant-spin-dot-spin"> | ||||||
|  |         <i class="ant-spin-dot-item" /> | ||||||
|  |         <i class="ant-spin-dot-item" /> | ||||||
|  |         <i class="ant-spin-dot-item" /> | ||||||
|  |         <i class="ant-spin-dot-item" /> | ||||||
|  |       </span> | ||||||
|  |     </div> | ||||||
|  |     <p>{{ text }}</p> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | export default { | ||||||
|  |   name: 'Loading', | ||||||
|  |   props: { | ||||||
|  |     text: { | ||||||
|  |       type: String, | ||||||
|  |       default: '数据加载中 ...', | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | <style lang="less" scoped> | ||||||
|  | .loading-content { | ||||||
|  |   width: 100%; | ||||||
|  |   height: 60%; | ||||||
|  |   display: flex; | ||||||
|  |   flex-direction: column; | ||||||
|  |   align-items: center; | ||||||
|  |   justify-content: center; | ||||||
|  |   font-size: 13px; | ||||||
|  |   p { | ||||||
|  |     margin-top: 10px; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* ant-spin 加载动画 start */ | ||||||
|  | .ant-spin { | ||||||
|  |   -webkit-box-sizing: border-box; | ||||||
|  |   box-sizing: border-box; | ||||||
|  |   margin: 0; | ||||||
|  |   padding: 0; | ||||||
|  |   color: rgba(0, 0, 0, 0.65); | ||||||
|  |   font-size: 14px; | ||||||
|  |   font-variant: tabular-nums; | ||||||
|  |   line-height: 1.5715; | ||||||
|  |   list-style: none; | ||||||
|  |   -webkit-font-feature-settings: 'tnum'; | ||||||
|  |   font-feature-settings: 'tnum'; | ||||||
|  |   position: absolute; | ||||||
|  |   display: none; | ||||||
|  |   color: #1890ff; | ||||||
|  |   text-align: center; | ||||||
|  |   vertical-align: middle; | ||||||
|  |   opacity: 0; | ||||||
|  |   -webkit-transition: -webkit-transform 0.3s | ||||||
|  |     cubic-bezier(0.78, 0.14, 0.15, 0.86); | ||||||
|  |   transition: -webkit-transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86); | ||||||
|  |   transition: transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86); | ||||||
|  |   transition: transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86), | ||||||
|  |     -webkit-transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ant-spin-spinning { | ||||||
|  |   position: static; | ||||||
|  |   display: inline-block; | ||||||
|  |   opacity: 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ant-spin-nested-loading { | ||||||
|  |   position: relative; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ant-spin-nested-loading > div > .ant-spin { | ||||||
|  |   position: absolute; | ||||||
|  |   top: 0; | ||||||
|  |   left: 0; | ||||||
|  |   z-index: 4; | ||||||
|  |   display: block; | ||||||
|  |   width: 100%; | ||||||
|  |   height: 100%; | ||||||
|  |   max-height: 400px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ant-spin-nested-loading > div > .ant-spin .ant-spin-dot { | ||||||
|  |   position: absolute; | ||||||
|  |   top: 50%; | ||||||
|  |   left: 50%; | ||||||
|  |   margin: -10px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ant-spin-nested-loading > div > .ant-spin .ant-spin-text { | ||||||
|  |   position: absolute; | ||||||
|  |   top: 50%; | ||||||
|  |   width: 100%; | ||||||
|  |   padding-top: 5px; | ||||||
|  |   text-shadow: 0 1px 2px #fff; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ant-spin-nested-loading > div > .ant-spin.ant-spin-show-text .ant-spin-dot { | ||||||
|  |   margin-top: -20px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ant-spin-nested-loading > div > .ant-spin-sm .ant-spin-dot { | ||||||
|  |   margin: -7px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ant-spin-nested-loading > div > .ant-spin-sm .ant-spin-text { | ||||||
|  |   padding-top: 2px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ant-spin-nested-loading > div > .ant-spin-sm.ant-spin-show-text .ant-spin-dot { | ||||||
|  |   margin-top: -17px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ant-spin-nested-loading > div > .ant-spin-lg .ant-spin-dot { | ||||||
|  |   margin: -16px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ant-spin-nested-loading > div > .ant-spin-lg .ant-spin-text { | ||||||
|  |   padding-top: 11px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ant-spin-nested-loading > div > .ant-spin-lg.ant-spin-show-text .ant-spin-dot { | ||||||
|  |   margin-top: -26px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ant-spin-container { | ||||||
|  |   position: relative; | ||||||
|  |   -webkit-transition: opacity 0.3s; | ||||||
|  |   transition: opacity 0.3s; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ant-spin-container::after { | ||||||
|  |   position: absolute; | ||||||
|  |   top: 0; | ||||||
|  |   right: 0; | ||||||
|  |   bottom: 0; | ||||||
|  |   left: 0; | ||||||
|  |   z-index: 10; | ||||||
|  |   display: none \9; | ||||||
|  |   width: 100%; | ||||||
|  |   height: 100%; | ||||||
|  |   background: #fff; | ||||||
|  |   opacity: 0; | ||||||
|  |   -webkit-transition: all 0.3s; | ||||||
|  |   transition: all 0.3s; | ||||||
|  |   content: ''; | ||||||
|  |   pointer-events: none; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ant-spin-blur { | ||||||
|  |   clear: both; | ||||||
|  |   overflow: hidden; | ||||||
|  |   opacity: 0.5; | ||||||
|  |   -webkit-user-select: none; | ||||||
|  |   -moz-user-select: none; | ||||||
|  |   -ms-user-select: none; | ||||||
|  |   user-select: none; | ||||||
|  |   pointer-events: none; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ant-spin-blur::after { | ||||||
|  |   opacity: 0.4; | ||||||
|  |   pointer-events: auto; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ant-spin-tip { | ||||||
|  |   color: rgba(0, 0, 0, 0.45); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ant-spin-dot { | ||||||
|  |   position: relative; | ||||||
|  |   display: inline-block; | ||||||
|  |   font-size: 20px; | ||||||
|  |   width: 1em; | ||||||
|  |   height: 1em; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ant-spin-dot-item { | ||||||
|  |   position: absolute; | ||||||
|  |   display: block; | ||||||
|  |   width: 9px; | ||||||
|  |   height: 9px; | ||||||
|  |   background-color: #1890ff; | ||||||
|  |   border-radius: 100%; | ||||||
|  |   -webkit-transform: scale(0.75); | ||||||
|  |   transform: scale(0.75); | ||||||
|  |   -webkit-transform-origin: 50% 50%; | ||||||
|  |   transform-origin: 50% 50%; | ||||||
|  |   opacity: 0.3; | ||||||
|  |   -webkit-animation: antSpinMove 1s infinite linear alternate; | ||||||
|  |   animation: antSpinMove 1s infinite linear alternate; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ant-spin-dot-item:nth-child(1) { | ||||||
|  |   top: 0; | ||||||
|  |   left: 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ant-spin-dot-item:nth-child(2) { | ||||||
|  |   top: 0; | ||||||
|  |   right: 0; | ||||||
|  |   -webkit-animation-delay: 0.4s; | ||||||
|  |   animation-delay: 0.4s; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ant-spin-dot-item:nth-child(3) { | ||||||
|  |   right: 0; | ||||||
|  |   bottom: 0; | ||||||
|  |   -webkit-animation-delay: 0.8s; | ||||||
|  |   animation-delay: 0.8s; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ant-spin-dot-item:nth-child(4) { | ||||||
|  |   bottom: 0; | ||||||
|  |   left: 0; | ||||||
|  |   -webkit-animation-delay: 1.2s; | ||||||
|  |   animation-delay: 1.2s; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ant-spin-dot-spin { | ||||||
|  |   -webkit-transform: rotate(45deg); | ||||||
|  |   transform: rotate(45deg); | ||||||
|  |   -webkit-animation: antRotate 1.2s infinite linear; | ||||||
|  |   animation: antRotate 1.2s infinite linear; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ant-spin-sm .ant-spin-dot { | ||||||
|  |   font-size: 14px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ant-spin-sm .ant-spin-dot i { | ||||||
|  |   width: 6px; | ||||||
|  |   height: 6px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ant-spin-lg .ant-spin-dot { | ||||||
|  |   font-size: 32px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ant-spin-lg .ant-spin-dot i { | ||||||
|  |   width: 14px; | ||||||
|  |   height: 14px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ant-spin.ant-spin-show-text .ant-spin-text { | ||||||
|  |   display: block; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @media all and (-ms-high-contrast: none), (-ms-high-contrast: active) { | ||||||
|  |   .ant-spin-blur { | ||||||
|  |     background: #fff; | ||||||
|  |     opacity: 0.5; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @-webkit-keyframes antSpinMove { | ||||||
|  |   to { | ||||||
|  |     opacity: 1; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @keyframes antSpinMove { | ||||||
|  |   to { | ||||||
|  |     opacity: 1; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @-webkit-keyframes antRotate { | ||||||
|  |   to { | ||||||
|  |     -webkit-transform: rotate(405deg); | ||||||
|  |     transform: rotate(405deg); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @keyframes antRotate { | ||||||
|  |   to { | ||||||
|  |     -webkit-transform: rotate(405deg); | ||||||
|  |     transform: rotate(405deg); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ant-spin-rtl { | ||||||
|  |   direction: rtl; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ant-spin-rtl .ant-spin-dot-spin { | ||||||
|  |   -webkit-transform: rotate(-45deg); | ||||||
|  |   transform: rotate(-45deg); | ||||||
|  |   -webkit-animation-name: antRotateRtl; | ||||||
|  |   animation-name: antRotateRtl; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @-webkit-keyframes antRotateRtl { | ||||||
|  |   to { | ||||||
|  |     -webkit-transform: rotate(-405deg); | ||||||
|  |     transform: rotate(-405deg); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @keyframes antRotateRtl { | ||||||
|  |   to { | ||||||
|  |     -webkit-transform: rotate(-405deg); | ||||||
|  |     transform: rotate(-405deg); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* ant-spin 加载动画 end */ | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,80 @@ | ||||||
|  | <template> | ||||||
|  |   <div> | ||||||
|  |     <div class="abs-module" v-show="isShow"> | ||||||
|  |       <div class="abs-box"> | ||||||
|  |         <i class="el-icon-circle-close" @click="close" /> | ||||||
|  |         <a href="https://www.aliyun.com/minisite/goods?userCode=kqyyppx2"> | ||||||
|  |           <img src="~@/assets/image/aliyun-abs.jpg" width="300" /> | ||||||
|  |         </a> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | <script> | ||||||
|  | export default { | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       isShow: false, | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   created() { | ||||||
|  |     if (this.getNum() <= 2) { | ||||||
|  |       setTimeout(() => { | ||||||
|  |         this.isShow = true | ||||||
|  |       }, 1000 * 60 * 2) | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     getNum() { | ||||||
|  |       return parseInt(sessionStorage.getItem('ABS_BOX')) || 0 | ||||||
|  |     }, | ||||||
|  |     close() { | ||||||
|  |       sessionStorage.setItem('ABS_BOX', this.getNum() + 1) | ||||||
|  |       this.isShow = false | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | <style lang="less" scoped> | ||||||
|  | .abs-module { | ||||||
|  |   position: fixed; | ||||||
|  |   width: 300px; | ||||||
|  |   height: 163.63px; | ||||||
|  |   right: 20px; | ||||||
|  |   top: 20px; | ||||||
|  |   border-radius: 5px; | ||||||
|  |   z-index: 9999; | ||||||
|  |   overflow: hidden; | ||||||
|  |   transition: all 2s; | ||||||
|  |   animation: absfade 1000ms infinite; | ||||||
|  | 
 | ||||||
|  |   .abs-box { | ||||||
|  |     width: 100%; | ||||||
|  |     height: 100%; | ||||||
|  |     position: relative; | ||||||
|  | 
 | ||||||
|  |     i { | ||||||
|  |       position: absolute; | ||||||
|  |       right: 10px; | ||||||
|  |       top: 10px; | ||||||
|  |       color: white; | ||||||
|  |       cursor: pointer; | ||||||
|  |       font-size: 22px; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @keyframes absfade { | ||||||
|  |   from { | ||||||
|  |     transform: scale(1); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   50% { | ||||||
|  |     transform: scale(1.02); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   to { | ||||||
|  |     transform: scale(1); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,246 @@ | ||||||
|  | <template> | ||||||
|  |   <div class="lum-dialog-mask"> | ||||||
|  |     <el-container class="lum-dialog-box"> | ||||||
|  |       <el-header class="header" height="50px"> | ||||||
|  |         <p>选择头像</p> | ||||||
|  |         <p class="tools"> | ||||||
|  |           <i class="el-icon-close" @click="$emit('close', 0)" /> | ||||||
|  |         </p> | ||||||
|  |       </el-header> | ||||||
|  |       <el-main class="main"> | ||||||
|  |         <el-container class="full-height"> | ||||||
|  |           <el-aside width="400px"> | ||||||
|  |             <div class="cropper-box"> | ||||||
|  |               <vue-cropper | ||||||
|  |                 ref="cropper" | ||||||
|  |                 mode="cover" | ||||||
|  |                 :img="option.img" | ||||||
|  |                 :output-size="option.size" | ||||||
|  |                 :output-type="option.outputType" | ||||||
|  |                 :info="true" | ||||||
|  |                 :full="option.full" | ||||||
|  |                 :fixed="fixed" | ||||||
|  |                 :fixed-number="fixedNumber" | ||||||
|  |                 :can-move="option.canMove" | ||||||
|  |                 :can-move-box="option.canMoveBox" | ||||||
|  |                 :fixed-box="option.fixedBox" | ||||||
|  |                 :original="option.original" | ||||||
|  |                 :auto-crop="option.autoCrop" | ||||||
|  |                 :auto-crop-width="option.autoCropWidth" | ||||||
|  |                 :auto-crop-height="option.autoCropHeight" | ||||||
|  |                 :center-box="option.centerBox" | ||||||
|  |                 :high="option.high" | ||||||
|  |                 @real-time="realTime" | ||||||
|  |               /> | ||||||
|  |               <input | ||||||
|  |                 type="file" | ||||||
|  |                 id="uploads" | ||||||
|  |                 ref="fileInput" | ||||||
|  |                 accept="image/png, image/jpeg, image/jpg" | ||||||
|  |                 style="display: none" | ||||||
|  |                 @change="uploadImg($event, 1)" | ||||||
|  |               /> | ||||||
|  |             </div> | ||||||
|  |             <div class="tools tools-flex"> | ||||||
|  |               <el-button | ||||||
|  |                 size="small" | ||||||
|  |                 plain | ||||||
|  |                 icon="el-icon-upload" | ||||||
|  |                 @click="clickUpload" | ||||||
|  |                 >上传图片 | ||||||
|  |               </el-button> | ||||||
|  |               <el-button | ||||||
|  |                 size="small" | ||||||
|  |                 plain | ||||||
|  |                 icon="el-icon-refresh" | ||||||
|  |                 @click="refreshCrop" | ||||||
|  |                 >刷新 | ||||||
|  |               </el-button> | ||||||
|  |               <el-button | ||||||
|  |                 size="small" | ||||||
|  |                 plain | ||||||
|  |                 icon="el-icon-refresh-left" | ||||||
|  |                 @click="rotateLeft" | ||||||
|  |                 >左转 | ||||||
|  |               </el-button> | ||||||
|  |               <el-button | ||||||
|  |                 size="small" | ||||||
|  |                 plain | ||||||
|  |                 icon="el-icon-refresh-right" | ||||||
|  |                 @click="rotateRight" | ||||||
|  |                 >右转 | ||||||
|  |               </el-button> | ||||||
|  |             </div> | ||||||
|  |           </el-aside> | ||||||
|  |           <el-main class="no-padding"> | ||||||
|  |             <div class="cropper-box"> | ||||||
|  |               <div class="preview-img"> | ||||||
|  |                 <img v-if="cusPreviewsImg" :src="cusPreviewsImg" /> | ||||||
|  |               </div> | ||||||
|  |             </div> | ||||||
|  |             <div class="tools"> | ||||||
|  |               <el-button type="primary" size="small" @click="uploadService"> | ||||||
|  |                 保存图片 | ||||||
|  |               </el-button> | ||||||
|  |             </div> | ||||||
|  |           </el-main> | ||||||
|  |         </el-container> | ||||||
|  |       </el-main> | ||||||
|  |     </el-container> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | <script> | ||||||
|  | import { VueCropper } from 'vue-cropper' | ||||||
|  | import { ServeUploadFileStream } from '@/api/upload' | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   name: 'AvatarCropper', | ||||||
|  |   components: { | ||||||
|  |     VueCropper, | ||||||
|  |   }, | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       cusPreviewsImg: '', | ||||||
|  |       previews: {}, | ||||||
|  |       option: { | ||||||
|  |         img: '', | ||||||
|  |         size: 1, | ||||||
|  |         full: false, | ||||||
|  |         outputType: 'png', | ||||||
|  |         canMove: true, | ||||||
|  |         fixedBox: true, | ||||||
|  |         original: false, | ||||||
|  |         canMoveBox: true, | ||||||
|  |         autoCrop: true, | ||||||
|  |         // 只有自动截图开启 宽度高度才生效 | ||||||
|  |         autoCropWidth: 200, | ||||||
|  |         autoCropHeight: 150, | ||||||
|  |         centerBox: false, | ||||||
|  |         high: true, | ||||||
|  |       }, | ||||||
|  |       fixed: true, | ||||||
|  |       fixedNumber: [1, 1], | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     clickUpload() { | ||||||
|  |       this.$refs.fileInput.click() | ||||||
|  |     }, | ||||||
|  |     clearCrop() { | ||||||
|  |       if (!this.cusPreviewsImg) return false | ||||||
|  |       this.$refs.cropper.clearCrop() | ||||||
|  |     }, | ||||||
|  |     refreshCrop() { | ||||||
|  |       if (!this.cusPreviewsImg) return false | ||||||
|  |       this.$refs.cropper.refresh() | ||||||
|  |     }, | ||||||
|  |     rotateLeft() { | ||||||
|  |       if (!this.cusPreviewsImg) return false | ||||||
|  |       this.$refs.cropper.rotateLeft() | ||||||
|  |     }, | ||||||
|  |     rotateRight() { | ||||||
|  |       if (!this.cusPreviewsImg) return false | ||||||
|  |       this.$refs.cropper.rotateRight() | ||||||
|  |     }, | ||||||
|  |     // 实时预览函数 | ||||||
|  |     realTime() { | ||||||
|  |       this.$refs.cropper.getCropData(img => { | ||||||
|  |         this.cusPreviewsImg = img | ||||||
|  |       }) | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     // 上传回调事件 | ||||||
|  |     uploadImg(e, num) { | ||||||
|  |       let file = e.target.files[0] | ||||||
|  |       if (!/\.(gif|jpg|jpeg|png|bmp|GIF|JPG|PNG)$/.test(e.target.value)) { | ||||||
|  |         this.$message('图片类型必须是.gif,jpeg,jpg,png,bmp中的一种') | ||||||
|  |         return false | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       let reader = new FileReader() | ||||||
|  |       reader.onload = e => { | ||||||
|  |         let data | ||||||
|  |         if (typeof e.target.result === 'object') { | ||||||
|  |           // 把Array Buffer转化为blob 如果是base64不需要 | ||||||
|  |           data = window.URL.createObjectURL(new Blob([e.target.result])) | ||||||
|  |         } else { | ||||||
|  |           data = e.target.result | ||||||
|  |         } | ||||||
|  |         if (num === 1) { | ||||||
|  |           this.option.img = data | ||||||
|  |         } else if (num === 2) { | ||||||
|  |           this.example2.img = data | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       // 转化为base64 | ||||||
|  |       // reader.readAsDataURL(file) | ||||||
|  |       // 转化为blob | ||||||
|  |       reader.readAsArrayBuffer(file) | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     // 上传图片到服务器 | ||||||
|  |     uploadService() { | ||||||
|  |       if (this.cusPreviewsImg == '') return | ||||||
|  |       ServeUploadFileStream({ | ||||||
|  |         fileStream: this.cusPreviewsImg, | ||||||
|  |       }) | ||||||
|  |         .then(res => { | ||||||
|  |           if (res.code == 200) { | ||||||
|  |             this.$emit('close', 1, res.data.avatar) | ||||||
|  |           } else { | ||||||
|  |             this.$message('文件上传失败,请稍后再试...') | ||||||
|  |           } | ||||||
|  |         }) | ||||||
|  |         .catch(() => { | ||||||
|  |           this.$message('文件上传失败,请稍后再试...') | ||||||
|  |         }) | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style lang="less" scoped> | ||||||
|  | .lum-dialog-box { | ||||||
|  |   height: 550px; | ||||||
|  |   max-width: 800px; | ||||||
|  | 
 | ||||||
|  |   .main { | ||||||
|  |     .cropper-box { | ||||||
|  |       height: 400px; | ||||||
|  |       display: flex; | ||||||
|  |       justify-content: center; | ||||||
|  |       align-items: center; | ||||||
|  | 
 | ||||||
|  |       .preview-img { | ||||||
|  |         width: 180px; | ||||||
|  |         height: 180px; | ||||||
|  |         border-radius: 50%; | ||||||
|  |         overflow: hidden; | ||||||
|  |         box-shadow: 0 0 4px #ccc; | ||||||
|  | 
 | ||||||
|  |         img { | ||||||
|  |           width: 100%; | ||||||
|  |           height: 100%; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .tools { | ||||||
|  |       height: 40px; | ||||||
|  |       margin-top: 20px; | ||||||
|  |       text-align: center; | ||||||
|  | 
 | ||||||
|  |       button { | ||||||
|  |         border-radius: 1px; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .tools-flex { | ||||||
|  |       display: flex; | ||||||
|  |       flex-direction: row; | ||||||
|  |       justify-content: space-between; | ||||||
|  |       align-items: center; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,129 @@ | ||||||
|  | <template> | ||||||
|  |   <div> | ||||||
|  |     <div class="reward" v-show="isShow"> | ||||||
|  |       <div class="title"> | ||||||
|  |         <span>Donate</span> | ||||||
|  |         <i class="el-icon-circle-close" @click="close" /> | ||||||
|  |       </div> | ||||||
|  |       <div class="main"> | ||||||
|  |         <div class="pay-box"> | ||||||
|  |           <img | ||||||
|  |             src="https://cdn.learnku.com/uploads/images/202101/30/46424/PPYHOUhCb4.jpg" | ||||||
|  |           /> | ||||||
|  |           <p>支付宝</p> | ||||||
|  |         </div> | ||||||
|  |         <div class="pay-box"> | ||||||
|  |           <img | ||||||
|  |             src="https://cdn.learnku.com/uploads/images/202101/30/46424/XLmCJjbvlQ.png" | ||||||
|  |           /> | ||||||
|  |           <p>微信</p> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |       <div class="footer"> | ||||||
|  |         开源不易,如果你觉得项目对你有帮助,可以请作者喝杯咖啡☕️!鼓励下... | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | <script> | ||||||
|  | export default { | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       isShow: false, | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   created() { | ||||||
|  |     if (this.getNum() <= 3) { | ||||||
|  |       setTimeout(() => { | ||||||
|  |         this.isShow = true | ||||||
|  |       }, 1000 * 30) | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     getNum() { | ||||||
|  |       return parseInt(localStorage.getItem('REWARD_BOX')) || 0 | ||||||
|  |     }, | ||||||
|  |     close() { | ||||||
|  |       localStorage.setItem('REWARD_BOX', this.getNum() + 1) | ||||||
|  |       this.isShow = false | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | <style lang="less" scoped> | ||||||
|  | .reward { | ||||||
|  |   position: fixed; | ||||||
|  |   width: 550px; | ||||||
|  |   height: 400px; | ||||||
|  |   right: 20px; | ||||||
|  |   bottom: 20px; | ||||||
|  |   border-radius: 5px; | ||||||
|  |   box-shadow: 0 0 12px #ccc; | ||||||
|  |   border: 1px solid rgb(228, 225, 225); | ||||||
|  |   box-sizing: border-box; | ||||||
|  |   overflow: hidden; | ||||||
|  |   user-select: none; | ||||||
|  |   z-index: 9999; | ||||||
|  |   background: white; | ||||||
|  | 
 | ||||||
|  |   .title { | ||||||
|  |     height: 50px; | ||||||
|  |     line-height: 50px; | ||||||
|  |     padding-left: 20px; | ||||||
|  |     width: 100%; | ||||||
|  |     font-size: 16px; | ||||||
|  |     background: #f9f7f7; | ||||||
|  |     position: relative; | ||||||
|  |     box-sizing: border-box; | ||||||
|  | 
 | ||||||
|  |     i { | ||||||
|  |       position: absolute; | ||||||
|  |       right: 15px; | ||||||
|  |       top: 18px; | ||||||
|  |       font-size: 18px; | ||||||
|  |       cursor: pointer; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .main { | ||||||
|  |     height: 300px; | ||||||
|  |     display: flex; | ||||||
|  |     align-items: center; | ||||||
|  |     justify-content: center; | ||||||
|  |     overflow: hidden; | ||||||
|  | 
 | ||||||
|  |     .pay-box { | ||||||
|  |       width: 200px; | ||||||
|  |       height: 240px; | ||||||
|  |       background: #1977ff; | ||||||
|  |       margin: 0 10px; | ||||||
|  |       display: flex; | ||||||
|  |       flex-direction: column; | ||||||
|  |       justify-content: center; | ||||||
|  |       align-items: center; | ||||||
|  |       border-radius: 5px; | ||||||
|  | 
 | ||||||
|  |       img { | ||||||
|  |         width: 150px; | ||||||
|  |         height: 150px; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       p { | ||||||
|  |         margin-top: 20px; | ||||||
|  |         color: white; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       &:last-child { | ||||||
|  |         background: #22ab38; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .footer { | ||||||
|  |     height: 50px; | ||||||
|  |     line-height: 50px; | ||||||
|  |     text-align: center; | ||||||
|  |     font-size: 13px; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,38 @@ | ||||||
|  | <template> | ||||||
|  |   <div></div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | // 待开发 | ||||||
|  | export default { | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       skins: [ | ||||||
|  |         { | ||||||
|  |           theme: '', | ||||||
|  |           class: 'default', | ||||||
|  |           text: '默认', | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           theme: '', | ||||||
|  |           class: 'red', | ||||||
|  |           text: '红色', | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           theme: '', | ||||||
|  |           class: 'dark', | ||||||
|  |           text: '暗黑', | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           theme: '', | ||||||
|  |           class: 'blue', | ||||||
|  |           text: '浅蓝', | ||||||
|  |         }, | ||||||
|  |       ], | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   created() {}, | ||||||
|  |   methods: {}, | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | <style lang="less" scoped></style> | ||||||
|  | @ -0,0 +1,44 @@ | ||||||
|  | <template> | ||||||
|  |   <div class="welcome-box"> | ||||||
|  |     <div class="famous-box"> | ||||||
|  |       <img src="~@/assets/image/chat.png" width="300" /> | ||||||
|  |       | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | export default { | ||||||
|  |   components: {}, | ||||||
|  |   data() { | ||||||
|  |     return {} | ||||||
|  |   }, | ||||||
|  |   created() {}, | ||||||
|  |   methods: {}, | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | <style lang="less" scoped> | ||||||
|  | .welcome-box { | ||||||
|  |   height: 100%; | ||||||
|  |   width: 100%; | ||||||
|  | 
 | ||||||
|  |   .famous-box { | ||||||
|  |     display: flex; | ||||||
|  |     justify-content: center; | ||||||
|  |     align-items: center; | ||||||
|  |     flex-direction: column; | ||||||
|  |     height: 100%; | ||||||
|  |     font-size: 24px; | ||||||
|  |     user-select: none; | ||||||
|  | 
 | ||||||
|  |     p { | ||||||
|  |       width: 100%; | ||||||
|  |       font-weight: 300; | ||||||
|  |       text-align: center; | ||||||
|  |       font-size: 15px; | ||||||
|  |       color: #b9b4b4; | ||||||
|  |       margin-top: -30px; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,103 @@ | ||||||
|  | <template> | ||||||
|  |   <!-- 新消息提示组件 --> | ||||||
|  |   <div class="notify-box"> | ||||||
|  |     <div class="lbox"> | ||||||
|  |       <el-avatar size="medium" :src="avatar" /> | ||||||
|  |     </div> | ||||||
|  |     <div class="rbox"> | ||||||
|  |       <div class="xheader"> | ||||||
|  |         <p class="title">好友申请消息</p> | ||||||
|  |         <p class="time">{{ datetime }}</p> | ||||||
|  |       </div> | ||||||
|  |       <div class="xbody"> | ||||||
|  |         <h4>申请备注:</h4> | ||||||
|  |         <div>{{ content }}</div> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | export default { | ||||||
|  |   components: {}, | ||||||
|  |   props: { | ||||||
|  |     params: { | ||||||
|  |       type: Object, | ||||||
|  |       default() {}, | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       avatar: | ||||||
|  |         'https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png', | ||||||
|  |       talk_type: 1, | ||||||
|  |       nickname: '阿萨纳斯卡', | ||||||
|  |       content: '阿斯纳俺家你卡萨啊看看番按实际开发n', | ||||||
|  |       datetime: '2021-06-18 23:15:12', | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   computed: {}, | ||||||
|  |   methods: {}, | ||||||
|  |   created() {}, | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | <style lang="less" scoped> | ||||||
|  | .notify-box { | ||||||
|  |   width: 300px; | ||||||
|  |   min-height: 100px; | ||||||
|  |   // background: rebeccapurple; | ||||||
|  |   display: flex; | ||||||
|  |   box-sizing: border-box; | ||||||
|  |   padding: 5px; | ||||||
|  | 
 | ||||||
|  |   .lbox { | ||||||
|  |     flex-basis: 50px; | ||||||
|  |     flex-shrink: 1; | ||||||
|  |     display: flex; | ||||||
|  |     justify-content: center; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .rbox { | ||||||
|  |     flex: 1 auto; | ||||||
|  |     margin-left: 5px; | ||||||
|  | 
 | ||||||
|  |     .xheader { | ||||||
|  |       height: 35px; | ||||||
|  |       width: 100%; | ||||||
|  |       display: flex; | ||||||
|  |       flex-direction: row; | ||||||
|  |       justify-content: space-between; | ||||||
|  |       align-items: center; | ||||||
|  | 
 | ||||||
|  |       .title { | ||||||
|  |         font-size: 13px; | ||||||
|  |         font-weight: 500; | ||||||
|  |       } | ||||||
|  |       .time { | ||||||
|  |         font-size: 12px; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .xbody { | ||||||
|  |       min-height: 60px; | ||||||
|  |       width: 100%; | ||||||
|  | 
 | ||||||
|  |       h4 { | ||||||
|  |         font-size: 13px; | ||||||
|  |         font-weight: 400; | ||||||
|  |         margin-bottom: 3px; | ||||||
|  |       } | ||||||
|  |       div { | ||||||
|  |         display: -webkit-box; | ||||||
|  |         -webkit-box-orient: vertical; | ||||||
|  |         -webkit-line-clamp: 3; | ||||||
|  |         overflow: hidden; | ||||||
|  |         background: #f3f5f7; | ||||||
|  |         font-size: 13px; | ||||||
|  |         padding: 5px; | ||||||
|  |         border-radius: 5px; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,122 @@ | ||||||
|  | <template> | ||||||
|  |   <!-- 新消息提示组件 --> | ||||||
|  |   <div class="notify-box pointer"> | ||||||
|  |     <div class="lbox"> | ||||||
|  |       <el-avatar size="medium" shape="square" :src="avatar" /> | ||||||
|  |     </div> | ||||||
|  |     <div class="rbox"> | ||||||
|  |       <div class="xheader"> | ||||||
|  |         <p class="title"> | ||||||
|  |           {{ talk_type == 1 ? '私信消息通知' : '群聊消息通知' }} | ||||||
|  |         </p> | ||||||
|  |         <p class="time"><i class="el-icon-time" /> {{ datetime | format }}</p> | ||||||
|  |       </div> | ||||||
|  |       <div class="xbody"> | ||||||
|  |         <p>@{{ nickname }}</p> | ||||||
|  |         <div>{{ content }}</div> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | import { parseTime } from '@/utils/functions' | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   components: {}, | ||||||
|  |   props: { | ||||||
|  |     avatar: { | ||||||
|  |       type: String, | ||||||
|  |       default: | ||||||
|  |         'https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png', | ||||||
|  |     }, | ||||||
|  |     talk_type: { | ||||||
|  |       type: Number, | ||||||
|  |       default: 1, | ||||||
|  |     }, | ||||||
|  |     nickname: { | ||||||
|  |       type: String, | ||||||
|  |       default: '', | ||||||
|  |     }, | ||||||
|  |     content: { | ||||||
|  |       type: String, | ||||||
|  |       default: '', | ||||||
|  |     }, | ||||||
|  |     datetime: { | ||||||
|  |       type: String, | ||||||
|  |       default: '', | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   data() { | ||||||
|  |     return {} | ||||||
|  |   }, | ||||||
|  |   filters: { | ||||||
|  |     format(datetime) { | ||||||
|  |       datetime = datetime || new Date() | ||||||
|  |       return parseTime(datetime, '{m}/{d} {h}:{i} 分') | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | <style lang="less" scoped> | ||||||
|  | .notify-box { | ||||||
|  |   width: 300px; | ||||||
|  |   min-height: 100px; | ||||||
|  |   display: flex; | ||||||
|  |   box-sizing: border-box; | ||||||
|  |   padding: 5px; | ||||||
|  | 
 | ||||||
|  |   .lbox { | ||||||
|  |     flex-basis: 50px; | ||||||
|  |     flex-shrink: 1; | ||||||
|  |     display: flex; | ||||||
|  |     justify-content: center; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .rbox { | ||||||
|  |     flex: 1 auto; | ||||||
|  |     margin-left: 5px; | ||||||
|  | 
 | ||||||
|  |     .xheader { | ||||||
|  |       height: 25px; | ||||||
|  |       width: 100%; | ||||||
|  |       display: flex; | ||||||
|  |       flex-direction: row; | ||||||
|  |       justify-content: space-between; | ||||||
|  |       align-items: center; | ||||||
|  | 
 | ||||||
|  |       .title { | ||||||
|  |         font-size: 13px; | ||||||
|  |         font-weight: 500; | ||||||
|  |       } | ||||||
|  |       .time { | ||||||
|  |         font-size: 12px; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .xbody { | ||||||
|  |       min-height: 60px; | ||||||
|  |       width: 100%; | ||||||
|  |       margin-top: 5px; | ||||||
|  | 
 | ||||||
|  |       p { | ||||||
|  |         font-size: 13px; | ||||||
|  |         font-weight: 400; | ||||||
|  |         color: #fb4208; | ||||||
|  |         margin-bottom: 4px; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       div { | ||||||
|  |         display: -webkit-box; | ||||||
|  |         -webkit-box-orient: vertical; | ||||||
|  |         -webkit-line-clamp: 3; | ||||||
|  |         overflow: hidden; | ||||||
|  |         background: #f3f5f7; | ||||||
|  |         font-size: 13px; | ||||||
|  |         padding: 5px; | ||||||
|  |         border-radius: 5px; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,38 @@ | ||||||
|  | <template> | ||||||
|  |   <svg :class="svgClass" aria-hidden="true" v-on="$listeners"> | ||||||
|  |     <use :xlink:href="iconName" /> | ||||||
|  |   </svg> | ||||||
|  | </template> | ||||||
|  | <script> | ||||||
|  | export default { | ||||||
|  |   name: 'svg-icon', | ||||||
|  |   props: { | ||||||
|  |     iconClass: { | ||||||
|  |       type: String, | ||||||
|  |       required: true, | ||||||
|  |     }, | ||||||
|  |     className: { | ||||||
|  |       type: String, | ||||||
|  |       default: '', | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   computed: { | ||||||
|  |     iconName() { | ||||||
|  |       return `#icon-${this.iconClass}` | ||||||
|  |     }, | ||||||
|  |     svgClass() { | ||||||
|  |       if (this.className) { | ||||||
|  |         return 'svg-icon ' + this.className | ||||||
|  |       } else { | ||||||
|  |         return 'svg-icon' | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | <style scoped> | ||||||
|  | .svg-icon { | ||||||
|  |   fill: currentColor; | ||||||
|  |   overflow: hidden; | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,119 @@ | ||||||
|  | <template> | ||||||
|  |   <div> | ||||||
|  |     <div class="lum-dialog-mask" v-show="isShow"> | ||||||
|  |       <el-container class="lum-dialog-box" v-outside="close"> | ||||||
|  |         <el-header class="header" height="50px"> | ||||||
|  |           <p>添加好友</p> | ||||||
|  |           <p class="tools"> | ||||||
|  |             <i class="el-icon-close" @click="close" /> | ||||||
|  |           </p> | ||||||
|  |         </el-header> | ||||||
|  |         <el-main class="main"> | ||||||
|  |           <el-input | ||||||
|  |             v-model="mobile" | ||||||
|  |             id="serach-mobile" | ||||||
|  |             class="input" | ||||||
|  |             prefix-icon="el-icon-search" | ||||||
|  |             placeholder="请输入对方手机号(精确查找)" | ||||||
|  |             clearable | ||||||
|  |             @keyup.enter.native="onSubmit" | ||||||
|  |             @input="error = false" | ||||||
|  |           /> | ||||||
|  |           <p v-show="error" class="error"> | ||||||
|  |             无法找到该用户,请检查搜索内容并重试 | ||||||
|  |           </p> | ||||||
|  |           <el-button | ||||||
|  |             type="primary" | ||||||
|  |             size="small" | ||||||
|  |             :loading="loading" | ||||||
|  |             @click="onSubmit" | ||||||
|  |             >立即查找 | ||||||
|  |           </el-button> | ||||||
|  |         </el-main> | ||||||
|  |       </el-container> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | <script> | ||||||
|  | import { ServeSearchContact } from '@/api/contacts' | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   name: 'UserSearch', | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       loading: false, | ||||||
|  |       isShow: false, | ||||||
|  |       mobile: '', | ||||||
|  |       error: false, | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     // 显示窗口 | ||||||
|  |     open() { | ||||||
|  |       this.mobile = '' | ||||||
|  |       this.isShow = true | ||||||
|  |       this.$nextTick(() => { | ||||||
|  |         document.getElementById('serach-mobile').focus() | ||||||
|  |       }) | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     // 关闭窗口 | ||||||
|  |     close() { | ||||||
|  |       this.isShow = false | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     onSubmit() { | ||||||
|  |       let { mobile } = this | ||||||
|  | 
 | ||||||
|  |       if (mobile == '') return false | ||||||
|  | 
 | ||||||
|  |       this.loading = true | ||||||
|  |       ServeSearchContact({ | ||||||
|  |         mobile, | ||||||
|  |       }) | ||||||
|  |         .then(res => { | ||||||
|  |           if (res.code == 200) { | ||||||
|  |             this.$user(res.data.id) | ||||||
|  |             this.close() | ||||||
|  |           } else { | ||||||
|  |             this.error = true | ||||||
|  |           } | ||||||
|  |         }) | ||||||
|  |         .finally(() => { | ||||||
|  |           this.loading = false | ||||||
|  |         }) | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | <style lang="less" scoped> | ||||||
|  | .lum-dialog-box { | ||||||
|  |   width: 450px; | ||||||
|  |   max-width: 450px; | ||||||
|  |   height: 250px; | ||||||
|  | 
 | ||||||
|  |   .main { | ||||||
|  |     display: flex; | ||||||
|  |     justify-content: center; | ||||||
|  |     align-items: center; | ||||||
|  |     flex-direction: column; | ||||||
|  | 
 | ||||||
|  |     .input { | ||||||
|  |       width: 85%; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .error { | ||||||
|  |       width: 85%; | ||||||
|  |       color: red; | ||||||
|  |       font-size: 12px; | ||||||
|  |       height: 50px; | ||||||
|  |       line-height: 50px; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     button { | ||||||
|  |       margin-top: 20px; | ||||||
|  |       width: 100px; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,483 @@ | ||||||
|  | <template> | ||||||
|  |   <div class="lum-dialog-mask animated fadeIn"> | ||||||
|  |     <el-container class="container" v-outside="close"> | ||||||
|  |       <el-header class="no-padding header" height="180px"> | ||||||
|  |         <i class="close el-icon-error pointer" @click="close" /> | ||||||
|  |         <div class="img-banner"> | ||||||
|  |           <img :src="detail.bag" class="img-banner" /> | ||||||
|  |         </div> | ||||||
|  |         <div class="user-header"> | ||||||
|  |           <div class="avatar"> | ||||||
|  |             <div class="avatar-box"> | ||||||
|  |               <img :src="detail.avatar" :onerror="$store.state.defaultAvatar" /> | ||||||
|  |             </div> | ||||||
|  |           </div> | ||||||
|  |           <div class="nickname"> | ||||||
|  |             <i class="iconfont icon-qianming" /> | ||||||
|  |             <span>{{ detail.nickname || "未设置昵称" }}</span> | ||||||
|  |             <div class="share no-select"> | ||||||
|  |               <i class="iconfont icon-fenxiang3" /> <span>分享</span> | ||||||
|  |             </div> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |       </el-header> | ||||||
|  |       <el-main class="no-padding main"> | ||||||
|  |         <div class="user-sign"> | ||||||
|  |           <div class="sign-arrow"></div> | ||||||
|  |           <i class="iconfont icon-bianji" /> | ||||||
|  |           <span>编辑个签,展示我的独特态度。 </span> | ||||||
|  |         </div> | ||||||
|  | 
 | ||||||
|  |         <div class="card-rows no-select"> | ||||||
|  |           <div class="card-row"> | ||||||
|  |             <label>手机</label> | ||||||
|  |             <span>{{ detail.mobile | mobile }}</span> | ||||||
|  |           </div> | ||||||
|  |           <div class="card-row"> | ||||||
|  |             <label>昵称</label> | ||||||
|  |             <span>{{ detail.nickname || "未设置昵称" }}</span> | ||||||
|  |           </div> | ||||||
|  |           <div class="card-row"> | ||||||
|  |             <label>性别</label> | ||||||
|  |             <span>{{ detail.gender | gender }}</span> | ||||||
|  |           </div> | ||||||
|  |           <div v-show="detail.friend_status == 2" class="card-row"> | ||||||
|  |             <label>备注</label> | ||||||
|  |             <span v-if="editRemark.isShow == false">{{ | ||||||
|  |               detail.nickname_remark ? detail.nickname_remark : "暂无备注" | ||||||
|  |             }}</span> | ||||||
|  |             <span v-else> | ||||||
|  |               <input | ||||||
|  |                 v-model="editRemark.text" | ||||||
|  |                 v-focus | ||||||
|  |                 class="friend-remark" | ||||||
|  |                 type="text" | ||||||
|  |                 @keyup.enter="editRemarkSubmit" | ||||||
|  |               /> | ||||||
|  |             </span> | ||||||
|  |             <i | ||||||
|  |               v-show="!editRemark.isShow" | ||||||
|  |               class="el-icon-edit-outline" | ||||||
|  |               @click="clickEditRemark" | ||||||
|  |             /> | ||||||
|  |           </div> | ||||||
|  |           <div class="card-row"> | ||||||
|  |             <label>邮箱</label> | ||||||
|  |             <span>未设置</span> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |       </el-main> | ||||||
|  |       <el-footer | ||||||
|  |         v-show="detail.friend_status !== 0" | ||||||
|  |         class="no-padding footer" | ||||||
|  |         height="50px" | ||||||
|  |       > | ||||||
|  |         <el-button | ||||||
|  |           v-if="detail.friend_status == 1 && detail.friend_apply == 0" | ||||||
|  |           type="primary" | ||||||
|  |           size="small" | ||||||
|  |           icon="el-icon-circle-plus-outline" | ||||||
|  |           @click="apply.isShow = true" | ||||||
|  |           >添加好友 | ||||||
|  |         </el-button> | ||||||
|  |         <el-button | ||||||
|  |           v-else-if="detail.friend_apply == 1" | ||||||
|  |           type="primary" | ||||||
|  |           size="small" | ||||||
|  |           >已发送好友申请,请耐心等待... | ||||||
|  |         </el-button> | ||||||
|  |         <el-button | ||||||
|  |           v-else-if="detail.friend_status == 2" | ||||||
|  |           type="primary" | ||||||
|  |           size="small" | ||||||
|  |           icon="el-icon-s-promotion" | ||||||
|  |           @click="sendMessage(detail)" | ||||||
|  |           >发消息 | ||||||
|  |         </el-button> | ||||||
|  |       </el-footer> | ||||||
|  | 
 | ||||||
|  |       <!-- 添加好友申请表单 --> | ||||||
|  |       <div | ||||||
|  |         v-outside="closeApply" | ||||||
|  |         class="friend-from" | ||||||
|  |         :class="{ 'friend-from-show': apply.isShow }" | ||||||
|  |       > | ||||||
|  |         <p> | ||||||
|  |           <span>请填写好友申请备注:</span> | ||||||
|  |           <span @click="closeApply">取消</span> | ||||||
|  |         </p> | ||||||
|  |         <div> | ||||||
|  |           <input | ||||||
|  |             v-model="apply.text" | ||||||
|  |             type="text" | ||||||
|  |             placeholder="(必填项)" | ||||||
|  |             @keyup.enter="sendApply" | ||||||
|  |           /> | ||||||
|  |           <el-button type="primary" size="small" @click="sendApply"> | ||||||
|  |             立即提交 | ||||||
|  |           </el-button> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |     </el-container> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | <script> | ||||||
|  | // import { ServeSearchUser } from "@/api/user"; | ||||||
|  | import { ServeCreateContact, ServeEditContactRemark } from "@/api/contacts"; | ||||||
|  | import { toTalk } from "@/utils/talk"; | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   name: "UserCardDetail", | ||||||
|  |   props: { | ||||||
|  |     user_id: { | ||||||
|  |       type: Number, | ||||||
|  |       default: 0, | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   filters: { | ||||||
|  |     gender(value) { | ||||||
|  |       let arr = ["未知", "男", "女"]; | ||||||
|  |       return arr[value] || "未知"; | ||||||
|  |     }, | ||||||
|  |     // 手机号格式化 | ||||||
|  |     mobile(value) { | ||||||
|  |       return ( | ||||||
|  |         value.substr(0, 3) + " " + value.substr(3, 4) + " " + value.substr(7, 4) | ||||||
|  |       ); | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       detail: { | ||||||
|  |         mobile: "", | ||||||
|  |         nickname: "", | ||||||
|  |         avatar: "", | ||||||
|  |         motto: "", | ||||||
|  |         friend_status: 0, | ||||||
|  |         friend_apply: 0, | ||||||
|  |         nickname_remark: "", | ||||||
|  |         bag: require("@/assets/image/default-user-banner.png"), | ||||||
|  |         gender: 0, | ||||||
|  |       }, | ||||||
|  | 
 | ||||||
|  |       // 好友备注表单 | ||||||
|  |       editRemark: { | ||||||
|  |         isShow: false, | ||||||
|  |         text: "", | ||||||
|  |       }, | ||||||
|  | 
 | ||||||
|  |       // 好友申请表单 | ||||||
|  |       apply: { | ||||||
|  |         isShow: false, | ||||||
|  |         text: "", | ||||||
|  |       }, | ||||||
|  | 
 | ||||||
|  |       contacts: false, | ||||||
|  |     }; | ||||||
|  |   }, | ||||||
|  |   created() { | ||||||
|  |     // this.loadUserDetail(); | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     close() { | ||||||
|  |       if (this.contacts) return false; | ||||||
|  | 
 | ||||||
|  |       this.$emit("close"); | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     // 点击编辑备注信息 | ||||||
|  |     clickEditRemark() { | ||||||
|  |       this.editRemark.isShow = true; | ||||||
|  |       this.editRemark.text = this.detail.nickname_remark; | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     // 获取用户信息 | ||||||
|  |     // loadUserDetail() { | ||||||
|  |     //   ServeSearchUser({ | ||||||
|  |     //     user_id: this.user_id, | ||||||
|  |     //   }).then((res) => { | ||||||
|  |     //     if (res.code == 200) { | ||||||
|  |     //       this.detail.user_id = res.data.id; | ||||||
|  |     //       Object.assign(this.detail, res.data); | ||||||
|  |     //     } | ||||||
|  |     //   }); | ||||||
|  |     // }, | ||||||
|  | 
 | ||||||
|  |     // 发送添加好友申请 | ||||||
|  |     sendApply() { | ||||||
|  |       if (this.apply.text == "") return; | ||||||
|  |       ServeCreateContact({ | ||||||
|  |         friend_id: this.detail.user_id, | ||||||
|  |         remark: this.apply.text, | ||||||
|  |       }).then((res) => { | ||||||
|  |         if (res.code == 200) { | ||||||
|  |           this.apply.isShow = false; | ||||||
|  |           this.apply.text = ""; | ||||||
|  |           this.detail.friend_apply = 1; | ||||||
|  |         } else { | ||||||
|  |           this.$message.error("发送好友申请失败,请稍后再试..."); | ||||||
|  |         } | ||||||
|  |       }); | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     // 编辑好友备注信息 | ||||||
|  |     editRemarkSubmit() { | ||||||
|  |       let data = { | ||||||
|  |         friend_id: this.detail.user_id, | ||||||
|  |         remarks: this.editRemark.text, | ||||||
|  |       }; | ||||||
|  | 
 | ||||||
|  |       if (data.remarks == this.detail.nickname_remark) { | ||||||
|  |         this.editRemark.isShow = false; | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       ServeEditContactRemark(data).then((res) => { | ||||||
|  |         if (res.code == 200) { | ||||||
|  |           this.editRemark.isShow = false; | ||||||
|  |           this.detail.nickname_remark = data.remarks; | ||||||
|  |           this.$emit("changeRemark", data); | ||||||
|  |         } | ||||||
|  |       }); | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     // 隐藏申请表单 | ||||||
|  |     closeApply() { | ||||||
|  |       this.apply.isShow = false; | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     // 发送好友消息 | ||||||
|  |     sendMessage() { | ||||||
|  |       this.close(); | ||||||
|  |       toTalk(1, this.user_id); | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | }; | ||||||
|  | </script> | ||||||
|  | <style lang="less" scoped> | ||||||
|  | .container { | ||||||
|  |   position: absolute; | ||||||
|  |   left: 50%; | ||||||
|  |   top: 50%; | ||||||
|  |   transform: translate(-50%, -50%); | ||||||
|  |   background-color: white; | ||||||
|  |   width: 350px; | ||||||
|  |   height: 600px; | ||||||
|  |   overflow: hidden; | ||||||
|  |   border-radius: 3px; | ||||||
|  | 
 | ||||||
|  |   .header { | ||||||
|  |     position: relative; | ||||||
|  | 
 | ||||||
|  |     .close { | ||||||
|  |       position: absolute; | ||||||
|  |       right: 10px; | ||||||
|  |       top: 10px; | ||||||
|  |       color: white; | ||||||
|  |       transition: all 1s; | ||||||
|  |       z-index: 1; | ||||||
|  |       font-size: 20px; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .img-banner { | ||||||
|  |       width: 100%; | ||||||
|  |       height: 100%; | ||||||
|  |       background-image: url(~@/assets/image/default-user-banner.png); | ||||||
|  |       background-size: 100%; | ||||||
|  |       transition: all 0.2s linear; | ||||||
|  |       cursor: pointer; | ||||||
|  |       overflow: hidden; | ||||||
|  | 
 | ||||||
|  |       img:hover { | ||||||
|  |         -webkit-transform: scale(1.1); | ||||||
|  |         transform: scale(1.1); | ||||||
|  |         -webkit-filter: contrast(130%); | ||||||
|  |         filter: contrast(130%); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .main { | ||||||
|  |     background-color: white; | ||||||
|  |     padding: 45px 16px 0; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .footer { | ||||||
|  |     display: flex; | ||||||
|  |     justify-content: center; | ||||||
|  |     align-items: center; | ||||||
|  |     border-top: 1px solid #f5eeee; | ||||||
|  | 
 | ||||||
|  |     button { | ||||||
|  |       width: 90%; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .user-header { | ||||||
|  |   width: 100%; | ||||||
|  |   height: 80px; | ||||||
|  |   position: absolute; | ||||||
|  |   bottom: -40px; | ||||||
|  |   display: flex; | ||||||
|  |   flex-direction: row; | ||||||
|  | 
 | ||||||
|  |   .avatar { | ||||||
|  |     width: 100px; | ||||||
|  |     flex-shrink: 0; | ||||||
|  |     display: flex; | ||||||
|  |     justify-content: center; | ||||||
|  | 
 | ||||||
|  |     .avatar-box { | ||||||
|  |       width: 80px; | ||||||
|  |       height: 80px; | ||||||
|  |       background-color: white; | ||||||
|  |       border-radius: 50%; | ||||||
|  |       display: flex; | ||||||
|  |       justify-content: center; | ||||||
|  |       align-items: center; | ||||||
|  | 
 | ||||||
|  |       img { | ||||||
|  |         height: 70px; | ||||||
|  |         width: 70px; | ||||||
|  |         border-radius: 50%; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .nickname { | ||||||
|  |     flex: auto; | ||||||
|  |     padding-top: 50px; | ||||||
|  |     font-size: 16px; | ||||||
|  |     font-weight: 400; | ||||||
|  | 
 | ||||||
|  |     span { | ||||||
|  |       margin-left: 5px; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .share { | ||||||
|  |       display: inline-flex; | ||||||
|  |       width: 50px; | ||||||
|  |       height: 22px; | ||||||
|  |       background: #ff5722; | ||||||
|  |       color: white; | ||||||
|  |       align-items: center; | ||||||
|  |       justify-content: center; | ||||||
|  |       padding: 3px 8px; | ||||||
|  |       border-radius: 20px; | ||||||
|  |       transform: scale(0.7); | ||||||
|  |       cursor: pointer; | ||||||
|  |       i { | ||||||
|  |         margin-top: 2px; | ||||||
|  |       } | ||||||
|  |       span { | ||||||
|  |         font-size: 14px; | ||||||
|  |         margin-left: 4px; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .user-sign { | ||||||
|  |   min-height: 26px; | ||||||
|  |   border-radius: 5px; | ||||||
|  |   padding: 5px; | ||||||
|  |   line-height: 25px; | ||||||
|  |   background: #f3f5f7; | ||||||
|  |   color: #7d7d7d; | ||||||
|  |   font-size: 12px; | ||||||
|  |   margin-bottom: 20px; | ||||||
|  |   position: relative; | ||||||
|  |   display: -webkit-box; | ||||||
|  |   -webkit-box-orient: vertical; | ||||||
|  |   -webkit-line-clamp: 3; | ||||||
|  |   position: relative; | ||||||
|  | 
 | ||||||
|  |   .sign-arrow { | ||||||
|  |     position: absolute; | ||||||
|  |     width: 0; | ||||||
|  |     height: 0; | ||||||
|  |     font-size: 0; | ||||||
|  |     border: 5px solid hsla(0, 0%, 96.9%, 0); | ||||||
|  |     border-bottom-color: #f3f5f7; | ||||||
|  |     left: 28px; | ||||||
|  |     top: -9px; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .card-rows { | ||||||
|  |   .card-row { | ||||||
|  |     height: 35px; | ||||||
|  |     line-height: 35px; | ||||||
|  |     font-size: 14px; | ||||||
|  |     position: relative; | ||||||
|  |     cursor: pointer; | ||||||
|  |     color: #736f6f; | ||||||
|  | 
 | ||||||
|  |     label { | ||||||
|  |       margin-right: 25px; | ||||||
|  |       color: #cbc5c5; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .friend-remark { | ||||||
|  |       border-bottom: 1px dashed #bec3d0; | ||||||
|  |       padding-bottom: 2px; | ||||||
|  |       color: #736f6f; | ||||||
|  |       width: 60%; | ||||||
|  |       padding-right: 5px; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .el-icon-edit-outline { | ||||||
|  |       margin-left: 3px !important; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* 好友申请表单 */ | ||||||
|  | .friend-from { | ||||||
|  |   position: absolute; | ||||||
|  |   background: #fbf6f6; | ||||||
|  |   height: 80px; | ||||||
|  |   z-index: 2; | ||||||
|  |   width: 100%; | ||||||
|  |   bottom: -80px; | ||||||
|  |   left: 0; | ||||||
|  |   transition: all 0.5s ease-in-out; | ||||||
|  | 
 | ||||||
|  |   p { | ||||||
|  |     height: 20px; | ||||||
|  |     line-height: 20px; | ||||||
|  |     padding: 7px 5px 5px 15px; | ||||||
|  |     font-size: 13px; | ||||||
|  | 
 | ||||||
|  |     span { | ||||||
|  |       &:nth-child(2) { | ||||||
|  |         float: right; | ||||||
|  |         margin-right: 13px; | ||||||
|  |         color: #32caff; | ||||||
|  |         cursor: pointer; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   div { | ||||||
|  |     height: 31px; | ||||||
|  |     line-height: 20px; | ||||||
|  |     padding: 7px 5px 5px 15px; | ||||||
|  |     font-size: 13px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   input { | ||||||
|  |     height: 30px; | ||||||
|  |     line-height: 30px; | ||||||
|  |     width: 220px; | ||||||
|  |     border-radius: 3px; | ||||||
|  |     padding: 0 5px; | ||||||
|  |     margin-right: 5px; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .friend-from-show { | ||||||
|  |   bottom: 0; | ||||||
|  | } | ||||||
|  | </style> | ||||||