Procházet zdrojové kódy

feat: 新增Card组件&新增搜索结果页面

cuiyalong před 4 roky
rodič
revize
55daa0be71

+ 5 - 3
jydocs-pc/public/index.html

@@ -1,19 +1,20 @@
 <!DOCTYPE html>
-<html lang="en">
+<html>
   <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">
     <link rel="icon" href="<%= BASE_URL %>favicon.ico">
     <title>app1</title>
+    <% if (process.env.NODE_ENV === 'development') { %>
       <link href="https://web2-jytest.jianyu360.cn/css/bootstrap.min.css" rel="stylesheet">
       <link href="https://web2-jytest.jianyu360.cn/css/bootswatch.min.css" rel="stylesheet">
       <link href="https://web2-jytest.jianyu360.cn/css/font.css?v=6302" rel="stylesheet">
       <link href="https://web2-jytest.jianyu360.cn/css/jy.css?v=6302" rel="stylesheet">
       <link href="https://web2-jytest.jianyu360.cn/css/common.css?v=6302" rel="stylesheet">
       <link href="https://web2-jytest.jianyu360.cn/css/animate.css" rel="stylesheet">
-      <link rel="stylesheet" href="https://web2-jytest.jianyu360.cn/css/unicorn.main.css" />
-      <link rel="stylesheet" href="https://web2-jytest.jianyu360.cn/css/unicorn.grey.css" />
+      <link href="https://web2-jytest.jianyu360.cn/css/unicorn.main.css" rel="stylesheet" />
+      <link href="https://web2-jytest.jianyu360.cn/css/unicorn.grey.css" rel="stylesheet" />
       <script src="https://web2-jytest.jianyu360.cn/js/jquery-3.2.1.min.js?v=6302"></script>
       <script src="https://web2-jytest.jianyu360.cn/js/jquery.cookie.js"></script>
       <script src="https://web2-jytest.jianyu360.cn/js/bootstrap.min.js"></script>
@@ -22,6 +23,7 @@
       <link href='https://web2-jytest.jianyu360.cn/pccss/reset_pc.css' rel="stylesheet" type="text/css"/>
       <link href='https://web2-jytest.jianyu360.cn/css/pc.css?v=6302' rel="stylesheet"/>
       <link href='https://web2-jytest.jianyu360.cn/pccss/public-nav-1200.css?v=6302' rel="stylesheet" type="text/css"/>
+    <% } %>
   </head>
   <body>
     <noscript>

binární
jydocs-pc/src/assets/images/no-data.png


+ 9 - 5
jydocs-pc/src/assets/style/_variables.scss

@@ -17,12 +17,16 @@ $bg-color-6: linear-gradient(90deg, #7446A0 0%, #A380C4 100%);
 
 $font-text--title: 17px;
 
-$color-text--default: #ffffff;
-$color-text--active: #E9D596;
-$color-input--default: #BFBFBF;
-$color-text--highlight: #E9D596;
+$color-text--default: #1D1D1D;
+$color-text--active: #2CB7CA;
+$color-input--default: #1D1D1D;
+$color-text--highlight: #2CB7CA;
 
-$bg-main-color: #0D0F14;
+$bg-main-color: #fff;
+
+.highlight-text {
+  color: $color-text--highlight;
+}
 
 .flex {
   width: 100%;

+ 0 - 28
jydocs-pc/src/components/CardItem.vue

@@ -1,28 +0,0 @@
-<template>
-    <div class="card-item flex-r-c">
-        <img src="" alt="图片">
-        <div class="flex-c-c">
-            <span>优化招投标市场营商环境与国企采购人主体负责人责人.......</span>
-            <div class="flex-c-c">
-                <span>贡献者:xxx.......</span>
-                <span>2020次下载:xxx.......</span>
-            </div>
-            <span>500剑鱼币</span>
-        </div>
-    </div>
-</template>
-
-<script>
-export default {
-  name: 'Card-Item',
-  props: {
-    msg: String
-  }
-}
-</script>
-
-<style scoped lang="scss">
-    .card-item {
-
-    }
-</style>

+ 31 - 0
jydocs-pc/src/components/NoData.vue

@@ -0,0 +1,31 @@
+<template>
+  <div class="no-data">
+    <el-image :src="require('@/assets/images/no-data.png')"></el-image>
+    <div class="tip-text">
+      <slot><p>暂无数据</p></slot>
+    </div>
+  </div>
+</template>
+
+<script>
+import { Image } from 'element-ui'
+export default {
+  name: 'no-data',
+}
+</script>
+
+<style lang="scss" scoped>
+.no-data {
+  margin: 0 auto;
+  text-align: center;
+  .el-image {
+    width: 220px;
+  }
+  .tip-text {
+    margin-top: 14px;
+    color: #686868;
+    font-size: 16px;
+    line-height: 24px;
+  }
+}
+</style>

+ 233 - 0
jydocs-pc/src/components/doc-item-card/Card.vue

@@ -0,0 +1,233 @@
+<template>
+  <div class="docs-card-container" @click="clickCard">
+    <div class="docs-card hover flex-r-c" v-if="cardType === 'oneline'" key="docs-card">
+      <div class="docs-card-header flex-r-c flex">
+        <span class="card-title-icon" :class="docTypeIcon"></span>
+        <span class="card-title flex" v-html="hightLightTitle"></span>
+      </div>
+      <div class="subinfo-container">
+        <!-- 引用组件处如果使用了插槽。则会覆盖slot中的内容 -->
+        <slot name="default">
+          <span
+            class="subinfo-item noline"
+            :class="index === subInfo.length - 1 ? 'last' : ''"
+            v-for="(item, index) in subInfo"
+            :key="index"
+          >{{ item }}</span>
+        </slot>
+      </div>
+    </div>
+    <div class="docs-card hover flex-r-c" v-else-if="cardType === 'image'" key="docs-card">
+      <div class="image-container">
+        <el-image :src="imageSrc" lazy></el-image>
+        <span class="doc-type-icon" :class="docTypeIcon"></span>
+      </div>
+      <div class="image-info-container flex-c-c flex">
+        <div class="card-title">{{ title }}</div>
+        <div class="card-info">
+          <div class="card-info-item uploader" v-if="uploader">贡献者:{{ uploader }}</div>
+          <div class="card-info-item subinfo-container">
+            <span
+              class="subinfo-item"
+              :class="index === subInfo.length - 1 ? 'last' : ''"
+              v-for="(item, index) in subInfo"
+              :key="index"
+            >{{ item }}</span>
+          </div>
+          <div class="card-info-item price">
+            <Price :price="price" />
+          </div>
+        </div>
+      </div>
+    </div>
+    <div class="docs-card hover" v-else key="docs-card">
+      <div class="docs-card-header flex-r-c left">
+        <span class="card-title-icon" :class="docTypeIcon"></span>
+        <span class="card-title flex" v-html="hightLightTitle"></span>
+      </div>
+      <div class="docs-card-content">
+        <span class="card-desc" v-html="hightLightDesc"></span>
+      </div>
+      <div class="docs-card-footer flex-r-c">
+        <div class="c-f-left subinfo-container">
+          <span
+            class="f-l-item subinfo-item card-time"
+            :class="index === subInfo.length - 1 ? 'last' : ''"
+            v-for="(item, index) in subInfo"
+            :key="index"
+          >{{ item }}</span>
+        </div>
+        <div class="c-f-right flex-r-c">
+          <Price :price="price" />
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { Image } from 'element-ui'
+import Price from '@/components/doc-item-card/Price'
+import { replaceKeyword } from '@/utils/'
+
+export default {
+  name: 'docs-card',
+  components: {
+    [Image.name]: Image,
+    Price
+  },
+  props: {
+    // online, image
+    cardType: {
+      type: String,
+      default: ''
+    },
+    highlightKey: {
+      type: Array,
+      default () {
+        return []
+      }
+    },
+    title: {
+      type: String,
+      required: true
+    },
+    desc: {
+      type: String,
+      default: ''
+    },
+    docType: {
+      type: String,
+      default: 'pdf'
+    },
+    subInfo: {
+      type: Array,
+      default () {
+        return []
+      }
+    },
+    imageSrc: {
+      type: String,
+      default: ''
+    },
+    uploader: {
+      type: String,
+      default: ''
+    },
+    price: {
+      type: [String, Number],
+      default: 0
+    }
+  },
+  computed: {
+    docTypeIcon () {
+      return `el-icon-jy-${this.docType}`
+    },
+    hightLightTitle () {
+      return replaceKeyword(this.title, this.highlightKey, [
+        '<span class="highlight-text">',
+        '</span>'
+      ])
+    },
+    hightLightDesc () {
+      return replaceKeyword(this.desc, this.highlightKey, [
+        '<span class="highlight-text">',
+        '</span>'
+      ])
+    }
+  },
+  methods: {
+    clickCard () {
+      this.$emit('onClick')
+    }
+  }
+}
+</script>
+
+<!-- Add "scoped" attribute to limit CSS to this component only -->
+<style lang="scss" scoped>
+.docs-card {
+  width: 100%;
+  padding: 18px;
+  font-size: 14px;
+  line-height: 24px;
+  border-bottom: 1px solid rgba($color: #000, $alpha: 0.05);
+  cursor: pointer;
+
+  @include diy-icon("word", 24);
+  @include diy-icon("excel", 24);
+  @include diy-icon("ppt", 24);
+  @include diy-icon("pdf", 24);
+
+  .docs-card-header {
+    font-size: 16px;
+    color: #1d1d1d;
+    .card-title-icon {
+      margin-right: 8px;
+    }
+    .card-title {
+      line-height: 24px;
+    }
+  }
+
+  .docs-card-content {
+    margin: 10px 0;
+    color: #686868;
+  }
+
+  .subinfo-container {
+    color: #999;
+    .subinfo-item {
+      position: relative;
+      margin-right: 16px;
+
+      &.noline:after,
+      &.last:after {
+        content: unset;
+      }
+
+      &:after {
+        content: "";
+        position: absolute;
+        top: 50%;
+        right: -8px;
+        margin-top: -6px;
+        width: 1px;
+        height: 12px;
+        background-color: rgba($color: #000, $alpha: 0.05);
+      }
+    }
+  }
+
+  .image-container {
+    position: relative;
+    width: 110px;
+    height: 150px;
+    border: 1px solid rgba($color: #000, $alpha: 0.05);
+    border-radius: 6px;
+    overflow: hidden;
+    .el-image {
+      width: 100%;
+      height: 100%;
+    }
+    .doc-type-icon {
+      position: absolute;
+      right: 2px;
+      bottom: 3px;
+    }
+  }
+  .image-info-container {
+    margin-left: 17px;
+    .card-info-item {
+      margin-top: 2px;
+    }
+  }
+
+  &.hover:hover {
+    background-color: #f5f6f7;
+    .card-title {
+      color: $color-text--highlight;
+    }
+  }
+}
+</style>

+ 32 - 0
jydocs-pc/src/components/doc-item-card/Price.vue

@@ -0,0 +1,32 @@
+<template>
+  <div class="price-container">
+    <span class="p-l el-icon-jy-iconLogoLight"></span>
+    <span class="p-r">{{ price }}</span>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'price',
+  props: {
+    price: {
+      type: [String, Number],
+      default: 0
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss" scoped>
+.price-container {
+  display: flex;
+  align-items: center;
+  @include diy-icon("iconLogoLight", 24);
+
+  .p-r {
+    margin-left: 4px;
+    font-size: 18px;
+    color: #ff3a20;
+  }
+}
+</style>

+ 3 - 0
jydocs-pc/src/main.js

@@ -2,7 +2,10 @@ import Vue from 'vue'
 import App from './App.vue'
 import router from './router'
 import singleSpaVue from 'single-spa-vue'
+import { Loading } from 'element-ui'
+import '@/utils/'
 
+Vue.use(Loading.directive)
 Vue.config.productionTip = false
 
 const vueOptions = {

+ 50 - 0
jydocs-pc/src/utils/functions/limit-input-numbers.js

@@ -0,0 +1,50 @@
+// FROM: https://juejin.im/post/5b6b9308f265da0f4e62fecc
+// demo: http://192.168.3.207:10080/jianyu/page_h5/src/master/src/components/screen_price.vue
+// limit-input-number.js
+
+// 设置组件中的指定属性的值
+const setValue = function (exp, value, context) {
+  value = isNaN(value) ? '' : value
+  // eslint-disable-next-line
+  new Function ('context', 'value', `context.${exp} = value`)(context, value)
+}
+
+// 自定义指令
+export default {
+  bind (el, { expression }, { context }) {
+    // 初始化lastValue
+    el.lastValue = el.value
+    // 通过dataset 判断是否允许小数点
+    const allowDot = !!el.dataset.dotrange
+    const dotRange = el.dataset.dotrange || '{0,2}' // 默认
+    const pattern = `^[0-9]+${allowDot ? `(.[0-9]${dotRange})?` : ''}$`
+
+    if (!expression) {
+      throw new TypeError('请绑定expression')
+    }
+
+    el.handleInputEvent = function (e) {
+      setTimeout(() => {
+        if (e.target.value === '') {
+          setValue(expression, '', context)
+          // 遇到非法数值,则重置
+          e.target.value = ''
+        } else if (e.target.value !== '' && !new RegExp(pattern).test(e.target.value)) {
+          setValue(expression, parseFloat(e.target.lastValue), context)
+          // 遇到非法数值,则重置为lastValue
+          e.target.value = e.target.lastValue
+          if (allowDot) {
+            // 这个时候,根据业务逻辑,可以收起键盘,给出吐司提示
+            e.target.blur()
+            // context.showToast(`小数点后最多${dotRange.replace(/[}{]/g, '').split(',')[1]}位`)
+          }
+        }
+        e.target.lastValue = e.target.value
+      }, 0)
+    }
+    el.addEventListener('input', el.handleInputEvent, false)
+  },
+  unbind (el) {
+    el && el.removeEventListener('input', el.handleInputEvent, false)
+  }
+}

+ 26 - 0
jydocs-pc/src/utils/globalDirectives.js

@@ -0,0 +1,26 @@
+import Vue from 'vue'
+import limitInputNumbers from '@/utils/functions/limit-input-numbers'
+
+// 输入数字
+Vue.directive('limit-input-numbers', limitInputNumbers)
+
+// Vue自定义指令文档:https://cn.vuejs.org/v2/guide/custom-directive.html
+Vue.directive('auto-focus', {
+  // 当被绑定的元素插入到 DOM 中时……
+  inserted: function (el) {
+    const autoFocusArr = ['input', 'textarea']
+    const tagName = el.nodeName.toLowerCase()
+    const isTag = autoFocusArr.find((item) => {
+      return item === tagName
+    })
+
+    setTimeout(() => {
+      if (isTag) {
+        el.focus()
+      } else {
+        const dom = el.querySelector('.van-field__control')
+        dom.focus()
+      }
+    }, 30)
+  }
+})

+ 31 - 0
jydocs-pc/src/utils/globalFilters.js

@@ -0,0 +1,31 @@
+import Vue from 'vue'
+import {
+  dateFormatter,
+  dateFromNow,
+  addSpaceForTel,
+  upPrice,
+  formatPrice,
+  fen2Yuan,
+  addSpaceForBank,
+  addConfusionForBank
+} from './globalFunctions'
+
+// 注册全局过滤器
+// 时间格式化(同时间格式化函数)
+Vue.filter('dateFormatter', dateFormatter)
+// 时间戳转换 多少秒、多少分、多少小时前、多少天前  超出10天显示年月日
+Vue.filter('dateFromNow', dateFromNow)
+// 手机号加空格
+Vue.filter('addSpaceForTel', addSpaceForTel)
+
+// 分转元
+Vue.filter('fen2Yuan', fen2Yuan)
+// 金额大写
+Vue.filter('upPrice', upPrice)
+// 金额3位逗号分隔
+Vue.filter('formatPrice', formatPrice)
+
+// 银行卡号字符串加空格
+Vue.filter('addSpaceForBank', addSpaceForBank)
+// 银行卡加*
+Vue.filter('addConfusionForBank', addConfusionForBank)

+ 305 - 0
jydocs-pc/src/utils/globalFunctions.js

@@ -0,0 +1,305 @@
+// 字符串处理相关函数
+// 手机号中间4位加* ------------>
+export function addConfusionForTel (tel) {
+  const reg = /^(\d{3})\d{4}(\d{4})$/
+  return tel.replace(reg, '$1****$2')
+}
+// 手机号加空格 ------------>
+export function addSpaceForTel (tel) {
+  const regMap = {
+    isConfuse: /^(\d{3})\*{4}(\d{4})$/,
+    addSpace: /^(\d{3})(\d{4})(\d{4})$/
+  }
+  const confusion = regMap.isConfuse.test(tel)
+  if (confusion) {
+    return tel.replace(regMap.isConfuse, '$1 **** $2')
+  } else {
+    return tel.replace(regMap.addSpace, '$1 $2 $3')
+  }
+}
+// 银行卡加空格
+export function addSpaceForBank (v, hasConfusion = false) {
+  // 无*银行卡号加空格
+  if (hasConfusion) {
+    // 带有*的银行卡号字符串加空格
+    return v.replace(/\s/g, '').replace(/(.{4})/g, '$1 ')
+  } else {
+    // 纯数字银行卡号字符串加空格
+    return v.replace(/\s/g, '').replace(/(\d{4})(?=\d)/g, '$1 ')
+  }
+}
+// 银行卡加 **
+export function addConfusionForBank (v) {
+  if (String(v).length < 12) {
+    return v.replace(/\s/g, '').replace(/^(\d{2})\d+(\d{2})$/, '$1 **** **** $2')
+  } else {
+    return v.replace(/\s/g, '').replace(/^(\d{4})\d+(\d{4})$/, '$1 **** **** $2')
+  }
+}
+
+// 金额处理
+// 分转元
+export function fen2Yuan (v) {
+  return v / 100
+}
+
+// 金额大写,链接:https://juejin.im/post/5a2a7a5051882535cd4abfce
+// upDigit(1682) result:"人民币壹仟陆佰捌拾贰元整"
+// upDigit(-1693) result:"欠壹仟陆佰玖拾叁元整"
+export function upPrice (n) {
+  const fraction = ['角', '分', '厘']
+  const digit = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖']
+  const unit = [
+    ['元', '万', '亿'],
+    ['', '拾', '佰', '仟']
+  ]
+  // const head = n < 0 ? '欠人民币' : '人民币'
+  const head = ''
+  n = Math.abs(n)
+  let s = ''
+  for (let i = 0; i < fraction.length; i++) {
+    s += (digit[Math.floor(n * 10 * Math.pow(10, i)) % 10] + fraction[i]).replace(/零./, '')
+  }
+  s = s || '整'
+  n = Math.floor(n)
+  for (let i = 0; i < unit[0].length && n > 0; i++) {
+    let p = ''
+    for (let j = 0; j < unit[1].length && n > 0; j++) {
+      p = digit[n % 10] + unit[1][j] + p
+      n = Math.floor(n / 10)
+    }
+    s = p.replace(/(零.)*零$/, '').replace(/^$/, '零') + unit[0][i] + s
+    // s = p + unit[0][i] + s;
+  }
+  return head + s.replace(/(零.)*零元/, '元').replace(/(零.)+/g, '零').replace(/^整$/, '零元整')
+}
+
+// 金额3位逗号分隔  ------------>
+/**
+ * @param s 要格式化的数字(四舍五入)
+ * @param n 保留几位小数(不传或者传-1 --> 如果为整数,则不保留小数。如果为浮点数,则保留两位小数)
+ * @param comma 是否小数点前每3位添加逗号
+ */
+export function newFormat (s = 0, n = -1, comma = false) {
+  n = n === -1 ? 0 : n
+  if (n > 20 || n < -1) {
+    n = 2
+  }
+  s = Number(s)
+  return s.toLocaleString('zh-CN', {
+    style: 'decimal',
+    useGrouping: comma,
+    minimumFractionDigits: n,
+    maximumFractionDigits: n
+  })
+}
+export function formatPrice (s, n = -1, comma = false) {
+  // 如果不传s或者s为空,则直接返回0
+  if (!s) return 0
+
+  if (n !== -1) n = n > 0 && n <= 20 ? n : 2
+  const intS = parseInt(String(s))
+  let point = '.'
+  let left = []
+  let right = ''
+  s = parseFloat((s + '').replace(/[^\d.-]/g, ''))
+  // 没传n或者n为-1,默认(如果为整数,则不保留小数。如果为浮点数,则保留两位小数)
+  if (n === -1) {
+    if (s === intS) {
+      n = 0
+      right = ''
+      point = ''
+    } else {
+      n = 2
+      s = s.toFixed(n)
+      right = s.split('.')[1]
+    }
+    s = s + ''
+    left = s.split('.')[0].split('').reverse()
+  } else {
+    s = parseFloat((s + '').replace(/[^\d.-]/g, '')).toFixed(n) + ''
+    left = s.split('.')[0].split('').reverse()
+    right = s.split('.')[1]
+  }
+
+  if (comma) {
+    let t = ''
+    for (let i = 0; i < left.length; i++) {
+      t += left[i] + ((i + 1) % 3 === 0 && (i + 1) !== left.length ? ',' : '')
+    }
+    return t.split('').reverse().join('') + point + right
+  }
+
+  return left.reverse().join('') + point + right
+}
+
+// 时间格式化相关函数
+/*
+* 时间格式化函数(将时间格式化为,2019年08月12日,2019-08-12,2019/08/12的形式)
+*   pattern参数(想要什么格式的数据就传入什么格式的数据)
+*     · 'yyyy-MM-dd'  ---> 输出如2019-09-20
+*     · 'yyyy-MM-dd HH:mm'  --->  输出如2019-09-20 18:20
+*     · 'yyyy-MM-dd HH:mm:ss'  --->  输出如2019-09-20 06:20:23
+*     · 'yyyy/MM/dd'  --->  输出如2019/09/20
+*     · 'yyyy年MM月dd日'  --->  输出如2019年09月20日
+*     · 'yyyy年MM月dd日 HH时mm分'  --->  输出如2019年09月20日 18时20分
+*     · 'yyyy年MM月dd日 HH时mm分ss秒'  --->  输出如2019年09月20日 18时20分23秒
+*     · 'yyyy年MM月dd日 HH时mm分ss秒 EE'  --->  输出如2019年09月20日 18时20分23秒 周二
+*     · 'yyyy年MM月dd日 HH时mm分ss秒 EEE'  --->  输出如2019年09月20日 18时20分23秒 星期二
+*  参考: https://www.cnblogs.com/mr-wuxiansheng/p/6296646.html
+*/
+export function dateFormatter (date, fmt = 'yyyy-MM-dd HH:mm:ss') {
+  // 将传入的date转为时间对象
+  if (!date) return ''
+  date = new Date(date)
+  const o = {
+    'y+': date.getFullYear(),
+    'M+': date.getMonth() + 1, // 月份
+    'd+': date.getDate(), // 日
+    // 12小时制
+    'h+': date.getHours() % 12 === 0 ? 12 : date.getHours() % 12, // 小时
+    // 24小时制
+    'H+': date.getHours(), // 小时
+    'm+': date.getMinutes(), // 分
+    's+': date.getSeconds(), // 秒
+    'q+': Math.floor((date.getMonth() + 3) / 3), // 季度
+    S: date.getMilliseconds(), // 毫秒
+    'E+': date.getDay() // 周
+  }
+  const week = ['日', '一', '二', '三', '四', '五', '六']
+
+  if (/(y+)/.test(fmt)) {
+    fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length))
+  }
+  if (/(E+)/.test(fmt)) {
+    fmt = fmt.replace(RegExp.$1, ((RegExp.$1.length > 1) ? (RegExp.$1.length > 2 ? '星期' : '周') : '') + week[date.getDay()])
+  }
+  for (const k in o) {
+    if (new RegExp('(' + k + ')').test(fmt)) {
+      fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length)))
+    }
+  }
+  return fmt
+}
+
+// 时间戳转换 多少秒、多少分、多少小时前、多少天前  超出10天显示年月日
+// 传入一个时间戳
+export function dateFromNow (timestamp) {
+  const date1 = new Date(timestamp) // 开始时间
+  const date2 = new Date() // 结束时间
+  const date3 = date2.getTime() - date1.getTime() // 时间差的毫秒数
+  // 计算出相差天数
+  const days = Math.floor(date3 / (24 * 3600 * 1000))
+  // 计算出小时数
+  const leave1 = date3 % (24 * 3600 * 1000) // 计算天数后剩余的毫秒数
+  const hours = Math.floor(leave1 / (3600 * 1000))
+  // 计算相差分钟数
+  const leave2 = leave1 % (3600 * 1000) // 计算小时数后剩余的毫秒数
+  const minutes = Math.floor(leave2 / (60 * 1000))
+  // 计算相差秒数
+  let td = '30秒前'
+  if (days > 0) {
+    if (days > 10) {
+      const date1year = date1.getFullYear()
+      const date2year = date2.getFullYear()
+      let date1month = date1.getMonth() + 1
+      let date1day = date1.getDate()
+      if (date1month < 10) {
+        date1month = '0' + date1month
+      }
+      if (date1day < 10) {
+        date1day = '0' + date1day
+      }
+      if (date1year < date2year) {
+        td = date1.getFullYear() + '-' + date1month + '-' + date1day
+      } else {
+        td = date1month + '-' + date1day
+      }
+    } else {
+      td = days + '天前'
+    }
+  } else if (hours > 0) {
+    td = hours + '小时前'
+  } else if (minutes > 0) {
+    td = minutes + '分钟前'
+  }
+  return td
+}
+
+/**
+ * 通用关键字高亮替换
+ * @param {String} value 要高亮的字符串
+ * @param {String|Array} oldChar 要被替换的字符串(或数组)
+ * @param {String|Array} newChar 要替换成的字符串(或数组)
+ *
+ * 比如:要将 - `剑鱼标讯工具函数` 字符串中的 `工具` 高亮
+ * 则此时 value -> `剑鱼标讯工具函数`
+ *        oldChar -> `工具`
+ *        newChar -> `<span class="highlight-text">工具</span>`
+ *
+ * 批量高亮-----
+ * 比如:要将 - `剑鱼标讯工具函数` 字符串中的 `工具` `剑鱼` 高亮
+ * 则此时 value -> `剑鱼标讯工具函数批量高亮`
+ *        oldChar -> ['工具', '剑鱼']
+ *        newChar -> ['<span class="highlight-text">', '</span>']
+ *
+ *   注意:此时newChar为一个长度为2的数组,数组中为高亮标签的起始标签和结束标签
+ *
+ */
+export function replaceKeyword (value, oldChar, newChar) {
+  if (!oldChar || !newChar) return value
+  // oldChar的字符串数组,用来循环替换
+  var oldCharArr = []
+
+  if (Array.isArray(oldChar)) {
+    oldCharArr = oldChar.concat()
+  } else {
+    oldCharArr.push(oldChar)
+  }
+
+  // 数组去重
+  oldCharArr = Array.from(new Set(oldCharArr))
+
+  try {
+    oldCharArr.forEach(function (item) {
+      // 去空格之后为空字符串,则直接跳过当前替换
+      if (item.replace(/\s+/g, '')) {
+        var oc = item
+        oc = oc.replace(/\$/g, '\\$')
+          .replace(/\(/g, '\\(')
+          .replace(/\)/g, '\\)')
+          .replace(/\*/g, '\\*')
+          .replace(/\+/g, '\\+')
+          .replace(/\./g, '\\.')
+          .replace(/\[/g, '\\[')
+          .replace(/\]/g, '\\]')
+          .replace(/\?/g, '\\?')
+          .replace(/\\/g, '\\')
+          .replace(/\//g, '\\/')
+          .replace(/\^/g, '\\^')
+          .replace(/\{/g, '\\{')
+          .replace(/\}/g, '\\}')
+          .replace(/\|/g, '\\|')
+
+        if (Array.isArray(newChar)) {
+          // 批量高亮
+          var tempV = value
+          value = value.replace(new RegExp('(' + oc + ')', 'gmi'), newChar[0] + oc + newChar[1])
+          if (value === tempV && oc.indexOf('+') !== -1) {
+            var splitReg = oc.split('\\+')
+            splitReg.map(function (v) {
+              value = value.replace(new RegExp('(' + v + ')', 'gmi'), newChar[0] + v + newChar[1])
+            })
+          }
+        } else {
+          // 普通单个高亮
+          value = value.replace(new RegExp('(' + oc + ')', 'gmi'), newChar)
+        }
+      }
+    })
+  } catch (e) {
+    console.log(e)
+    return value
+  }
+  return value
+}

+ 4 - 0
jydocs-pc/src/utils/index.js

@@ -0,0 +1,4 @@
+import './globalDirectives'
+import './globalFilters'
+
+export * from './globalFunctions'

+ 0 - 2
jydocs-pc/src/views/Home.vue

@@ -80,12 +80,10 @@ import { ajaxGetTest } from '../api/modules/user'
 import { Button } from 'element-ui'
 import SearchInput from '../components/Search'
 import ListItem from '../components/ListItem'
-import CardItem from '../components/CardItem'
 
 export default {
   name: 'home',
   components: {
-    CardItem,
     ListItem,
     SearchInput,
     [Button.name]: Button

+ 151 - 13
jydocs-pc/src/views/Search.vue

@@ -1,28 +1,95 @@
 <template>
-  <div class="home">
-      <search-input @submit="goSubmit" @recovery="getTest"></search-input>
-      {{ajaxData}}
-      <el-button @click="getTest" :loading="fullscreenLoading" type="primary">模拟Ajax</el-button>
-      <el-button  @click="getLoginStatus" type="info">获取登录状态</el-button>
+  <div class="search-container">
+    <search-input @submit="goSubmit" @recovery="getTest"></search-input>
+    <div class="search-result-container">
+      <div class="sort-list">
+        <span
+          class="sort-item"
+          v-for="(item, index) in sortList"
+          :key="index"
+          @click="sortAndSearch(item, index)"
+          :class="index === listState.sortActive ? 'active': ''"
+        >
+          <span class="sort-text">{{ item.name }}</span>
+          <span class="sort-icon" :class="item.sortBy ? 'el-icon-top' : 'el-icon-bottom'"></span>
+        </span>
+      </div>
+      <div class="search-result-list" v-loading="listState.loading">
+        <doc-card
+          :title="title"
+          desc="关于设计图。。。"
+          :highlightKey="['设计图','更新']"
+          :subInfo="['设计图','更新']"
+          @onClick="clickFn"
+        />
+        <no-data v-if="listState.list.length === 0 && listState.loaded">暂无我的文库</no-data>
+      </div>
+      <div class="search-pagination">
+        <el-pagination
+          background
+          layout="prev, pager, next, ->, total"
+          :hide-on-single-page="true"
+          :current-page="listState.currentPage"
+          :page-size="listState.pageSize"
+          :page-count="listState.pageCount"
+          :total="listState.total"
+          @current-change="onPageChange"
+        >
+        </el-pagination>
+      </div>
+    </div>
+    {{ajaxData}}
+    <button @click="getTest" type="primary">模拟Ajax</button>
+    <button  @click="getLoginStatus" type="info">获取登录状态</button>
   </div>
 </template>
 
 <script>
+import { Icon, Pagination, Image } from 'element-ui'
+import SearchInput from '@/components/Search'
+import DocCard from '@/components/doc-item-card/Card'
+import NoData from '@/components/NoData'
 import { ajaxGetSearch } from '../api/modules/search'
-import { Button } from 'element-ui'
-import SearchInput from '../components/Search'
 
 export default {
-  name: 'home',
+  name: 'seach',
   components: {
+    [Icon.name]: Icon,
+    [Pagination.name]: Pagination,
+    [Image.name]: Image,
     SearchInput,
-    [Button.name]: Button
+    DocCard,
+    NoData
   },
   data () {
     return {
-      fullscreenLoading: false,
+      sortList: [
+        {
+          name: '上传时间',
+          sortBy: 1
+        },
+        {
+          name: '下载次数',
+          sortBy: 0
+        },
+        {
+          name: '浏览人数',
+          sortBy: 0
+        }
+      ],
+      listState: {
+        loaded: false, // 是否已经搜索过
+        sortActive: 0,
+        loading: false,
+        currentPage: 2, // 当前页
+        pageSize: 10, // 每页多少条数据
+        pageCount: 10, // 一共多少页
+        total: 100, // 一共多少条数据
+        list: [] // 查询请求返回的数据
+      },
       searchQuery: {},
-      ajaxData: {}
+      ajaxData: {},
+      title: '设计图变动更新设计图变动更新设计图变动更新设计图变动更新'
     }
   },
   methods: {
@@ -35,7 +102,6 @@ export default {
       this.getTest(search)
     },
     getTest (search) {
-      this.fullscreenLoading = true
       ajaxGetSearch({
         keyWord: search.text,
         tag: search.type,
@@ -46,12 +112,84 @@ export default {
       }).then(res => {
         console.log(res.data)
         this.ajaxData = res.data
-        this.fullscreenLoading = false
       })
     },
     getLoginStatus: function () {
       alert(loginflag)
+    },
+    sortAndSearch (item, index) {
+      console.log(...arguments)
+    },
+    clickFn () {
+      console.log('clickItem')
+    },
+    onPageChange (p) {
+      this.listState.currentPage = p
+      this.listState.loading = true
+      setTimeout(() => {
+        this.listState.loading = false
+      }, 2000)
+      console.log(JSON.stringify(this.listState, null, 4))
     }
   }
 }
 </script>
+<style lang="scss">
+.el-pagination.is-background .el-pager {
+  li {
+    background-color: #fff;
+    border: 1px solid rgba($color: #000, $alpha: 0.05);
+  }
+
+  li:not(.disabled):hover {
+    color: #fff;
+    background-color: $color-text--highlight;
+  }
+}
+</style>
+<style lang="scss" scoped>
+.search-result-container {
+  margin: 0 auto;
+  .sort-list {
+    display: flex;
+    align-items: center;
+    margin: 0 18px;
+    height: 48px;
+    border-radius: 4px;
+    background: #F5F6F7;
+    .sort-item {
+      display: flex;
+      align-items: center;
+      padding: 0 20px;
+      height: 100%;
+      font-size: 14px;
+      color: #686868;
+      cursor: pointer;
+      .sort-text {
+        margin-right: 4px;
+      }
+      .sort-icon {
+        color: #aaa;
+      }
+      &.active {
+        .sort-text,
+        .sort-icon {
+          color: $color-text--highlight;
+        }
+      }
+      &:hover {
+        .sort-text,
+        .sort-icon {
+          color: $color-text--highlight;
+        }
+      }
+    }
+  }
+  .search-result-list {
+    min-height: 500px;
+  }
+  .search-pagination {
+    text-align: right;
+  }
+}
+</style>