BL Data Query Helper

try to take over the world!

// ==UserScript==
// @name         BL Data Query Helper
// @namespace    http://tampermonkey.net/
// @version      0.1
// @include      http*://www.blushmark.com/*
// @description  try to take over the world!
// @author       You
// @match        http://*/*
// @grant        GM_addStyle
// ==/UserScript==

const willRequests = []
const onGoingRequests = []
const maxConcurrency = 5

const beginRequest = (func) => {
  if (func) {
    if (onGoingRequests.length < maxConcurrency) {
      onGoingRequests.push(func)
      func()
    } else {
      willRequests.push(func)
    }
  }
}
const stopRequest = () => {
  onGoingRequests.pop()
  if (willRequests.length > 0) {
    const func = willRequests.shift()
    onGoingRequests.push(func)
    func()
  }
}

// 强依赖网站的参数
// 1.cookie中的 hasLogin, login_token
// 2.商品div中的class goods-item
// 3.商品div中的class goods-item 的上级div 要有 data-goods-id data-style-id参数

const getQueryStringValue = (key) => {
  return decodeURIComponent(window.location.search.replace(new RegExp('^(?:.*[&\\?]' + encodeURIComponent(key).replace(/[\.\+\*]/g, '\\$&') + '(?:\\=([^&]*))?)?.*$', 'i'), '$1'))
}

const getHost = () => {
  const hostName = window.location.hostname
  let host = 'https://www.blushmark.com/prod'
  if (hostName.startsWith('ft') || hostName.startsWith('ft-x')) {
    host = `https://${hostName}/test`
  } else if (hostName.startsWith('p')) {
    host = `https://${hostName}/pre`
  }
  return host
}

const getUrl = (url, params = {}) => {
  let host = getHost()
  console.log('url====>', url)
  console.log('host====>', host)
  console.log('params====>', params)
  const keys = Object.keys(params)
  keys && keys.map((key) => {
    url = url + (url.includes('?') ? '&' : '?') + `${key}=${params[key]}`
  })
  return host + url
}

const GET = (path, params, callback) => {
  beginRequest(() => {
    const xhr = new XMLHttpRequest()
    const url = getUrl(path, params)
    console.log('url====>', url)
    xhr.open('GET', url, true)
    xhr.onreadystatechange = function() {
      if (xhr.readyState == 4) {
        const info = JSON.parse(xhr.responseText)
        if (info && info.code == 0) {
          callback(info.data)
        } else {
          callback(undefined)
        }
        stopRequest()
      }
    }
    xhr.send()
  })
}

const fetchData = (goodsId, styleId, callback) => {
  const isAllListPage = window.location.pathname.startsWith('/categories/0_0/')
  const isClearance = window.location.pathname.startsWith('/clearance')
  const isSortByNew = window.location.search.includes('sort_by=new')
  const params = {
    styleId: styleId,
    callback: goodsId,
    type: (isAllListPage ? getQueryStringValue('type') : ''),
    pageType: (isClearance ? 'clearance' : isSortByNew ? 'just_in' : 'normal')
  }
  GET('/1.0/plugin/ctr-detail', params, callback)
}

const indexGetData = (ec, el, list_page_path, callback) => {
  const params = {
    path: list_page_path,
    ec: ec,
    el: el
  }
  GET('/1.0/home/plugin-ctr', params, callback)
}

const productGetData = (goods_id, style_id, size_id, callback) => {
  const params = {
    goods_id: goods_id,
    style_id: style_id,
    size_id: size_id
  }
  GET('/1.0/home/getStockTransit', params, callback)
}

const HTTP = {
    fetchData,
    indexGetData,
    productGetData
}

const elementIdentifier = 'chrome-extension-info'
const elementProductIdentifer = 'chrome-extension-info-product'
const MOUSE_VISITED_CLASS_NAME = 'goods-item'
const MOUSE_VISITED_CLASS_NAME_INDEX = 'ctr-index'
const MOUSE_VISITED_CLASS_NAME_PRODUCT = 'ctr-product'

// 强依赖网站的参数
// 1.cookie中的 hasLogin, login_token
// 2.商品div中的class goods-item
// 3.商品div中的class goods-item 的上级div 要有 data-goods-id data-style-id参数
const createRowElement = (dataElement, text) => {
  const container = document.createElement('span')
  container.className = 'extension-container'
  const textElement = document.createTextNode(text)
  container.appendChild(textElement)
  dataElement.appendChild(container)
  return container
}

/**
 * 获取containerElement
 * @param element
 */
const getContainerElement = (element) => {
  let containerElement = null
  if (element && element.className.indexOf(MOUSE_VISITED_CLASS_NAME) >= 0) {
    // 商品在此获取数据
    const findGoodsImageElementFunc = (elements) => {
      elements && Array.from(elements).some(e => {
        if (e && e.classList && e.classList.value.indexOf('goods-item-pic') >= 0) {
          containerElement = e
          return true
        }
        findGoodsImageElementFunc(e.children)
      })
    }
    findGoodsImageElementFunc(element.children)
  } else {
    // 首页和详情页在此获取数据
    containerElement = element
  }
  return containerElement
}

const showListCover = (containerElement) => {
  if (!containerElement || !containerElement.parentElement) {
    return
  }
  // 商品获取方式
  const goodsId = containerElement.parentElement.getAttribute('data-goods-id')
  const styleId = containerElement.parentElement.getAttribute('data-style-id')
  const pageType = containerElement.parentElement.getAttribute('page-type')
  // 参数不合法 退出
  if (!goodsId || !styleId || !pageType) return

  const children = Array.from(containerElement.children)
  let isExist = false
  children && children.map(e => {
    if (e.id === elementIdentifier) {
      if (e.dataId === goodsId + '' + styleId) {
        isExist = true
      } else {
        // 存在一个其他商品的数据 移除
        e.parentNode.removeChild(e)
      }
    }
  })
  // 已经存在了 不再次展示
  if (isExist) return
  const dataElement = document.createElement('div')
  dataElement.id = elementIdentifier
  dataElement.dataId = goodsId + '' + styleId
  dataElement.style.zIndex = 100

  const ctrElement = createRowElement(dataElement, 'CTR: -%')
  let justInElement
  if (pageType == 'just_in') {
    justInElement = createRowElement(dataElement, 'BST_CTR: -')
  }
  const crElement = createRowElement(dataElement, 'CR: -%')
  const ctrCrElement = createRowElement(dataElement, pageType == 'just_in' ? 'BST_CTR*CR: -' : 'CTR*CR: -')
  const saleElement = createRowElement(dataElement, 'SAL: -')
  const averageElement = createRowElement(dataElement, 'AVE: -')
  const clickElement = createRowElement(dataElement, 'CLI: -')
  const impressionElement = createRowElement(dataElement, 'IMP: -')
  const userElement = createRowElement(dataElement, 'DET: -')
  const ostElement = createRowElement(dataElement, 'OST: -')
  const abElement = createRowElement(dataElement, '')
  abElement.style.display = 'none'
  containerElement && containerElement.appendChild(dataElement)
  HTTP.fetchData(goodsId, styleId, (data) => {
    if (data) {
      const { cr, ctr, sales, viewCount, clickCount, showNumber, onSaleTime, ab_test, ctrCr, goodsSales, bestSellerCtr, averageDailySales } = data
      ctrElement.textContent = 'CTR: ' + ctr
      crElement.textContent = 'CR: ' + cr
      saleElement.textContent = 'SAL: ' + sales + '(' + goodsSales + ')'
      clickElement.textContent = 'CLI: ' + clickCount
      impressionElement.textContent = 'IMP: ' + viewCount
      userElement.textContent = 'DET: ' + showNumber
      ostElement.textContent = 'OST: ' + onSaleTime
      ctrCrElement.textContent = (pageType == 'just_in' ? 'BST_CTR*CR:' : 'CTR*CR: ') + ctrCr
      averageElement.textContent = 'AVE: ' + averageDailySales
      if (ab_test) {
        abElement.textContent = 'A/B Test'
        abElement.style.display = 'block'
      }

      if (pageType == 'just_in') {
        if (bestSellerCtr == undefined) {
          justInElement.parentElement.removeChild(justInElement)
        } else { justInElement.textContent = 'BST_CTR:' + bestSellerCtr }
      }
    }
  })
}

const showHomeCover = (element, containerElement) => {
  if (!element || !containerElement) {
    return
  }
  const children = Array.from(containerElement.children)
  let isExist = false
  children && children.map(e => {
    if (e.id === elementIdentifier) {
      isExist = true
    }
  })
  // 已经存在了 不再次展示
  if (isExist) return
  const ec = element.getAttribute('ec')
  const el = element.getAttribute('el')
  const list_page_path = element.getAttribute('list_page_path')
  // 参数不合法
  if (!ec || !el || !list_page_path) return

  const dataElement = document.createElement('div')
  dataElement.id = elementIdentifier
  dataElement.style.zIndex = 100

  const ctrElement = createRowElement(dataElement, 'CTR: -%')
  const clickElement = createRowElement(dataElement, 'Click: -')
  const viewElement = createRowElement(dataElement, 'Impression: -')
  HTTP.indexGetData(ec, el, list_page_path, (data) => {
    if (data) {
      const { ctr, click_count, view_count } = data
      ctrElement.textContent = 'CTR: ' + (ctr || '0%')
      clickElement.textContent = 'Click: ' + (click_count || 0)
      viewElement.textContent = 'Impression: ' + (view_count || 0)
    }
  })
  containerElement && containerElement.appendChild(dataElement)
}

const showDetailCover = (element, containerElement) => {
  if (!element || !containerElement) {
    return
  }
  const children = Array.from(containerElement.children)
  let isExist = false
  children && children.map(e => {
    if (e.id === elementProductIdentifer && e.style_id === element.getAttribute('selectedColorId')) {
      isExist = true
    }

    if (e.style_id !== element.getAttribute('selectedColorId')) {
      if (e.id === elementProductIdentifer) {
        e && e.parentNode.removeChild(e)
      }
    }
  })

  // 已经存在了 不再次展示
  if (isExist) return

  const goods_id = element.getAttribute('selectedGoodsId')
  const style_id = element.getAttribute('selectedColorId')
  const size_id = element.getAttribute('selectedSizeId')
  // 参数不合法
  if (!goods_id || !style_id || !size_id) return

  const dataElement = document.createElement('div')
  dataElement.id = elementProductIdentifer
  dataElement.style.zIndex = 1
  const showElement = createRowElement(dataElement, '-')
  dataElement.style_id = style_id
  const showElement2 = createRowElement(dataElement, '')
  HTTP.productGetData(goods_id, style_id, size_id, (data) => {
    if (data) {
      if (data.has_virtual) {
        const result = data.stockTransit && data.stockTransit.split('[')
        showElement.innerHTML = result[0] + '<br>[' + result[1]
      } else {
        showElement.innerHTML = data.stockTransit
      }
      if (data.has_virtual) { showElement2.textContent = 'Virtual' }
    }
  })
  containerElement && containerElement.appendChild(dataElement)
}

const isLogin = () => {
  return document.cookie && document.cookie.includes('hasLogin=1;')
}

const beginTimer = () => {
  console.log('开始脚本====>')
  // 每秒检查 一次
  setInterval(() => {
    // 遍历所有节点,取所有的商品模块
    // 判断该商品模块是否已经有
    // 如果没有,则加入模块,并请求接口
    // 如果已经有了,则啥都不做
    if (isLogin()) {
    // 如果是首页商品不展示内容
    // 列表页面
      const elements = Array.from(document.getElementsByClassName(MOUSE_VISITED_CLASS_NAME))
      if (window.location.pathname != '/' && elements && elements.length > 0) {
        elements.map((element) => {
          let containerElement = getContainerElement(element)
          showListCover(containerElement)
        })
      }
      // 首页
      const indexElements = Array.from(document.getElementsByClassName(MOUSE_VISITED_CLASS_NAME_INDEX))
      if (indexElements && indexElements.length > 0) {
        indexElements.map((element) => {
          showHomeCover(element, element)
        })
      }
      // 详情页面
      const productElements = Array.from(document.getElementsByClassName(MOUSE_VISITED_CLASS_NAME_PRODUCT))
      if (productElements && productElements.length > 0) {
        productElements.map((element) => {
          showDetailCover(element, element)
        })
      }
    }
  }, 1000)
}

const main = () => {
  beginTimer()
}

main()


GM_addStyle(`
#chrome-extension-info-product{
    position: absolute;
    top: 0;
    bottom:0;
    right: 0;
    left: 0;
    height: 35px;
}

#chrome-extension-info {
    position: absolute;
    bottom: 0;
    right: 0;
    left: 0;
    min-height: 60px;
    background: rgba(255, 255, 255, 0.8);
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    padding: 10px 0px;
    min-width: 150px;
}

#chrome-extension-info .extension-container {
    min-width: calc((100% - 84px)/ 2);
    display: inline-block;
    font-size: 12px;
    color: #333;
    font-family: 'Montserrat SemiBold';
    line-height: 13px;
    text-align: left;
    margin-left: 20px;
    padding-left: 8px;
    position: relative;
    margin-bottom: 5px;
}

#chrome-extension-info .extension-container::before {
    content: '';
    position: absolute;
    left: -8px;
    top: 0px;
    width: 10px;
    height: 10px;
    border: 1px solid #FFFFFF;
    background: #F2CE99;
    border-radius: 10px;
    line-height: 13px;
}

#chrome-extension-info .extension-container:nth-child(2n+1)::before {
    background: #F2CE99;
    border: 1px solid #FFFFFF;
}

#chrome-extension-info .extension-container:nth-child(2n)::before {
    background: #93D1F5;
    border: 1px solid #FFFFFF;
}

#chrome-extension-info-product .extension-container{
    position: absolute;
    left: 0;
    width: 100%;
    height: 15px;
    text-align: center;
}

#chrome-extension-info-product .extension-container:nth-child(1){
    bottom:-15px;
    color: red;
}

#chrome-extension-info-product .extension-container:nth-child(2){
    top:-15px;
    color: blue;
}
`)

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址