yangfeng 5 сар өмнө
parent
commit
1bc8668d44
52 өөрчлөгдсөн 6317 нэмэгдсэн , 817 устгасан
  1. 18 0
      apps/bigmember_pc/src/api/modules/dataExport.js
  2. 3 3
      apps/bigmember_pc/src/components/article-item/ArticleItem.vue
  3. 123 78
      apps/bigmember_pc/src/components/collect-info/CollectInfo.vue
  4. 7 3
      apps/bigmember_pc/src/components/filter-items/PersonStaffsSelector.vue
  5. 86 40
      apps/bigmember_pc/src/components/selector/timeDropdown.vue
  6. 35 5
      apps/bigmember_pc/src/components/subscribe-manager/powerPerson.vue
  7. 2 1
      apps/bigmember_pc/src/router/router-interceptors.js
  8. 24 0
      apps/bigmember_pc/src/router/routers.js
  9. 383 0
      apps/bigmember_pc/src/views/datapack/Home.vue
  10. 1 1
      apps/bigmember_pc/src/views/datapack/buy.vue
  11. 196 0
      apps/bigmember_pc/src/views/datapack/model/history.js
  12. 224 0
      apps/bigmember_pc/src/views/datapack/model/home-filter.js
  13. 260 0
      apps/bigmember_pc/src/views/datapack/model/home.js
  14. 38 0
      apps/bigmember_pc/src/views/datapack/model/useDistribute.js
  15. 323 0
      apps/bigmember_pc/src/views/datapack/pages/History.vue
  16. 324 0
      apps/bigmember_pc/src/views/datapack/pages/Home.vue
  17. 154 20
      apps/bigmember_pc/src/views/order/components/data-export/info.vue
  18. 1 1
      apps/bigmember_pc/src/views/order/components/data-pack/buy-tip.vue
  19. 82 1
      apps/bigmember_pc/src/views/search/bidding/index.vue
  20. 95 3
      apps/bigmember_pc/src/views/search/bidding/model/base.js
  21. 5 0
      apps/bigmember_pc/src/views/search/bidding/model/modules/data-employ-actions.js
  22. 31 23
      apps/bigmember_pc/src/views/search/layout/search-list.vue
  23. 1 0
      apps/bigmember_pc/vite.config.js
  24. 1 1
      apps/mobile/.env.production
  25. 5 1
      apps/mobile/package.json
  26. 8 1
      apps/mobile/scripts/updateGitInfo.js
  27. 27 0
      apps/mobile/src/api/modules/pay.js
  28. 13 7
      apps/mobile/src/components/selector/date-time-list/index.vue
  29. 20 0
      apps/mobile/src/router/modules/packs.js
  30. 16 13
      apps/mobile/src/ui/checkbox-group/index.vue
  31. 27 7
      apps/mobile/src/ui/checkbox/index.vue
  32. 18 4
      apps/mobile/src/views/article/components/ContentMainText.vue
  33. 165 0
      apps/mobile/src/views/article/components/PDF.vue
  34. 170 48
      apps/mobile/src/views/create-order/components/dataexport/ProductionCard.vue
  35. 10 10
      apps/mobile/src/views/create-order/components/datapack/SpecCell.vue
  36. 56 52
      apps/mobile/src/views/dataexport/LimitPreviewData.vue
  37. 157 134
      apps/mobile/src/views/invoice/Invoicing.vue
  38. 35 26
      apps/mobile/src/views/order/components/dataexport/ProductionCard.vue
  39. 2 1
      apps/mobile/src/views/order/components/datapack/Introduction.vue
  40. 24 26
      apps/mobile/src/views/order/components/datapack/ProductionCard.vue
  41. 63 13
      apps/mobile/src/views/order/components/datapack/QuestionTip.vue
  42. 483 0
      apps/mobile/src/views/packs/DataExportRecord.vue
  43. 233 0
      apps/mobile/src/views/packs/DataPackIndex.vue
  44. 336 0
      apps/mobile/src/views/packs/components/AccountUsageDetailList.vue
  45. 608 0
      apps/mobile/src/views/packs/components/DataPackFilters.vue
  46. 376 0
      apps/mobile/src/views/packs/components/ExportRecordFilters.vue
  47. 43 0
      apps/mobile/src/views/packs/components/SelectPerson.vue
  48. 213 0
      apps/mobile/src/views/packs/styles/pack-style.scss
  49. 8 4
      apps/mobile/src/views/search/layout.vue
  50. 241 12
      apps/mobile/src/views/search/result/bidding/index.vue
  51. 112 47
      apps/mobile/src/views/tabbar/Subscribe.vue
  52. 431 231
      pnpm-lock.yaml

+ 18 - 0
apps/bigmember_pc/src/api/modules/dataExport.js

@@ -44,6 +44,24 @@ export function getDataPackUsage(data) {
     data
   })
 }
+// 获取数据导出页面数据包信息
+export function getDataPackRechargeList(data) {
+  data = qs.stringify(data)
+  return request({
+    url: '/subscribepay/dataExportPack/rechargeList',
+    method: 'post',
+    data
+  })
+}
+// 获取数据导出页面数据包信息
+export function getDataPackRecordList(data) {
+  data = qs.stringify(data)
+  return request({
+    url: '/subscribepay/dataExportPack/recordList',
+    method: 'post',
+    data
+  })
+}
 
 // 根据ID获取数据导出相关信息
 export function getDataExportInfo(data) {

+ 3 - 3
apps/bigmember_pc/src/components/article-item/ArticleItem.vue

@@ -321,7 +321,7 @@
             <i class="l-d-item-label">报名截止日期:</i>
             {{ article.signEndTimeText }}
           </p>
-          
+
         </div>
         <div
           class="l-d-item"
@@ -472,9 +472,9 @@ export default {
         ['<span class="highlight-text-orange-bd">', '</span>']
       )
       if (this.article.filetext_search) {
-        return `${this.index}. ${hightLightedTitle}${this.calcFileText}`
+        return this.index ? `${this.index}. ${hightLightedTitle}${this.calcFileText}` : `${hightLightedTitle}${this.calcFileText}`
       } else {
-        return `${this.index}. ${hightLightedTitle}`
+        return this.index ? `${this.index}. ${hightLightedTitle}`: `${hightLightedTitle}`
       }
     },
     // 处理正文显示,有匹配词且匹配上才显示正文

+ 123 - 78
apps/bigmember_pc/src/components/collect-info/CollectInfo.vue

@@ -1,9 +1,9 @@
 <template>
   <div class="collect-info">
-    <div class="user-data-dialog" v-if="showForm">
-      <div class="mask"></div>
+    <div v-if="showForm" class="user-data-dialog">
+      <div class="mask" />
       <div class="dialog-container" :class="{ 'use-bg': useImgBg.use }">
-        <div class="dialog-header--use-bg" v-if="useImgBg.use">
+        <div v-if="useImgBg.use" class="dialog-header--use-bg">
           <img
             class="dialog-header-img--bg"
             :src="useImgBg.bg"
@@ -16,58 +16,60 @@
           />
         </div>
         <div
-          class="dialog-header fs18"
           v-if="computedDialogInfo.dialogTitleTop && !useImgBg.use"
+          class="dialog-header fs18"
           v-text="computedDialogInfo.dialogTitleTop"
-        ></div>
+        />
         <div
-          class="dialog-header mt-4"
           v-if="computedDialogInfo.dialogTitle"
+          class="dialog-header mt-4"
           v-html="computedDialogInfo.dialogTitle"
-        ></div>
+        />
         <div class="dialog-content">
           <el-form
+            ref="ruleForm"
             class="user-form"
             :model="form"
             :rules="rules"
-            ref="ruleForm"
           >
             <!-- 基本信息 -->
             <div class="basic clearfix">
-              <div class="form-title">基本信息</div>
-              <div class="form-main clearfix">
-                <div class="short-control fl">
+              <div v-if="showSecondstage" class="form-title">基本信息</div>
+              <div
+                class="form-main clearfix"
+                :class="{ 'form-main-none': !showSecondstage }"
+              >
+                <div class="fl" :class="nameClass">
                   <el-form-item label="姓名 :" prop="name">
                     <el-input
                       v-model.trim="form.name"
                       class="data-short-input item-input"
                       placeholder="请输入姓名"
-                      @focus="nameFocus"
                       required
-                    >
-                    </el-input>
+                      @focus="nameFocus"
+                    />
                   </el-form-item>
                 </div>
-                <div class="short-control fr">
+                <div class="fr" :class="phoneClass">
                   <el-form-item label="手机号 :" prop="phone">
                     <el-input
                       v-model.trim="form.phone"
                       maxlength="11"
-                      @input="inputPhone"
                       class="data-short-input item-input"
                       placeholder="请输入准确的手机号"
+                      @input="inputPhone"
                       @focus="phoneFocus"
-                    ></el-input>
+                    />
                   </el-form-item>
                 </div>
-                <div class="short-control fl" v-if="moduleShow.email">
+                <div v-if="moduleShow.email" class="short-control fl">
                   <el-form-item label="邮箱 :" prop="mail">
                     <el-input
                       v-model.trim="form.mail"
                       class="data-short-input item-input"
                       placeholder="请输入邮箱"
                       @focus="mailFocus"
-                    ></el-input>
+                    />
                   </el-form-item>
                 </div>
                 <!-- <div class="short-control fr" style="display:none;">
@@ -93,52 +95,54 @@
               </div>
             </div>
             <div class="company clearfix">
-              <div class="form-title">公司信息</div>
-              <div class="form-main">
+              <div v-if="showSecondstage" class="form-title">公司信息</div>
+              <div
+                class="form-main"
+                :class="{ 'form-main-none': !showSecondstage }"
+              >
                 <div class="long-control" style="position: relative">
                   <el-form-item label="公司名称 :" prop="companyName">
                     <el-input
                       v-model.trim="form.companyName"
                       class="data-long-input item-input company-name"
+                      placeholder="请输入准确的公司名称"
                       @focus="companyFocus"
                       @input="searchCompany"
-                      placeholder="请输入准确的公司名称"
-                    >
-                    </el-input>
+                    />
                   </el-form-item>
-                  <div class="company-result" v-if="showSearchResult">
+                  <div v-if="showSearchResult" class="company-result">
                     <div
-                      class="company-list"
                       v-for="item in companyList"
                       :key="item"
+                      class="company-list"
                       @click="selectCompany(item)"
                       v-html="item"
-                    ></div>
+                    />
                   </div>
                 </div>
-                <div class="long-control" v-if="moduleShow.companyType">
+                <div v-if="moduleShow.companyType" class="long-control">
                   <el-form-item
                     label="公司类型 :"
                     prop="companyType"
                     class="company-type"
                   >
                     <el-checkbox-group v-model="form.companyType">
-                      <el-checkbox label="投标企业"></el-checkbox>
-                      <el-checkbox label="招标采购单位"></el-checkbox>
-                      <el-checkbox label="厂商"></el-checkbox>
-                      <el-checkbox label="招标代理机构"></el-checkbox>
-                      <el-checkbox label="经销商"></el-checkbox>
-                      <el-checkbox label="服务提供商"></el-checkbox>
-                      <el-checkbox label="其他"></el-checkbox>
+                      <el-checkbox label="投标企业" />
+                      <el-checkbox label="招标采购单位" />
+                      <el-checkbox label="厂商" />
+                      <el-checkbox label="招标代理机构" />
+                      <el-checkbox label="经销商" />
+                      <el-checkbox label="服务提供商" />
+                      <el-checkbox label="其他" />
                     </el-checkbox-group>
                   </el-form-item>
                 </div>
-                <div class="long-control" v-if="moduleShow.job">
+                <div v-if="moduleShow.job" class="long-control">
                   <el-form-item label="职位 :" prop="job">
                     <el-select
+                      v-model="form.job"
                       popper-class="collect-custom-select"
                       :popper-append-to-body="false"
-                      v-model="form.job"
                       placeholder="请选择职位"
                       class="data-short-input item-input job-input"
                       clearable
@@ -148,18 +152,17 @@
                         :key="item.value"
                         :label="item.label"
                         :value="item.value"
-                      >
-                      </el-option>
+                      />
                     </el-select>
                     <!-- <el-input v-if="showOtherJob" v-model.trim="form.otherJob" class="data-short-input item-input job-name-iput"
                       @focus="otherFocus" placeholder="请输入职位名称">
                     </el-input> -->
                     <el-select
-                      style="margin-left: 12px"
                       v-if="showBranch"
+                      v-model="form.branch"
+                      style="margin-left: 12px"
                       :popper-append-to-body="false"
                       popper-class="collect-custom-select"
-                      v-model="form.branch"
                       placeholder="请选择部门"
                       class="data-short-input item-input job-input"
                       clearable
@@ -169,8 +172,7 @@
                         :key="item.value"
                         :label="item.label"
                         :value="item.value"
-                      >
-                      </el-option>
+                      />
                     </el-select>
                   </el-form-item>
                 </div>
@@ -187,47 +189,49 @@
                     <el-input type="textarea" autosize resize="none" v-model="form.business" placeholder="请输入业务范围,让合作伙伴充分了解公司业务内容"></el-input>
                   </el-form-item>
                 </div> -->
-                <div class="long-control" v-if="moduleShow.dataInput">
+                <div v-if="moduleShow.dataInput" class="long-control">
                   <el-form-item label="数据需求 :">
                     <el-input
+                      v-model="form.dataInput"
                       type="textarea"
                       :autosize="{ minRows: 3, maxRows: 4 }"
                       resize="none"
-                      v-model="form.dataInput"
                       placeholder="请描述下您需要的数据"
-                    ></el-input>
+                    />
                   </el-form-item>
                 </div>
               </div>
             </div>
-            <div class="warm-prompt" v-if="moduleShow.tip">
-              <span class="icon-warning"></span>
+            <div v-if="moduleShow.tip" class="warm-prompt">
+              <span class="icon-warning" />
               <span class="warm-text"
                 >温馨提示:请提供准确的信息,我们将为您推荐更准确、更个性化的商机和服务</span
               >
             </div>
-            <div class="agree-service" v-if="moduleShow.agree">
-              <el-checkbox v-model="form.agreeChecked"
-                >&nbsp;我同意剑鱼标讯将业务范围及合作需求提供给潜在合作伙伴搜索、查看</el-checkbox
-              >
+            <div v-if="moduleShow.agree" class="agree-service">
+              <el-checkbox v-model="form.agreeChecked">
+                &nbsp;我同意剑鱼标讯将业务范围及合作需求提供给潜在合作伙伴搜索、查看
+              </el-checkbox>
             </div>
             <div class="dialog-footer">
-              <el-button class="cancel-btn" @click="cancelForm">{{
-                computedDialogInfo.cancelText
-              }}</el-button>
-              <el-button class="submit-btn" @click="submitForm('ruleForm')">{{
-                computedDialogInfo.submitText
-              }}</el-button>
+              <el-button class="cancel-btn" @click="cancelForm">
+                {{ computedDialogInfo.cancelText }}
+              </el-button>
+              <el-button class="submit-btn" @click="submitForm('ruleForm')">
+                {{ computedDialogInfo.submitText }}
+              </el-button>
             </div>
           </el-form>
         </div>
       </div>
     </div>
-    <div id="success-dialog" style="display: none" v-show="showSuccess">
-      <div class="mask"></div>
+    <div v-show="showSuccess" id="success-dialog" style="display: none">
+      <div class="mask" />
       <div class="success-dialog-container">
         <div class="success-title">提交成功</div>
-        <div class="success-content">{{ successText }}</div>
+        <div class="success-content">
+          {{ successText }}
+        </div>
         <div class="success-footer">
           <button type="button" class="custom-btn go-know" @click="knowHandle">
             我知道了
@@ -237,44 +241,45 @@
     </div>
   </div>
 </template>
+
 <script>
 import {
-  industryJson,
-  jobJson,
-  companyScaleJson,
-  branchJson
-} from '@/assets/js/selector.js'
-import {
-  Form,
-  FormItem,
   Button,
-  CheckboxGroup,
+  Cascader,
   Checkbox,
-  Select,
+  CheckboxGroup,
+  Form,
+  FormItem,
   Input,
   Option,
-  Cascader
+  Select
 } from 'element-ui'
 import { mapState } from 'vuex'
+import {
+  branchJson,
+  companyScaleJson,
+  industryJson,
+  jobJson
+} from '@/assets/js/selector.js'
 import { debounce } from '@/utils/'
 import { ajaxGetSourceTitleAndDesc } from '@/api/modules'
 /**
  * 根据keys校验object必填项
  * @param {Array} keys - 待校验字段keys
- * @param {Object} target - 待校验object
+ * @param {object} target - 待校验object
  * @returns {boolean} - 是否通过
  */
 function checkRequiredKeys(keys, target) {
   try {
     // 职位/部门特殊处理, 满足时移除部门校验
-    if (keys.indexOf('branch') !== -1 && keys.indexOf('position') !== -1) {
+    if (keys.includes('branch') && keys.includes('position')) {
       if (target.position === '总裁' || target.position === '总经理') {
         keys.splice(keys.indexOf('branch'), 1)
       }
     }
-    return !keys.some(function (k) {
-      var tempValue = target[k]
-      var result = false
+    return !keys.some((k) => {
+      let tempValue = target[k]
+      let result = false
       if (typeof tempValue === 'number') {
         tempValue = tempValue.toString()
       }
@@ -305,6 +310,12 @@ export default {
     [Option.name]: Option,
     [Cascader.name]: Cascader
   },
+  props: {
+    showSecondstage: {
+      type: Boolean,
+      default: true
+    }
+  },
   data() {
     var validName = (rule, value, callback) => {
       if (value === '') {
@@ -569,6 +580,8 @@ export default {
           '请升级大会员,提前1-3个月获取项目采购计划,获取采购内容,提前运作提高中标率。',
         pc_article_customization:
           '请留下您的联系方式及定制数据字段需求,我们将安排专业的数据经理与您对接,为您打造专属的数据服务方案,可快速交付!',
+        pc_DataExportPayment_IndustryFields:
+          '请留下您的联系方式及定制数据字段需求,我们将安排专业的数据经理与您对接,为您打造专属的数据服务方案,可快速交付!',
         jyarticle_see3_plus_pc:
           '请完善个人信息,即刻享无限次查看标讯的权益,如需查看超前项目请联系客服:400-108-6670',
         article_purchase_intention:
@@ -631,6 +644,7 @@ export default {
         pc_article_project_limit: '申请监控更多项目',
         pc_article_cqxmmore: '申请查看超前项目',
         pc_article_customization: '量身定制专属的数据解决方案',
+        pc_DataExportPayment_IndustryFields: '量身定制专属的数据解决方案',
         jyarticle_see3_plus_pc: '免费享无限次查看标讯体验',
         pc_article_original_one: '申请更多查看原文链接权限',
         pc_article_original_more: '申请更多查看原文链接权限',
@@ -668,6 +682,8 @@ export default {
         pc_article_certificateServices: '标讯详情页-咨询企业认证服务',
         pc_article_CustomerRecommend: '标讯详情页-申请客户推荐',
         pc_article_customization: '标讯详情页-申请数据定制',
+        pc_DataExportPayment_IndustryFields:
+          '数据自助导出-支付订单-数据规格-申请数据定制行业字段-pc',
         jyarticle_see3_plus_pc: '免费享无限次查看标讯体验',
         pc_article_original_one: '标讯详情页-免费用户获取1次查看原文链接机会',
         pc_article_original_more: '标讯详情页-获取更多查看原文链接机会',
@@ -694,6 +710,20 @@ export default {
     ...mapState({
       info: (state) => state.user.info
     }),
+    phoneClass() {
+      if (this.source == 'pc_DataExportPayment_IndustryFields') {
+        return 'long-control'
+      } else {
+        return 'short-control'
+      }
+    },
+    nameClass() {
+      if (this.source == 'pc_DataExportPayment_IndustryFields') {
+        return 'long-control'
+      } else {
+        return 'short-control'
+      }
+    },
     showOtherJob: function () {
       return this.form.job === '其他'
     },
@@ -725,6 +755,12 @@ export default {
             '/common-module/pc-dialog/image/title/api-leave-title-text.png'
           break
         }
+        case 'pc_DataExportPayment_IndustryFields': {
+          result.use = true
+          result.title =
+            '/common-module/pc-dialog/image/title/custom-data-title-text.png'
+          break
+        }
       }
 
       return result
@@ -771,7 +807,10 @@ export default {
         this.source === 'pc_article_project_more'
       ) {
         return '已收到您提交的升级大会员申请,我们会尽快联系您并预约演示时间。'
-      } else if (this.source === 'pc_article_customization') {
+      } else if (
+        this.source === 'pc_article_customization' ||
+        this.source === 'pc_DataExportPayment_IndustryFields'
+      ) {
         return '已收到您提交的数据定制申请,我们的数据经理会尽快联系您~'
       } else if (this.source === 'peugeot_supplier_regist') {
         return '我们会尽快联系您完成供应商报名,请耐心等待。'
@@ -886,6 +925,7 @@ export default {
       this.moduleShow.tip = true
 
       switch (source) {
+        case 'pc_DataExportPayment_IndustryFields':
         case 'pc_article_customization': {
           this.moduleShow.companyType = false
           this.moduleShow.job = false
@@ -919,6 +959,7 @@ export default {
     addMoreParams(source, params, type = true) {
       let result = params
       switch (source) {
+        case 'pc_DataExportPayment_IndustryFields':
         case 'pc_article_customization': {
           if (type) {
             result.data_requirement = this.form.dataInput
@@ -1263,6 +1304,7 @@ export default {
 }
 /* eslint-enable */
 </script>
+
 <style lang="scss">
 /* element-ui reset */
 .custom-cascader {
@@ -1516,6 +1558,9 @@ export default {
   .form-main {
     margin: 10px 0 14px;
   }
+  .form-main-none {
+    margin: 0;
+  }
   .short-control {
     width: calc(50% - 16px);
   }

+ 7 - 3
apps/bigmember_pc/src/components/filter-items/PersonStaffsSelector.vue

@@ -4,7 +4,7 @@
     :placeholder="placeholder"
     :trigger="trigger"
     :value="computedVal"
-    @visible="onVisibleChange"
+    @onClick="onVisibleChange"
   ></Layout>
 </template>
 
@@ -28,6 +28,10 @@ export default {
       type: String,
       default: '接收人员'
     },
+    preText: {
+      type: String,
+      default: '接收人员'
+    },
     value: {
       type: [String, Object],
       default: ''
@@ -37,9 +41,9 @@ export default {
     computedVal() {
       if (this.value) {
         if (this.value.indexOf(',') !== -1) {
-          return '接收人员' + this.value.split(',').length + '人'
+          return this.preText + this.value.split(',').length + '人'
         } else {
-          return '接收人员1人'
+          return this.preText + '1人'
         }
       } else {
         return ''

+ 86 - 40
apps/bigmember_pc/src/components/selector/timeDropdown.vue

@@ -1,32 +1,70 @@
 <template>
-  <div class="select-container" :class="{ 'custom-select-auto-container': !isDefault }">
-    <el-select ref="selectSelector" v-model="activeLabel" :popper-append-to-body="false"
-      @visible-change="onVisibleChange" @mouseenter.native="onSelectMouseEnter" @mouseleave.native="onSelectMouseLeave"
-      :placeholder="placeholder" popper-class="select-custom">
+  <div
+    class="select-container"
+    :class="{ 'custom-select-auto-container': !isDefault }"
+  >
+    <el-select
+      ref="selectSelector"
+      v-model="activeLabel"
+      :popper-append-to-body="false"
+      :placeholder="placeholder"
+      popper-class="select-custom"
+      @visible-change="onVisibleChange"
+      @mouseenter.native="onSelectMouseEnter"
+      @mouseleave.native="onSelectMouseLeave"
+    >
       <template v-if="!isDefault" slot="prefix">
         <div class="select-prefix">
-          <span class="select-prefix-value highlight-text" v-if="activeLabel">{{ activeLabel }}</span>
-          <span class="select-prefix-value" v-else>{{ placeholder }}</span>
-          <i class="iconfont icon-xiala" :class="{ 'is-reverse': isFocus }"></i>
+          <span v-if="activeLabel" class="select-prefix-value highlight-text">{{
+            activeLabel
+          }}</span>
+          <span v-else class="select-prefix-value">{{ placeholder }}</span>
+          <i class="iconfont icon-xiala" :class="{ 'is-reverse': isFocus }" />
         </div>
       </template>
       <template slot="empty">
         <div class="time-container">
-          <div class="time-item" :class="{ 'active': item.value === activeValue || (item.disabled && isCustom) }"
-            v-for="item in options" :key="item.label" :label="item.label" :value="item.value"
-            @click="handleChange(item)">
-            <el-popover v-if="item.disabled" class="custom-popover" :append-to-body="false" placement="right-end"
-              :trigger="popoverTrigger" :offset="12" v-model="showPopover" @show="popShow" @hide="popHide"
-              ref="customPopover">
+          <div
+            v-for="item in options"
+            :key="item.label"
+            class="time-item"
+            :class="{
+              active: item.value === activeValue || (item.disabled && isCustom)
+            }"
+            :label="item.label"
+            :value="item.value"
+            @click="handleChange(item)"
+          >
+            <el-popover
+              v-if="item.disabled"
+              ref="customPopover"
+              v-model="showPopover"
+              class="custom-popover"
+              :append-to-body="false"
+              placement="right-end"
+              :trigger="popoverTrigger"
+              :offset="12"
+              @show="popShow"
+              @hide="popHide"
+            >
               <div class="custom-time">
-                <el-date-picker ref="timePick" class="time-pick" :append-to-body="false" v-model="p_time"
-                  type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"
-                  value-format="timestamp" align="center" @change="dateChange">
-                </el-date-picker>
+                <el-date-picker
+                  ref="timePick"
+                  v-model="p_time"
+                  class="time-pick"
+                  :append-to-body="false"
+                  type="daterange"
+                  range-separator="-"
+                  start-placeholder="开始日期"
+                  end-placeholder="结束日期"
+                  value-format="timestamp"
+                  align="center"
+                  @change="dateChange"
+                />
               </div>
               <div slot="reference" class="custom-label">
                 <span>{{ item.label }}</span>
-                <i class="el-icon-arrow-right"></i>
+                <i class="el-icon-arrow-right" />
               </div>
             </el-popover>
             <span v-else>{{ item.label }}</span>
@@ -36,11 +74,14 @@
     </el-select>
   </div>
 </template>
+
 <script>
-import { Select, Option, Popover, Button, Input, DatePicker } from 'element-ui'
+import { Button, DatePicker, Input, Option, Popover, Select } from 'element-ui'
+import dayjs from 'dayjs'
 import { dateFormatter } from '@/utils/'
+
 export default {
-  name: 'timeDropdown',
+  name: 'TimeDropdown',
   components: {
     [Select.name]: Select,
     [Option.name]: Option,
@@ -54,7 +95,7 @@ export default {
      * select 回显的输入框是否使用默认样式
      * true:自带输入框
      * false: 回显框为根据回显内容自适应宽度,下拉箭头为实心三角箭头
-    */
+     */
     isDefault: {
       type: Boolean,
       default: false
@@ -62,7 +103,7 @@ export default {
     /**
      * 下拉框展开方式
      * hover: 鼠标悬浮 click: 点击
-    */
+     */
     trigger: {
       type: String,
       default: 'click'
@@ -74,7 +115,7 @@ export default {
     /**
      * 自定义popover展开方式
      * hover: 鼠标悬浮 click: 点击
-    */
+     */
     popoverTrigger: {
       type: String,
       default: 'hover'
@@ -142,19 +183,19 @@ export default {
     activeLabel() {
       const time = this.activeValue
       if (time) {
-        if (this.options.find(v => v.value === time)) {
-          let label = this.options.find(v => v.value === time).label
+        if (this.options.find((v) => v.value === time)) {
+          const label = this.options.find((v) => v.value === time).label
           if (label === '自定义') {
-            let start = time.split('-')[0]
-            let end = time.split('-')[1]
-            return dateFormatter(Number(start), 'yyyy-MM-dd') + '-' + dateFormatter(Number(end), 'yyyy-MM-dd')
+            const start = time.split('-')[0]
+            const end = time.split('-')[1]
+            return `${dateFormatter(Number(start), 'yyyy-MM-dd')}-${dateFormatter(Number(end), 'yyyy-MM-dd')}`
           } else {
             return label || ''
           }
         } else {
-          let start = time.split('-')[0]
-          let end = time.split('-')[1]
-          return dateFormatter(Number(start), 'yyyy-MM-dd') + '-' + dateFormatter(Number(end), 'yyyy-MM-dd')
+          const start = time.split('-')[0]
+          const end = time.split('-')[1]
+          return `${dateFormatter(Number(start), 'yyyy-MM-dd')}-${dateFormatter(Number(end), 'yyyy-MM-dd')}`
         }
       } else {
         return ''
@@ -168,7 +209,9 @@ export default {
           setTimeout(() => {
             // popover在下拉框展示时需要重新计算位置,通过先将popover弹框透明度将为0等位置计算完成后再恢复
             this.$refs.customPopover[0].updatePopper()
-            const $popover = this.$root.$el.querySelector('.custom-popover > .el-popover')
+            const $popover = this.$root.$el.querySelector(
+              '.custom-popover > .el-popover'
+            )
             $popover.style.opacity = '1'
             if (this.$refs.timePick) {
               this.$refs.timePick[0].focus()
@@ -218,12 +261,13 @@ export default {
     },
     dateChange(val) {
       if (!val || val.length === 0) return
-      let start = val[0]
+      const start = val[0]
       let end = val[1]
+      end = dayjs(end).endOf('day').valueOf()
       this.activeValue = `${start}-${end}`
       this.time.start = start
       this.time.end = end
-      this.options.forEach(item => {
+      this.options.forEach((item) => {
         if (item.label === '自定义') {
           item.value = `${start}-${end}`
         }
@@ -260,7 +304,9 @@ export default {
     setState(data) {
       this.isCustom = false
       if (data) {
-        const valueArr = this.options.filter(v => !v.disabled).map(t => t.value)
+        const valueArr = this.options
+          .filter((v) => !v.disabled)
+          .map((t) => t.value)
         if (valueArr.includes(data)) {
           this.activeValue = data
         } else {
@@ -274,7 +320,9 @@ export default {
           this.p_time = [start, end]
           this.showPopover = true
           this.$nextTick(() => {
-            const $popover = this.$root.$el.querySelector('.custom-popover > .el-popover')
+            const $popover = this.$root.$el.querySelector(
+              '.custom-popover > .el-popover'
+            )
             $popover.style.opacity = '0'
           })
         }
@@ -296,7 +344,6 @@ export default {
 
   .time-pick {
     ::v-deep {
-
       .el-range-input,
       .el-input__icon,
       .el-range-separator {
@@ -309,7 +356,6 @@ export default {
         top: -48px !important;
       }
     }
-
   }
 
   ::v-deep {
@@ -360,7 +406,7 @@ export default {
         font-size: 16px;
         flex-shrink: 0;
         transform: rotate(0deg);
-        transition: transform .5s;
+        transition: transform 0.5s;
 
         &.is-reverse {
           transform: rotate(180deg);
@@ -427,7 +473,7 @@ export default {
       cursor: pointer;
 
       &:hover {
-        background: #ECECEC;
+        background: #ececec;
       }
 
       &.active {

+ 35 - 5
apps/bigmember_pc/src/components/subscribe-manager/powerPerson.vue

@@ -87,7 +87,7 @@
         >
       </p>
       <span slot="footer" class="dialog-footer btns">
-        <button :disabled="showBtn" @click="saveDepart" class="save">
+        <button :disabled="saveButtonDisabled" @click="saveDepart" class="save">
           保存
         </button>
         <button @click="centerCancel" class="cancle">取消</button>
@@ -132,6 +132,16 @@ export default {
     vt: {
       type: String,
       default: ''
+    },
+    // 允许空保存
+    noSelect: {
+      type: Boolean,
+      default: false
+    },
+    // 回显选中项
+    selectIds: {
+      type: String,
+      default: ''
     }
   },
   data() {
@@ -203,7 +213,7 @@ export default {
       handler(newval) {
         this.showBtn = !newval.length
       }
-    },
+    }
   },
   computed: {
     ...mapState({
@@ -247,9 +257,25 @@ export default {
         arr = []
       }
       return arr
+    },
+    saveButtonDisabled() {
+      if (this.noSelect) {
+        return false
+      }
+      return this.showBtn
     }
   },
   methods: {
+    // 回显选择人员
+    echoSelectIds(ids = this.selectIds) {
+      const Ids = ids.split(',')
+      console.log('echo ids', Ids)
+      if (this.personList.length) {
+        this.selectedList = this.personList.filter((v) =>
+          Ids.includes(String(v.id))
+        )
+      }
+    },
     debounce(fn, delay) {
       let timer = null
       return function () {
@@ -261,7 +287,7 @@ export default {
       }
     },
     // 获取人员列表
-    async getData(flag = 'no', num) {
+    async getData(flag = 'no', num, echo = 'no', inject = () => {}) {
       this.filterType = !!num
       this.loading = true
       const selectAreas = []
@@ -274,6 +300,7 @@ export default {
           }
         })
       })
+      inject(this)
       const params = {
         region: num ? '' : selectAreas.toString(),
         queryType: num || ''
@@ -289,6 +316,9 @@ export default {
         })
         this.personList = res.data
         this.personSpareList = res.data
+        if (echo === 'yes') {
+          this.echoSelectIds()
+        }
         if (flag === 'yes') {
           if (this.searchVal) {
             this.allQuan = false
@@ -333,7 +363,7 @@ export default {
           delId.push(v.id)
         })
         this.centerDialogVisible = false
-        if (delId.length !== 0) {
+        if (delId.length !== 0 || this.noSelect) {
           if (this.filterType) {
             this.$store.commit('user/getSelectList', this.selectedList)
             this.$emit('filterDiatribution', delId.toString())
@@ -541,7 +571,7 @@ export default {
             border: 1px solid #ececec;
             border-radius: 22px;
           }
-          .el-input__prefix{
+          .el-input__prefix {
             display: flex;
             align-items: center;
           }

+ 2 - 1
apps/bigmember_pc/src/router/router-interceptors.js

@@ -16,7 +16,8 @@ const powerCheckPathWhiteRegList = [
   /order/,
   /article/,
   /pdf/,
-  /doc\/api/
+  /doc\/api/,
+  /data_pack/,
 ]
 // 权限控制白名单-路由名
 const powerCheckWhiteList = [

+ 24 - 0
apps/bigmember_pc/src/router/routers.js

@@ -40,6 +40,30 @@ export default [
     name: 'big_subscribe',
     component: () => import('@/views/subscribe/SubPush.vue')
   },
+  // 数据导出记录
+  {
+    path: '/data_pack',
+    name: 'data_pack',
+    component: () => import('@/views/datapack/Home.vue'),
+    children: [
+      {
+        path: '',
+        redirect: '/data_pack/index'
+      },
+      // 我的数据流量包
+      {
+        path: 'index',
+        name: 'data_pack_index',
+        component: () => import('@/views/datapack/pages/Home.vue')
+      },
+      // 数据导出记录
+      {
+        path: 'history',
+        name: 'data_pack_history',
+        component: () => import('@/views/datapack/pages/History.vue')
+      },
+    ]
+  },
   // 潜在客户/对手
   {
     path: '/potential_cor_list/:type',

+ 383 - 0
apps/bigmember_pc/src/views/datapack/Home.vue

@@ -0,0 +1,383 @@
+<script setup>
+
+import { getCurrentInstance, onMounted, watch, ref, computed } from 'vue'
+import { useRoute, useRouter } from 'vue-router/composables'
+import { TabPane, Tabs} from 'element-ui'
+import { initPageModel } from '@/views/datapack/model/home'
+
+const router = useRouter()
+const route = useRoute()
+
+const activeTab = ref('')
+
+const tabs = [
+  { path: 'index', label: '我的数据流量包' },
+  { path: 'history', label: '数据导出记录' }
+]
+
+const that = getCurrentInstance().proxy
+
+function handleTabClick(tab) {
+  that.$router.push({ path: `/data_pack/${tab.name}` })
+}
+
+// 监听路由变化,更新激活标签
+watch(route, (to) => {
+  activeTab.value = to.path.replace('/data_pack/', '')
+  initPageModel()
+})
+
+// 在组件挂载时初始化激活标签
+onMounted(() => {
+  activeTab.value = route.path.replace('/data_pack/', '')
+  initPageModel()
+})
+
+
+</script>
+
+<template>
+  <div class='data-pack-layout'>
+    <div class='d-packet-tabs'>
+      <el-tabs v-model="activeTab" @tab-click="handleTabClick">
+        <el-tab-pane
+          v-for="tab in tabs"
+          :key="tab.path"
+          :label="tab.label"
+          :name="tab.path"
+        ></el-tab-pane>
+        <router-view></router-view>
+      </el-tabs>
+    </div>
+  </div>
+</template>
+
+<style lang='scss'>
+.data-pack-layout {
+  .flex {
+    display: flex;
+    align-items: center;
+  }
+  .d-packet-container {
+    line-height: 1;
+    padding-bottom: 70px;
+    min-height: calc(100vh - 364px);
+    background: #ececec;
+  }
+  .d-packet-tabs .el-tabs__header {
+    background: rgba(255,255,255,0.5);
+    box-shadow: 0px -1px 0px 0px rgba(0,0,0,0.05) inset;
+    margin: 0;
+  }
+
+  .d-packet-tabs .el-tabs__header .el-tabs__nav-wrap, .d-packet-tabs .el-tabs__header .el-tabs__nav-wrap .el-tabs__nav {
+    display: flex;
+    flex-direction: initial;
+    justify-content: center;
+  }
+
+  .d-packet-tabs .el-tabs__nav-wrap::after {
+    background: none;
+  }
+
+  .d-packet-tabs .el-tabs__header .el-tabs__nav-wrap .el-tabs__item {
+    width: 240px;
+    height: 48px;
+    line-height: 48px;
+    font-size: 14px;
+    text-align: center;
+    color: #686868;
+    padding: 0;
+  }
+
+  .d-packet-tabs .el-tabs__header .el-tabs__nav-wrap .el-tabs__item.is-active {
+    color: #1d1d1d;
+  }
+
+  .d-packet-tabs .el-tabs__header .el-tabs__nav-wrap .el-tabs__item:hover {
+    color: #1d1d1d;
+  }
+
+  .d-packet-tabs .el-tabs__content {
+    width: 100%;
+    min-height: 500px;
+    padding-bottom: 93px;
+  }
+
+  .data-box {
+    width: 1080px;
+    margin: 48px auto 0 auto;
+  }
+
+  .data-box .up-line h3 {
+    line-height: 28px;
+    font-size: 18px;
+    margin-bottom: 24px;
+  }
+
+  .data-box .up-line .d-conts {
+    flex-wrap: wrap;
+    justify-content: space-between;
+  }
+
+  .data-box .up-line .d-conts .d-card {
+    width: 49%;
+    min-height: 142px;
+    background: #ffffff;
+    border-radius: 8px;
+    box-shadow: 0px 0px 24px 0px rgba(0,0,0,0.06);
+    margin-bottom: 20px;
+    padding: 24px 32px;
+  }
+
+  .data-box .up-line .d-conts .d-card h4 {
+    line-height: 24px;
+    background-position: left;
+    padding-left: 28px;
+  }
+
+  .data-box .up-line .d-conts .d-card .words {
+    flex-direction: inherit;
+    justify-content: space-around;
+    align-items: center;
+  }
+
+  .data-box .up-line .d-conts .d-card .words .nums {
+    text-align: center;
+    margin-top: 16px;
+  }
+
+  .data-box .up-line .d-conts .d-card .words .nums .p1 {
+    font-size: 20px;
+    color: #1d1d1d;
+    line-height: 32px;
+  }
+
+  .data-box .up-line .d-conts .d-card .words .nums .p1 i {
+    font-size: 14px;
+    color: #686868;
+    margin-left: 4px;
+  }
+
+  .data-box .up-line .d-conts .d-card .words .nums .p2 {
+    font-size: 14px;
+    color: #686868;
+    line-height: 22px;
+  }
+
+  .data-box .up-line .d-conts .d-card .words .r-code .p1, .data-box .up-line .d-conts .d-card .words .r-code .p1 i {
+    color: #FF9F40;
+  }
+
+  .data-box .down-line .d-chong {
+    flex-direction: column;
+    width: 100%;
+    height: 242px;
+    background: #ffffff;
+    border-radius: 8px;
+    box-shadow: 0px 0px 24px 0px rgba(0,0,0,0.06);
+  }
+
+  .data-box .down-line .d-chong .d-head {
+    display: flex;
+    flex-direction: initial;
+    align-items: center;
+    justify-content: space-between;
+    height: 122px;
+    border-radius: 8px 8px 0 0;
+    padding: 28px 32px;
+    background: -webkit-linear-gradient(180deg,#14a7d6 0%, #1ec2db 100%);
+  }
+
+  .data-box .down-line .d-chong .d-head .s-zhu {
+    color: #fff;
+  }
+
+  .data-box .down-line .d-chong .d-head .el-button {
+    border: none;
+  }
+
+  .data-box .down-line .d-chong .d-head .s-zhu .p1 {
+    font-size: 16px;
+    font-weight: 700;
+    line-height: 24px;
+  }
+
+  .data-box .down-line .d-chong .d-head .s-zhu .p1 i {
+    font-size: 24px;
+    margin: 0 8px;
+  }
+
+  .data-box .down-line .d-chong .d-head .s-zhu .p2 {
+    font-size: 14px;
+    color: rgba(255,255,255,0.90);
+    line-height: 22px;
+    margin-top: 8px;
+  }
+
+  .data-box .down-line .d-chong .d-head .el-button {
+    padding: 0;
+    width: 132px;
+    height: 36px;
+    border-radius: 6px;
+    color: #2cb7ca;
+    font-size: 16px;
+  }
+
+  .data-box .down-line .d-chong .c-cont {
+    justify-content: space-around;
+    padding: 32px 0;
+  }
+
+  .data-box .down-line .d-chong .c-cont .l-shu {
+    width: 50%;
+  }
+
+  .data-box .down-line .d-chong .c-cont .nums {
+    width: 50%;
+    text-align: center;
+  }
+
+  .data-box .down-line .d-chong .c-cont .nums .p1 {
+    font-size: 20px;
+    color: #1d1d1d;
+    line-height: 32px;
+  }
+
+  .data-box .down-line .d-chong .c-cont .nums .p1 i {
+    font-size: 14px;
+    color: #686868;
+    margin-left: 4px;
+  }
+
+  .data-box .down-line .d-chong .c-cont .nums .p2 {
+    font-size: 14px;
+    color: #686868;
+    line-height: 22px;
+  }
+
+  .data-box .down-line .d-chong .c-cont .r-code .p1, .data-box .down-line .d-chong .c-cont .r-code .p1 i {
+    color: #FF9F40;
+  }
+
+  .data-box .down-line .d-chong .c-cont .el-divider--vertical {
+    height: 56px;
+  }
+
+  .data-box .up-biao {
+    margin: 32px 0 24px 0;
+  }
+
+  .data-box .up-biao .el-table {
+    border-radius: 8px 8px 0 0;
+  }
+
+  .data-box .up-biao .el-table tr th {
+    color: #1d1d1d;
+    font-weight: 700;
+  }
+
+  .data-box .up-biao .el-table .el-table__header {
+    padding-top: 20px;
+  }
+
+  .data-box .up-biao .biao-ge {
+    background: #fff;
+    padding-bottom: 32px;
+    border-radius: 8px;
+  }
+
+  .data-box .up-biao .biao-ge .el-table .el-loading-mask {
+    z-index: 99!important;
+  }
+
+  .data-box .up-biao .biao-ge .el-table .searchConditions {
+    text-align: left;
+  }
+
+  .data-box .up-biao .biao-ge .el-table .searchConditions .secondEillps {
+    text-overflow: -o-ellipsis-lastline;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    display: -webkit-box;
+    -webkit-line-clamp: 2;
+    line-clamp: 2;
+    -webkit-box-orient: vertical;
+  }
+
+  .data-box .up-biao .biao-ge .el-table .searchConditions .secondEillps .imptySeach {
+    width: 100%;
+    text-align: center;
+    display: inline-block;
+  }
+
+  .data-box .up-biao .biao-ge .el-pagination {
+    margin-top: 32px;
+    padding: 0;
+    padding: 2px 32px;
+    text-align: right;
+  }
+
+  .el-pagination.is-background .btn-next, .el-pagination.is-background .btn-prev, .el-pagination.is-background .el-pager li {
+    background: none;
+    border: 1px solid #f4f4f5;
+  }
+
+  .data-box .up-biao .no-data {
+    width: 160px;
+    margin: 56px auto 0 auto;
+  }
+
+  .data-box .up-biao .no-data .tip-text {
+    color: #999999;
+    line-height: 22px;
+    font-size: 14px;
+    text-align: center;
+    margin-top: 24px;
+  }
+
+  .exports {
+    margin-top: 32px;
+    padding-bottom: 32px;
+  }
+
+  .exports .up-biao {
+    margin: 0;
+  }
+
+  .exports .biao-ge {
+    padding-top: 20px;
+  }
+
+  .exports .up-biao .el-table tr th {
+    padding: 8px 0;
+  }
+
+  .exports .el-table tr td {
+    /* padding: 0; */
+    height: 72px;
+  }
+  .exports .el-table tr td .dian-ji {
+    color: #2CB7CA;
+    text-decoration: none;
+  }
+  .exports .el-table .el-table__empty-block .no-data {
+    margin-top: 0;
+  }
+
+  .exports .up-biao .el-table .el-table__header {
+    padding-top: 12px;
+  }
+
+  .exports .el-table::before {
+    height: 0;
+  }
+
+  /* 媒体查询 */
+  @media screen and (max-width: 1460px) {
+    .data-box {
+      width: 1012px;
+    }
+  }
+}
+</style>

+ 1 - 1
apps/bigmember_pc/src/views/datapack/buy.vue

@@ -77,7 +77,7 @@
           <template v-slot:buy-tip-group>
             <p class="buy-tip">
               购买须知:
-              <br />1、数据流量包是数据自助导出充值服务,最低1000条起充,仅针对线上自助充值使<br />用(与线下企业数据充值账户不可共享);<br />
+              <br />1、数据流量包是个人数据自助导出充值服务,最低1000条起充,仅针对本人线上自助充值使<br />用(与企业数据流量包账户不可共享);<br />
               2、已购数据包条数按导出数据量扣除(数据已排重,相同数据多次导出,不重复计<br />费),有效期2年,因产品特殊性,购买后不支持退款。
             </p>
           </template>

+ 196 - 0
apps/bigmember_pc/src/views/datapack/model/history.js

@@ -0,0 +1,196 @@
+import { ref, computed } from 'vue'
+import { calcNotExactTime } from '@/utils'
+import dayjs from 'dayjs'
+import { changePageSizeOrNumOfHistory } from '@/views/datapack/model/home'
+import SelectSelector from '@/components/filter-items/SelectSelector.vue'
+import PersonStaffsSelector from '@/components/filter-items/PersonStaffsSelector.vue'
+import TimeDropdown from '@/components/selector/timeDropdown.vue'
+
+// 定义默认值
+const defaultFilters = {
+  staffs: '',
+  type: '',
+  selectTime: ''
+}
+
+// 创建 ref
+const filters = ref({ ...defaultFilters })
+const filtersKey = ref(`history-k-${Date.now()}`)
+
+// 重置值的方法
+const resetFilters = () => {
+  filters.value = { ...defaultFilters }
+  filtersKey.value = (`history-k-${Date.now()}`)
+}
+
+const computedFilters = computed(() => {
+  const isExactTime = /^\d+/.test(filters.value.selectTime)
+  const tranTime = {}
+  if (isExactTime) {
+    const selectTime = filters.value.selectTime?.split('-')
+    console.log(selectTime)
+    tranTime.start = dayjs(+selectTime[0]).unix()
+    tranTime.end = dayjs(+selectTime[1]).endOf('day').unix()
+  } else {
+    const selectTime = calcNotExactTime(filters.value.selectTime)
+    tranTime.start = dayjs(selectTime.start).unix()
+    tranTime.end = dayjs(selectTime.end).unix()
+  }
+
+  return {
+    userIds: filters.value.staffs,
+    payWay: filters.value.type,
+    starttime: tranTime.start,
+    endtime: tranTime.end
+  }
+})
+
+const payWayList = [
+  {
+    label: '全部',
+    value: ''
+  },
+  {
+    label: '个人支付',
+    value: '1'
+  },
+  {
+    label: '单日限量数据包',
+    value: '2'
+  },
+  {
+    label: '个人数据流量包(高级字段包)',
+    value: '3'
+  },
+  {
+    label: '个人数据流量包(标准字段包)',
+    value: '4'
+  },
+  {
+    label: '企业数据流量包',
+    value: '5'
+  }
+]
+
+const filterSchema = [
+  {
+    key: 'staffs',
+    label: '操作人',
+    defaultVal: '',
+    _name: 'type',
+    _type: 'component',
+    expand: {
+      component: PersonStaffsSelector,
+      hooks: {},
+      props: {
+        placeholder: '全部',
+        preText: '操作人',
+        cname: 'staffs'
+      }
+    }
+  },
+  {
+    key: 'selectTime',
+    label: '导出时间',
+    defaultVal: '',
+    _name: 'selectTime',
+    _type: 'component',
+    expand: {
+      component: TimeDropdown,
+      props: {
+        trigger: 'hover',
+        placeholder: '全部',
+        customPlacement: 'left-start',
+        selectData: [
+          {
+            value: '',
+            label: '全部'
+          },
+          {
+            value: 'lately-7',
+            label: '最近7天'
+          },
+          {
+            value: 'lately-30',
+            label: '最近30天'
+          },
+          {
+            value: 'lately90',
+            label: '最近90天'
+          },
+          {
+            value: 'thisyear',
+            label: '最近1年'
+          },
+          {
+            value: '0',
+            label: '自定义',
+            disabled: true
+          }
+        ],
+        selectorType: 'line',
+        singleChoice: true
+      },
+      hooks: {}
+    }
+  },
+  {
+    key: 'type',
+    label: '支付方式',
+    defaultVal: '',
+    _name: 'type',
+    _type: 'component',
+    expand: {
+      component: SelectSelector,
+      hooks: {},
+      props: {
+        options: payWayList,
+        placeholder: '全部',
+        cname: 'type'
+      }
+    }
+  }
+]
+
+function doSubmitDistribute(e) {
+  console.log('e', e)
+  filters.value.staffs = e
+  changePageSizeOrNumOfHistory({ params: computedFilters.value })
+}
+
+function doChangeFilter(f) {
+  changePageSizeOrNumOfHistory({ num: 1, params: computedFilters.value })
+}
+
+function onChangePageNum(num) {
+  changePageSizeOrNumOfHistory({ num, params: computedFilters.value })
+}
+function onChangePageSize(size) {
+  changePageSizeOrNumOfHistory({ size, params: computedFilters.value })
+}
+
+const usePowerRef = ref(null)
+
+function openDistribute(e) {
+  usePowerRef.value.titleMsg = '选择操作人'
+  usePowerRef.value.searchVal = ''
+  usePowerRef.value.centerDialogVisible = true
+  usePowerRef.value.selectDataIds = ''
+  usePowerRef.value.getData('yes', '1', 'yes', function (_this) {
+    _this.filterType = false
+  })
+}
+
+export {
+  filters,
+  filtersKey,
+  computedFilters,
+  filterSchema,
+  doChangeFilter,
+  doSubmitDistribute,
+  onChangePageSize,
+  onChangePageNum,
+  usePowerRef,
+  openDistribute,
+  resetFilters
+}

+ 224 - 0
apps/bigmember_pc/src/views/datapack/model/home-filter.js

@@ -0,0 +1,224 @@
+import { ref, computed } from 'vue'
+import { calcNotExactTime } from '@/utils'
+import dayjs from 'dayjs'
+import { changePageSizeOrNumOfHome } from '@/views/datapack/model/home'
+import SelectSelector from '@/components/filter-items/SelectSelector.vue'
+import PersonStaffsSelector from '@/components/filter-items/PersonStaffsSelector.vue'
+import TimeDropdown from '@/components/selector/timeDropdown.vue'
+
+// 定义默认值
+const defaultFilters = {
+  staffs: '',
+  account: '',
+  type: '',
+  selectTime: ''
+}
+
+// 创建 ref
+const filters = ref({ ...defaultFilters })
+const filtersKey = ref(`home-k-${Date.now()}`)
+// 重置值的方法
+const resetFilters = () => {
+  filters.value = { ...defaultFilters }
+  filtersKey.value = (`home-k-${Date.now()}`)
+}
+
+const computedFilters = computed(() => {
+  const isExactTime = /^\d+/.test(filters.value.selectTime)
+  const tranTime = {}
+  if (filters.value.selectTime) {
+    if (isExactTime) {
+      const selectTime = filters.value.selectTime?.split('-')
+      console.log(selectTime)
+      tranTime.start = dayjs(+selectTime[0]).format('YYYY-MM-DD HH:mm:ss')
+      tranTime.end = dayjs(+selectTime[1]).format('YYYY-MM-DD HH:mm:ss')
+    } else {
+      const selectTime = calcNotExactTime(filters.value.selectTime)
+      tranTime.start = dayjs(selectTime.start).format('YYYY-MM-DD HH:mm:ss')
+      tranTime.end = dayjs(selectTime.end).format('YYYY-MM-DD HH:mm:ss')
+    }
+  }
+
+  return {
+    userIds: filters.value.staffs,
+    account: filters.value.account,
+    type: filters.value.type,
+    starttime: tranTime.start,
+    endtime: tranTime.end
+  }
+})
+
+const accountList = [
+  {
+    label: '全部',
+    value: ''
+  },
+  {
+    label: '企业数据流量包',
+    value: '3'
+  },
+  {
+    label: '个人数据流量包(标准字段包)',
+    value: '2'
+  },
+  {
+    label: '个人数据流量包(高级字段包)',
+    value: '1'
+  }
+]
+const consumptionTypeList = [
+  {
+    label: '全部',
+    value: ''
+  },
+  {
+    label: '新增',
+    value: '2'
+  },
+  {
+    label: '消费',
+    value: '1'
+  },
+  {
+    label: '作废',
+    value: '3'
+  }
+]
+
+const filterSchema = [
+  {
+    key: 'staffs',
+    label: '人员',
+    defaultVal: '',
+    _name: 'type',
+    _type: 'component',
+    expand: {
+      component: PersonStaffsSelector,
+      hooks: {},
+      props: {
+        placeholder: '全部',
+        preText: '人员',
+        cname: 'staffs'
+      }
+    }
+  },
+  {
+    key: 'account',
+    label: '账户',
+    defaultVal: '',
+    _name: 'type',
+    _type: 'component',
+    expand: {
+      component: SelectSelector,
+      hooks: {},
+      props: {
+        options: accountList,
+        placeholder: '全部',
+        cname: 'account'
+      }
+    }
+  },
+  {
+    key: 'type',
+    label: '类型',
+    defaultVal: '',
+    _name: 'type',
+    _type: 'component',
+    expand: {
+      component: SelectSelector,
+      hooks: {},
+      props: {
+        options: consumptionTypeList,
+        placeholder: '全部',
+        cname: 'type'
+      }
+    }
+  },
+  {
+    key: 'selectTime',
+    label: '时间',
+    defaultVal: '',
+    _name: 'selectTime',
+    _type: 'component',
+    expand: {
+      component: TimeDropdown,
+      props: {
+        trigger: 'hover',
+        placeholder: '全部',
+        selectData: [
+          {
+            value: '',
+            label: '全部'
+          },
+          {
+            value: 'lately-7',
+            label: '最近7天'
+          },
+          {
+            value: 'lately-30',
+            label: '最近30天'
+          },
+          {
+            value: 'lately90',
+            label: '最近90天'
+          },
+          {
+            value: 'thisyear',
+            label: '最近1年'
+          },
+          {
+            value: '0',
+            label: '自定义',
+            disabled: true
+          }
+        ],
+        selectorType: 'line',
+        singleChoice: true
+      },
+      hooks: {}
+    }
+  }
+]
+
+function doSubmitDistribute(e) {
+  console.log('e', e)
+  filters.value.staffs = e
+  changePageSizeOrNumOfHome({ params: computedFilters.value })
+}
+
+function doChangeFilter(f) {
+  changePageSizeOrNumOfHome({ num: 1, params: computedFilters.value })
+}
+
+function onChangePageNum(num) {
+  changePageSizeOrNumOfHome({ num, params: computedFilters.value })
+}
+function onChangePageSize(size) {
+  changePageSizeOrNumOfHome({ size, params: computedFilters.value })
+}
+
+const usePowerRef = ref(null)
+
+function openDistribute(e) {
+  usePowerRef.value.titleMsg = '选择人员'
+  usePowerRef.value.searchVal = ''
+  usePowerRef.value.centerDialogVisible = true
+  usePowerRef.value.selectDataIds = ''
+  usePowerRef.value.getData('yes', '1', 'yes', function (_this) {
+    _this.filterType = false
+  })
+}
+
+export {
+  filters,
+  filtersKey,
+  computedFilters,
+  filterSchema,
+  doChangeFilter,
+  doSubmitDistribute,
+  onChangePageSize,
+  onChangePageNum,
+  usePowerRef,
+  openDistribute,
+  resetFilters
+}

+ 260 - 0
apps/bigmember_pc/src/views/datapack/model/home.js

@@ -0,0 +1,260 @@
+import { ref, onMounted, watch } from 'vue'
+import {
+  getDataPackRechargeList,
+  getDataPackRecordList,
+  getDataPackUsage
+} from '@/api/modules'
+import { openLinkInWorkspace } from '@/utils'
+import dayjs from 'dayjs'
+
+const dLoading = ref(false)
+const isLoading = ref(false)
+const dataList = ref({
+  entPack: [],
+  dailyPack: [],
+  personalPack: []
+})
+const chargeList = ref({
+  pageNum: 1,
+  pageSize: 10,
+  total: 0,
+  isAdmin: false,
+  list: []
+})
+const exportList = ref({
+  pageNum: 1,
+  pageSize: 10,
+  total: 0,
+  isAdmin: false,
+  list: []
+})
+
+function onClickRow(row, c, event) {
+  console.log('row', row, c, event)
+  if (
+    event.target.className !== 'dian-ji' &&
+    row.search != null &&
+    (row.search.comeinfrom === 'supersearchPage' ||
+      row.search.comeinfrom === 'exportPage')
+  ) {
+    openLinkInWorkspace(true, {
+      url: `/front/dataExport/toSieve?id=${row.search.id}`,
+      newTab: false
+    })
+  }
+  if (row?.data_from === '数据定制导出') {
+    openLinkInWorkspace(true, {
+      url: `/seplatform/client/rule/edit?id=${row.search.id}`,
+      newTab: false
+    })
+  }
+}
+
+function goBuyPage() {
+  window.open('/front/dataPack/createOrder', '_blank')
+}
+
+function searchChange(val) {
+  if (Array.isArray(val)) {
+    val = val.join(',')
+  }
+  let str = ''
+  if (val && val !== undefined) {
+    str = String(val).replace(/,/g, '、')
+  }
+  return str
+}
+
+async function recharge() {
+  try {
+    const res = await getDataPackUsage()
+    if (res.error_code === 0) {
+      dataList.value.entPack =
+        Object.keys(res.data.entPack || {}).length === 0
+          ? false
+          : res.data.entPack
+      dataList.value.dailyPack = res.data.dailyPack
+      dataList.value.personalPack =
+        Object.keys(res.data.personalPack || {}).length === 0
+          ? false
+          : res.data.personalPack
+    }
+  } catch (error) {
+    console.error(error)
+  }
+}
+
+async function chargeDetail(params = {}) {
+  dLoading.value = true
+  try {
+    const obj = Object.assign(
+      {},
+      {
+        pageNum: chargeList.value.pageNum,
+        pageSize: chargeList.value.pageSize
+      },
+      params
+    )
+    const res = await getDataPackRechargeList(obj)
+    dLoading.value = false
+    if (obj.pageNum === 1) {
+      chargeList.value.total = res.total || 0
+    }
+    chargeList.value.isAdmin = res.isAdmin || false
+    chargeList.value.list =
+      (res.list || []).map((v) => {
+        return Object.assign({}, v, {
+          time: v.createTime
+            ? dayjs(v.createTime * 1000).format('YYYY-MM-DD HH:mm')
+            : '-',
+          operator: v.operator || '-'
+        })
+      }) || []
+  } catch (error) {
+    console.error(error)
+  } finally {
+    dLoading.value = false
+  }
+}
+
+async function exported(params = {}) {
+  isLoading.value = true
+  try {
+    const obj = Object.assign(
+      {},
+      {
+        pageNum: exportList.value.pageNum,
+        pageSize: exportList.value.pageSize
+      },
+      params
+    )
+    const res = await getDataPackRecordList(obj)
+    if (res.error_code === 0) {
+      isLoading.value = false
+      if (obj.pageNum === 1) {
+        exportList.value.total = res.data.total || 0
+      }
+      exportList.value.isAdmin = res.data.isAdmin || false
+      if (res.data.list) {
+        exportList.value.list = res.data.list.map((v) => ({
+          id: v.id,
+          data_from: v.data_from,
+          personal_name: (v.personal_name || '-').replace(/\s+/g, '<br>'),
+          export_num: v.export_num,
+          deduct_num: v.deduct_num,
+          pay_way: v.pay_way,
+          down_url: v.down_url,
+          export_timestamp: v.export_timestamp
+            ? dayjs(v.export_timestamp * 1000).format('YYYY-MM-DD HH:mm')
+            : '-',
+          search: v.search
+        }))
+      }
+    }
+  } catch (error) {
+    console.error(error)
+  } finally {
+    isLoading.value = false
+  }
+}
+
+function getPageData1(page) {
+  chargeList.value.pageNum = page
+  chargeList.value.list = []
+  chargeDetail()
+}
+
+function changePageSizeOrNumOfHistory({ size, num, params = {} } = {}) {
+  if (size) {
+    exportList.value.pageSize = size
+  }
+  if (num) {
+    exportList.value.pageNum = num
+  }
+  exportList.value.list = []
+  exported(params)
+}
+
+function changePageSizeOrNumOfHome({ size, num, params = {} } = {}) {
+  if (size) {
+    chargeList.value.pageSize = size
+  }
+  if (num) {
+    chargeList.value.pageNum = num
+  }
+  exportList.value.list = []
+  chargeDetail(params)
+}
+
+function onSizeChange1(val) {
+  chargeList.value.list = []
+  chargeList.value.pageSize = val
+  chargeList.value.pageNum = 1
+  chargeDetail()
+}
+
+function onSizeChangeOfHistory(val, params) {
+  exportList.value.list = []
+  exportList.value.pageSize = val || exportList.value.pageSize
+  exportList.value.pageNum = 1
+  exported(params)
+}
+
+function changeNum(data) {
+  return data ? data.toLocaleString() : 0
+}
+
+function changeDate(time, l = '.', format) {
+  const d = new Date(time * 1000)
+  const Y = d.getFullYear() + l
+  const M =
+    (d.getMonth() + 1 < 10 ? '0' + (d.getMonth() + 1) : d.getMonth() + 1) + l
+  const D = (d.getDate() < 10 ? '0' + d.getDate() : d.getDate()) + ' '
+  const h = (d.getHours() < 10 ? '0' + d.getHours() : d.getHours()) + ':'
+  const m = (d.getMinutes() < 10 ? '0' + d.getMinutes() : d.getMinutes()) + ':'
+  const s = d.getSeconds() < 10 ? '0' + d.getSeconds() : d.getSeconds()
+  if (format === 'ymd') {
+    return (Y + M + D).replace(/^\s+|\s+$/g, '')
+  } else {
+    return (Y + M + D + h + m + s).replace(/^\s+|\s+$/g, '')
+  }
+}
+
+function dateFormate(val) {
+  if (val !== undefined) {
+    const str = val.split('_')
+    return `${changeDate(str[0], '/', 'ymd')} - ${changeDate(
+      str[1],
+      '/',
+      'ymd'
+    )}`
+  }
+}
+
+function initPageModel() {
+  recharge()
+  chargeDetail()
+  exported()
+}
+
+export {
+  initPageModel,
+  dLoading,
+  isLoading,
+  dataList,
+  chargeList,
+  exportList,
+  onClickRow,
+  goBuyPage,
+  searchChange,
+  recharge,
+  chargeDetail,
+  exported,
+  changeNum,
+  changeDate,
+  getPageData1,
+  onSizeChange1,
+  changePageSizeOrNumOfHistory,
+  changePageSizeOrNumOfHome,
+  dateFormate
+}

+ 38 - 0
apps/bigmember_pc/src/views/datapack/model/useDistribute.js

@@ -0,0 +1,38 @@
+import { getCurrentInstance, ref } from 'vue'
+import { ajaxSetDidDistributor } from '@/api/modules'
+
+export function useDistribute() {
+  const usePowerRef = ref(null)
+  function openDistribute(e) {
+    usePowerRef.value.titleMsg = '选择操作人'
+    usePowerRef.value.searchVal = ''
+    usePowerRef.value.centerDialogVisible = true
+    usePowerRef.value.selectDataIds = ''
+    usePowerRef.value.getData('yes')
+  }
+
+  // 提交分发
+  function doSubmitDistribute(data) {
+    ajaxSetDidDistributor({
+      infoids: [params.id],
+      staffs: data
+    }).then((res) => {
+      const $message = getCurrentInstance().proxy.$message
+      if (res.error_code === 0) {
+        if (res.data === 1) {
+          $message({ message: '分发成功', type: 'success' })
+        } else {
+          $message({ message: res.error_msg, type: 'warning' })
+        }
+      } else {
+        $message({ message: res.error_msg, type: 'warning' })
+      }
+    })
+  }
+
+  return {
+    usePowerRef,
+    openDistribute,
+    doSubmitDistribute
+  }
+}

+ 323 - 0
apps/bigmember_pc/src/views/datapack/pages/History.vue

@@ -0,0 +1,323 @@
+<script setup>
+import Empty from '@/components/common/Empty.vue'
+import SearchSchemaFilter from '@/views/search/components/search-schema-filter.vue'
+import powerPerson from '@/components/subscribe-manager/powerPerson.vue'
+import {
+  isLoading,
+  exportList,
+  dataList,
+  onClickRow,
+  changeNum,
+  dateFormate,
+  searchChange
+} from '@/views/datapack/model/home'
+import {
+  filters,
+  filtersKey,
+  computedFilters,
+  filterSchema,
+  doChangeFilter,
+  doSubmitDistribute,
+  onChangePageSize,
+  onChangePageNum,
+  usePowerRef,
+  openDistribute,
+  resetFilters
+} from '@/views/datapack/model/history'
+import { onMounted } from 'vue'
+
+onMounted(() => {
+  resetFilters()
+  onChangePageNum(1)
+})
+</script>
+
+<template>
+  <div class="data-pack-page--history">
+    <div class="data-box exports">
+      <!-- 分发企业选择, 只有企业展示,默认vt为企业-->
+      <power-person
+        @manualDiatribution="doSubmitDistribute"
+        vt="q"
+        :selectIds="filters.staffs"
+        :noSelect="true"
+        ref="usePowerRef"
+      ></power-person>
+      <div class="up-biao">
+        <div class="biao-ge">
+          <div
+            class="page-filter-container"
+            :class="{
+              'hide-person': !exportList.isAdmin,
+              'hide-ent-type': !dataList.entPack
+            }"
+          >
+            <!--  更多筛选  -->
+            <search-schema-filter
+              :key="filtersKey"
+              v-model="filters"
+              :schema="filterSchema"
+              :show-label="true"
+              :show-row-label="false"
+              style-type="row"
+              class="subscribe-filter"
+              @openPersonDialog="openDistribute"
+              @change="doChangeFilter"
+            >
+            </search-schema-filter>
+            <div>
+              共<span class="highlight-text">{{ exportList.total }}</span
+              >条数据
+            </div>
+          </div>
+
+          <el-table
+            class="data-export-record"
+            v-loading="isLoading"
+            :data="exportList.list"
+            style="width: 100%; color: #1d1d1d; cursor: pointer"
+            @row-click="onClickRow"
+          >
+            <div slot="empty">
+              <div class="no-data">
+                <Empty>暂无导出记录</Empty>
+              </div>
+            </div>
+            <el-table-column
+              v-if="exportList.isAdmin"
+              prop="personal_name"
+              label="操作人"
+              align="left"
+              width="152"
+            >
+              <template slot-scope="scope">
+                <span v-html="scope.row.personal_name"></span>
+              </template>
+            </el-table-column>
+            <el-table-column
+              prop="export_timestamp"
+              label="导出时间"
+              align="center"
+              width="152"
+            ></el-table-column>
+            <el-table-column
+              prop="data_from"
+              label="数据来源"
+              align="center"
+              width="120"
+            ></el-table-column>
+            <el-table-column label="搜索条件" align="center">
+              <template slot-scope="scope">
+                <div class="searchConditions">
+                  <el-tooltip
+                    v-if="scope.row.search"
+                    placement="top"
+                    :disabled="
+                      !(
+                        (scope.row.search.area &&
+                          scope.row.search.area.length > 0) ||
+                        scope.row.search.publishtime ||
+                        (scope.row.search.buyerclass &&
+                          scope.row.search.buyerclass.length > 0)
+                      )
+                    "
+                  >
+                    <div
+                      slot="content"
+                      class="changeCell"
+                      style="max-width: 300px; line-height: 18px"
+                    >
+                      <span
+                        v-if="
+                          scope.row.search.buyerclass &&
+                          scope.row.search.buyerclass.length > 0
+                        "
+                        >采购单位类型:{{
+                          searchChange(scope.row.search.buyerclass)
+                        }}<br
+                      /></span>
+                      <span v-if="scope.row.search.publishtime"
+                        >时间:{{ dateFormate(scope.row.search.publishtime)
+                        }}<br
+                      /></span>
+                      <span
+                        v-if="
+                          scope.row.search.area && scope.row.search.area.length
+                        "
+                      >
+                        地区:{{ searchChange(scope.row.search.area) }}</span
+                      >
+                    </div>
+                    <div
+                      class="secondEillps"
+                      :data-scope="scope.row.search.buyerclass"
+                    >
+                      <span
+                        v-if="
+                          scope.row.search.buyerclass &&
+                          scope.row.search.buyerclass.length > 0
+                        "
+                        >采购单位类型:{{
+                          searchChange(scope.row.search.buyerclass)
+                        }}<br
+                      /></span>
+                      <span v-if="scope.row.search.publishtime">
+                        时间:{{ dateFormate(scope.row.search.publishtime)
+                        }}<br
+                      /></span>
+                      <span
+                        v-if="
+                          scope.row.search.area && scope.row.search.area.length
+                        "
+                      >
+                        地区:{{ searchChange(scope.row.search.area) }}</span
+                      >
+                      <span
+                        class="imptySeach"
+                        v-if="
+                          !(
+                            (scope.row.search.area &&
+                              scope.row.search.area.length > 0) ||
+                            scope.row.search.publishtime ||
+                            (scope.row.search.buyerclass &&
+                              scope.row.search.buyerclass.length > 0)
+                          )
+                        "
+                        >--</span
+                      >
+                    </div>
+                  </el-tooltip>
+                  <div class="secondEillps" v-else>
+                    <span class="imptySeach">--</span>
+                  </div>
+                </div>
+              </template>
+            </el-table-column>
+            <el-table-column
+              prop="pay_way"
+              label="支付方式"
+              align="center"
+              width="136"
+            ></el-table-column>
+            <el-table-column
+              prop="export_num"
+              label="导出条数"
+              align="center"
+            ></el-table-column>
+            <el-table-column prop="deduct_num" label="新扣条数" align="center">
+              <template slot-scope="scope">
+                <span>{{
+                  scope.row.deduct_num == -1
+                    ? '-'
+                    : changeNum(scope.row.deduct_num)
+                }}</span>
+              </template>
+            </el-table-column>
+            <el-table-column label="下载链接" align="center">
+              <template slot-scope="scope">
+                <a class="dian-ji" :href="scope.row.down_url">点击下载</a>
+              </template>
+            </el-table-column>
+          </el-table>
+          <div v-if="exportList.total > 0">
+            <el-pagination
+              :key="exportList.pageSize"
+              popper-class="pagination-custom-select"
+              background
+              layout="prev, pager, next, sizes, jumper"
+              @current-change="onChangePageNum"
+              :page-size="exportList.pageSize"
+              :total="exportList.total"
+              :page-sizes="[5, 10, 50, 100]"
+              :show-confirm-btn="true"
+              @size-change="onChangePageSize"
+            >
+            </el-pagination>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.data-pack-page--history {
+  ::v-deep {
+    .biao-ge {
+      box-shadow: 0px 4px 8px 0px rgba(0, 0, 0, 0.08);
+    }
+
+    .data-export-record {
+      .is-left {
+        padding-left: 30px !important;
+      }
+    }
+    .data-export-record {
+      border-top: 1px solid rgba(0, 0, 0, 0.05);
+    }
+    .page-filter-container {
+      padding: 10px 30px 12px 30px;
+      font-size: 14px;
+      color: #686868;
+
+      .icon-xiala:before {
+        content: '\e6ad';
+      }
+
+      &.hide-person {
+        .search-schema-filter-item:first-child {
+          display: none;
+        }
+      }
+      &.hide-ent-type .filter-item[label='企业数据流量包'] {
+        display: none;
+      }
+
+      .search-schema-filter-container .search-schema-filter-label {
+        width: auto;
+      }
+      .search-schema-filter-container.use-style-row .search-schema-filter-box {
+        margin-left: 0;
+      }
+
+      .select-container.custom-select-auto-container,
+      .filter-layout {
+        min-width: 80px;
+        > .el-dropdown {
+          width: 100%;
+        }
+      }
+
+      .select-container.custom-select-auto-container {
+        .el-select,
+        .select-prefix,
+        .el-input--prefix,
+        .el-input__prefix {
+          width: 100%;
+        }
+      }
+
+      .select-container.custom-select-auto-container .select-prefix,
+      .filter-layout .select-prefix {
+        border-radius: 4px;
+        padding-right: 4px;
+        border: 1px solid #e0e0e0;
+      }
+
+      .search-schema-filter-container.use-style-row {
+        margin-bottom: 12px;
+      }
+      .select-container.custom-select-auto-container .select-prefix,
+      .filter-layout .select-prefix {
+        border-radius: 4px;
+        padding-right: 4px;
+      }
+      .search-schema-filter-container.use-style-row .search-schema-filter-item {
+        line-height: 28px;
+        margin-right: 24px;
+        min-width: 168px;
+      }
+    }
+  }
+}
+</style>

+ 324 - 0
apps/bigmember_pc/src/views/datapack/pages/Home.vue

@@ -0,0 +1,324 @@
+<script setup>
+import {
+  dataList,
+  changeNum,
+  chargeList,
+  dLoading,
+  changeDate,
+  goBuyPage
+} from '@/views/datapack/model/home'
+import SearchSchemaFilter from '@/views/search/components/search-schema-filter.vue'
+import powerPerson from '@/components/subscribe-manager/powerPerson.vue'
+import Empty from '@/components/common/Empty.vue'
+import {
+  filters,
+  filtersKey,
+  computedFilters,
+  filterSchema,
+  doChangeFilter,
+  doSubmitDistribute,
+  onChangePageSize,
+  onChangePageNum,
+  usePowerRef,
+  openDistribute,
+  resetFilters
+} from '@/views/datapack/model/home-filter'
+import { getCurrentInstance, onMounted } from 'vue'
+
+// 联系客服
+const that = getCurrentInstance().proxy
+function openCustomer() {
+  that?.contactCustomer(that)
+}
+
+onMounted(() => {
+  resetFilters()
+  onChangePageNum(1)
+})
+</script>
+
+<template>
+  <div class="data-pack-page--home">
+    <div class="data-box">
+      <div class="up-line" v-if="dataList.entPack">
+        <h3>企业数据流量包</h3>
+        <div class="d-conts flex">
+          <div class="d-card" v-for="item in dataList.entPack" :key="item.id">
+            <h4 style="padding-left: 0">
+              <span class="iconfont j-icon icon-hui8 highlight-text"></span>
+              {{ item.entName }}
+            </h4>
+            <div class="words flex">
+              <div class="nums">
+                <p class="p1">{{ changeNum(item.entAllCount) }}<i>条</i></p>
+                <p class="p2">数据流量包账户余额</p>
+              </div>
+              <div class="nums r-code">
+                <p class="p1">0<i>条</i></p>
+                <p class="p2">30天内即将到期</p>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+      <div class="up-line down-line" v-if="dataList.personalPack">
+        <h3>个人数据流量包</h3>
+        <div class="d-chong">
+          <div class="d-head">
+            <div class="s-zhu">
+              <p class="p1">
+                数据流量包账户余额<i>{{
+                  dataList.personalPack.normalTotal +
+                  dataList.personalPack.seniorTotal
+                }}</i
+                >条
+              </p>
+              <p class="p2">
+                注:如需充值企业数据流量包(企业人员共享),可在线
+                <em
+                  class="highlight t-ud"
+                  style="text-decoration: underline; cursor: pointer"
+                  @click="openCustomer"
+                  >联系客服</em
+                >
+                或拨打客服电话400-108-6670
+              </p>
+            </div>
+            <el-button @click="goBuyPage">立即充值</el-button>
+          </div>
+          <div class="c-cont flex">
+            <div class="l-shu flex">
+              <div class="nums">
+                <p class="p1">
+                  {{ changeNum(dataList.personalPack.normalTotal) }}<i>条</i>
+                </p>
+                <p class="p2">标准字段包余额</p>
+              </div>
+              <div class="nums r-code">
+                <p class="p1">
+                  {{ changeNum(dataList.personalPack.normalThirty) }}<i>条</i>
+                </p>
+                <p class="p2">标准字段包30天内即将到期</p>
+              </div>
+            </div>
+            <el-divider direction="vertical"></el-divider>
+            <div class="l-shu flex">
+              <div class="nums">
+                <p class="p1">
+                  {{ changeNum(dataList.personalPack.seniorTotal) }}<i>条</i>
+                </p>
+                <p class="p2">高级字段包余额</p>
+              </div>
+              <div class="nums r-code">
+                <p class="p1">
+                  {{ changeNum(dataList.personalPack.seniorThirty) }}<i>条</i>
+                </p>
+                <p class="p2">高级字段包30天内即将到期</p>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+      <!-- 分发企业选择, 只有企业展示,默认vt为企业-->
+      <power-person
+        @manualDiatribution="doSubmitDistribute"
+        vt="q"
+        :selectIds="filters.staffs"
+        :noSelect="true"
+        ref="usePowerRef"
+      ></power-person>
+      <div class="up-line up-biao online-recharge">
+        <div class="data-pack-search-filter-container">
+          <h3 style="margin-bottom: 0">账户余额明细</h3>
+          <div
+            class="page-filter-container"
+            :class="{
+              'hide-person': !chargeList.isAdmin,
+              'hide-ent-type': !dataList.entPack
+            }"
+          >
+            <!--  更多筛选  -->
+            <search-schema-filter
+              :key="filtersKey"
+              v-model="filters"
+              :schema="filterSchema"
+              :show-label="true"
+              :show-row-label="false"
+              style-type="row"
+              class="subscribe-filter"
+              @openPersonDialog="openDistribute"
+              @change="doChangeFilter"
+            >
+            </search-schema-filter>
+          </div>
+        </div>
+        <div class="biao-ge" v-if="chargeList.list">
+          <el-table
+            class="online-recharge"
+            v-loading="dLoading"
+            :data="chargeList.list"
+            style="width: 100%; color: #1d1d1d"
+          >
+            <div slot="empty">
+              <div class="no-data">
+                <Empty>暂无数据</Empty>
+              </div>
+            </div>
+
+            <el-table-column
+              prop="time"
+              label="时间"
+              align="left"
+            ></el-table-column>
+            <el-table-column
+              v-if="chargeList.isAdmin"
+              prop="operator"
+              label="人员"
+              align="center"
+            ></el-table-column>
+            <el-table-column
+              prop="account"
+              label="账户"
+              align="center"
+            ></el-table-column>
+            <el-table-column prop="type" label="类型" width="80" align="center">
+              <template slot-scope="scope">
+                <span class="type-tag-item" v-if="scope.row.type === '新增'">{{
+                  scope.row.type
+                }}</span>
+                <span class="type-tag-item is-red" v-else>{{
+                  scope.row.type
+                }}</span>
+              </template>
+            </el-table-column>
+            <el-table-column label="余额变化(条)" align="center">
+              <template slot-scope="scope">
+                <span
+                  :class="{ 'highlight-text': scope.row.type === '新增' }"
+                  >{{ changeNum(scope.row.number) }}</span
+                >
+                <br />
+                <span
+                  v-if="scope.row.giveNumber"
+                  style="color: rgb(44, 183, 202)"
+                >
+                  赠11{{ changeNum(scope.row.giveNumber) }}条</span
+                >
+              </template>
+            </el-table-column>
+            <el-table-column prop="reasonTitle" label="原因描述" align="center">
+              <template slot-scope="scope">
+                <span>{{ scope.row.reasonTitle }}</span>
+                <br />
+                <span>{{ scope.row.reasonContent }}</span>
+              </template>
+            </el-table-column>
+          </el-table>
+          <el-pagination
+            v-if="chargeList.total > 0"
+            popper-class="pagination-custom-select"
+            background
+            layout="prev, pager, next, sizes, jumper"
+            @current-change="onChangePageNum"
+            :page-size="chargeList.pageSize"
+            :total="chargeList.total"
+            :page-sizes="[5, 10, 50, 100]"
+            :show-confirm-btn="true"
+            @size-change="onChangePageSize"
+          >
+          </el-pagination>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.data-pack-page--home {
+  //
+  ::v-deep {
+    .data-pack-search-filter-container {
+      display: flex;
+      flex-direction: row;
+      align-items: center;
+      margin-bottom: 24px;
+      justify-content: space-between;
+
+      .icon-xiala:before {
+        content: '\e6ad';
+      }
+    }
+
+    .is-left.el-table__cell {
+      padding-left: 20px;
+    }
+
+    .page-filter-container {
+      &.hide-person {
+        .search-schema-filter-item:first-child {
+          display: none;
+        }
+      }
+      .select-container .time-container .custom-popover .el-popover {
+        transform: translate3d(-648px, -138px, 0);
+      }
+
+      &.hide-ent-type .filter-item[label='企业数据流量包'] {
+        display: none;
+      }
+    }
+
+    .search-schema-filter-container .search-schema-filter-label {
+      width: auto;
+    }
+    .search-schema-filter-container.use-style-row .search-schema-filter-box {
+      margin-left: 0;
+    }
+
+    .select-container.custom-select-auto-container,
+    .filter-layout {
+      min-width: 80px;
+      > .el-dropdown {
+        width: 100%;
+      }
+    }
+
+    .select-container.custom-select-auto-container {
+      .el-select,
+      .select-prefix,
+      .el-input--prefix,
+      .el-input__prefix {
+        width: 100%;
+      }
+    }
+
+    .select-container.custom-select-auto-container .select-prefix,
+    .filter-layout .select-prefix {
+      border-radius: 4px;
+      padding-right: 4px;
+      border: 1px solid #e0e0e0;
+    }
+    .search-schema-filter-container.use-style-row .search-schema-filter-item {
+      line-height: 28px;
+      margin-left: 24px;
+      min-width: 150px;
+      margin-right: 0;
+    }
+
+    .type-tag-item {
+      background: rgba(42, 190, 209, 1);
+      border: 0.5px solid rgba(0, 0, 0, 0.1);
+      width: 40px;
+      color: #fff;
+      font-weight: 400;
+      font-size: 12px;
+      line-height: 20px;
+      border-radius: 4px;
+      padding: 2px 8px;
+      &.is-red {
+        background: rgba(255, 58, 32, 1);
+      }
+    }
+  }
+}
+</style>

+ 154 - 20
apps/bigmember_pc/src/views/order/components/data-export/info.vue

@@ -88,6 +88,20 @@
                 预览数据
               </div>
             </div>
+            <div class="datapackage-buy-tip-group tip-red">
+              <span
+                >如需更多行业字段,如资金来源、报价方式、企业资质、人员资质、业绩资格、企业信用、建设规模等字段,您可</span
+              >
+              <span
+                style="color: #2abed1; cursor: pointer"
+                @click="
+                  $refs.collectRef.isNeedSubmit(
+                    'pc_DataExportPayment_IndustryFields'
+                  )
+                "
+                >申请数据定制></span
+              >
+            </div>
           </div>
         </SelectorCard>
         <!--E-个人支付模式下切换字段规格-->
@@ -185,6 +199,7 @@
       </el-dialog>
       <!--E-数据流量包扣除不足提示-->
     </section>
+    <CollectInfo ref="collectRef" :showSecondstage="false"></CollectInfo>
   </div>
 </template>
 
@@ -201,12 +216,14 @@ import { mapState, mapMutations, mapActions, mapGetters } from 'vuex'
 import { formatNumber } from '@/utils'
 import OrderSpecList from '@/views/order/ui/spec/list.vue'
 import SelectorCard from '@/components/selector/SelectorCard.vue'
+import CollectInfo from '@/components/collect-info/CollectInfo.vue'
 import { Loading, Dialog } from 'element-ui'
 import qs from 'qs'
 
 export default {
   name: 'data-export-order-info',
   components: {
+    CollectInfo,
     OrderSpecList,
     SelectorCard,
     [Dialog.name]: Dialog
@@ -484,6 +501,12 @@ export default {
       'changeProductChoiceSpec'
     ]),
     ...mapActions('user', ['getUserAccountInfo']),
+    emptyValue(t) {
+      return t === null || t === undefined
+    },
+    hasValue(t) {
+      return !this.emptyValue(t)
+    },
     // 返回筛选页面
     backToFilter() {
       if (history.length === 1) {
@@ -939,7 +962,8 @@ export default {
       if (entPack) {
         entPack.forEach((e) => {
           accountList.push({
-            title: '线下充值账户',
+            // title: '线下充值账户',
+            title: '企业数据流量包',
             tip: e.entName,
             levelKey: 'senior',
             online: false,
@@ -952,7 +976,8 @@ export default {
       if (personalPack) {
         if (personalPack.seniorTotal > 0) {
           accountList.push({
-            title: '线上充值账户(高级字段包)',
+            // title: '线上充值账户(高级字段包)',
+            title: '个人数据流量包(高级字段包)',
             level: 2, // 1标准字段包 2高级字段包
             levelKey: 'senior',
             online: true,
@@ -963,7 +988,8 @@ export default {
         }
         if (personalPack.normalTotal > 0) {
           accountList.push({
-            title: '线上充值账户(标准字段包)',
+            // title: '线上充值账户(标准字段包)',
+            title: '个人数据流量包(标准字段包)',
             level: 1, // 1标准字段包 2高级字段包
             levelKey: 'normal',
             online: true,
@@ -976,7 +1002,8 @@ export default {
       if (accountList.length === 0) {
         this.paySpecList[0].show = true
         accountList.push({
-          title: '线上充值账户(标准字段包)',
+          // title: '个人数据流量包(标准字段包)',
+          title: '个人数据流量包(标准字段包)',
           level: 1,
           levelKey: 'normal',
           online: true,
@@ -1152,17 +1179,77 @@ export default {
                 text: enough ? `${lastCount}条` : ' / 条'
               }
             ])
+          this.balanceConfMap[this.balanceConfKey].tipTextSelfShow = false
+          this.balanceConfMap[this.balanceConfKey].tipTextAdminShow = false
           return enough
         } else {
           const entAllLastCount = account.data.entAllCount // 企业所剩余额
-          const userLastCountToday = account.data.surplusToday // 此用户今日所剩余额
-          const limitToday = account.data.limitToday // 此用户单日限额
+          // 单日限额/余额
+          const userLimitToday = account.data.limitToday // 此用户单日限额,没返回表示无限制
+          const userLastCountToday = account.data.surplusToday // 此用户今日所剩余额,没返回表示无限制
+          // 总量限额/余额
+          const userLimitTotal = account.data.maxNums // 全部总量限额,没返回表示无限制
+          const userLastTotal = account.data.surplusNums // 全部剩余限额,没返回表示无限制
 
-          const entCount = entAllLastCount - deduct
-          const userCount = userLastCountToday - deduct
+          // 扣除后额度
+          const entLastCount = entAllLastCount - deduct
+          let userLastCount = 0
+          const dialogTipConf = {
+            showLimitToday: false,
+            showLimitTotal: false
+          }
+
+          if (
+            this.emptyValue(userLimitToday) &&
+            this.emptyValue(userLimitTotal)
+          ) {
+            // 1. 未设置单日限额 未设置总量限额
+            // 则个人额度就是企业额度
+            userLastCount = entAllLastCount - deduct
+          } else if (
+            this.emptyValue(userLimitToday) &&
+            this.hasValue(userLimitTotal)
+          ) {
+            // 2. 未设置单日限额 设置总量限额
+            // 扣除按照总量限额计算
+            userLastCount = userLastTotal - deduct
+
+            dialogTipConf.showLimitToday = false
+            dialogTipConf.showLimitTotal = userLastCount < 0
+          } else if (
+            this.hasValue(userLimitToday) &&
+            this.emptyValue(userLimitTotal)
+          ) {
+            // 3. 设置单日限额 未设置总量限额
+            // 扣除按照单日限额计算
+            userLastCount = userLastCountToday - deduct
+
+            dialogTipConf.showLimitToday = userLastCount < 0
+            dialogTipConf.showLimitTotal = false
+          } else {
+            // 4. 设置了单日限额 设置了总量限额
+            // 单日限额和总量限额都要满足(取最小的)
+            const dailyLast = userLastCountToday - deduct
+            const totalLast = userLastTotal - deduct
+            // 优化前---
+            // if (dailyLast >= 0 && totalLast >= 0) {
+            //   userLastCount = Math.min(dailyLast, totalLast)
+            // } else if (dailyLast >= 0 && totalLast < 0) {
+            //   userLastCount = totalLast
+            // } else if (dailyLast < 0 && totalLast >= 0) {
+            //   userLastCount = dailyLast
+            // } else {
+            //   userLastCount = Math.min(dailyLast, totalLast)
+            // }
+            // 优化后---
+            userLastCount = Math.min(dailyLast, totalLast)
+            dialogTipConf.showLimitToday = dailyLast < 0
+            dialogTipConf.showLimitTotal = totalLast < 0
+          }
 
-          const entEnough = entCount >= 0
-          const userEnough = userCount >= 0
+          // 扣完是否够用
+          const entEnough = entLastCount >= 0
+          const userEnough = userLastCount >= 0
 
           let descList = descListCommon.concat([
             {
@@ -1171,25 +1258,51 @@ export default {
               label: '所属企业账户余额',
               split: ':',
               text: `<span class="red">${entAllLastCount}</span>条`
-            },
-            {
+            }
+          ])
+
+          if (this.hasValue(userLimitToday)) {
+            descList.push({
               showInDesc: true,
-              showInDialog: false,
+              showInDialog: true,
               label: '您每日导出限额',
               split: ':',
-              text: `<span class="red">${limitToday}</span>条`
-            },
-            {
+              text: `<span class="red">${userLimitToday}</span>条`
+            })
+          }
+
+          if (this.hasValue(userLastCountToday)) {
+            descList.push({
               showInDesc: true,
               showInDialog: true,
               label: '您今日余额',
               split: ':',
               text: `<span class="red">${userLastCountToday}</span>条`
-            }
-          ])
+            })
+          }
+
+          if (this.hasValue(userLimitTotal)) {
+            descList.push({
+              showInDesc: true,
+              showInDialog: true,
+              label: '您导出总量限额',
+              split: ':',
+              text: `${userLimitTotal}条`
+            })
+          }
+
+          if (this.hasValue(userLastTotal)) {
+            descList.push({
+              showInDesc: true,
+              showInDialog: true,
+              label: '您还可导出',
+              split: ':',
+              text: `${userLastTotal}条`
+            })
+          }
 
           if (!entEnough) {
-            descList = descListCommon.concat([
+            descList = descList.concat([
               {
                 showInDesc: false,
                 showInDialog: true,
@@ -1204,11 +1317,30 @@ export default {
           }
 
           this.balanceConfMap[this.balanceConfKey].descList = descList
+          this.calcDialogTipText(dialogTipConf)
 
           return entEnough && userEnough
         }
       }
     },
+    calcDialogTipText(accountInfo = {}) {
+      const { showLimitToday, showLimitTotal } = accountInfo
+      const inlineArr = []
+      if (showLimitToday) {
+        inlineArr.push('每日导出限额')
+      }
+      if (showLimitTotal) {
+        inlineArr.push('导出总量限额')
+      }
+
+      let prefix = ''
+      if (inlineArr.length > 0) {
+        prefix = `您的${inlineArr.join('、')}`
+      }
+      const text = `${prefix}已超过管理员设置数量,请联系管理员或先按照个人支付`
+      this.balanceConfMap.pack1.tipTextSelf = text
+      this.balanceConfMap.pack1.tipTextAdmin = text
+    },
     dialogChargeNow() {
       this.closeBalanceDialog()
       setTimeout(() => {
@@ -1268,7 +1400,9 @@ export default {
 }
 
 .data-export-order-info {
-  @import './style/info.scss';
+  ::v-deep {
+    @import './style/info.scss';
+  }
   ::v-deep {
     .spec-card {
       height: 80px;

+ 1 - 1
apps/bigmember_pc/src/views/order/components/data-pack/buy-tip.vue

@@ -1,7 +1,7 @@
 <template>
   <p class="buy-tip">
     购买须知:
-    <br />1、数据流量包是数据自助导出充值服务,最低1000条起充,仅针对线上自助充值使用(与线下企业数据充值账户不可共享);
+    <br />1、数据流量包是个人数据自助导出充值服务,最低1000条起充,仅针对本人线上自助充值使用(与企业数据流量包账户不可共享);
     <br />2、已购数据包条数按导出数据量扣除(数据已排重,相同数据多次导出,不重复计费),有效期2年,因产品特殊性,购买后不支持退款。
   </p>
 </template>

+ 82 - 1
apps/bigmember_pc/src/views/search/bidding/index.vue

@@ -1,5 +1,5 @@
 <script setup>
-import { ref } from 'vue'
+import { ref, computed } from 'vue'
 import SearchBidHeader from '@/views/search/bidding/components/search-bid-header.vue'
 import SearchBidFilter from '@/views/search/bidding/components/search-bid-filter.vue'
 import searchFilterHeader from '@/views/search/bidding/components/search-filter-header.vue'
@@ -48,6 +48,7 @@ const {
   toDetail,
   list,
   tableList,
+  bdwAdInfo,
   tableFixedTop,
   tagToDetail,
   setExport,
@@ -98,6 +99,11 @@ const articleRef = ref({
   push: false
 })
 
+//P640项目名称/标的物 搜索结果引流广告位
+const bdwAdItem = computed(() => {
+  return  bdwAdInfo.value.sCount > 0 ?  bdwAdInfo.value.sList[0] : {}
+})
+
 // 订阅提交(P643临时注掉,以后要上,勿删)
 // const extractSubRef = ref()
 // const onConfirmSubscribe = (params) => {
@@ -109,6 +115,11 @@ const articleRef = ref({
 //     }
 //   })
 // }
+
+// 跳转到开通超级订阅页面
+function jumpBuySvip() {
+  window.open('/swordfish/page_big_pc/free/svip/buy?type=buy')
+}
 </script>
 
 <template>
@@ -151,7 +162,9 @@ const articleRef = ref({
         :show-pagination="activeItemStyleType !== 'T'"
         :is-table="activeItemStyleType === 'T'"
         :table-fixed-top="tableFixedTop"
+        :show-center-ad="true"
       >
+        {{listState.pageNum}}
         <template #other-action-item v-if="inInjectBI">
           <div class="all-add bi-add-button" @click="onAddInfoOfBI()">添加</div>
         </template>
@@ -170,6 +183,39 @@ const articleRef = ref({
             :table-fixed-top="tableFixedTop"
           ></search-list-table>
         </template>
+        <!--P640新增部分项目名称/标的物引流广告位-->
+        <template #center-insert-ad="{ index }">
+          <div class="list-center-ad" v-if="index===6 &&  !inBIPropertyIframe && !isInBI && !inBITopNet && bdwAdInfo.sCount > 0">
+            <div class="ad-tip-row">
+              <span>信息不够精准?根据“项目名称/标的物”,为您精准匹配到 <strong>{{bdwAdInfo.sCount}}条</strong> 信息</span>
+              <span class="jump-btn-span" @click="jumpBuySvip">开通超级订阅,立享更多搜索权限,寻找商机更精准&nbsp;&gt;</span>
+            </div>
+            <article-item
+              class="list-item"
+              :model="activeItemStyleType"
+              :class="{ visited: bdwAdItem.visited || bdwAdItem.ca_isvisit }"
+              :match-keys="inputKeywordsState.matchKeys"
+              :article="bdwAdItem"
+              index=""
+              :tag-click-list="['area', 'subtype']"
+              @onClick="toDetail(bdwAdItem)"
+              @tag-click="tagToDetail(bdwAdItem, $event)"
+              @onCollect="onClickSingleCollect"
+              @onJoinBid="onJoinBid"
+              :config="{
+                bidding: true,
+                detail: true,
+                gray: true,
+                joinBid: false,
+                table: false,
+                collect: !isInBI.value,
+                push: false
+              }"
+            >
+            </article-item>
+          </div>
+        </template>
+        <!--P640新增部分结束-->
         <template
           v-slot:item="{ item, index }"
           v-if="activeItemStyleType !== 'T'"
@@ -762,4 +808,39 @@ const articleRef = ref({
     color: #f7f9fa;
   }
 }
+::v-deep {
+  .list-center-ad{
+    padding-top:20px;
+    margin-bottom: 4px;
+    width:100%;
+    min-height:132px;
+    padding-left:35px;
+    background: linear-gradient(180deg,rgba(255,199,64,0.12) 0%, rgba(255,199,64, 0) 100%);
+    .ad-tip-row{
+      padding-left:15px;
+
+      span{
+        font-size:14px;
+        line-height:22px;
+      }
+      span:nth-of-type(1) {
+        color:#1d1d1d;
+        font-weight: bold;
+        strong {
+          color: #FF9F40;
+          margin: 0 1px;
+        }
+      }
+      .jump-btn-span{
+        background: #FF9F40;
+        color:#fff;
+        padding: 4px 12px;
+        border-radius: 4px;
+        margin-left:25px;
+        cursor: pointer;
+      }
+    }
+  }
+}
+
 </style>

+ 95 - 3
apps/bigmember_pc/src/views/search/bidding/model/base.js

@@ -9,7 +9,8 @@ import  {
   FilterModel2ViewModel,
   getParam,
   openLinkInWorkspace,
-  InContainer
+  InContainer,
+  extractKeywords
 } from '@/utils'
 import $bus from '@/utils/bus'
 // 筛选条件动态组件方法
@@ -41,6 +42,7 @@ import { beforeSearchModel } from './modules/before-search'
 import { recommendCardModel } from './modules/recommend-card'
 // 搜索历史业务模型
 import useSearchHistoryModel from '@jy/data-models/modules/quick-search-history/model'
+import dayjs from 'dayjs'
 // 订阅弹框业务
 // import { userSubscribeInfoModel } from './modules/data-subscribe'
 
@@ -93,7 +95,7 @@ export default function () {
 
   // 是否在工作台内
   // 本地调试,可改为工作台内isInApp = ref(true),  isInWeb = ref(false)   提交记得改回!
-  const isInApp = ref(InContainer.inApp) // InContainer.inApp
+  const isInApp =  ref(InContainer.inApp) // InContainer.inApp
   const isInWeb = ref(InContainer.inWeb) // InContainer.inWeb
 
   // isInApp.value = true
@@ -339,7 +341,8 @@ export default function () {
   const activeList = computed(() => {
     let arr = []
     if(list.value && list.value.length > 0) {
-      arr = list.value.map((v) => {
+      const sArr = sortListTitleDetail(list.value)
+      arr = sArr.map((v) => {
         v._id = v.id
         v.checked = selectIds.value.includes(v.id)
         // 中标单位联系方式处理
@@ -363,6 +366,53 @@ export default function () {
     }
     return arr
   })
+  // p640需求,如果筛选类型同时包含标题和正文,先展示标题包含关键词,再展示内容包含关键词的数据
+  // 如果不同时包含标题和正文,则不处理
+  function sortListTitleDetail (baseArr) {
+    if(filterBase.value.selectType.includes('title') && filterBase.value.selectType.includes('content')) {
+      // 辅助函数:检查字符串是否包含关键词数组中的任何一个关键词
+      function containsKeyword(str, keywords) {
+        return keywords.some(keyword => str ? str.includes(keyword) : '');
+      }
+      let arr = []
+      const baseTimeMap = {}
+      const baseTimeKeys = []
+      baseArr.forEach(v => {
+        const ymd = dayjs(v.publishTime * 1000).startOf('day').unix()
+        if (!Array.isArray(baseTimeMap[ymd])) {
+          baseTimeMap[ymd] = []
+        }
+        baseTimeMap[ymd].push(v)
+        if (!baseTimeKeys.includes(ymd)) {
+          baseTimeKeys.push(ymd)
+        }
+      })
+
+      const sortBaseTimeKeys = baseTimeKeys.sort((a,b) => b - a)
+
+      function sortItem (arr) {
+        // 筛选对象数组
+        const matchKeys = inputKeywordsState.value.matchKeys
+        const titleMatches = arr.filter(item => containsKeyword(item.title, matchKeys))
+        const contentMatches = arr.filter(item => extractKeywords(item.detail, matchKeys) && !titleMatches.some(tm => tm.title === item.title))
+        const noMatches = arr.filter(item => !containsKeyword(item.title, matchKeys) && !extractKeywords(item.detail, matchKeys));
+        // 合并结果
+        const resultArr = [...titleMatches, ...contentMatches, ...noMatches]
+        return resultArr
+      }
+
+
+      for (let i = 0; i < sortBaseTimeKeys.length; i++) {
+        const baseTimeMapKey = sortBaseTimeKeys[i]
+        const sortItemArr = sortItem(baseTimeMap[baseTimeMapKey])
+        arr = arr.concat(sortItemArr)
+      }
+
+      return arr
+    } else {
+      return baseArr
+    }
+  }
   // 表格展示的数据
   const tableList = ref([])
 
@@ -371,6 +421,11 @@ export default function () {
     interceptOtherWords: '',
     interceptLimit: 0
   })
+  // P640需求-列表中项目名称/标的物广告位数据信息
+  const bdwAdInfo= ref({
+    sCount: 0,
+    sList: []
+  })
 
   // 根据权限处理列表头部的操作按钮展示
   const limitFilterHeaderActions =  computed(() => {
@@ -730,6 +785,9 @@ export default function () {
   }
   // 处理查询请求后的数据以及其他业务
   function afterQueryAjax (res, searchType) {
+    // 处理标的物广告信息(一些数据格式化)
+    calcBdwInfo(res.origin)
+
     // 存储筛选条件
     if(isInApp.value && isLogin.value && listState.pageNum === 1 && !inBIPropertyIframe) {
       saveSearchGroupToLocal()
@@ -821,6 +879,30 @@ export default function () {
       searchMode: Number(inputKeywordsState.value.searchMode)
     })
   }
+  // 处理标的物广告信息(一些数据格式化)
+  function calcBdwInfo(origin) {
+    // P640处理标的物广告信息
+
+    if(origin.sCount > 0) {
+      bdwAdInfo.value.sCount = origin.sCount
+      const sArr = origin.sList.map(item => {
+        // 是否已读字段
+        const visited = that.$visited.check({
+          type: 'articleContent',
+          id: item.id
+        })
+        that.$set(item, 'visited', visited)
+
+        // 收藏字段显示
+        that.$set(item, 'collection', item.isCollected ? 1 : 0)
+
+        // 收录字段
+        that.$set(item, 'isEmploy', false)
+        return item
+      })
+      bdwAdInfo.value.sList = sArr
+    }
+  }
 
   // 是否展示筛选条件
   const showFilter = ref(true)
@@ -1135,6 +1217,15 @@ export default function () {
       }
       return temp
     })
+   // 标的物广告信息收藏操作更新
+    bdwAdInfo.value.sList = bdwAdInfo.value.sList.map(temp => {
+      if (type === 'R' && ids.includes(temp.id)) {
+        that.$set(temp, 'collection', 0)
+      } else if(type === 'C' && ids.includes(temp.id)){
+        that.$set(temp, 'collection', 1)
+      }
+      return temp
+    })
   })
   /********* 标讯收藏部分end ********/
 
@@ -1580,6 +1671,7 @@ export default function () {
     cooperateCode,
     list,
     tableList,
+    bdwAdInfo,
     searchModelOptions,
     searchListProps,
     SearchTabsModel,

+ 5 - 0
apps/bigmember_pc/src/views/search/bidding/model/modules/data-employ-actions.js

@@ -95,6 +95,11 @@ export function dataEmployActionsModel (gParams) {
 
   // 收录操作
   function onSingleEmploy(item) {
+    const v = {
+      _id: item.id,
+      ...item
+    }
+    localStorage.setItem('property-data', JSON.stringify(v))
     let params = {}
     params = {
       idArr: item.id,

+ 31 - 23
apps/bigmember_pc/src/views/search/layout/search-list.vue

@@ -40,7 +40,7 @@ const props = defineProps({
       pageNum: 1, // 当前页
       pageSize: 50, // 每页多少条数据
       total: 0 // 一共多少条数据
-    })
+    }),
   },
   mtb60: {
     type: Boolean,
@@ -59,6 +59,10 @@ const props = defineProps({
   tableFixedTop: {
     type: Boolean,
     default: false
+  },
+  showCenterAd: {
+    type: Boolean,
+    default: false
   }
 })
 const canShowPagination = computed(() => {
@@ -149,30 +153,34 @@ function onCurrentChange($event) {
         ></empty>
       </slot>
       <slot name="list" :list="list" v-if="!listState.loading && !isTable">
-        <div
-          class="search-result-item flex flex-(row)"
-          v-for="(item, index) in list"
-          :key="'search-item' + index"
-        >
-          <slot name="item-checkbox" v-if="showSelect">
-            <!--  选择  -->
-            <el-checkbox
-              class="p-t-18px p-l-16px"
-              :value="item.checked"
-              @change="$emit('doChangeSelect', item)"
-            >
-            </el-checkbox>
-          </slot>
-          <div class="flex-1 max-w-full of-hidden">
-            <slot
-              name="item"
-              :item="item"
-              :index="listState.pageSize * (listState.pageNum - 1) + index + 1"
-            >
-              {{ item }}
+        <template   v-for="(item, index) in list">
+          <!-- P640新增项目名称/标的物引流广告-->
+          <slot name="center-insert-ad" :index="listState.pageSize * (listState.pageNum - 1) + index + 1" v-if="showCenterAd"></slot>
+          <div
+            class="search-result-item flex flex-(row)"
+            :key="'search-item' + index"
+          >
+            <slot name="item-checkbox" v-if="showSelect">
+              <!--  选择  -->
+              <el-checkbox
+                class="p-t-18px p-l-16px"
+                :value="item.checked"
+                @change="$emit('doChangeSelect', item)"
+              >
+              </el-checkbox>
             </slot>
+            <div class="flex-1 max-w-full of-hidden">
+              <slot
+                name="item"
+                :item="item"
+                :index="listState.pageSize * (listState.pageNum - 1) + index + 1"
+              >
+                {{ item }}
+              </slot>
+            </div>
           </div>
-        </div>
+        </template>
+
       </slot>
       <slot
         name="table"

+ 1 - 0
apps/bigmember_pc/vite.config.js

@@ -137,6 +137,7 @@ export default defineConfig({
   css: {
     preprocessorOptions: {
       scss: {
+        silenceDeprecations: ['legacy-js-api', 'import'], // 静默所有相关警告
         additionalData: `
           @import "@/assets/style/_mixin.scss";
           @import "@/assets/style/_variables.scss";

+ 1 - 1
apps/mobile/.env.production

@@ -4,4 +4,4 @@ VITE_APP_BASE_PUBLIC='https://cdn-common.jianyu360.cn/jy_mobile/'
 VITE_APP_IMAGE_BASE=''
 VITE_APP_APP_PROJECT_BASE=''
 VITE_APP_WX_PROJECT_BASE=''
-VITE_APP_GIT_BRANCH='v1.0.74'
+VITE_APP_GIT_BRANCH='v1.0.93'

+ 5 - 1
apps/mobile/package.json

@@ -19,12 +19,15 @@
     "@sentry/vue": "^7.64.0",
     "@tinymce/tinymce-vue": "^3.2.8",
     "aliyun_numberauthsdk_web": "^2.1.9",
+    "canvas": "2.8.0",
     "dayjs": "^1.11.8",
+    "dommatrix": "^1.0.3",
     "html2canvas": "^1.4.1",
     "js-cookie": "^3.0.1",
     "lodash": "^4.17.21",
     "lottie-web": "^5.12.0",
     "moment": "^2.29.1",
+    "pdfh5": "^1.4.9",
     "qs": "^6.11.2",
     "rgbaster": "^2.1.1",
     "svga": "^2.0.6",
@@ -34,7 +37,8 @@
     "vue-awesome-swiper": "^4.1.1",
     "vue-meta-info": "^0.1.7",
     "vuedraggable": "^2.24.3",
-    "vuex": "^3.6.2"
+    "vuex": "^3.6.2",
+    "web-streams-polyfill": "^4.1.0"
   },
   "devDependencies": {
     "@jonny1994/postcss-px-to-viewport": "^1.1.0",

+ 8 - 1
apps/mobile/scripts/updateGitInfo.js

@@ -18,8 +18,15 @@ const getGitBranch = () => {
 }
 
 const extractVersion = (branch) => {
+  let numberBranch = branch
+  if (numberBranch.indexOf('/')) {
+    numberBranch = numberBranch.split('/')[1]
+  }
+  if (numberBranch.indexOf('_')) {
+    numberBranch = numberBranch.split('_')[0]
+  }
   const versionRegex = /(v.*?)$/
-  const matches = branch.match(versionRegex)
+  const matches = numberBranch.match(versionRegex)
   return matches ? matches[0] : ''
 }
 

+ 27 - 0
apps/mobile/src/api/modules/pay.js

@@ -110,6 +110,33 @@ export function getDataPackUsage() {
   })
 }
 
+// 线上充值明细
+export function getDataExportRechargeList(data) {
+  return request({
+    url: '/subscribepay/dataExportPack/rechargeList',
+    method: 'post',
+    data: qs.stringify(data)
+  })
+}
+
+// 数据导出记录
+export function getDataExportRecordList(data) {
+  return request({
+    url: '/subscribepay/dataExportPack/recordList',
+    method: 'post',
+    data: qs.stringify(data)
+  })
+}
+
+// 数据导出记录-下载-发送邮箱
+export function sendDataExportFileEmail(data) {
+  return request({
+    url: '/subscribepay/dataExportPack/sendMail',
+    method: 'post',
+    data: qs.stringify(data)
+  })
+}
+
 // 获取数据导出筛选条数
 export function getDataExportFilterInfo(data) {
   data = qs.stringify(data)

+ 13 - 7
apps/mobile/src/components/selector/date-time-list/index.vue

@@ -1,30 +1,32 @@
 <template>
   <div class="date-time-selector-content bg-white">
     <CheckboxGroup
+      ref="checkboxGroup"
       v-model="state"
       :config="checkboxConfig"
       :options="checkboxOptions"
+      :marked="marked"
+      :before-change-action="beforeChangeAction"
       @change="onCheckboxChange"
       @item-select="onCheckboxSelect"
-      :beforeChangeAction="beforeChangeAction"
-      ref="checkboxGroup"
     />
     <DateTimeGroup
       v-show="dateTimeGroupActive"
+      ref="dateTimeGroup"
       :highlight="dateTimeGroupActive"
-      :startPlaceholder="startPlaceholder"
-      :endPlaceholder="endPlaceholder"
-      :endTimeFeature="endTimeFeature"
+      :start-placeholder="startPlaceholder"
+      :end-placeholder="endPlaceholder"
+      :end-time-feature="endTimeFeature"
       @change="onChange"
-      ref="dateTimeGroup"
     />
   </div>
 </template>
+
 <script>
 import DateTimeGroup from '@/components/selector/date-time-group'
 import { CheckboxGroup } from '@/ui'
 import { dateTimeKeys } from '@/data'
-import { dateFormatter, calcNotExactTime } from '@/utils'
+import { calcNotExactTime, dateFormatter } from '@/utils'
 
 export default {
   name: 'DateTimeListSelector',
@@ -44,6 +46,10 @@ export default {
         return []
       }
     },
+    marked: {
+      type: Boolean,
+      default: false
+    },
     endTimeFeature: {
       type: Boolean,
       default: false

+ 20 - 0
apps/mobile/src/router/modules/packs.js

@@ -0,0 +1,20 @@
+export default [
+  {
+    path: '/data-pack/index',
+    name: 'dataPackIndex',
+    component: () => import('@/views/packs/DataPackIndex.vue'),
+    meta: {
+      header: true,
+      title: '我的数据流量包'
+    }
+  },
+  {
+    path: '/data-pack/record',
+    name: 'dataPackRecord',
+    component: () => import('@/views/packs/DataExportRecord.vue'),
+    meta: {
+      header: true,
+      title: '数据导出记录'
+    }
+  }
+]

+ 16 - 13
apps/mobile/src/ui/checkbox-group/index.vue

@@ -11,18 +11,16 @@
       v-bind="item.props"
       :checked-icon="getConfig.icon"
       :disabled="item.disabled"
+      :marked="marked"
       :value="getSelectList.includes(String(item[getConfig.findKey]))"
       @change="onChangeItem($event, item)"
     >
       <!-- @slot 默认插槽提供 item, 自定义插槽提供 $index 索引  -->
-      <slot
-        v-if="item.slot"
-        :name="item.slot"
-        :item="item"
-        :$index="index"
-      ></slot>
-      <slot v-else :item="item">{{ item[getConfig.findLabel] }}</slot>
-      <span v-if="item.vip" class="v-vip-icon"></span>
+      <slot v-if="item.slot" :name="item.slot" :item="item" :$index="index" />
+      <slot v-else :item="item">
+        {{ item[getConfig.findLabel] }}
+      </slot>
+      <span v-if="item.vip" class="v-vip-icon" />
       <span v-if="item.tag" class="jy-tag">{{ item.tag }}</span>
     </CheckBox>
   </div>
@@ -30,11 +28,16 @@
 
 <script>
 import { CheckBox } from '@/ui'
+
 export default {
   name: 'CheckboxGroup',
   components: {
     [CheckBox.name]: CheckBox
   },
+  model: {
+    prop: 'value',
+    event: 'change'
+  },
   props: {
     /**
      * 所有选中项的标识符,对应 options item key
@@ -43,6 +46,10 @@ export default {
       type: [String, Array],
       default: () => []
     },
+    marked: {
+      type: Boolean,
+      default: false
+    },
     /**
      * 配置项
      *
@@ -97,10 +104,6 @@ export default {
       default: (v) => v
     }
   },
-  model: {
-    prop: 'value',
-    event: 'change'
-  },
   computed: {
     getConfig() {
       return Object.assign(
@@ -252,7 +255,7 @@ export default {
       margin-right: 0;
     }
   }
-  .v-vip-icon{
+  .v-vip-icon {
     display: inline-block;
     width: 14px;
     height: 14px;

+ 27 - 7
apps/mobile/src/ui/checkbox/index.vue

@@ -3,22 +3,28 @@
     class="jy-checkbox"
     :class="{
       checked: value,
-      'after-icon': checkedIcon
+      'after-icon': checkedIcon,
+      'b-mark': marked
     }"
     @click="onChange"
   >
-    <slot></slot>
+    <slot />
     <AppIcon v-if="value && checkedIcon" class="after-icon" name="jiaobiao" />
   </div>
 </template>
 
 <script>
 import { AppIcon } from '@/ui'
+
 export default {
   name: 'CheckBox',
   components: {
     [AppIcon.name]: AppIcon
   },
+  model: {
+    prop: 'value',
+    event: 'change'
+  },
   props: {
     /**
      * 是否为选中状态
@@ -34,6 +40,10 @@ export default {
       type: Boolean,
       default: true
     },
+    marked: {
+      type: Boolean,
+      default: false
+    },
     /**
      * change 前置钩子函数,用于自定义校验
      */
@@ -42,10 +52,6 @@ export default {
       default: (v) => v
     }
   },
-  model: {
-    prop: 'value',
-    event: 'change'
-  },
   methods: {
     onChange() {
       this.$emit('change', this.beforeChangeAction(!this.value))
@@ -74,8 +80,22 @@ export default {
   }
   &.checked {
     background: #e8fafd;
-    color: #2abed1;
+    color: $color_main;
+    &.b-mark {
+      // background: $color_main_background;
+      &::after {
+        content: '';
+        position: absolute;
+        right: 0;
+        bottom: 0;
+        background: url(@/assets/image/icon/icon-button-mark.png);
+        width: 14px;
+        height: 14px;
+        background-size: 100% 100%;
+      }
+    }
   }
+
   &.after-icon {
     padding-right: 16px;
     padding-left: 16px;

+ 18 - 4
apps/mobile/src/views/article/components/ContentMainText.vue

@@ -16,7 +16,8 @@
     </template>
     <template v-else>
       <section class="content-main-text">
-        <pre v-html="content.contentHighlighted" />
+        <PDF v-if="pdfUrl" :url="pdfUrl"></PDF>
+        <pre v-else v-html="content.contentHighlighted" />
       </section>
       <AttachmentDownloadCard class="attachment-download-section" />
       <div />
@@ -48,6 +49,7 @@
 </template>
 
 <script>
+import { Loading } from 'vant'
 import { mapGetters, mapState } from 'vuex'
 import ContentModuleCard from '@/views/article/ui/ContentModuleCard.vue'
 import AttachmentDownloadCard from '@/views/article/components/AttachmentDownloadCard.vue'
@@ -56,14 +58,17 @@ import Reward from '@/views/article/components/Reward.vue'
 import { LINKS } from '@/data'
 import { openAppOrWxPage } from '@/utils/'
 import EventBus from '@/utils/eventBus'
+import PDF from '@/views/article/components/PDF.vue'
 
 export default {
   name: 'ContentMainText',
   components: {
+    [Loading.name]: Loading,
     AttachmentDownloadCard,
     OriginLink,
     Reward,
-    ContentModuleCard
+    ContentModuleCard,
+    PDF
   },
   props: {
     showMask: {
@@ -74,7 +79,8 @@ export default {
   },
   computed: {
     ...mapState({
-      content: (state) => state.article.mainModel.content
+      content: (state) => state.article.mainModel.content,
+      mainModel: (state) => state.article.mainModel
     }),
     canRead() {
       return this.content.isCanRead
@@ -108,7 +114,11 @@ export default {
     rewardShow() {
       return this.canRead && this.$envs.inWX
     },
-    ...mapGetters('user', ['isLogin'])
+    ...mapGetters('user', ['isLogin']),
+    pdfUrl() {
+      // console.log(this.mainModel.content._od.pdfUrl)
+      return this.mainModel.content._od.pdfUrl
+    }
   },
   methods: {
     toUnlockContent() {
@@ -148,6 +158,9 @@ export default {
     padding: 12px 16px;
     background-color: $white;
   }
+  .pdf-box {
+    margin: -8px;
+  }
 }
 .mask-content {
   ::v-deep {
@@ -177,6 +190,7 @@ export default {
 }
 
 .content-main-text {
+  position: relative;
   margin-bottom: 16px;
   ::v-deep {
     pre {

+ 165 - 0
apps/mobile/src/views/article/components/PDF.vue

@@ -0,0 +1,165 @@
+<template>
+  <div class="pdf-box">
+    <van-loading v-if="loading" size="32px" vertical>加载中</van-loading>
+    <div v-show="isRender" id="pdf-canvas" style="height: 100%; width: 100%">
+      <div class="pagination-container">
+        <span class="pagination-now">1</span>
+        <em>/</em>
+        <span class="pagination-total"></span>
+      </div>
+    </div>
+    <div v-show="!isRender && !loading" class="error-container">
+      加载异常,点击<span class="highlight-label" @click="initPdfH5">刷新</span
+      >重试
+    </div>
+  </div>
+</template>
+
+<script>
+/**
+ * 需求:支持用手指对文件大小进行缩放查看(同百度文库),最小缩放到原初始尺寸,最大缩放到原初始尺寸的2倍
+ * pdf预览方案选择参考:https://blog.csdn.net/weixin_42717027/article/details/136225233
+ * pdfh5文档实例:https://gitee.com/gjTool/pdfh5/tree/master
+ * */
+import Pdfh5 from 'pdfh5'
+import 'pdfh5/css/pdfh5.css'
+import { Loading } from 'vant'
+export default {
+  name: 'Pdf',
+  components: {
+    [Loading.name]: Loading
+  },
+  props: {
+    url: {
+      type: String,
+      default: ''
+    }
+  },
+  data() {
+    return {
+      pdfH5: null,
+      isRender: true,
+      loading: true,
+      currentPage: 1
+    }
+  },
+  mounted() {
+    this.initPdfH5()
+  },
+  beforeDestroy() {
+    if (this.pdfH5) {
+      this.pdfH5.destroy() // 销毁实例
+    }
+  },
+  methods: {
+    initPdfH5() {
+      this.loading = true
+      this.pdfH5 = new Pdfh5('#pdf-canvas', {
+        pdfurl: this.url, // 'http://localhost:8080/jy_mobile/src/assets/image/demo.pdf',
+        lazy: true, // 启用懒加载
+        renderType: 'canvas', // 渲染方式,可选值:canvas、svg
+        maxZoom: 2, // 手势缩放最大倍数
+        pageNum: false, // 是否显示左上角页码(自带的计算页码会有偏差,用下面updateCurrentPage方法重新计算渲染)
+        backTop: false // 是否显示右下角返回顶部按钮
+      })
+      // 监听pdf准备开始渲染,此时可以拿到pdf总页数
+      this.pdfH5.on('ready', function (totalNum) {
+        console.log('总页数:' + totalNum)
+        this.loading = false
+        const pageTotal = document.querySelector('.pagination-total')
+        pageTotal.textContent = totalNum
+      })
+      // 监听滚动事件
+      let scrollTimeout
+      this.pdfH5.on('scroll', (scrollTop, currentNum) => {
+        // console.log('scrollTop:' + scrollTop, 'currentNum:' + currentNum)
+        clearTimeout(scrollTimeout)
+        scrollTimeout = setTimeout(() => {
+          this.updateCurrentPage()
+        }, 100) // 100ms 防抖
+      })
+      // 防抖处理
+      let renderTimeout
+      this.pdfH5.on('zoom', (scale) => {
+        clearTimeout(renderTimeout)
+        renderTimeout = setTimeout(() => {
+          console.log('缩放比例:', scale)
+        }, 300) // 300ms 防抖
+      })
+      // 监听pdf加载完成事件,加载失败、渲染成功都会触发
+      this.pdfH5.on('complete', (status, msg, time) => {
+        console.log(
+          '状态:' + status + ',信息:' + msg + ',耗时:' + time + '毫秒'
+        )
+        if (status === 'error') {
+          this.isRender = false
+          this.loading = false
+        } else {
+          this.isRender = true
+          this.loading = false
+        }
+      })
+    },
+    updateCurrentPage() {
+      const container = document.querySelector('.viewerContainer')
+      const pages = container.querySelectorAll('.pageContainer')
+      let currentPage = 1
+
+      pages.forEach((page, index) => {
+        const rect = page.getBoundingClientRect()
+        if (rect.top <= container.offsetHeight / 2) {
+          currentPage = index + 1
+        }
+      })
+
+      this.currentPage = currentPage
+      const pageNow = document.querySelector('.pagination-now')
+      pageNow.textContent = currentPage
+      console.log('当前页码:', this.currentPage)
+    }
+  }
+}
+</script>
+
+<style>
+.pdf-box {
+  height: 500px;
+}
+.error-container {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  font-size: 16px;
+}
+.highlight-label {
+  color: #2abed1;
+  text-decoration-line: underline;
+}
+.van-loading {
+  height: 100%;
+  justify-content: center;
+}
+.van-loading__text {
+  margin-top: 20px;
+}
+.pagination-container {
+  position: absolute;
+  width: calc(100% - 14px);
+  height: 30px;
+  top: 0;
+  left: 6.5px;
+  background: #4e4e4e;
+  text-align: center;
+  line-height: 30px;
+  color: #fff;
+  font-size: 16px;
+}
+.pdfjs {
+  padding-top: 30px;
+}
+.pdfjs .pdfViewer {
+  padding-top: 1px;
+}
+</style>

+ 170 - 48
apps/mobile/src/views/create-order/components/dataexport/ProductionCard.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="production-card data-export">
-    <Progress :active="exportInfo.step"></Progress>
+    <Progress :active="exportInfo.step" />
     <div class="msg-count-container bg-white border-line-t">
       <div class="m-c-left">
         已选择 <span class="msg-count">{{ exportInfo.count }}</span> 条数据
@@ -10,23 +10,25 @@
       </div>
     </div>
     <SpecList
+      v-model="payActive"
       class="pay-list"
       title="请选择支付方式"
       :list="payList"
-      activeType="value"
-      v-model="payActive"
+      active-type="value"
       @change="payListChange"
     >
       <template #card-content="{ spec }">
-        <div class="spec-i-label">{{ spec.label }}</div>
-        <div class="spec-i-text color-red" v-if="spec.desc">
+        <div class="spec-i-label">
+          {{ spec.label }}
+        </div>
+        <div v-if="spec.desc" class="spec-i-text color-red">
           {{ spec.desc }}
         </div>
       </template>
       <template #footer>
         <div class="pay-way-tip">
           <span>数据流量包:按条数预充值,价格更优!</span>
-          <span class="arrow-rightx3"></span>
+          <span class="arrow-rightx3" />
           <span id="buy-data-pack-for-link" @click="chargeNow">
             <img src="@/assets/image/area-pack/buy-now.png" alt="" />
           </span>
@@ -35,16 +37,16 @@
     </SpecList>
     <!-- 数据流量包 可选账户列表 -->
     <van-radio-group
-      class="account-list bg-white"
       v-show="payActive === 'pack' && accountList.length > 1"
       v-model="accountListActive"
+      class="account-list bg-white"
       @change="accountListActiveChange"
     >
       <van-cell-group>
         <van-cell
           v-for="(item, index) in accountList"
-          clickable
           :key="index"
+          clickable
           :title="item.title"
           :label="item.tip"
           @click="accountListActive = index"
@@ -55,7 +57,7 @@
                 <span
                   class="j-icon checkbox"
                   :class="{ checked: props.checked }"
-                ></span>
+                />
               </template>
             </van-radio>
           </template>
@@ -64,15 +66,17 @@
     </van-radio-group>
     <!-- 个人支付 -->
     <SpecList
-      class="s-list"
       v-show="payActive === 'pay'"
+      class="s-list"
       title="请选择数据规格"
       :list="specList"
       :active="productSpec.id"
       @activeChange="specCardChange"
     >
       <template #card-content="{ spec }">
-        <div class="spec-i-label">{{ spec.label }}</div>
+        <div class="spec-i-label">
+          {{ spec.label }}
+        </div>
         <div class="spec-i-sub mt8">
           <span class="spec-i-text del">{{ spec.before }}元/条</span>
           <span class="spec-i-text">{{ spec.price }}元/条</span>
@@ -82,7 +86,7 @@
         <div class="b-group">
           <div class="spec-question">
             <span>标准字段包、高级字段包</span>
-            <van-icon name="question-o" @click="showQuestion"></van-icon>
+            <van-icon name="question-o" @click="showQuestion" />
           </div>
           <div class="prev-data">
             <span @click="onPreviewData">预览数据</span>
@@ -90,7 +94,11 @@
         </div>
       </template>
     </SpecList>
-    <QuestionTip v-model="dialog.question" :show-signaturedate="true" />
+    <QuestionTip
+      v-model="dialog.question"
+      :show-signaturedate="true"
+      source="DataExportPayment_IndustryFields"
+    />
     <van-dialog
       v-model="dialog.balance"
       title="对不起,余额不足"
@@ -106,29 +114,30 @@
       <div class="balance-tip-content desc-list">
         <div class="desc-list">
           <div
-            class="desc-item"
             v-for="(item, index) in balanceConf.descList"
-            :key="index"
             v-show="item.showInDialog"
+            :key="index"
+            class="desc-item"
           >
             <span class="desc-item-label">{{ item.label }}</span>
             <span class="desc-item-split">{{ item.split }}</span>
-            <span class="desc-item-text">{{ item.text }}</span>
+            <span class="desc-item-text" v-html="item.text" />
           </div>
         </div>
-        <p class="tip-warning" v-show="balanceConf.tipTextSelfShow">
+        <p v-show="balanceConf.tipTextSelfShow" class="tip-warning">
           {{ balanceConf.tipTextSelf }}
         </p>
-        <p class="tip-warning" v-show="balanceConf.tipTextAdminShow">
+        <p v-show="balanceConf.tipTextAdminShow" class="tip-warning">
           {{ balanceConf.tipTextAdmin }}
         </p>
       </div>
     </van-dialog>
   </div>
 </template>
+
 <script>
-import { mapState, mapMutations, mapActions, mapGetters } from 'vuex'
-import { Icon, Cell, CellGroup, Radio, RadioGroup } from 'vant'
+import { mapActions, mapGetters, mapMutations, mapState } from 'vuex'
+import { Cell, CellGroup, Icon, Radio, RadioGroup } from 'vant'
 import SpecList from '@/components/create-order/SpecList'
 import Progress from '@/views/order/components/dataexport/DataExportProgress'
 import QuestionTip from '@/views/order/components/datapack/QuestionTip'
@@ -137,16 +146,15 @@ import { openAppOrWxPage } from '@/utils'
 import { accMul } from '@/utils/math'
 import orderActivityHelper from '@/utils/mixins/modules/order-activity-helper'
 import {
-  packPayApi,
+  getDataExportFilterInfo,
   getDataExportInfo,
-  getDataPackUsage,
   getDataExportPrice,
-  getDataExportFilterInfo
+  getDataPackUsage,
+  packPayApi
 } from '@/api/modules'
 
 export default {
   name: 'DataExportProductionCard',
-  mixins: [mixinHeader, orderActivityHelper],
   components: {
     [Icon.name]: Icon,
     [Cell.name]: Cell,
@@ -157,6 +165,7 @@ export default {
     Progress,
     QuestionTip
   },
+  mixins: [mixinHeader, orderActivityHelper],
   data() {
     return {
       pageLayoutConf: {
@@ -314,7 +323,7 @@ export default {
           // 从extend中取,取不到则取默认值
           before: specExtendInfo?.originalPrice || specList[index]?.before,
           price: specExtendInfo?.discountPrice || specList[index]?.after,
-          key: key,
+          key,
           tipText: spec._format.tag,
           tipType: 'gift', // gift/discount/'' 为空则显示默认的蓝色
           _data: spec
@@ -346,7 +355,7 @@ export default {
     balanceConfKey() {
       const active = this.payActive
       if (active === 'pack') {
-        if (this.activeAccountList.online) {
+        if (this.activeAccountList?.online) {
           return 'pack'
         } else {
           return 'pack1'
@@ -404,6 +413,12 @@ export default {
       const { id } = this.$route.query
       this.exportInfo.id = id
     },
+    emptyValue(t) {
+      return t === null || t === undefined
+    },
+    hasValue(t) {
+      return !this.emptyValue(t)
+    },
     setDefaultSelected() {
       const { type } = this.$route.query
       // 默认选中
@@ -465,7 +480,7 @@ export default {
       }
     },
     // 提交UI变化相关的数据
-    refreshUIState: function () {
+    refreshUIState() {
       const desc = this.getDescInfo()
       const personalPay = this.payActive === 'pay'
       const payload = {
@@ -599,7 +614,7 @@ export default {
       }
       if (this.payActive === 'pack') {
         payload.doType = doTypeMap[this.balanceConfKey]
-        if (this.activeAccountList.online) {
+        if (this.activeAccountList?.online) {
           payload.packType = this.activeAccountList.levelKey
         } else {
           payload.entId = this.activeAccountList.data.entId
@@ -762,7 +777,8 @@ export default {
       if (entPack) {
         entPack.forEach((e) => {
           accountList.push({
-            title: '线下充值账户',
+            // title: '线下充值账户',
+            title: '企业数据流量包',
             tip: e.entName,
             levelKey: 'senior',
             online: false,
@@ -775,7 +791,8 @@ export default {
       if (personalPack) {
         if (personalPack.seniorTotal > 0) {
           accountList.push({
-            title: '线上充值账户(高级字段包)',
+            // title: '线上充值账户(高级字段包)',
+            title: '个人数据流量包(高级字段包)',
             level: 2, // 1标准字段包 2高级字段包
             levelKey: 'senior',
             online: true,
@@ -786,7 +803,8 @@ export default {
         }
         if (personalPack.normalTotal > 0) {
           accountList.push({
-            title: '线上充值账户(标准字段包)',
+            // title: '线上充值账户(标准字段包)',
+            title: '个人数据流量包(标准字段包)',
             level: 1, // 1标准字段包 2高级字段包
             levelKey: 'normal',
             online: true,
@@ -799,7 +817,8 @@ export default {
       if (accountList.length === 0) {
         this.paySpecList[0].show = true
         accountList.push({
-          title: '线上充值账户(标准字段包)',
+          // title: '线上充值账户(标准字段包)',
+          title: '个人数据流量包(标准字段包)',
           level: 1,
           levelKey: 'normal',
           online: true,
@@ -965,17 +984,77 @@ export default {
                 text: enough ? `${lastCount}条` : ' / 条'
               }
             ])
+          this.balanceConfMap[this.balanceConfKey].tipTextSelfShow = false
+          this.balanceConfMap[this.balanceConfKey].tipTextAdminShow = false
           return enough
         } else {
           const entAllLastCount = account.data.entAllCount // 企业所剩余额
-          const userLastCountToday = account.data.surplusToday // 此用户今日所剩余额
-          const limitToday = account.data.limitToday // 此用户单日限额
-          // 重复的数据不再扣除
-          const entCount = entAllLastCount - deduct
-          const userCount = userLastCountToday - deduct
+          // 单日限额/余额
+          const userLimitToday = account.data.limitToday // 此用户单日限额,没返回表示无限制
+          const userLastCountToday = account.data.surplusToday // 此用户今日所剩余额,没返回表示无限制
+          // 总量限额/余额
+          const userLimitTotal = account.data.maxNums // 全部总量限额,没返回表示无限制
+          const userLastTotal = account.data.surplusNums // 全部剩余限额,没返回表示无限制
+
+          // 扣除后额度
+          const entLastCount = entAllLastCount - deduct
+          let userLastCount = 0
+          const dialogTipConf = {
+            showLimitToday: false,
+            showLimitTotal: false
+          }
+
+          if (
+            this.emptyValue(userLimitToday) &&
+            this.emptyValue(userLimitTotal)
+          ) {
+            // 1. 未设置单日限额 未设置总量限额
+            // 则个人额度就是企业额度
+            userLastCount = entAllLastCount - deduct
+          } else if (
+            this.emptyValue(userLimitToday) &&
+            this.hasValue(userLimitTotal)
+          ) {
+            // 2. 未设置单日限额 设置总量限额
+            // 扣除按照总量限额计算
+            userLastCount = userLastTotal - deduct
+
+            dialogTipConf.showLimitToday = false
+            dialogTipConf.showLimitTotal = userLastCount < 0
+          } else if (
+            this.hasValue(userLimitToday) &&
+            this.emptyValue(userLimitTotal)
+          ) {
+            // 3. 设置单日限额 未设置总量限额
+            // 扣除按照单日限额计算
+            userLastCount = userLastCountToday - deduct
+
+            dialogTipConf.showLimitToday = userLastCount < 0
+            dialogTipConf.showLimitTotal = false
+          } else {
+            // 4. 设置了单日限额 设置了总量限额
+            // 单日限额和总量限额都要满足(取最小的)
+            const dailyLast = userLastCountToday - deduct
+            const totalLast = userLastTotal - deduct
+            // 优化前---
+            // if (dailyLast >= 0 && totalLast >= 0) {
+            //   userLastCount = Math.min(dailyLast, totalLast)
+            // } else if (dailyLast >= 0 && totalLast < 0) {
+            //   userLastCount = totalLast
+            // } else if (dailyLast < 0 && totalLast >= 0) {
+            //   userLastCount = dailyLast
+            // } else {
+            //   userLastCount = Math.min(dailyLast, totalLast)
+            // }
+            // 优化后---
+            userLastCount = Math.min(dailyLast, totalLast)
+            dialogTipConf.showLimitToday = dailyLast < 0
+            dialogTipConf.showLimitTotal = totalLast < 0
+          }
 
-          const entEnough = entCount >= 0
-          const userEnough = userCount >= 0
+          // 扣完是否够用
+          const entEnough = entLastCount >= 0
+          const userEnough = userLastCount >= 0
 
           let descList = descListCommon.concat([
             {
@@ -984,22 +1063,45 @@ export default {
               label: '所属企业账户余额',
               split: ':',
               text: `${entAllLastCount}条`
-            },
-            {
+            }
+          ])
+
+          if (this.hasValue(userLimitToday)) {
+            descList.push({
               showInDesc: true,
-              showInDialog: false,
+              showInDialog: true,
               label: '您每日导出限额',
               split: ':',
-              text: `${limitToday}条`
-            },
-            {
+              text: `${userLimitToday}条`
+            })
+          }
+          if (this.hasValue(userLastCountToday)) {
+            descList.push({
               showInDesc: true,
               showInDialog: true,
               label: '您今日余额',
               split: ':',
               text: `${userLastCountToday}条`
-            }
-          ])
+            })
+          }
+          if (this.hasValue(userLimitTotal)) {
+            descList.push({
+              showInDesc: true,
+              showInDialog: true,
+              label: '您导出总量限额',
+              split: ':',
+              text: `${userLimitTotal}条`
+            })
+          }
+          if (this.hasValue(userLastTotal)) {
+            descList.push({
+              showInDesc: true,
+              showInDialog: true,
+              label: '您还可导出',
+              split: ':',
+              text: `${userLastTotal}条`
+            })
+          }
 
           if (!entEnough) {
             descList = descListCommon.concat([
@@ -1017,11 +1119,30 @@ export default {
           }
 
           this.balanceConfMap[this.balanceConfKey].descList = descList
+          this.calcDialogTipText(dialogTipConf)
 
           return entEnough && userEnough
         }
       }
     },
+    calcDialogTipText(accountInfo = {}) {
+      const { showLimitToday, showLimitTotal } = accountInfo
+      const inlineArr = []
+      if (showLimitToday) {
+        inlineArr.push('每日导出限额')
+      }
+      if (showLimitTotal) {
+        inlineArr.push('导出总量限额')
+      }
+
+      let prefix = ''
+      if (inlineArr.length > 0) {
+        prefix = `您的${inlineArr.join('、')}`
+      }
+      const text = `${prefix}已超过管理员设置数量,请联系管理员或先按照个人支付`
+      this.balanceConfMap.pack1.tipTextSelf = text
+      this.balanceConfMap.pack1.tipTextAdmin = text
+    },
     dialogChargeNow() {
       this.closeBalanceDialog()
       setTimeout(() => {
@@ -1037,7 +1158,7 @@ export default {
     closeBalanceDialog() {
       this.dialog.balance = false
     },
-    showQuestion: function () {
+    showQuestion() {
       this.dialog.question = true
     },
     backToFilter() {
@@ -1053,6 +1174,7 @@ export default {
   }
 }
 </script>
+
 <style lang="scss" scoped>
 .msg-count-container {
   margin-bottom: 8px;

+ 10 - 10
apps/mobile/src/views/create-order/components/datapack/SpecCell.vue

@@ -1,16 +1,16 @@
 <template>
-  <!-- 产品规格组件-->
+  <!-- 产品规格组件 -->
   <div>
-    <spec-list
+    <SpecList
       title="请选择数据规格"
       :list="specList || []"
-      @activeChange="specChange"
       :active="productSpec.id"
+      @activeChange="specChange"
     >
       <template #header>
         <div :style="specInfo.desc === '标准字段包' ? 'margin-top:20px;' : ''">
           <span>{{ specInfo.desc }}</span>
-          <span class="sub-title" v-if="Object.keys(extend).length > 0">
+          <span v-if="Object.keys(extend).length > 0" class="sub-title">
             <span class="sub-text del"
               >{{ extend.originalPrice + extend.priceUnit.toString() }}/{{
                 extend.specsUnit
@@ -24,26 +24,26 @@
           </span>
         </div>
       </template>
-      <template #footer v-if="specInfo.desc === '高级字段包'">
+      <template v-if="specInfo.desc === '高级字段包'" #footer>
         <div class="spec-question">
           <span>标准字段包、高级字段包</span>
-          <van-icon name="question-o" @click="showQuestion"></van-icon>
+          <van-icon name="question-o" @click="showQuestion" />
         </div>
       </template>
-    </spec-list>
-    <QuestionTip v-model="dialog.question" />
+    </SpecList>
+    <QuestionTip v-model="dialog.question" source="dataPack_IndustryFields" />
   </div>
 </template>
 
 <script>
 import { Icon } from 'vant'
+import { mapGetters, mapState } from 'vuex'
 import SpecList from '@/components/create-order/SpecList'
 import QuestionTip from '@/views/order/components/datapack/QuestionTip'
-import { mapGetters, mapState } from 'vuex'
 import { fen2Yuan, getPureNumber } from '@/utils'
 
 export default {
-  name: 'spec-cell',
+  name: 'SpecCell',
   components: {
     [Icon.name]: Icon,
     SpecList,

+ 56 - 52
apps/mobile/src/views/dataexport/LimitPreviewData.vue

@@ -1,22 +1,22 @@
 <template>
   <div class="limit-previewData">
-    <Progress :active="exportInfo.step"></Progress>
+    <Progress :active="exportInfo.step" />
     <div class="data_option">
       <div class="data_total">
         找到 <span style="color: #2abed1">{{ total }}</span> 条数据
       </div>
       <div class="data_field" @click="showQuestion">
         <span>字段包说明</span>
-        <van-icon name="question-o"></van-icon>
+        <van-icon name="question-o" />
       </div>
     </div>
     <div
-      id="previeData_main"
       v-if="isFixed"
+      id="previeData_main"
       class="previeData_main"
       :class="[isFixed ? 'isFixed' : '', isWxFixed ? 'isWxFixed' : '']"
     >
-      <div id="previeData_thead" class="previeData_thead"></div>
+      <div id="previeData_thead" class="previeData_thead" />
     </div>
     <van-tabs
       v-if="result && result.length > 0"
@@ -25,9 +25,9 @@
       color="#2cb7ca"
       line-width="150px"
       line-height="2px"
-      @click="toggle"
       sticky
       :offset-top="offsetTop"
+      @click="toggle"
     >
       <van-tab title="标准字段包" name="1">
         <div class="table-container">
@@ -37,7 +37,7 @@
             cellspacing="0"
             cellpadding="0"
           >
-            <thead class="thead" id="mycanvas_bz">
+            <thead id="mycanvas_bz" class="thead">
               <tr>
                 <td>序号</td>
                 <td>匹配关键词</td>
@@ -77,7 +77,7 @@
       <van-tab title="高级字段包" name="2">
         <div class="table-container tableGJ">
           <table class="table table-gj" cellspacing="0" cellpadding="0">
-            <thead class="thead" id="mycanvas_gj">
+            <thead id="mycanvas_gj" class="thead">
               <tr>
                 <td rowspan="2">序号</td>
                 <td rowspan="2">匹配关键词</td>
@@ -199,15 +199,19 @@
           </table>
         </div>
       </van-tab>
-      <QuestionTip v-model="question" :show-signaturedate="true" />
-      <div class="export-table-footer" v-if="total > 20">
+      <QuestionTip
+        v-model="question"
+        source="DataExportFilter_IndustryFields"
+        :show-signaturedate="true"
+      />
+      <div v-if="total > 20" class="export-table-footer">
         <p class="view-text">导出后可查看更多数据</p>
-        <div @click="setExport" class="export-text">
+        <div class="export-text" @click="setExport">
           <span>立即导出</span>
-          <i class="icon iconfont icon-youbian"></i>
+          <i class="icon iconfont icon-youbian" />
         </div>
       </div>
-      <div class="maxExportTips" v-if="total > 20000">
+      <div v-if="total > 20000" class="maxExportTips">
         <span
           >您选择的数据超过了导出数据最大值20,000,请优化条件后导出。您也可联系客服:400-108-6670,或添加
           <span class="blue" @click="show_wxqr = true">客服微信></span
@@ -219,31 +223,33 @@
           找到 <span style="color: #2abed1">{{ total }}</span> 条数据
         </div>
         <div class="pre-footer-group">
-          <button @click="setLimitData" class="pre-footer-cancel pre-btncom">
+          <button class="pre-footer-cancel pre-btncom" @click="setLimitData">
             修改条件
           </button>
-          <button @click="setExport" class="pre-footer-confirm pre-btncom">
+          <button class="pre-footer-confirm pre-btncom" @click="setExport">
             立即导出
           </button>
         </div>
       </div>
     </van-tabs>
-    <AppEmpty v-else>{{ emptyText }}</AppEmpty>
+    <AppEmpty v-else>
+      {{ emptyText }}
+    </AppEmpty>
     <van-overlay :show="showMask" z-index="100">
       <div class="wrapper">
-        <span class="loading"></span>
+        <span class="loading" />
       </div>
     </van-overlay>
     <van-overlay :show="show_wxqr" z-index="99" @click="show_wxqr = false">
       <div class="wx_box" @click.stop>
-        <div class="wx_box_close" @click="show_wxqr = false"></div>
+        <div class="wx_box_close" @click="show_wxqr = false" />
         <div class="content_box">
           <div class="head">
             <img src="@/assets/image/dataExport/wx_icon.png" alt="" /><span
               >客服微信</span
             >
           </div>
-          <div class="qr-box" v-if="kefuInfo.wxer">
+          <div v-if="kefuInfo.wxer" class="qr-box">
             <img :src="kefuInfo.wxer" alt="" class="qr" />
           </div>
           <p class="text">微信扫一扫</p>
@@ -253,15 +259,17 @@
     </van-overlay>
   </div>
 </template>
+
 <script>
-import { Tab, Tabs, Overlay, Icon } from 'vant'
+import { Icon, Overlay, Tab, Tabs } from 'vant'
+import html2canvas from 'html2canvas'
 import Progress from '@/views/order/components/dataexport/DataExportProgress'
 import QuestionTip from '@/views/order/components/datapack/QuestionTip'
 import { AppEmpty } from '@/ui'
-import { getPreview, getCustomInfo } from '@/api/modules'
-import html2canvas from 'html2canvas'
+import { getCustomInfo, getPreview } from '@/api/modules'
+
 export default {
-  name: 'limit-previewData',
+  name: 'LimitPreviewData',
   components: {
     [Tab.name]: Tab,
     [Tabs.name]: Tabs,
@@ -288,6 +296,16 @@ export default {
     },
     kefuInfo: {}
   }),
+  computed: {},
+  watch: {
+    isFixed(newval, oldval) {
+      if (newval) {
+        this.$nextTick(() => {
+          this.setHtmlCanvas(this.active)
+        })
+      }
+    }
+  },
   created() {
     if (this.$route.query.type === 'senior') {
       this.active = '2'
@@ -303,26 +321,16 @@ export default {
     window.addEventListener('scroll', this.getScroll, true)
     this.getKefuInfo()
   },
-  watch: {
-    isFixed(newval, oldval) {
-      if (newval) {
-        this.$nextTick(() => {
-          this.setHtmlCanvas(this.active)
-        })
-      }
-    }
-  },
-  computed: {},
 
   methods: {
     // 获取客服二维码信息
-    getKefuInfo () {
+    getKefuInfo() {
       const params = {
         type: 'kf'
       }
-      getCustomInfo(params).then(res => {
+      getCustomInfo(params).then((res) => {
         const { error_code: code, data } = res
-        if(code === 0 && data) {
+        if (code === 0 && data) {
           this.kefuInfo = data
         }
       })
@@ -338,7 +346,7 @@ export default {
         ).scrollLeft
       }
       if ($scrollLeft) {
-        $scrollLeft.style.transform = 'translateX(-' + $scrolltable + 'px)'
+        $scrollLeft.style.transform = `translateX(-${$scrolltable}px)`
       }
       if (top > 140) {
         this.isFixed = true
@@ -370,10 +378,10 @@ export default {
       const height = node.offsetHeight // dom高
       const scale = 2 // 放大倍数 这个相当于清晰度 调大一点更清晰一点
       html2canvas(node, {
-        width: width,
-        height: height,
+        width,
+        height,
         backgroundColor: '#EBF5FE',
-        scale: scale,
+        scale,
         X: 0,
         Y: 0,
         // scrollX: -3, // 如果左边多个白边 设置该偏移-3 或者更多
@@ -392,19 +400,15 @@ export default {
     setExport() {
       if (this.active === '2') {
         this.$router.push(
-          '/common/order/create/dataexport?id=' +
-            this.$route.params.id +
-            '&type=senior'
+          `/common/order/create/dataexport?id=${this.$route.params.id}&type=senior`
         )
       } else {
         this.$router.push(
-          '/common/order/create/dataexport?id=' +
-            this.$route.params.id +
-            '&type=standard'
+          `/common/order/create/dataexport?id=${this.$route.params.id}&type=standard`
         )
       }
     },
-    showQuestion: function () {
+    showQuestion() {
       this.question = true
     },
     async getPreviewData(type) {
@@ -422,16 +426,16 @@ export default {
           //   this.total = res.total
           // }
           this.total = res.total
-          let result = res.data
+          const result = res.data
           result.forEach((ele) => {
             if (ele.s_winner) {
               ele.s_winnerlist = ele.s_winner.split(',')
             } else {
               ele.s_winnerlist = []
             }
-            let company_email_list = []
-            let company_phone_list = []
-            let legal_person_list = []
+            const company_email_list = []
+            const company_phone_list = []
+            const legal_person_list = []
             if (ele.winnerMaps && ele.winnerMaps.length > 0) {
               ele.s_winnerlist.forEach((a) => {
                 company_email_list.push('')
@@ -477,7 +481,7 @@ export default {
     textFormatForMosaic(text, phone) {
       if (typeof text === 'string' || typeof text === 'number') {
         if (phone) {
-          return String(text).replace(/([\w\W]*)([\w\W]{4})$/, '$1****')
+          return String(text).replace(/([\s\S]*)([\s\S]{4})$/, '$1****')
         } else {
           return String(text).replace(/^(.)(.*)/, '$1**')
         }
@@ -486,7 +490,7 @@ export default {
     },
     moneyFormatForCover(money) {
       if (typeof money === 'string' || typeof money === 'number') {
-        return String(money).replace(/(\d)(\.){0,1}(\d)/, '$1$2*')
+        return String(money).replace(/(\d)(\.)?(\d)/, '$1$2*')
       }
       return money
     }

+ 157 - 134
apps/mobile/src/views/invoice/Invoicing.vue

@@ -4,7 +4,9 @@
       <div class="item">
         <div class="text_box">
           <p class="title">订单编号:</p>
-          <p class="value">{{ infoMap.code }}</p>
+          <p class="value">
+            {{ infoMap.code }}
+          </p>
         </div>
       </div>
       <div class="item mt-10">
@@ -12,7 +14,7 @@
           <p class="title">开票金额:</p>
           <p class="value">{{ infoMap.price }}元</p>
         </div>
-        <div class="rightBtn" @click="tipShow = true" v-if="!$envs.inApp">
+        <div v-if="!$envs.inApp" class="rightBtn" @click="tipShow = true">
           开票规则
         </div>
       </div>
@@ -21,209 +23,211 @@
       <div class="j-content">
         <div class="item-box-type">
           <div class="item-box-title redstar">发票类型</div>
-          <radioGroup
+          <RadioGroup
             v-model="infoMap.type"
             :list="typelist"
             @change="initshowModule"
-          ></radioGroup>
+          />
         </div>
         <van-field
           v-model.trim="infoMap.content"
           label="发票内容"
           readonly
-          @click="chooseContent"
           right-icon="arrow"
           right-icon-size="14"
-        ></van-field>
+          @click="chooseContent"
+        />
         <div class="item-box-type">
           <div class="item-box-title redstar">发票抬头</div>
-          <radioGroup
+          <RadioGroup
             v-model="infoMap.invoiceHeader"
             :list="invoiceHeaderlist"
             @change="initshowModule"
-          ></radioGroup>
+          />
         </div>
 
         <div class="companybox">
-          <div class="associate-ent-group" v-show="isAssociateShow">
+          <div v-show="isAssociateShow" class="associate-ent-group">
             <div
+              v-for="(item, i) in searchList"
+              :key="i"
               class="associate-ent-item"
               @click="selectEnt(item)"
-              v-for="(item, i) in searchList"
-              v-bind:key="i"
               v-html="highlightText(item.name, infoMap.company)"
-            ></div>
+            />
           </div>
           <van-field
+            v-if="showModule.company"
             v-model.trim="infoMap.company"
             label="公司名称"
             maxlength="50"
-            @focus="infoCheckMap.company = ''"
-            @input="entOnChange('input')"
-            @blur="entOnChange('blur')"
-            :errorMessage="infoCheckMap.company"
+            :error-message="infoCheckMap.company"
             :required="requireds.company"
             placeholder="请输入准确的公司名称"
-            v-if="showModule.company"
             type="textarea"
             rows="1"
             autosize
-          ></van-field>
+            @focus="infoCheckMap.company = ''"
+            @input="entOnChange('input')"
+            @blur="entOnChange('blur')"
+          />
         </div>
 
         <van-field
+          v-if="showModule.dutyparagraph"
           v-model.trim="infoMap.dutyparagraph"
           label="单位税号"
-          @focus="infoCheckMap.dutyparagraph = ''"
-          @blur="getCheckMap('dutyparagraph')"
-          :errorMessage="infoCheckMap.dutyparagraph"
+          :error-message="infoCheckMap.dutyparagraph"
           :required="requireds.dutyparagraph"
           placeholder="请输入纳税人识别号"
-          v-if="showModule.dutyparagraph"
           type="textarea"
           rows="1"
           autosize
-        ></van-field>
+          @focus="infoCheckMap.dutyparagraph = ''"
+          @blur="getCheckMap('dutyparagraph')"
+        />
 
         <van-field
+          v-if="showModule.unitAddress"
           v-model.trim="infoMap.unitAddress"
           label="单位地址"
-          @focus="infoCheckMap.unitAddress = ''"
-          @blur="getCheckMap('unitAddress')"
-          :errorMessage="infoCheckMap.unitAddress"
+          :error-message="infoCheckMap.unitAddress"
           :required="requireds.unitAddress"
           placeholder="请输入单位地址"
-          v-if="showModule.unitAddress"
           type="textarea"
           rows="1"
           autosize
           maxlength="200"
-        ></van-field>
+          @focus="infoCheckMap.unitAddress = ''"
+          @blur="getCheckMap('unitAddress')"
+        />
 
         <van-field
+          v-if="showModule.tel"
           v-model.trim="infoMap.tel"
           label="电话号码"
-          @focus="infoCheckMap.tel = ''"
-          @blur="getCheckMap('tel')"
-          :errorMessage="infoCheckMap.tel"
+          :error-message="infoCheckMap.tel"
           :required="requireds.tel"
           placeholder="请输入电话号码"
-          v-if="showModule.tel"
-        ></van-field>
+          @focus="infoCheckMap.tel = ''"
+          @blur="getCheckMap('tel')"
+        />
 
         <van-field
+          v-if="showModule.bank"
           v-model.trim="infoMap.bank"
           label="开户银行"
-          @focus="infoCheckMap.bank = ''"
-          @blur="getCheckMap('bank')"
-          :errorMessage="infoCheckMap.bank"
+          :error-message="infoCheckMap.bank"
           :required="requireds.bank"
           placeholder="请输入开户银行"
-          v-if="showModule.bank"
           type="textarea"
           rows="1"
           autosize
           maxlength="200"
-        ></van-field>
+          @focus="infoCheckMap.bank = ''"
+          @blur="getCheckMap('bank')"
+        />
 
         <van-field
+          v-if="showModule.bankCode"
           v-model.trim="infoMap.bankCode"
           label="银行账号"
-          @focus="infoCheckMap.bankCode = ''"
-          @blur="getCheckMap('bankCode')"
-          :errorMessage="infoCheckMap.bankCode"
+          :error-message="infoCheckMap.bankCode"
           :required="requireds.bankCode"
           placeholder="请输入银行账号"
-          v-if="showModule.bankCode"
           type="textarea"
           maxlength="50"
           rows="1"
           autosize
-        ></van-field>
+          @focus="infoCheckMap.bankCode = ''"
+          @blur="getCheckMap('bankCode')"
+        />
       </div>
       <div
-        class="j-content-nocolor"
         v-show="!showDesc"
+        class="j-content-nocolor"
         @click="showDesc = !showDesc"
       >
         <div class="showMore">
-          <div class="line" style="margin-right: 12px"></div>
+          <div class="line" style="margin-right: 12px" />
           <span class="showMore-text">开票备注</span>
-          <div class="ic-drop"></div>
-          <div class="line" style="margin-left: 8px"></div>
+          <div class="ic-drop" />
+          <div class="line" style="margin-left: 8px" />
         </div>
       </div>
-      <div class="j-content" v-show="showDesc">
+      <div v-show="showDesc" class="j-content">
         <van-field
+          v-if="showModule.desc"
           v-model.trim="infoMap.desc"
           label="开票备注"
-          @focus="infoCheckMap.desc = ''"
           :required="requireds.desc"
-          :errorMessage="infoCheckMap.desc"
+          :error-message="infoCheckMap.desc"
           placeholder="此部分内容会展示在发票“备注”上,请按照贵司财务要求进行填写"
-          v-if="showModule.desc"
           maxlength="200"
           type="textarea"
           rows="3"
           autosize
-        ></van-field>
+          @focus="infoCheckMap.desc = ''"
+        />
       </div>
       <div class="j-content">
         <van-field
+          v-if="showModule.name"
           v-model.trim="infoMap.name"
           label="收件人"
-          @focus="infoCheckMap.name = ''"
-          @blur="getCheckMap('name')"
-          :errorMessage="infoCheckMap.name"
+          :error-message="infoCheckMap.name"
           :required="requireds.name"
           placeholder="请输入收件人"
-          v-if="showModule.name"
           maxlength="200"
-        ></van-field>
+          @focus="infoCheckMap.name = ''"
+          @blur="getCheckMap('name')"
+        />
 
         <van-field
           v-model.trim="infoMap.phone"
           label="联系电话"
-          @focus="infoCheckMap.phone = ''"
-          @blur="getCheckMap('phone')"
-          :errorMessage="infoCheckMap.phone"
+          :error-message="infoCheckMap.phone"
           :required="requireds.phone"
           placeholder="请输入联系电话"
-        ></van-field>
+          @focus="infoCheckMap.phone = ''"
+          @blur="getCheckMap('phone')"
+        />
 
         <van-field
+          v-if="showModule.deliveryAddress"
           v-model.trim="infoMap.deliveryAddress"
           label="收件地址"
-          @focus="infoCheckMap.deliveryAddress = ''"
-          @blur="getCheckMap('deliveryAddress')"
-          :errorMessage="infoCheckMap.deliveryAddress"
+          :error-message="infoCheckMap.deliveryAddress"
           :required="requireds.deliveryAddress"
           placeholder="请输入收件地址"
           type="textarea"
           rows="1"
           autosize
-          v-if="showModule.deliveryAddress"
           maxlength="200"
-        ></van-field>
+          @focus="infoCheckMap.deliveryAddress = ''"
+          @blur="getCheckMap('deliveryAddress')"
+        />
 
         <van-field
+          v-if="showModule.email"
           v-model.trim="infoMap.email"
           label="电子邮箱"
-          @focus="infoCheckMap.email = ''"
-          @blur="getCheckMap('email')"
-          :errorMessage="infoCheckMap.email"
+          :error-message="infoCheckMap.email"
           :required="requireds.email"
           placeholder="请输入电子邮箱"
           type="textarea"
           rows="1"
           autosize
-          v-if="showModule.email"
           maxlength="50"
-        ></van-field>
+          @focus="infoCheckMap.email = ''"
+          @blur="getCheckMap('email')"
+        />
       </div>
     </div>
     <div class="j-bottom">
-      <p class="b-desc">{{ desc }}</p>
+      <p class="b-desc">
+        {{ desc }}
+      </p>
       <div class="j-button-group">
         <button
           class="j-button-confirm"
@@ -234,45 +238,45 @@
         </button>
       </div>
     </div>
-    <popupTip
+    <PopupTip
       v-model="tipShow"
       text="开票规则:<br>1.平台提供电子普通发票、电子专用发票,发票内容为可选“信息技术服务-技术服务费”、“信息技术服务-会员费”、“信息技术服务-招投标数据服务”;<br>2.您申请的电子发票将在3个工作日内由平台开具并发送至您的邮箱,请注意查收;<br>3.电子发票共有3种格式:PDF、OFD、XML,手机端查看发票默认为PDF格式,如需OFD、XML格式,您可前往邮箱或剑鱼标讯电脑端查看发票并下载;4.如有问题可联系客服,客服电话:400-108-6670。"
-    >
-    </popupTip>
+    />
     <PopupSelect
-      :selectList="selectList"
       ref="PopupSelect"
-      popTitle="发票内容"
+      :select-list="selectList"
+      pop-title="发票内容"
       @changeHandle="changeHandle"
       @confirmHandle="confirmHandle"
-    ></PopupSelect>
+    />
   </div>
 </template>
+
 <script>
+import { Button, Field, Icon } from 'vant'
 import EventBus from '@/utils/eventBus'
 import {
   ajaxGetCompanyAssociation,
-  ajaxInvoiceSubmit,
-  ajaxInvoiceQuery,
-  ajaxInvoiceNewshow,
-  ajaxInvoiceSwitch,
   ajaxInvoiceAavailable,
-  ajaxInvoiceNewReplace
+  ajaxInvoiceNewReplace,
+  ajaxInvoiceNewshow,
+  ajaxInvoiceQuery,
+  ajaxInvoiceSubmit,
+  ajaxInvoiceSwitch
 } from '@/api/modules'
 import { replaceKeyword } from '@/utils/utils'
-import { Field, Icon, Button } from 'vant'
 import popupTip from '@/components/invoice/popupTip'
 import radioGroup from '@/components/invoice/radioGroup'
 import PopupSelect from '@/components/invoice/PopupSelect'
+
 export default {
-  // eslint-disable-next-line vue/multi-word-component-names
   name: 'Invoicing',
   components: {
     [Field.name]: Field,
     [Icon.name]: Icon,
     [Button.name]: Button,
-    popupTip,
-    radioGroup,
+    PopupTip: popupTip,
+    RadioGroup: radioGroup,
     PopupSelect
   },
   data() {
@@ -399,11 +403,11 @@ export default {
       if (this.infoMap.name && this.infoMap.name.length > 10) {
         return false
       }
-      var namereg = /^[\u4E00-\u9FA5A-Za-z\s]+(·[\u4E00-\u9FA5A-Za-z]+)*$/ //中英文或加.的少数民族名字
+      const namereg = /^[\u4E00-\u9FA5A-Z\s]+(·[\u4E00-\u9FA5A-Z]+)*$/i // 中英文或加.的少数民族名字
       return namereg.test(this.infoMap.name)
     },
     checkEmail() {
-      let emailRegExp = /\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/
+      const emailRegExp = /\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/
       if (this.infoMap.email.length > 50) {
         return false
       }
@@ -429,7 +433,7 @@ export default {
       return false
     },
     checkBankcode() {
-      let RegExp = /^\d+$/
+      const RegExp = /^\d+$/
       if (!this.infoMap.bankCode) {
         return false
       }
@@ -465,7 +469,7 @@ export default {
   },
   methods: {
     async initData() {
-      let {
+      const {
         onlyIdentifying,
         invoiceMoney,
         operator,
@@ -473,12 +477,12 @@ export default {
         type,
         invoiceId
       } = this.$route.query
-      this.onlyIdentifying = onlyIdentifying ? onlyIdentifying : ''
-      this.invoiceMoney = invoiceMoney ? invoiceMoney : ''
-      this.operator = operator ? operator : ''
-      this.order_code = order_code ? order_code : ''
-      this.type = type ? type : ''
-      this.invoiceId = invoiceId ? invoiceId : ''
+      this.onlyIdentifying = onlyIdentifying || ''
+      this.invoiceMoney = invoiceMoney || ''
+      this.operator = operator || ''
+      this.order_code = order_code || ''
+      this.type = type || ''
+      this.invoiceId = invoiceId || ''
       if (this.enterSource === 'qrcode') {
         this.urlparms = {
           onlyIdentifying: this.onlyIdentifying,
@@ -502,7 +506,7 @@ export default {
         if (res.data) {
           this.infoMap.code = res.data.orderCodes ? res.data.orderCodes : ''
           this.infoMap.price = res.data.money ? res.data.money / 100 : 0
-          this.initEnt(res.data.company_name)
+          // this.initEnt(res.data.company_name) // 不再根据公司名称初始化税号
           if (
             res.data.invoice &&
             this.type !== 'again' &&
@@ -517,14 +521,22 @@ export default {
             })
           }
           if (
-            res.data.invoice &&
-            (this.type === 'again' || this.type === 'Replace')
+            (res.data.invoice &&
+              (this.type === 'again' || this.type === 'Replace')) ||
+            res.data.showData
           ) {
-            // 换开或重开信息回显
-            this.infoMap.type = this.DigitstoChinese_type(
-              res.data.invoice.invoice_variety
-            )
-            this.infoMap.content = res.data.invoice.invoice_content || ''
+            // 换开或重开信息回显或者有上次发票信息回显
+            let data = {}
+            if (res.data.showData) {
+              // 有上次发票信息回显
+              data = res.data.showData
+            }
+            if (this.type === 'again' || this.type === 'Replace') {
+              // 换开或重开信息回显
+              data = res.data.invoice
+            }
+            this.infoMap.type = this.DigitstoChinese_type(data.invoice_variety)
+            this.infoMap.content = data.invoice_content || ''
             this.selectList.forEach((item) => {
               if (this.infoMap.content.includes(item.title)) {
                 item.checked = true
@@ -533,21 +545,19 @@ export default {
               }
             })
             this.infoMap.invoiceHeader = this.DigitstoChinese_invoiceHeader(
-              res.data.invoice.invoice_type
+              data.invoice_type
             )
-            this.infoMap.company = res.data.invoice.company_name || ''
-            this.infoMap.dutyparagraph =
-              res.data.invoice.taxpayer_identnum || ''
-            this.infoMap.unitAddress = res.data.invoice.company_address || ''
-            this.infoMap.tel = res.data.invoice.company_phone || ''
-            this.infoMap.bank = res.data.invoice.bank_name || ''
-            this.infoMap.bankCode = res.data.invoice.bank_account || ''
-            this.infoMap.desc = res.data.invoice.remark || ''
-            this.infoMap.name = res.data.invoice.recipient || ''
-            this.infoMap.phone = res.data.invoice.phone || ''
-            this.infoMap.email = res.data.invoice.mail || ''
-            this.infoMap.deliveryAddress =
-              res.data.invoice.delivery_address || ''
+            this.infoMap.company = data.company_name || ''
+            this.infoMap.dutyparagraph = data.taxpayer_identnum || ''
+            this.infoMap.unitAddress = data.company_address || ''
+            this.infoMap.tel = data.company_phone || ''
+            this.infoMap.bank = data.bank_name || ''
+            this.infoMap.bankCode = data.bank_account || ''
+            this.infoMap.desc = data.remark || ''
+            this.infoMap.name = data.recipient || ''
+            this.infoMap.phone = data.phone || ''
+            this.infoMap.email = data.mail || ''
+            this.infoMap.deliveryAddress = data.delivery_address || ''
           }
         }
       } else {
@@ -573,9 +583,10 @@ export default {
           duration: 0,
           message: 'loading...'
         })
-        const { status } = await ajaxInvoiceAavailable({
-          order_code: this.order_code
-        })
+        // 移动端开发票手机号校验支持400开头手机号 不在限制
+        // const { status } = await ajaxInvoiceAavailable({
+        //   order_code: this.order_code
+        // })
         // if (status === 0) {
         //   // 不支持开票
         //   if (this.$envs.inWX) {
@@ -589,14 +600,23 @@ export default {
           stype: 1,
           code: this.order_code
         })
-        let data = res.data
-        this.infoMap.price = data.pay_money ? data.pay_money / 100 : 0
-        if (this.type === 'again' || this.type === 'Replace') {
+        this.infoMap.price = res.data.pay_money ? res.data.pay_money / 100 : 0
+        if (
+          this.type === 'again' ||
+          this.type === 'Replace' ||
+          res.data.showData
+        ) {
           const resdata = await ajaxInvoiceNewshow({
             stype: 2,
             code: this.invoiceId
           })
-          let data = resdata.data.invoice
+          let data = {}
+          if (res.data.showData) {
+            data = res.data.showData
+          }
+          if (this.type === 'again' || this.type === 'Replace') {
+            data = resdata.data.invoice
+          }
           if (this.type === 'Replace') {
             this.infoMap.price = data.invoice_money
               ? data.invoice_money / 100
@@ -656,14 +676,14 @@ export default {
           ele.checked = false
         }
       })
-      this.infoMap.content = '信息技术服务-' + item.title
+      this.infoMap.content = `信息技术服务-${item.title}`
     },
     confirmHandle() {
       this.$refs.PopupSelect.visibleHandle(false)
     },
     confirm_ok() {
       const loading = this.$toast.loading({ duration: 0, message: '提交中...' })
-      let parms = {
+      const parms = {
         company_name: this.showModule.company ? this.infoMap.company : '',
         phone: this.showModule.phone ? this.infoMap.phone : '',
         mail: this.showModule.email ? this.infoMap.email : '',
@@ -1090,10 +1110,11 @@ export default {
           this.infoCheckMap.tel = ''
         }
       } else {
-        this.requireds.bankCode = true
-        this.requireds.bank = true
-        this.requireds.unitAddress = true
-        this.requireds.tel = true
+        // 专票不在限制以下几个为必填
+        this.requireds.bankCode = false
+        this.requireds.bank = false
+        this.requireds.unitAddress = false
+        this.requireds.tel = false
       }
     },
     invoiceHeaderFilter(val) {
@@ -1133,13 +1154,15 @@ export default {
       }
     },
     checkPhone(val) {
-      return /^(1[3|4|5|6|7|8|9])\d{9}$|^0\d{2,3}-?\d{7,8}$|^400[016789]\d{6}$|^400-[016789]\d{2}-\d{4}$/.test(val)
+      return /^(1[3-9|])\d{9}$|^0\d{2,3}-?\d{7,8}$|^400[016-9]\d{6}$|^400-[016-9]\d{2}-\d{4}$/.test(
+        val
+      )
     },
     highlightText(value, keyStr) {
       return replaceKeyword(
         value,
         keyStr,
-        '<span class="highlight-text">' + keyStr + '</span>'
+        `<span class="highlight-text">${keyStr}</span>`
       )
     }
   }

+ 35 - 26
apps/mobile/src/views/order/components/dataexport/ProductionCard.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="production-card data-export">
-    <Progress :active="exportInfo.step"></Progress>
+    <Progress :active="exportInfo.step" />
     <div class="msg-count-container bg-white border-line-t">
       <div class="m-c-left">
         已选择 <span class="msg-count">{{ exportInfo.count }}</span> 条数据
@@ -10,23 +10,25 @@
       </div>
     </div>
     <SpecList
+      v-model="payActive"
       class="pay-list"
       title="请选择支付方式"
       :list="payList"
-      activeType="value"
-      v-model="payActive"
+      active-type="value"
       @change="payListChange"
     >
       <template #card-content="{ spec }">
-        <div class="spec-i-label">{{ spec.label }}</div>
-        <div class="spec-i-text color-red" v-if="spec.desc">
+        <div class="spec-i-label">
+          {{ spec.label }}
+        </div>
+        <div v-if="spec.desc" class="spec-i-text color-red">
           {{ spec.desc }}
         </div>
       </template>
       <template #footer>
         <div class="pay-way-tip">
           <span>数据流量包:按条数预充值,价格更优!</span>
-          <span class="arrow-rightx3"></span>
+          <span class="arrow-rightx3" />
           <span id="buy-data-pack-for-link" @click="chargeNow">
             <img src="@/assets/image/area-pack/buy-now.png" alt="" />
           </span>
@@ -35,16 +37,16 @@
     </SpecList>
     <!-- 数据流量包 可选账户列表 -->
     <van-radio-group
-      class="account-list bg-white"
       v-show="payActive === 'pack' && accountList.length > 1"
       v-model="accountListActive"
+      class="account-list bg-white"
       @change="accountListActiveChange"
     >
       <van-cell-group>
         <van-cell
           v-for="(item, index) in accountList"
-          clickable
           :key="index"
+          clickable
           :title="item.title"
           :label="item.tip"
           @click="accountListActive = index"
@@ -55,7 +57,7 @@
                 <span
                   class="j-icon checkbox"
                   :class="{ checked: props.checked }"
-                ></span>
+                />
               </template>
             </van-radio>
           </template>
@@ -64,15 +66,17 @@
     </van-radio-group>
     <!-- 个人支付 -->
     <SpecList
-      class="s-list"
       v-show="payActive === 'pay'"
+      v-model="specActive"
+      class="s-list"
       title="请选择数据规格"
       :list="specList"
-      v-model="specActive"
       @change="personalSpecChange"
     >
       <template #card-content="{ spec }">
-        <div class="spec-i-label">{{ spec.label }}</div>
+        <div class="spec-i-label">
+          {{ spec.label }}
+        </div>
         <div class="spec-i-sub mt8">
           <span class="spec-i-text del">{{ spec.before }}元/条</span>
           <span class="spec-i-text">{{ spec.price }}元/条</span>
@@ -82,7 +86,7 @@
         <div class="b-group">
           <div class="spec-question">
             <span>标准字段包、高级字段包</span>
-            <van-icon name="question-o" @click="showQuestion"></van-icon>
+            <van-icon name="question-o" @click="showQuestion" />
           </div>
           <div class="prev-data">
             <span @click="onPrevdata">预览数据</span>
@@ -90,7 +94,10 @@
         </div>
       </template>
     </SpecList>
-    <QuestionTip v-model="dialog.question" />
+    <QuestionTip
+      v-model="dialog.question"
+      source="DataExportPayment_IndustryFields"
+    />
     <van-dialog
       v-model="dialog.balance"
       title="对不起,余额不足"
@@ -106,28 +113,29 @@
       <div class="balance-tip-content desc-list">
         <div class="desc-list">
           <div
-            class="desc-item"
             v-for="(item, index) in balanceConf.descList"
-            :key="index"
             v-show="item.showInDialog"
+            :key="index"
+            class="desc-item"
           >
             <span class="desc-item-label">{{ item.label }}</span>
             <span class="desc-item-split">{{ item.split }}</span>
             <span class="desc-item-text">{{ item.text }}</span>
           </div>
         </div>
-        <p class="tip-warning" v-show="balanceConf.tipTextSelfShow">
+        <p v-show="balanceConf.tipTextSelfShow" class="tip-warning">
           {{ balanceConf.tipTextSelf }}
         </p>
-        <p class="tip-warning" v-show="balanceConf.tipTextAdminShow">
+        <p v-show="balanceConf.tipTextAdminShow" class="tip-warning">
           {{ balanceConf.tipTextAdmin }}
         </p>
       </div>
     </van-dialog>
   </div>
 </template>
+
 <script>
-import { Icon, Cell, CellGroup, Radio, RadioGroup } from 'vant'
+import { Cell, CellGroup, Icon, Radio, RadioGroup } from 'vant'
 import SpecList from '@/components/create-order/SpecList'
 import Progress from '@/views/order/components/dataexport/DataExportProgress'
 import QuestionTip from '@/views/order/components/datapack/QuestionTip'
@@ -135,15 +143,14 @@ import { mixinHeader } from '@/utils/mixins/header'
 import { yuan2Fen } from '@/utils'
 import { accMul } from '@/utils/math'
 import {
+  getDataExportFilterInfo,
   getDataExportInfo,
-  getDataPackUsage,
   getDataExportPrice,
-  getDataExportFilterInfo
+  getDataPackUsage
 } from '@/api/modules'
 
 export default {
   name: 'DataExportProductionCard',
-  mixins: [mixinHeader],
   components: {
     [Icon.name]: Icon,
     [Cell.name]: Cell,
@@ -154,6 +161,7 @@ export default {
     Progress,
     QuestionTip
   },
+  mixins: [mixinHeader],
   data() {
     return {
       pageLayoutConf: {
@@ -343,10 +351,10 @@ export default {
     onPrevdata() {
       if (this.specActive === 1) {
         this.$router.push(
-          '/dataexport/preview/' + this.exportInfo.id + '?type=senior'
+          `/dataexport/preview/${this.exportInfo.id}?type=senior`
         )
       } else {
-        this.$router.push('/dataexport/preview/' + this.exportInfo.id)
+        this.$router.push(`/dataexport/preview/${this.exportInfo.id}`)
       }
     },
     async payListChange() {
@@ -793,7 +801,7 @@ export default {
       }
     },
     // 支付总金额
-    calcPrice: function () {
+    calcPrice() {
       const { price, before } = this.activeSpec
       const { orderMinPrice } = this.exportConf
       const { count } = this.exportInfo
@@ -852,7 +860,7 @@ export default {
     closeBalanceDialog() {
       this.dialog.balance = false
     },
-    showQuestion: function () {
+    showQuestion() {
       this.dialog.question = true
     },
     backToFilter() {
@@ -868,6 +876,7 @@ export default {
   }
 }
 </script>
+
 <style lang="scss" scoped>
 .msg-count-container {
   margin-bottom: 8px;

+ 2 - 1
apps/mobile/src/views/order/components/datapack/Introduction.vue

@@ -2,13 +2,14 @@
   <div class="j-introduction">
     <p>购买须知:</p>
     <p>
-      1、数据流量包是数据自助导出充值服务,最低1000条起充,仅针对线上自助充值使用(与线下企业数据充值账户不可共享);
+      1、数据流量包是个人数据自助导出充值服务,最低1000条起充,仅针对本人线上自助充值使用(与企业数据流量包账户不可共享);
     </p>
     <p>
       2、已购数据包条数按导出数据量扣除(数据已排重,相同数据多次导出,不重复计费),有效期2年,因产品特殊性,购买后不支持退款。
     </p>
   </div>
 </template>
+
 <script>
 export default {
   name: 'DataPackIntroduction',

+ 24 - 26
apps/mobile/src/views/order/components/datapack/ProductionCard.vue

@@ -5,37 +5,33 @@
         center
         title="充值条数"
         is-link
-        :value="charge.count + '条'"
+        :value="`${charge.count}条`"
         @click="chargeCount.pickerShow = true"
       >
         <template>
           <span
             >{{ charge.count }}条<span
-              class="highlight-text"
               v-if="getNowPopSelectValue"
+              class="highlight-text"
             >
               赠{{ getNowPopSelectValue }}条</span
             ></span
           >
         </template>
       </van-cell>
-      <van-cell
-        center
-        title="有效期"
-        :value="charge.duration + '年'"
-      ></van-cell>
+      <van-cell center title="有效期" :value="`${charge.duration}年`" />
     </van-cell-group>
     <SpecList
+      v-model="specActive"
       title="请选择数据规格"
       :list="specList"
-      v-model="specActive"
       @change="specChange"
     >
       <template #card-content="{ spec }">
         <div class="spec-i-label">
           {{ spec.label }}
-          <div class="activity-badge" v-if="spec.activityInfo">
-            <span class="j-icon icon-crown"></span>&nbsp;{{ spec.activityInfo }}
+          <div v-if="spec.activityInfo" class="activity-badge">
+            <span class="j-icon icon-crown" />&nbsp;{{ spec.activityInfo }}
           </div>
         </div>
         <div class="spec-i-sub">
@@ -46,7 +42,7 @@
       <template #footer>
         <div class="spec-question">
           <span>标准字段包、高级字段包</span>
-          <van-icon name="question-o" @click="showQuestion"></van-icon>
+          <van-icon name="question-o" @click="showQuestion" />
         </div>
       </template>
     </SpecList>
@@ -54,9 +50,9 @@
       v-model="chargeCount.pickerShow"
       round
       position="bottom"
-      @closed="chargeCountPopupClosed"
       get-container="body"
       :style="{ height: '45%' }"
+      @closed="chargeCountPopupClosed"
     >
       <PopupLayout
         title="充值条数"
@@ -64,11 +60,11 @@
       >
         <div class="select-group">
           <div
-            class="select-item"
             v-for="(item, index) in chargeCount.infoList"
             :key="index"
-            @click="selectPop(item)"
+            class="select-item"
             :class="{ active: chargeCount.value === item.value }"
+            @click="selectPop(item)"
           >
             <span class="item-label"
               >{{ item.label
@@ -80,7 +76,7 @@
                 }}条</span
               ></span
             >
-            <span class="j-icon base-icon icon-check"></span>
+            <span class="j-icon base-icon icon-check" />
           </div>
         </div>
         <template #footer>
@@ -92,16 +88,17 @@
         </template>
       </PopupLayout>
     </van-popup>
-    <QuestionTip v-model="dialog.question" />
+    <QuestionTip v-model="dialog.question" source="dataPack_IndustryFields" />
   </div>
 </template>
+
 <script>
-import { Cell, CellGroup, Popup, Icon } from 'vant'
+import { Cell, CellGroup, Icon, Popup } from 'vant'
 import SpecList from '@/components/create-order/SpecList'
 import PopupLayout from '@/components/common/PopupLayout'
 import QuestionTip from '@/views/order/components/datapack/QuestionTip'
 import { mixinHeader } from '@/utils/mixins/header'
-import { yuan2Fen, fen2Yuan } from '@/utils'
+import { fen2Yuan, yuan2Fen } from '@/utils'
 
 import {
   getDataPackGoodsPrice,
@@ -112,7 +109,6 @@ import {
 
 export default {
   name: 'DataPackProductionCard',
-  mixins: [mixinHeader],
   components: {
     [Cell.name]: Cell,
     [Icon.name]: Icon,
@@ -122,6 +118,7 @@ export default {
     QuestionTip,
     SpecList
   },
+  mixins: [mixinHeader],
   data() {
     return {
       pageLayoutConf: {
@@ -218,7 +215,7 @@ export default {
     }
   },
   computed: {
-    getNowPopSelectValue: function () {
+    getNowPopSelectValue() {
       return this.specActive === 1
         ? this.cachePopSelect.senior_num
         : this.cachePopSelect.normal_num
@@ -476,7 +473,7 @@ export default {
       if (Array.isArray(info.packs_showList)) {
         info.packs_showList.forEach((item) => {
           list.push({
-            label: item.packNum + '条',
+            label: `${item.packNum}条`,
             value: item.packNum,
             normal_discount: item.normal_discount,
             senior_discount: item.senior_discount,
@@ -519,7 +516,7 @@ export default {
       this.specList[1].price = info.unitPrice_senior * cDiscount * info.discount
     },
     // 计算卡片展示金额 和 支付总金额
-    calcPrice: function () {
+    calcPrice() {
       // 获取被选中的规格卡片info
       const specItem = this.getSpecItem()
       // 获取被选中的条数info
@@ -541,7 +538,7 @@ export default {
       this.computedPrice.pay = yuan2Fen(afterPrice)
     },
     // 获取被选中的规格卡片info
-    getSpecItem: function () {
+    getSpecItem() {
       let t = {}
       this.specList.some((item) => {
         const gotThis = item.id === this.specActive
@@ -553,7 +550,7 @@ export default {
       return t
     },
     // 获取被选中的条数info
-    getCountItem: function () {
+    getCountItem() {
       let t = {}
       this.chargeCount.infoList.some((item) => {
         const gotThis = item.value === this.charge.count
@@ -564,7 +561,7 @@ export default {
       })
       return t
     },
-    selectPop: function (item) {
+    selectPop(item) {
       this.chargeCount.cache = item
       this.chargeCount.value = item.value
     },
@@ -589,7 +586,7 @@ export default {
       this.chargeCount.pickerShow = false
       this.changeCardBadge()
     },
-    showQuestion: function () {
+    showQuestion() {
       this.dialog.question = true
     },
     /**
@@ -687,6 +684,7 @@ export default {
   }
 }
 </script>
+
 <style lang="scss" scoped>
 .data-pack {
   .van-cell {

+ 63 - 13
apps/mobile/src/views/order/components/datapack/QuestionTip.vue

@@ -9,15 +9,22 @@
     :style="{ top: '50%' }"
     @confirm="onConfirm"
   >
+    <div v-if="source" class="sourceBox">
+      以下数据示例仅为部分字段,如需更多行业字段,如资金来源、报价方式、企业资质、人员资质、业绩资格、企业信用、建设规模等字段,您可<span
+        class="sourceBtn"
+        @click="keepFunds"
+        >申请数据定制></span
+      >
+    </div>
     <div class="content-list">
-      <div class="content-item">
-        <div class="content-label">· 标准字段包</div>
+      <div class="content-item bz-content">
+        <div class="content-label">标准字段包:</div>
         <div class="content-text">
           匹配关键词、省份、城市、公告标题、剑鱼标讯地址、公告类别、发布时间、采购单位、中标单位、中标金额、项目名称、公告内容
         </div>
       </div>
       <div class="content-item">
-        <div class="content-label">· 高级字段包</div>
+        <div class="content-label">高级字段包:</div>
         <div class="content-text">
           匹配关键词、省份、城市、区县、公告标题、公告类别、公告内容、发布时间、公告地址、剑鱼标讯地址、项目名称、行业、项目范围、预算金额、中标金额、报名截止日期、开标日期、投标截止日期、{{
             showSignaturedate ? '合同签订时间、' : ''
@@ -27,9 +34,16 @@
     </div>
   </van-dialog>
 </template>
+
 <script>
+import { openLinkOfOther } from '@/utils'
+
 export default {
   name: 'QuestionTip',
+  model: {
+    prop: 'show',
+    event: 'change'
+  },
   props: {
     show: {
       type: Boolean,
@@ -39,13 +53,24 @@ export default {
     showSignaturedate: {
       type: Boolean,
       default: false
+    },
+    source: {
+      type: String,
+      default: ''
     }
   },
-  model: {
-    prop: 'show',
-    event: 'change'
-  },
   methods: {
+    keepFunds() {
+      let openLink = ''
+      if (this.$envs.inWX) {
+        openLink = `/weixin/frontPage/bigmember/free/perfect_info?source=wx_${this.source}`
+      } else if (this.$envs.inH5) {
+        openLink = `/jyapp/frontPage/bigmember/free/perfect_info?source=h5_${this.source}`
+      } else {
+        openLink = `/jyapp/frontPage/bigmember/free/perfect_info?source=app_${this.source}`
+      }
+      openLinkOfOther(openLink)
+    },
     onConfirm() {
       this.$emit('change', false)
     }
@@ -57,19 +82,44 @@ export default {
 .question-tip {
   ::v-deep {
     .van-dialog__header {
-      font-size: 17px;
-      font-weight: 700;
+      font-size: 18px;
+      color: #171826;
+      line-height: 26px;
+      // font-weight: 700;
     }
+
     .van-dialog__content {
-      padding: 16px;
     }
   }
+
   .content-label {
-    margin: 12px 0;
-    font-weight: 700;
+    margin: 8px 0 4px 0;
+    // font-weight: 700;
+    font-size: 15px;
+    color: #171826;
+    line-height: 20px;
+    font-weight: 500;
   }
+
   .content-text {
-    line-height: 19px;
+    line-height: 20px;
+    font-size: 14px;
+    color: #9b9ca3;
+  }
+  .content-item {
+    margin: 0 30px 24px 30px;
+  }
+  .sourceBox {
+    margin: 8px 20px 0 20px;
+    background-color: #fff4e8;
+    color: #ff9f40;
+    font-size: 13px;
+    padding: 10px;
+    line-height: 20px;
+    border-radius: 8px;
+  }
+  .sourceBtn {
+    color: #2abed1;
   }
 }
 </style>

+ 483 - 0
apps/mobile/src/views/packs/DataExportRecord.vue

@@ -0,0 +1,483 @@
+<template>
+  <div class="ex-port-container">
+    <div class="j-header">
+      <ExportRecordFilters
+        v-model="filters"
+        :show-operator-filter="isAdmin"
+        :has-ent-pack="hasEntPack"
+        @change="onFilterConfirmCancel"
+      />
+    </div>
+    <div class="j-main ex-port-content ex-port">
+      <div class="data-box export-box">
+        <div v-if="tabSearchInfoShow" class="tab-search-info">
+          <p class="search-total-count">
+            <template v-if="listState.total > 0">
+              共<span class="highlight-text"> {{ listState.total }} </span
+              >条数据
+            </template>
+          </p>
+        </div>
+        <div
+          v-if="listState.list.length !== 0"
+          class="up-line bot-line list-container"
+        >
+          <van-list
+            v-model="listState.loading"
+            :finished="listState.finished"
+            finished-text=""
+            @load="getList"
+          >
+            <div
+              v-for="(item, index) in listState.list"
+              :key="index"
+              class="export-card d-conts flex"
+            >
+              <div v-if="item.personal_name && isAdmin" class="card-line">
+                <div class="card-line-label">操作人:</div>
+                <div class="card-line-value">
+                  {{ item.personal_name }}
+                </div>
+              </div>
+              <div class="card-line">
+                <div class="card-line-label">导出时间:</div>
+                <div class="card-line-value">
+                  {{ item.exportTimestampText }}
+                </div>
+              </div>
+              <div class="card-line">
+                <div class="card-line-label">数据来源:</div>
+                <div class="card-line-value">
+                  {{ item.data_from }}
+                </div>
+              </div>
+              <div class="card-line">
+                <div class="card-line-label">导出条数:</div>
+                <div class="card-line-value">
+                  {{ changeNum(item.export_num) }}条
+                </div>
+              </div>
+              <div class="card-line">
+                <div class="card-line-label">支付方式:</div>
+                <div class="card-line-value">
+                  {{ item.pay_way }}
+                </div>
+              </div>
+              <div class="card-line">
+                <div class="card-line-label">新扣条数:</div>
+                <div class="card-line-value">
+                  {{
+                    item.deduct_num === -1
+                      ? '-'
+                      : `${changeNum(item.deduct_num)}条`
+                  }}
+                </div>
+              </div>
+              <div class="card-line">
+                <div class="card-line-label">下载链接:</div>
+                <div class="card-line-value">
+                  <span
+                    class="highlight-text"
+                    @click="downloaded(item.down_url)"
+                    >点击下载</span
+                  >
+                </div>
+              </div>
+            </div>
+          </van-list>
+        </div>
+      </div>
+      <AppEmpty v-if="listState.list.length === 0 && listState.loaded">
+        <p slot="default">暂无数据</p>
+      </AppEmpty>
+      <van-dialog
+        v-model="emailDialog.show"
+        title="发送邮箱地址"
+        :show-confirm-button="false"
+      >
+        <p class="d-p1">数据导出文件将以邮件的形式发送至您的邮箱</p>
+        <div class="v-inpt" :class="{ isActive: !isEmail }">
+          <van-field v-model="emailDialog.value" placeholder="输入邮箱地址" />
+        </div>
+        <p v-show="isEmail" class="d-p2">
+          {{ pVal }}
+        </p>
+        <van-divider />
+        <div class="btns flex">
+          <van-button class="x-btns" @click="canceled"> 取消 </van-button>
+          <div class="s-line" />
+          <van-button class="c-btns" @click="confirmed"> 确认 </van-button>
+        </div>
+      </van-dialog>
+    </div>
+  </div>
+</template>
+
+<script>
+import { Button, Divider, Field, List } from 'vant'
+import dayjs from 'dayjs'
+import ExportRecordFilters from './components/ExportRecordFilters'
+import { AppEmpty } from '@/ui'
+import {
+  getDataExportRecordList,
+  getDataPackUsage,
+  sendDataExportFileEmail
+} from '@/api/modules/'
+import { dateFormatter } from '@/utils/'
+import downloadApp from '@/utils/mixins/modules/to-download-app'
+
+export default {
+  components: {
+    [List.name]: List,
+    [Button.name]: Button,
+    [Field.name]: Field,
+    [Divider.name]: Divider,
+    [AppEmpty.name]: AppEmpty,
+    ExportRecordFilters
+  },
+  mixins: [downloadApp],
+  data() {
+    return {
+      isAdmin: false,
+      emailDialog: {
+        show: false,
+        value: ''
+      },
+      isEmail: false,
+      pVal: '',
+      filters: {},
+      listState: {
+        total: -1,
+        pageNum: 1,
+        pageSize: 10,
+        loading: true,
+        loaded: false,
+        finished: false,
+        list: []
+      },
+      dataList: {
+        entPack: [],
+        dailyPack: [],
+        personalPack: []
+      },
+      downUrl: ''
+    }
+  },
+  computed: {
+    hasEntPack() {
+      return (
+        Array.isArray(this.dataList.entPack) && this.dataList.entPack.length > 0
+      )
+    },
+    tabSearchInfoShow() {
+      const { total } = this.listState
+      return total > 0
+    }
+  },
+  created() {
+    this.doSearch()
+    this.getPackageInfo()
+  },
+  methods: {
+    async getPackageInfo() {
+      // 线上充值账户、线上自助充值账户
+      const { data, error_code: code } = await getDataPackUsage()
+      if (code === 0 && data) {
+        if (Array.isArray(data.entPack)) {
+          this.dataList.entPack = data.entPack
+        }
+        this.dataList.dailyPack = data.dailyPack
+        this.dataList.personalPack = data.personalPack || {}
+      }
+    },
+    resetListState() {
+      Object.assign(this.listState, this.$options.data().listState)
+    },
+    doSearch() {
+      // 重置页码
+      this.resetListState()
+      this.listState.loading = true
+      this.getList()
+    },
+    getTimeFilter(t) {
+      const p = {
+        starttime: '',
+        endtime: ''
+      }
+      if (!t) return p
+      if (t.start) {
+        p.starttime = dayjs(t.start).unix()
+      }
+      if (t.end) {
+        p.endtime = dayjs(t.end).unix()
+      }
+      return p
+    },
+    async getList() {
+      const { operator, dateTime, payWay } = this.filters
+      const timeObj = this.getTimeFilter(dateTime)
+      const params = {
+        userIds: Array.isArray(operator) ? operator.join(',') : '',
+        payWay: Array.isArray(payWay) ? payWay.join(',') : '',
+        ...timeObj,
+        pageSize: this.listState.pageSize,
+        pageNum: this.listState.pageNum
+      }
+      const t = this.listState
+      try {
+        const { data } = await getDataExportRecordList(params)
+        if (data) {
+          // 请求完成后的回调
+          this.afterSearch(params, data)
+          // 列表赋值
+          let list = data.list
+          const count = data.count
+          const total = data.total
+          if (Array.isArray(list)) {
+            // 整理每一项
+            list = this.preSortList(list)
+            if (t.pageNum === 1) {
+              t.list = []
+            }
+            t.list = t.list.concat(list)
+          } else {
+            // list不为数组,则直接finish
+            t.loaded = true
+            t.loading = false
+            t.finished = true
+          }
+
+          if (count && count !== -1) {
+            t.count = count
+          }
+          if (total) {
+            t.total = total
+          }
+
+          // 加载状态结束
+          t.loaded = true
+          t.loading = false
+
+          // 翻页
+          const hasNextPage = t.pageNum * t.pageSize < t.total
+
+          if (hasNextPage) {
+            t.pageNum++
+          } else {
+            t.finished = true
+          }
+        } else {
+          t.loaded = true
+          t.loading = false
+          t.finished = true
+        }
+      } catch (error) {
+        t.loaded = true
+        t.loading = false
+        t.finished = true
+        console.warn(error)
+      }
+    },
+    preSortList(list = []) {
+      if (!Array.isArray(list)) return []
+      return list.map(this.preSortItem)
+    },
+    preSortItem(v) {
+      if (!v) return
+      return {
+        ...v,
+        exportTimestampText: this.formatDate(v.export_timestamp)
+      }
+    },
+    afterSearch(params, data) {
+      this.isAdmin = data.isAdmin
+    },
+    onFilterConfirmCancel() {
+      this.doSearch()
+    },
+    downloaded(url) {
+      if (this.$envs.inWxMini) {
+        return this.toFollowGuidePage()
+      }
+      this.downUrl = url
+      if (this.$envs.inApp) {
+        if (this.$envs.inIOS) {
+          // ios
+          this.emailDialog.show = true
+        } else {
+          // andorid/wx
+          const a1 = document.createElement('a')
+          a1.href = url
+          a1.click()
+        }
+      } else {
+        // 微信浏览器
+        window.open(url)
+      }
+    },
+    confirmed() {
+      if (this.emailDialog.value === '') {
+        this.pVal = '邮箱不能为空'
+        this.isEmail = true
+        return
+      }
+      const status = /^[\w-]+@[\w-]+(?:\.[\w-]+)+$/.test(this.emailDialog.value)
+      if (status) {
+        this.isEmail = false
+        this.pVal = ''
+        this.emailDialog.show = false
+
+        const payload = {
+          email: this.emailDialog.value,
+          downurl: this.downUrl
+        }
+
+        sendDataExportFileEmail(payload).then((res) => {
+          if (res.error_code === 0) {
+            this.$toast('发送成功')
+          } else {
+            this.$toast('发送失败')
+          }
+        })
+      } else {
+        this.pVal = '邮箱格式不正确'
+        this.isEmail = true
+      }
+    },
+    canceled() {
+      this.emailDialog.show = false
+      this.emailDialog.value = ''
+      this.isEmail = false
+    },
+    changeNum(data) {
+      // 数字处理函数
+      if (data) {
+        return data.toLocaleString()
+      } else {
+        return 0
+      }
+    },
+    formatDate(time, fmt = 'yyyy-MM-dd HH:mm') {
+      // 时间处理
+      if (!time) {
+        return ''
+      }
+      return dateFormatter(time * 1000, fmt)
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+@import './styles/pack-style.scss';
+
+.tab-search-info {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: 4px 16px;
+  padding-top: 8px;
+  line-height: 30px;
+  .tab-right {
+    display: flex;
+    align-items: center;
+  }
+}
+.search-total-count {
+  font-size: 14px;
+  color: #5f5e64;
+}
+
+.list-container {
+  padding: 0 12px;
+}
+
+.card-line {
+  display: flex;
+  align-items: center;
+  width: 100%;
+  height: 32px;
+  font-size: 13px;
+  line-height: 20px;
+
+  .card-line-label {
+    min-width: 78px;
+    color: #5f5e64;
+  }
+  .card-line-value {
+    flex: 1;
+    color: #171826;
+  }
+}
+
+::v-deep {
+  .van-dialog {
+    .van-dialog__header {
+      color: #171826;
+      line-height: 26px;
+      font-size: 18px;
+    }
+    .van-dialog__content {
+      .d-p1 {
+        color: #9b9ca3;
+        line-height: 18px;
+        font-size: 12px;
+        text-align: center;
+        margin-top: 8px;
+        padding: 0 24px;
+      }
+      .d-p2 {
+        line-height: 18px;
+        font-size: 12px;
+        margin: 0.06rem 0;
+        color: #ee0a24;
+        padding-left: 40px;
+      }
+
+      .v-inpt {
+        margin-top: 16px;
+        padding: 0 24px;
+      }
+
+      .isActive {
+        margin-bottom: 24px;
+      }
+
+      .van-field {
+        border: 1px solid rgba(0, 0, 0, 0.1);
+        border-radius: 4px;
+        padding: 12px 16px;
+      }
+      .van-field__control {
+        line-height: 22px;
+        font-size: 15px;
+      }
+
+      .btns {
+        .van-button {
+          width: 50%;
+          height: 46px;
+          border: none;
+          color: #171826;
+          line-height: 26px;
+          font-size: 18px;
+          padding: 0;
+        }
+
+        .c-btns {
+          color: $color_main;
+        }
+        .s-line {
+          width: 1px;
+          height: 46px;
+          background: #e6e6e6;
+        }
+        .van-divider {
+          margin: 0;
+          border-color: #e6e6e6;
+        }
+      }
+    }
+  }
+}
+</style>

+ 233 - 0
apps/mobile/src/views/packs/DataPackIndex.vue

@@ -0,0 +1,233 @@
+<template>
+  <div class="data-pack-index">
+    <div class="j-main">
+      <div class="data-pack-content">
+        <div v-if="dataList.entPack.length > 0" class="up-line">
+          <h3 class="c-module-title">企业数据流量包</h3>
+          <div class="d-conts flex">
+            <div v-for="item in dataList.entPack" :key="item.id" class="d-card">
+              <h4 class="icon-company-container">
+                <AppIcon name="mine_company" />
+                <span>{{ item.entName }}</span>
+              </h4>
+              <van-divider />
+              <div class="words flex">
+                <div class="nums">
+                  <p class="u-p2">数据流量包账户余额</p>
+                  <p class="u-p1">{{ changeNum(item.entAllCount) }}<i>条</i></p>
+                </div>
+                <div class="nums r-code">
+                  <p class="u-p2">30天内即将到期</p>
+                  <p class="u-p1">0<i>条</i></p>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+        <div v-if="dataList.personalPack" class="up-line down-line">
+          <h3 class="c-module-title">个人数据流量包</h3>
+          <div class="d-chong">
+            <div class="d-head">
+              <div class="s-zhu flex">
+                <p class="u-p1">数据流量包账户余额</p>
+                <p class="u-p2">
+                  <i>{{
+                    changeNum(
+                      dataList.personalPack.normalTotal +
+                        dataList.personalPack.seniorTotal
+                    )
+                  }}</i
+                  >条
+                </p>
+              </div>
+            </div>
+            <div class="c-cont flex">
+              <div class="l-shu flex">
+                <div class="nums">
+                  <p class="u-p2">标准字段包余额</p>
+                  <p class="u-p1">
+                    {{ changeNum(dataList.personalPack.normalTotal)
+                    }}<i class="font-w-400">条</i>
+                  </p>
+                </div>
+                <div class="nums r-code">
+                  <p class="u-p2">标准字段包30天内即将到期</p>
+                  <p class="u-p1">
+                    {{ changeNum(dataList.personalPack.normalThirty)
+                    }}<i class="font-w-400">条</i>
+                  </p>
+                </div>
+              </div>
+              <div class="l-shu flex b-shu">
+                <div class="nums">
+                  <p class="u-p2">高级字段包余额</p>
+                  <p class="u-p1">
+                    {{ changeNum(dataList.personalPack.seniorTotal)
+                    }}<i class="font-w-400">条</i>
+                  </p>
+                </div>
+                <div class="nums r-code">
+                  <p class="u-p2">高级字段包30天内即将到期</p>
+                  <p class="u-p1">
+                    {{ changeNum(dataList.personalPack.seniorThirty)
+                    }}<i class="font-w-400">条</i>
+                  </p>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+        <div class="up-line bot-line balance-detail">
+          <h3 class="c-module-title">账户余额明细</h3>
+          <div class="c-list">
+            <AccountUsageDetailList :has-ent-pack="hasEntPack" />
+          </div>
+        </div>
+      </div>
+    </div>
+    <div class="j-footer">
+      <div class="btns-tips-container">
+        <div class="footer-tips">
+          <p>注:如需充值企业数据流量包(企业人员共享)</p>
+          <p>
+            可在线<span class="underline" @click="toCustomer">联系客服</span
+            >或拨打客服电话<span class="underline" @click="callPhone">{{
+              phoneNumber
+            }}</span>
+          </p>
+        </div>
+        <div class="footer-btns">
+          <van-button type="primary" size="large" @click="soonCharge">
+            立即充值
+          </van-button>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { Button, Divider, List } from 'vant'
+import AccountUsageDetailList from './components/AccountUsageDetailList.vue'
+import { AppEmpty, AppIcon } from '@/ui'
+import { getDataPackUsage } from '@/api/modules/'
+import { callPhone } from '@/utils/callFn'
+import { openAppOrWxPage } from '@/utils/'
+import { LINKS } from '@/data'
+
+export default {
+  components: {
+    [List.name]: List,
+    [Button.name]: Button,
+    [Divider.name]: Divider,
+    [AppEmpty.name]: AppEmpty,
+    [AppIcon.name]: AppIcon,
+    AccountUsageDetailList
+  },
+  data() {
+    return {
+      phoneNumber: '400-108-6670',
+      dataList: {
+        entPack: [],
+        dailyPack: [],
+        personalPack: []
+      }
+    }
+  },
+  computed: {
+    hasEntPack() {
+      return (
+        Array.isArray(this.dataList.entPack) && this.dataList.entPack.length > 0
+      )
+    }
+  },
+  created() {
+    this.getRechargeList()
+  },
+  methods: {
+    async getRechargeList() {
+      // 线上充值账户、线上自助充值账户
+      const { data, error_code: code } = await getDataPackUsage()
+      if (code === 0 && data) {
+        if (Array.isArray(data.entPack)) {
+          this.dataList.entPack = data.entPack
+        }
+        this.dataList.dailyPack = data.dailyPack
+        this.dataList.personalPack = data.personalPack || {}
+      }
+    },
+    // 立即充值
+    soonCharge() {
+      this.$router.push({
+        path: '/common/order/create/datapack'
+      })
+    },
+    toCustomer() {
+      openAppOrWxPage(LINKS.客服)
+    },
+    callPhone() {
+      if (this.$envs.inApp) {
+        callPhone(this.phoneNumber)
+      } else {
+        location.href = `tel:${this.phoneNumber}`
+      }
+    },
+    changeNum(data) {
+      // 数字处理函数
+      if (data) {
+        return data.toLocaleString()
+      } else {
+        return 0
+      }
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+@import './styles/pack-style.scss';
+
+.data-pack-index {
+  background: #f5f6f7;
+  .data-pack-content {
+    padding: 0 12px;
+  }
+}
+
+.balance-detail {
+  .c-module-title {
+    padding-bottom: 0;
+  }
+}
+
+.icon-company-container {
+  padding-left: 0 !important;
+  .icon-mine_company {
+    margin-right: 6px;
+    color: $main;
+  }
+}
+
+.j-footer {
+  background-color: $white;
+}
+.footer-tips {
+  padding: 8px 16px;
+  font-size: 13px;
+  color: #ff9f40;
+  line-height: 20px;
+  background: #fff4e8;
+  text-align: center;
+}
+.footer-btns {
+  padding: 8px 16px 12px;
+
+  ::v-deep {
+    .van-button {
+      height: 48px;
+      border-radius: 8px;
+      font-size: 18px;
+    }
+  }
+}
+</style>

+ 336 - 0
apps/mobile/src/views/packs/components/AccountUsageDetailList.vue

@@ -0,0 +1,336 @@
+<template>
+  <div class="list-container">
+    <div class="filter-container">
+      <DataPackFilters
+        v-model="filters"
+        :show-operator-filter="isAdmin"
+        :has-ent-pack="hasEntPack"
+        @change="onFilterConfirmCancel"
+      />
+    </div>
+    <div class="list-container">
+      <van-list
+        v-model="listState.loading"
+        :finished="listState.finished"
+        finished-text=""
+        @load="getList"
+      >
+        <div
+          v-for="(item, index) in listState.list"
+          :key="item.id"
+          class="list-item-card clickable"
+          :class="{ 'last-item': index + 1 === listState.list.length }"
+        >
+          <div class="list-item-hd">
+            <span class="card-type-line">
+              {{ item.account }}
+            </span>
+          </div>
+          <div class="list-item-bd">
+            <div class="list-item-bd-line bd-line-time-user">
+              <div class="card-time">
+                {{ changeDate(item.createTime) }}
+              </div>
+              <div v-if="item.operator && isAdmin" class="card-user">
+                {{ item.operator }}
+              </div>
+            </div>
+            <div class="list-item-bd-line bd-line-type-count">
+              <div class="card-prod-type">
+                <div
+                  v-if="item.type"
+                  class="j-tag-item border round"
+                  :class="item.numberTagClassName"
+                >
+                  {{ item.type }}
+                </div>
+                <div class="card-tag-name">
+                  <div v-if="item.reasonTitle" class="card-name">
+                    {{ item.reasonTitle }}
+                  </div>
+                  <div v-if="item.reasonContent" class="card-deadline">
+                    {{ item.reasonContent }}
+                  </div>
+                </div>
+              </div>
+              <div class="card-count">
+                <p
+                  class="c-c-t-group"
+                  :class="{ 'highlight-text': item.numberHighlight }"
+                >
+                  <span class="card-count-text c-c-t-1">{{ item.number }}</span
+                  ><span class="c-c-t-2"> 条</span>
+                </p>
+                <p v-if="item.giveNumber" class="give-number">
+                  <span>赠{{ changeNum(item.giveNumber) }}条</span>
+                </p>
+              </div>
+            </div>
+          </div>
+        </div>
+      </van-list>
+      <AppEmpty v-if="listState.list.length === 0 && listState.loaded">
+        <p slot="default">暂无数据</p>
+      </AppEmpty>
+    </div>
+  </div>
+</template>
+
+<script>
+import { List, Tag } from 'vant'
+import dayjs from 'dayjs'
+import DataPackFilters from './DataPackFilters'
+import { AppEmpty } from '@/ui'
+import { getDataExportRechargeList } from '@/api/modules/'
+import { dateFormatter } from '@/utils/'
+
+export default {
+  components: {
+    [List.name]: List,
+    [Tag.name]: Tag,
+    [AppEmpty.name]: AppEmpty,
+    DataPackFilters
+  },
+  props: {
+    hasEntPack: {
+      type: Boolean,
+      default: false
+    }
+  },
+  data() {
+    return {
+      isAdmin: false,
+      filters: {},
+      listState: {
+        pageNum: 1,
+        total: -1,
+        pageSize: 5,
+        loading: false,
+        loaded: false,
+        finished: false,
+        list: []
+      }
+    }
+  },
+  created() {
+    this.doSearch()
+  },
+  methods: {
+    resetListState() {
+      Object.assign(this.listState, this.$options.data().listState)
+    },
+    doSearch() {
+      // 重置页码
+      this.resetListState()
+      this.listState.loading = true
+      this.getList()
+    },
+    getTimeFilter(t) {
+      const p = {
+        starttime: '',
+        endtime: ''
+      }
+      if (!t) return p
+      if (t.start) {
+        p.starttime = dayjs(t.start).format('YYYY-MM-DD HH:mm:ss')
+      }
+      if (t.end) {
+        p.endtime = dayjs(t.end).format('YYYY-MM-DD HH:mm:ss')
+      }
+      return p
+    },
+    async getList() {
+      const { operator, time, account, consumptionType } = this.filters
+      const timeObj = this.getTimeFilter(time)
+      const params = {
+        userIds: Array.isArray(operator) ? operator.join(',') : '',
+        account: Array.isArray(account) ? account.join(',') : '',
+        type: Array.isArray(consumptionType) ? consumptionType.join(',') : '',
+        ...timeObj,
+        pageSize: this.listState.pageSize,
+        pageNum: this.listState.pageNum
+      }
+      const t = this.listState
+      try {
+        const data = await getDataExportRechargeList(params)
+        if (data) {
+          // 请求完成后的回调
+          this.afterSearch(params, data)
+          // 列表赋值
+          let list = data.list
+          const count = data.count
+          const total = data.total
+          if (Array.isArray(list)) {
+            // 整理每一项
+            list = this.preSortList(list)
+            if (t.pageNum === 1) {
+              t.list = []
+            }
+            t.list = t.list.concat(list)
+          } else {
+            // list不为数组,则直接finish
+            t.loaded = true
+            t.loading = false
+            t.finished = true
+          }
+
+          if (count && count !== -1) {
+            t.count = count
+          }
+          if (total) {
+            t.total = total
+          }
+
+          // 加载状态结束
+          t.loaded = true
+          t.loading = false
+
+          // 翻页
+          const hasNextPage = t.pageNum * t.pageSize < t.total
+
+          if (hasNextPage) {
+            t.pageNum++
+          } else {
+            t.finished = true
+          }
+        } else {
+          t.loaded = true
+          t.loading = false
+          t.finished = true
+        }
+      } catch (error) {
+        t.loaded = true
+        t.loading = false
+        t.finished = true
+        console.warn(error)
+      }
+    },
+    preSortList(list = []) {
+      if (!Array.isArray(list)) return []
+      return list.map(this.preSortItem)
+    },
+    preSortItem(v) {
+      if (!v) return
+
+      return {
+        ...v,
+        numberTagClassName: v.type === '新增' ? 'main' : 'red',
+        numberHighlight: v.type === '新增'
+      }
+    },
+    afterSearch(params, data) {
+      this.isAdmin = data.isAdmin
+    },
+    onFilterConfirmCancel() {
+      this.doSearch()
+    },
+    changeNum(data) {
+      // 数字处理函数
+      if (data) {
+        return data.toLocaleString()
+      } else {
+        return 0
+      }
+    },
+    changeDate(time, fmt = 'yyyy-MM-dd HH:mm') {
+      if (!time) {
+        return ''
+      }
+      return dateFormatter(time * 1000, fmt)
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.list-container {
+  padding-bottom: 12px;
+  ::v-deep {
+    .fixed-position {
+      margin-top: 0;
+    }
+  }
+}
+.list-item-card {
+  position: relative;
+  padding: 12px 0;
+  background-color: #fff;
+  .card-type-line {
+    padding: 2px 8px;
+    font-size: 12px;
+    line-height: 18px;
+    color: $color_main;
+    border-top-right-radius: 2px;
+    border-bottom-right-radius: 8px;
+    background-color: $color_main_background;
+  }
+
+  .list-item-bd {
+    margin-top: 8px;
+    padding: 0 16px;
+  }
+  .list-item-bd-line {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+  }
+  .bd-line-time-user {
+    margin-bottom: 10px;
+    color: #5f5e64;
+    font-size: 12px;
+    line-height: 18px;
+  }
+  .bd-line-type-count {
+    align-items: flex-start;
+  }
+  .card-tag-name {
+    flex: 1;
+    margin-bottom: 4px;
+    margin-left: 8px;
+    font-size: 14px;
+    line-height: 20px;
+  }
+  .card-prod-type {
+    display: flex;
+    flex: 1;
+  }
+  .card-count {
+    &.add {
+      color: $color_main;
+    }
+  }
+  .c-c-t-group {
+    vertical-align: bottom;
+    .c-c-t-1 {
+      font-size: 16px;
+      line-height: 24px;
+    }
+    .c-c-t-2 {
+      font-size: 12px;
+      line-height: 18px;
+    }
+  }
+  .give-number,
+  .card-deadline {
+    color: #9b9ca3;
+    font-size: 12px;
+    line-height: 18px;
+  }
+
+  .card-count {
+    text-align: right;
+  }
+
+  &:not(.last-item) {
+    border-bottom: 1px solid #0000000d;
+  }
+  &:first-of-type {
+    border-top-left-radius: 12px;
+    border-top-right-radius: 12px;
+  }
+  &.last-item {
+    border-bottom-left-radius: 12px;
+    border-bottom-right-radius: 12px;
+  }
+}
+</style>

+ 608 - 0
apps/mobile/src/views/packs/components/DataPackFilters.vue

@@ -0,0 +1,608 @@
+<template>
+  <div class="filter-wrapper">
+    <van-sticky :offset-top="sticky.offsetTop" @change="stickyChange">
+      <div
+        v-for="item in menuList"
+        v-show="item.show"
+        :key="item.name"
+        class="filter-menu-item"
+        :class="{
+          active: popup[item.name],
+          highlight: dropdownMenuHighlight(item.name)
+        }"
+        @click="clickMenuItem(item)"
+      >
+        <div class="menu-text">
+          {{ item.calcTitle || item.title }}
+        </div>
+        <AppIcon name="down" class="menu-icon" />
+      </div>
+    </van-sticky>
+
+    <van-popup
+      v-model="popup.operator"
+      class="fix-pop-height"
+      round
+      position="bottom"
+      :safe-area-inset-bottom="true"
+      :close-on-popstate="true"
+      get-container="body"
+      :lazy-render="false"
+      style="max-height: 70%"
+    >
+      <PopupLayout title="选择人员" @closeIconClick="popup.operator = false">
+        <SelectPerson ref="personSelector" />
+        <div slot="footer">
+          <div class="j-button-group height40">
+            <button class="j-button-cancel" @click="popupReset('operator')">
+              重置
+            </button>
+            <button class="j-button-confirm" @click="popupConfirm('operator')">
+              确定
+            </button>
+          </div>
+        </div>
+      </PopupLayout>
+    </van-popup>
+    <van-popup
+      v-model="popup.account"
+      round
+      position="bottom"
+      :safe-area-inset-bottom="true"
+      :close-on-popstate="true"
+      get-container="body"
+      :lazy-render="false"
+      style="max-height: 70%"
+    >
+      <PopupLayout title="账户" @closeIconClick="popup.account = false">
+        <ScopeSelector
+          ref="accountSelector"
+          :options="getAccountOptions"
+          :multiple="false"
+          :default-val="cacheFilters.account"
+          default-icon=""
+          checked-icon="duihao"
+        />
+        <div slot="footer">
+          <div class="j-button-group height40">
+            <button class="j-button-cancel" @click="popupReset('account')">
+              重置
+            </button>
+            <button class="j-button-confirm" @click="popupConfirm('account')">
+              确定
+            </button>
+          </div>
+        </div>
+      </PopupLayout>
+    </van-popup>
+    <van-popup
+      v-model="popup.consumptionType"
+      round
+      position="bottom"
+      :safe-area-inset-bottom="true"
+      :close-on-popstate="true"
+      get-container="body"
+      :lazy-render="false"
+      style="max-height: 70%"
+    >
+      <PopupLayout title="类型" @closeIconClick="popup.consumptionType = false">
+        <ScopeSelector
+          ref="conTypeSelector"
+          :options="getConsumptionTypeList"
+          :multiple="false"
+          :default-val="cacheFilters.consumptionType"
+          default-icon=""
+          checked-icon="duihao"
+        />
+        <div slot="footer">
+          <div class="j-button-group height40">
+            <button
+              class="j-button-cancel"
+              @click="popupReset('consumptionType')"
+            >
+              重置
+            </button>
+            <button
+              class="j-button-confirm"
+              @click="popupConfirm('consumptionType')"
+            >
+              确定
+            </button>
+          </div>
+        </div>
+      </PopupLayout>
+    </van-popup>
+    <van-popup
+      v-model="popup.time"
+      round
+      position="bottom"
+      :safe-area-inset-bottom="true"
+      :close-on-popstate="true"
+      get-container="body"
+      :lazy-render="false"
+      style="max-height: 70%"
+    >
+      <PopupLayout title="时间" @closeIconClick="popup.time = false">
+        <div class="popup-layout-container">
+          <DateTimeList ref="dateTimeGroup" :options="getTimeOptions" marked />
+        </div>
+        <div slot="footer">
+          <div class="j-button-group height40">
+            <button class="j-button-cancel" @click="popupReset('time')">
+              重置
+            </button>
+            <button class="j-button-confirm" @click="popupConfirm('time')">
+              确定
+            </button>
+          </div>
+        </div>
+      </PopupLayout>
+    </van-popup>
+  </div>
+</template>
+
+<script>
+import { Popup, Sticky } from 'vant'
+import SelectPerson from './SelectPerson'
+import { AppIcon } from '@/ui'
+import PopupLayout from '@/components/common/PopupLayout'
+import ScopeSelector from '@/components/selector/scope/index'
+import DateTimeList from '@/components/selector/date-time-list'
+import { biddingSearchTime } from '@/data'
+import { deepCompare } from '@/utils'
+
+export default {
+  components: {
+    [AppIcon.name]: AppIcon,
+    [Popup.name]: Popup,
+    [Sticky.name]: Sticky,
+    SelectPerson,
+    ScopeSelector,
+    DateTimeList,
+    PopupLayout
+  },
+  model: {
+    prop: 'filters',
+    event: 'change'
+  },
+  props: {
+    showOperatorFilter: {
+      type: Boolean,
+      default: false
+    },
+    hasEntPack: {
+      type: Boolean,
+      default: false
+    },
+    filters: {
+      type: Object,
+      default() {
+        return {}
+      }
+    }
+  },
+  data() {
+    return {
+      conf: {
+        menuList: [
+          {
+            name: 'operator',
+            title: '操作人',
+            calcTitle: '',
+            show: true,
+            defaultValue: []
+          },
+          {
+            name: 'account',
+            title: '账户',
+            calcTitle: '',
+            show: true,
+            defaultValue: ['']
+          },
+          {
+            name: 'consumptionType',
+            title: '类型',
+            calcTitle: '',
+            show: true,
+            defaultValue: ['']
+          },
+          {
+            name: 'time',
+            title: '时间',
+            calcTitle: '',
+            show: true,
+            defaultValue: {}
+          }
+        ],
+        dateTimeList: biddingSearchTime,
+        accountList: [
+          {
+            label: '企业数据流量包',
+            value: 3
+          },
+          {
+            label: '个人数据流量包(标准字段包)',
+            value: 2
+          },
+          {
+            label: '个人数据流量包(高级字段包)',
+            value: 1
+          }
+        ],
+        consumptionTypeList: [
+          {
+            label: '新增',
+            value: 2
+          },
+          {
+            label: '消费',
+            value: 1
+          },
+          {
+            label: '作废',
+            value: 3
+          }
+        ]
+      },
+      sticky: {
+        offsetTop: 46,
+        isFixed: false
+      },
+      defaultFilterState: {},
+      cacheFilters: {},
+      popup: {}
+    }
+  },
+  computed: {
+    getAccountOptions() {
+      const { accountList: list } = this.conf
+      const all = {
+        label: '全部',
+        value: ''
+      }
+
+      return [all]
+        .concat(list)
+        .map((r) => {
+          let show = true
+          if (r.value === 3) {
+            show = this.hasEntPack
+          }
+          return {
+            ...r,
+            show,
+            key: r.value
+          }
+        })
+        .filter((r) => r.show)
+    },
+    menuList() {
+      const list = this.conf.menuList
+
+      list.forEach((m) => {
+        if (m.name === 'operator') {
+          m.show = this.showOperatorFilter
+        }
+      })
+
+      return list
+    },
+    getConsumptionTypeList() {
+      const { consumptionTypeList: list } = this.conf
+      const all = {
+        label: '全部',
+        value: ''
+      }
+      return [all].concat(list).map((r) => {
+        return {
+          ...r,
+          key: r.value
+        }
+      })
+    },
+    getTimeOptions() {
+      return this.conf.dateTimeList
+    }
+  },
+  watch: {
+    filter: {
+      deep: true,
+      handler(n) {
+        this.setState(n)
+      }
+    }
+  },
+  created() {
+    this.initValue()
+  },
+  mounted() {
+    this.calcOffsetTop()
+  },
+  methods: {
+    calcOffsetTop() {
+      const header = document.querySelector('.j-header.common-app-header')
+      if (header) {
+        this.sticky.offsetTop = header.clientHeight - 1
+      }
+    },
+    stickyChange(e) {
+      this.isFixed = e
+    },
+    setState(t = this.filters) {
+      const r = Object.assign({}, this.cacheFilters, t)
+      this.cacheFilters = r
+    },
+    initValue() {
+      this.menuList.forEach((m) => {
+        this.$set(this.defaultFilterState, m.name, m.defaultValue)
+        this.$set(this.cacheFilters, m.name, m.defaultValue)
+        this.$set(this.popup, m.name, false)
+      })
+    },
+    findMenuItemWithName(name) {
+      return this.menuList.find((m) => m.name === name)
+    },
+    clickMenuItem(item) {
+      this.popupState(item.name, true)
+      this.openedDropDown(item.name)
+    },
+    popupState(type, state = false) {
+      this.popup[type] = state
+    },
+    onChange(value = {}) {
+      const filters = {}
+      Object.assign(
+        filters,
+        JSON.parse(JSON.stringify(this.cacheFilters)),
+        value
+      )
+      this.$emit('change', filters)
+    },
+    dropdownMenuHighlight(type) {
+      let needHighlight = false
+
+      switch (type) {
+        case 'operator': {
+          const { operator } = this.cacheFilters
+          const { operator: operatorDefault } = this.defaultFilterState
+          const same = deepCompare(operator, operatorDefault)
+          needHighlight = !same
+          break
+        }
+        case 'account': {
+          const { account } = this.cacheFilters
+          const { account: accountDefault } = this.defaultFilterState
+          const same = deepCompare(account, accountDefault)
+          needHighlight = !same
+          break
+        }
+        case 'consumptionType': {
+          const { consumptionType } = this.cacheFilters
+          const { consumptionType: consumptionTypeDefault } =
+            this.defaultFilterState
+          const same = deepCompare(consumptionType, consumptionTypeDefault)
+          needHighlight = !same
+          break
+        }
+        case 'time': {
+          const { time } = this.cacheFilters
+          const { time: dateTimeDefault } = this.defaultFilterState
+          const same = deepCompare(time?.exact, dateTimeDefault?.exact)
+          if (time.start || time.end) {
+            needHighlight = !same
+          } else {
+            needHighlight = false
+          }
+          break
+        }
+      }
+      return needHighlight
+    },
+    // 从cacheFilters中恢复选择器状态
+    openedDropDown(type) {
+      switch (type) {
+        case 'operator': {
+          const { personSelector } = this.$refs
+          const { operator } = this.cacheFilters
+          this.cacheFilters.operator = operator
+          personSelector?.setState(operator)
+          personSelector?.onClear()
+          break
+        }
+        case 'account': {
+          const { accountSelector } = this.$refs
+          const { account } = this.cacheFilters
+          accountSelector?.setState(account)
+          break
+        }
+        case 'consumptionType': {
+          const { conTypeSelector } = this.$refs
+          const { consumptionType } = this.cacheFilters
+          conTypeSelector?.setState(consumptionType)
+          break
+        }
+        case 'time': {
+          const { dateTimeGroup } = this.$refs
+          const { time } = this.cacheFilters
+          dateTimeGroup?.setState(time)
+          break
+        }
+      }
+    },
+    popupReset(type) {
+      switch (type) {
+        case 'operator': {
+          const { personSelector } = this.$refs
+          const { operator } = this.defaultFilterState
+          this.cacheFilters.operator = operator
+          personSelector?.setState(operator)
+          personSelector?.onClear()
+          break
+        }
+        case 'account': {
+          const { accountSelector } = this.$refs
+          const { account } = this.defaultFilterState
+          this.cacheFilters.account = account
+          accountSelector?.setState(account)
+          break
+        }
+        case 'consumptionType': {
+          const { conTypeSelector } = this.$refs
+          const { consumptionType } = this.defaultFilterState
+          this.cacheFilters.consumptionType = consumptionType
+          conTypeSelector?.setState(consumptionType)
+          break
+        }
+        case 'time': {
+          const { dateTimeGroup } = this.$refs
+          const { time } = this.defaultFilterState
+          this.cacheFilters.time = time
+          dateTimeGroup?.setState(time)
+          break
+        }
+      }
+      // this.setDropdownMenuTextRoute(type)
+      this.popupState(type, false)
+      this.onChange()
+    },
+    popupConfirm(type) {
+      switch (type) {
+        case 'operator': {
+          const { personSelector } = this.$refs
+          personSelector?.onClear()
+          const state = personSelector?.getState()
+          this.$set(this.cacheFilters, 'operator', state)
+          break
+        }
+        case 'account': {
+          const { accountSelector } = this.$refs
+          const state = accountSelector?.getState()
+          this.$set(this.cacheFilters, 'account', state)
+          break
+        }
+        case 'consumptionType': {
+          const { conTypeSelector } = this.$refs
+          const state = conTypeSelector?.getState()
+          this.$set(this.cacheFilters, 'consumptionType', state)
+          break
+        }
+        case 'time': {
+          const { dateTimeGroup } = this.$refs
+          const state = dateTimeGroup?.getState()
+          this.$set(this.cacheFilters, 'time', state)
+          break
+        }
+      }
+      // this.setDropdownMenuTextRoute(type)
+      this.popupState(type, false)
+      this.onChange()
+    },
+    setDropdownMenuText(name, text) {
+      const t = this.findMenuItemWithName(name)
+      t.calcTitle = text || t.title
+    },
+    setDropdownMenuTextRoute(name) {
+      const t = this.findMenuItemWithName(name)
+      const title = t.title
+      let calcTitle = ''
+      switch (name) {
+        case 'operator': {
+          const { operator } = this.cacheFilters
+          if (Array.isArray(operator) && operator.length) {
+            calcTitle = `${title}${operator.length}个`
+          } else {
+            calcTitle = ''
+          }
+          break
+        }
+        case 'account': {
+          const { account } = this.cacheFilters
+          console.log(account)
+          break
+        }
+        case 'consumptionType': {
+          const { consumptionType } = this.cacheFilters
+          console.log(consumptionType)
+          break
+        }
+        case 'time': {
+          const { time } = this.cacheFilters
+          console.log(time)
+          break
+        }
+      }
+
+      this.setDropdownMenuText(name, calcTitle)
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.fix-pop-height {
+  height: 100%;
+  ::v-deep {
+    .j-main,
+    .default-sidebar-height {
+      height: 100%;
+    }
+  }
+}
+
+.popup-layout-container {
+  padding: 0 16px;
+}
+
+::v-deep {
+  .van-sticky {
+    display: flex;
+    width: 100%;
+  }
+  .van-sticky--fixed {
+    background: #fff;
+  }
+  .jy-checkbox-group {
+    .jy-checkbox {
+      min-width: 104px;
+      text-align: center;
+      &:nth-of-type(3n) {
+        margin-right: 0;
+      }
+    }
+  }
+}
+.filter-wrapper {
+  .filter-menu-item {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    flex: 1;
+    height: 40px;
+    .menu-text {
+      margin-right: 2px;
+      font-size: 14px;
+      line-height: 20px;
+      color: #5f5e64;
+    }
+    .menu-icon {
+      color: #c0c4cc;
+      transition: transform 0.5s ease;
+    }
+
+    &.highlight {
+      .menu-text {
+        color: $color_main;
+      }
+    }
+    &.active {
+      .menu-text {
+        color: $color_main;
+      }
+      .menu-icon {
+        color: $color_main;
+        transform: rotate(-180deg);
+      }
+    }
+  }
+}
+</style>

+ 376 - 0
apps/mobile/src/views/packs/components/ExportRecordFilters.vue

@@ -0,0 +1,376 @@
+<template>
+  <van-dropdown-menu
+    ref="dropdownMenu"
+    class="dropdown-menu-arrow"
+    swipe-threshold="5"
+    :close-on-click-outside="false"
+  >
+    <van-dropdown-item
+      v-if="showOperatorFilter"
+      ref="operatorDropdown"
+      :title-class="dropdownMenuHighlight('operator')"
+      title="操作人"
+      get-container="body"
+      :lazy-render="false"
+      @open="doOpen('operator')"
+      @opened="openedDropDown('operator')"
+    >
+      <DropdownLayout
+        max-height="50vh"
+        @cancel="onReset('operator')"
+        @confirm="onConfirm('operator')"
+      >
+        <div class="dropdown-operator-content">
+          <SelectPerson ref="personSelector" />
+        </div>
+      </DropdownLayout>
+    </van-dropdown-item>
+    <van-dropdown-item
+      ref="payWayDropdown"
+      :title-class="dropdownMenuHighlight('payWay')"
+      title="支付方式"
+      get-container="body"
+      @open="doOpen('payWay')"
+      @opened="openedDropDown('payWay')"
+    >
+      <DropdownLayout
+        @cancel="onReset('payWay')"
+        @confirm="onConfirm('payWay')"
+      >
+        <ScopeSelector
+          ref="payWaySelector"
+          :options="payWayOptions"
+          :multiple="false"
+          :default-val="filters.payWay"
+        />
+      </DropdownLayout>
+    </van-dropdown-item>
+    <van-dropdown-item
+      ref="dateTimeDropdown"
+      :title-class="dropdownMenuHighlight('dateTime')"
+      title="导出时间"
+      get-container="body"
+      @open="doOpen('dateTime')"
+      @opened="openedDropDown('dateTime')"
+    >
+      <DropdownLayout
+        @cancel="onReset('dateTime')"
+        @confirm="onConfirm('dateTime')"
+      >
+        <div class="dropdown-layout-container">
+          <DateTimeList ref="dateTimeGroup" :options="getTimeOptions" marked />
+        </div>
+      </DropdownLayout>
+    </van-dropdown-item>
+  </van-dropdown-menu>
+</template>
+
+<script>
+import { DropdownItem, DropdownMenu } from 'vant'
+import SelectPerson from './SelectPerson'
+import { AppIcon } from '@/ui'
+import DropdownLayout from '@/components/common/DropdownLayout'
+import ScopeSelector from '@/components/selector/scope/index'
+import DateTimeList from '@/components/selector/date-time-list'
+import { biddingSearchTime } from '@/data'
+import { deepCompare } from '@/utils'
+
+export default {
+  components: {
+    [AppIcon.name]: AppIcon,
+    [DropdownMenu.name]: DropdownMenu,
+    [DropdownItem.name]: DropdownItem,
+    SelectPerson,
+    ScopeSelector,
+    DateTimeList,
+    DropdownLayout
+  },
+  model: {
+    prop: 'filters',
+    event: 'change'
+  },
+  props: {
+    showOperatorFilter: {
+      type: Boolean,
+      default: false
+    },
+    hasEntPack: {
+      type: Boolean,
+      default: false
+    },
+    filters: {
+      type: Object,
+      default() {
+        return {}
+      }
+    }
+  },
+  data() {
+    return {
+      conf: {
+        menuList: [
+          {
+            name: 'operator',
+            defaultValue: []
+          },
+          {
+            name: 'payWay',
+            defaultValue: ['']
+          },
+          {
+            name: 'dateTime',
+            defaultValue: []
+          }
+        ],
+        dateTimeList: biddingSearchTime,
+        payWayList: [
+          {
+            label: '个人支付',
+            value: 1
+          },
+          {
+            label: '单日限量数据包',
+            value: 2
+          },
+          {
+            label: '个人数据流量包(高级字段包)',
+            value: 3
+          },
+          {
+            label: '个人数据流量包(标准字段包)',
+            value: 4
+          },
+          {
+            label: '企业数据流量包',
+            value: 5
+          }
+        ]
+      },
+      defaultFilterState: {},
+      cacheFilters: {}
+    }
+  },
+  computed: {
+    payWayOptions() {
+      const { payWayList: list } = this.conf
+      const all = {
+        label: '全部',
+        value: ''
+      }
+      return [all]
+        .concat(list)
+        .map((r) => {
+          let show = true
+          if (r.value === 5) {
+            show = this.hasEntPack
+          }
+          return {
+            ...r,
+            show,
+            key: r.value
+          }
+        })
+        .filter((r) => r.show)
+    },
+    getTimeOptions() {
+      return this.conf.dateTimeList
+    }
+  },
+  watch: {
+    filter: {
+      deep: true,
+      handler(n) {
+        this.setState(n)
+      }
+    }
+  },
+  created() {
+    this.initValue()
+  },
+  methods: {
+    setState(t = this.filters) {
+      const r = Object.assign({}, this.cacheFilters, t)
+      this.cacheFilters = r
+    },
+    initValue() {
+      this.conf.menuList.forEach((m) => {
+        this.$set(this.defaultFilterState, m.name, m.defaultValue)
+        this.$set(this.cacheFilters, m.name, m.defaultValue)
+      })
+    },
+    onChange(value = {}) {
+      const filters = {}
+      Object.assign(
+        filters,
+        JSON.parse(JSON.stringify(this.cacheFilters)),
+        value
+      )
+      this.$emit('change', filters)
+    },
+    doOpen(type) {
+      this.$emit('open', type)
+    },
+    dropdownMenuHighlight(type) {
+      const className = ['highlight']
+      let needHighlight = false
+
+      switch (type) {
+        case 'operator': {
+          const { operator } = this.cacheFilters
+          const { operator: operatorDefault } = this.defaultFilterState
+          const same = deepCompare(operator, operatorDefault)
+          needHighlight = !same
+          break
+        }
+        case 'payWay': {
+          const { payWay } = this.cacheFilters
+          const { payWay: payWayDefault } = this.defaultFilterState
+          const same = deepCompare(payWay, payWayDefault)
+          needHighlight = !same
+          break
+        }
+        case 'dateTime': {
+          const { dateTime } = this.cacheFilters
+          const { dateTime: dateTimeDefault } = this.defaultFilterState
+          const same = deepCompare(dateTime?.exact, dateTimeDefault?.exact)
+          if (dateTime.start || dateTime.end) {
+            needHighlight = !same
+          } else {
+            needHighlight = false
+          }
+          break
+        }
+      }
+      return needHighlight ? className.join(' ') : ''
+    },
+    // 从cacheFilters中恢复选择器状态
+    openedDropDown(type) {
+      switch (type) {
+        case 'operator': {
+          const { personSelector } = this.$refs
+          const { operator } = this.cacheFilters
+          this.cacheFilters.operator = operator
+          personSelector?.setState(operator)
+          personSelector?.onClear()
+          break
+        }
+        case 'payWay': {
+          const { payWaySelector } = this.$refs
+          const { payWay } = this.cacheFilters
+          payWaySelector?.setState(payWay)
+          break
+        }
+        case 'dateTime': {
+          const { dateTimeGroup } = this.$refs
+          const { dateTime } = this.cacheFilters
+          dateTimeGroup?.setState(dateTime)
+          break
+        }
+      }
+    },
+    // 重置选择器状态
+    resetSelectors(type) {
+      const filters = {}
+
+      switch (type) {
+        case 'operator': {
+          const { personSelector } = this.$refs
+          const { operator } = this.defaultFilterState
+          if (Array.isArray(operator)) {
+            filters.operator = operator
+            this.cacheFilters.operator = operator
+            personSelector?.setState(operator)
+          }
+          break
+        }
+        case 'payWay': {
+          const { payWaySelector } = this.$refs
+          const { payWay } = this.defaultFilterState
+          filters.payWay = payWay
+          this.cacheFilters.payWay = payWay
+          payWaySelector?.setState(payWay)
+          break
+        }
+        case 'dateTime': {
+          const { dateTimeGroup } = this.$refs
+          const { dateTime } = this.defaultFilterState
+          filters.dateTime = dateTime
+          this.cacheFilters.dateTime = dateTime
+          dateTimeGroup?.setState(dateTime)
+          break
+        }
+      }
+      return filters
+    },
+    resetModelChange(type) {
+      this.resetSelectors(type)
+      this.onChange()
+    },
+    onReset(type) {
+      this.resetModelChange(type)
+      this.$emit('reset')
+      this.$refs[`${type}Dropdown`]?.toggle(false)
+    },
+    confirmSelectors(type) {
+      const filters = {}
+      switch (type) {
+        case 'operator': {
+          const { personSelector } = this.$refs
+          const state = personSelector?.getState()
+          filters.operator = state
+          this.cacheFilters.operator = state
+          break
+        }
+        case 'payWay': {
+          const { payWaySelector } = this.$refs
+          const state = payWaySelector?.getState()
+          filters.payWay = state
+          this.cacheFilters.payWay = state
+          break
+        }
+        case 'dateTime': {
+          const { dateTimeGroup } = this.$refs
+          const state = dateTimeGroup?.getState()
+          filters.dateTime = state
+          this.cacheFilters.dateTime = state
+          break
+        }
+      }
+      return filters
+    },
+    confirmModelChange(type) {
+      this.confirmSelectors(type)
+      this.onChange()
+    },
+    onConfirm(type) {
+      this.confirmModelChange(type)
+      this.$emit('confirm', { type })
+      this.$refs[`${type}Dropdown`]?.toggle(false)
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+::v-deep {
+  .highlight {
+    color: $main;
+  }
+
+  .jy-checkbox-group {
+    .jy-checkbox {
+      min-width: 104px;
+      text-align: center;
+      &:nth-of-type(3n) {
+        margin-right: 0;
+      }
+    }
+  }
+}
+.dropdown-operator-content {
+  min-height: 290px;
+}
+.dropdown-layout-container {
+  padding: 16px;
+}
+</style>

+ 43 - 0
apps/mobile/src/views/packs/components/SelectPerson.vue

@@ -0,0 +1,43 @@
+<template>
+  <PersonSelector ref="personSelector" :source-list="list" />
+</template>
+
+<script>
+import { getDistributorPerson } from '@/api/modules'
+import PersonSelector from '@/components/selector/person/'
+
+export default {
+  name: 'SelectPerson',
+  components: {
+    PersonSelector
+  },
+  data: () => ({
+    list: []
+  }),
+  created() {
+    this.getPersonList()
+  },
+  methods: {
+    async getPersonList() {
+      const { data } = await getDistributorPerson('eType', { queryType: '1' })
+      if (data) {
+        this.list = data
+      }
+    },
+    getState() {
+      return this.$refs.personSelector.getState()
+    },
+    setState(result = []) {
+      this.$refs.personSelector.setState(result)
+    },
+    onClear() {
+      this.$refs.personSelector.onClear()
+    },
+    getIsAllChecked() {
+      return this.$refs.personSelector?.allChecked
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped></style>

+ 213 - 0
apps/mobile/src/views/packs/styles/pack-style.scss

@@ -0,0 +1,213 @@
+.flex {
+  align-items: center;
+}
+
+.font-w-400 {
+  font-weight: 400;
+}
+
+.up-line {
+  h3,
+  .c-module-title {
+    color: #9b9ca3;
+    line-height: 18px;
+    font-size: 12px;
+    padding: 16px 0 8px 0;
+    font-weight: 400;
+  }
+  .d-conts {
+    flex-wrap: wrap;
+    justify-content: space-between;
+
+    ::v-deep {
+      .van-divider {
+        margin: 6px 0 8px;
+      }
+    }
+
+    .d-card {
+      width: 100%;
+      background: #fff;
+      border-radius: 8px;
+      box-shadow: 0px 2px 8px 0px rgba(54, 147, 179, 0.05);
+      padding: 14px 16px;
+
+      &:not(:last-child) {
+        margin-bottom: 8px;
+      }
+
+      h4 {
+        line-height: 20px;
+        font-size: 13px;
+        background-position: left;
+        padding-left: 28px;
+      }
+
+      .words {
+        flex-direction: inherit;
+        justify-content: space-around;
+        align-items: center;
+
+        .nums {
+          text-align: center;
+
+          .u-p1 {
+            font-size: 18px;
+            color: #171826;
+            font-weight: 700;
+            line-height: 26px;
+            margin-top: 4px;
+
+            i {
+              font-size: 11px;
+              line-height: 18px;
+              font-weight: 400 !important;
+              color: #9b9ca3;
+              margin-left: 4px;
+            }
+          }
+
+          .u-p2 {
+            font-size: 11px;
+            color: #9b9ca3;
+            line-height: 18px;
+          }
+        }
+
+        .r-code {
+          .u-p1 {
+            color: #ff9f40;
+            i {
+              color: #9b9ca3;
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+.down-line {
+  .d-chong {
+    flex-direction: column;
+    width: 100%;
+    background: #fff;
+    border-radius: 8px;
+    box-shadow: 0px 0px 24px 0px rgba(0, 0, 0, 0.06);
+
+    .d-head {
+      display: flex;
+      flex-direction: initial;
+      align-items: center;
+      justify-content: center;
+      border-radius: 8px 8px 0 0;
+      padding: 12px;
+      background: linear-gradient(360deg, #1db5e5 0%, #2abed1 100%);
+
+      .s-zhu {
+        flex-direction: column;
+        color: #fff;
+
+        .u-p1 {
+          font-size: 11px;
+          line-height: 18px;
+        }
+        .u-p2 {
+          margin-top: 4px;
+          font-size: 11px;
+          line-height: 18px;
+          i {
+            font-size: 18px;
+            color: rgba(255, 255, 255, 0.9);
+            line-height: 26px;
+            margin-right: 4px;
+            font-weight: 700;
+          }
+        }
+      }
+    }
+
+    .c-cont {
+      flex-direction: column;
+      padding: 24px 0 16px 0;
+
+      .l-shu {
+        width: 100%;
+
+        .nums .u-p1 i {
+          font-weight: 400 !important;
+        }
+      }
+
+      .b-shu {
+        margin-top: 16px;
+      }
+
+      .nums {
+        width: 50%;
+        text-align: center;
+
+        .u-p1 {
+          font-size: 18px;
+          color: #171826;
+          font-weight: 700;
+          line-height: 26px;
+          margin-top: 4px;
+
+          i {
+            margin-left: 4px;
+            color: #9b9ca3 !important;
+          }
+        }
+
+        .u-p2,
+        .u-p1 i {
+          font-size: 11px;
+          color: #9b9ca3;
+          line-height: 18px;
+        }
+      }
+
+      .r-code {
+        .u-p1,
+        .u-p1 i {
+          color: #ff9f40;
+        }
+      }
+    }
+  }
+}
+
+.bot-line {
+  .d-conts {
+    justify-content: left;
+    background: #fff;
+    padding: 4px 16px;
+    border-radius: 8px;
+    margin-bottom: 12px;
+    .l-left {
+      p {
+        color: #5f5e64;
+        line-height: 32px;
+        font-size: 13px;
+      }
+    }
+    .r-right {
+      margin-left: 8px;
+
+      .downs {
+        color: #2abed1;
+        cursor: pointer;
+      }
+
+      p {
+        line-height: 32px;
+        min-height: 32px;
+        color: #171826;
+        a {
+          color: #2abed1;
+        }
+      }
+    }
+  }
+}

+ 8 - 4
apps/mobile/src/views/search/layout.vue

@@ -34,16 +34,19 @@ export default {
         input: '',
         placeholder: '',
         searchButton: true
-      }
+      },
     }
   },
   computed: {
-    ...mapGetters('user', ['isLogin']),
+    ...mapGetters('user', ['isLogin', 'isWhiteList']),
     emptySearch() {
       const routeName = this.$route.name
       if (routeName && routeName.includes('bidding-custom')) {
         return true
-      } else {
+      } else if(routeName && routeName.includes('search-middle-bidding') && this.isWhiteList) {
+        // 招标采购搜索白名单用户支持空搜索
+        return true
+      } else{
         return false
       }
     }
@@ -74,6 +77,7 @@ export default {
   },
   created() {
     if (this.isLogin) {
+      this.getWhiteListInfo()
       this.ajaxInfo()
       this.getQueryString()
     }
@@ -87,7 +91,7 @@ export default {
     next()
   },
   methods: {
-    ...mapActions('user', ['getEntPower', 'userVipSwitchState']),
+    ...mapActions('user', ['getEntPower', 'userVipSwitchState', 'getWhiteListInfo']),
     ...mapActions('search', ['setHistory']),
     /**
      * 获取用户信息

+ 241 - 12
apps/mobile/src/views/search/result/bidding/index.vue

@@ -183,6 +183,61 @@
           @do-leave="saveState"
         />
       </div>
+      <!-- P640项目名称/标的物引流广告模块-->
+      <div  class="project-bdw-ad-container" :style="{opacity: pageState.bdwCardLoaded ? 'unset' : '0'}" v-show="pageState.projectBdwInfo.sCount > 0">
+        <div class="project-bdw-ad">
+          <div class="top-tip">信息不够精准?根据“项目名称/标的物”,为您精准匹配到 <strong>{{pageState.projectBdwInfo.sCount}}条</strong> 信息</div>
+          <ProjectCell
+            v-visited:content="pageState.projectBdwInfo.sInfo.id"
+            class="list-item"
+            :class="pageState.projectBdwInfo.sInfo.className"
+            :card-type="calcProjectCardType(pageState.projectBdwInfo.sInfo)"
+            :time-fmt="getTimeFmt"
+            :detail-list="pageState.projectBdwInfo.sInfo.detailList"
+            :title="pageState.projectBdwInfo.sInfo.title"
+            :detail="filters.scope.includes('content') ? pageState.projectBdwInfo.sInfo.detail : null"
+            :filetext_search="pageState.projectBdwInfo.sInfo.filetext_search"
+            :fs_keys="pageState.projectBdwInfo.sInfo.fs_word"
+            :time="pageState.projectBdwInfo.sInfo.dateTime"
+            :is-file="pageState.projectBdwInfo.sInfo.isFile"
+            :keys="pageState.splitKeys"
+            :left-top-badge-text="pageState.projectBdwInfo.sInfo.leftTopBadgeText"
+            :tags="pageState.projectBdwInfo.sInfo.tagList"
+            @click="goToDetail(pageState.projectBdwInfo.sInfo)"
+          >
+            <template #buyerText="{ item }">
+                <span
+                  class="buyer-item link-clickable"
+                  @click.stop="toToBuyerProfile(item.text)"
+                >{{ item.text }}</span
+                >
+            </template>
+            <template #winnerText="{ item }">
+                <span
+                  v-for="(winner, index) in item.children"
+                  :key="index"
+                  class="winner-item highlight-text link-clickable j-splitter"
+                  data-j-splitter="、"
+                  @click.stop="toToEntProfile(winner.id)"
+                >{{ winner.text }}</span
+                >
+            </template>
+            <template slot="icon">
+              <div  @click.stop="doBdwCollection(pageState.projectBdwInfo.sInfo, )">
+                  <span
+                    class="j-icon"
+                    :class="{
+                      'icon-star-fill': pageState.projectBdwInfo.sInfo.star,
+                      'icon-star-streak': !pageState.projectBdwInfo.sInfo.star
+                    }"
+                  />
+                <span>&nbsp;{{ pageState.projectBdwInfo.sInfo.star ? '已收藏' : '收藏' }}</span>
+              </div>
+            </template>
+          </ProjectCell>
+          <div class="follow-text" @click="openSvip">开通超级订阅,立享更多搜索权限,寻找商机更精准 &gt;</div>
+        </div>
+      </div>
       <van-list
         v-show="pageState.listTabActive !== 'table'"
         ref="vanList"
@@ -537,6 +592,7 @@ import {
   searchIndexDataExport,
   selectEnt
 } from '@/api/modules'
+import dayjs from 'dayjs'
 
 export default {
   name: 'SearchResultBidding',
@@ -762,7 +818,15 @@ export default {
           random: 4,
           show: true,
           ad: true
-        }
+        },
+        // 项目名称/标的物标讯信息
+        projectBdwInfo: {
+          sCount: 0,
+          sInfo: {}
+        },
+
+        // 标的物引流广告是否加载完成
+        bdwCardLoaded: false
       },
       BIInfo: {
         ids: []
@@ -804,7 +868,8 @@ export default {
       'isNewSuper', // 新超级订阅
       'isMember',
       'bigMemberPower',
-      'isBusiness'
+      'isBusiness',
+      'isWhiteList'
     ]),
     noLoginOrFree() {
       if (this.isLogin) {
@@ -931,7 +996,7 @@ export default {
         'lately-30': '近30天'
       }
       const tipKey = tipMap[exact] || ''
-      if (keywords || this.additionalWordsArr.length) {
+      if (keywords || this.additionalWordsArr.length || this.isWhiteList) {
         return `对不起,没有找到<span class="highlight-text"> ${tipKey} </span>相关匹配的信息,修改时间范围或换个搜索词试试吧`
       } else {
         return '请输入关键词试试吧'
@@ -1034,11 +1099,19 @@ export default {
         }
       }
       this.moveRecommendCard(n)
+      // 项目名称/标的物引流广告插入到第五条公告后边
+      console.log(666666666)
+      const insertNum = this.listState.list.length > 4 ? 4 : this.listState.list.length
+      this.insertProjectBdwAd(insertNum)
     },
     searchList(list) {
       if (list && list.length && this.pageState.listTabActive === 'list') {
         // 市场分析报告插到第一条数据后面位置
         this.moveRecommendCard('list')
+       // 项目名称/标的物引流广告插入到第五条公告后边
+        console.log(777777)
+        const insertNum = list.length > 4 ? 4 : list.length
+        this.insertProjectBdwAd(insertNum)
       }
     }
   },
@@ -1622,13 +1695,21 @@ export default {
       if (t.pageNum === 1) {
         this.saveSearchGroupToLocal()
       }
-
+      this.pageState.projectBdwInfo.sCount =  0
+      this.pageState.projectBdwInfo.sInfo = {}
+      this.pageState.projectBdwInfo.bdwCardLoaded = false
       try {
         const {
           data = {},
           error_code: code = 0,
           error_msg: msg
         } = await getBiddingSearchList(params, this.restfulApiUserTypeDefault)
+        // P640处理标的物广告位信息
+        this.pageState.projectBdwInfo.sCount = data.sCount || 0
+        this.pageState.projectBdwInfo.sInfo = data.sList && data.sCount > 0 ? data.sList[0]: {}
+        // 计算项目名称标的物广告信息数据
+        this.calcProjectBdwTag()
+
         const isLimited =
           data.isLimit === 1 ||
           data.isLimit === undefined ||
@@ -1637,7 +1718,6 @@ export default {
           this.afterGetList(() => {
             // 搜索完5s后显示订阅设置弹窗
             setTimeout(() => {
-              console.log(this.subInfo)
               if (this.subInfo.isSubscribe && this.sunDialogNum === 0) {
                 this.showSubDialog = true
                 this.sunDialogNum++
@@ -1652,7 +1732,9 @@ export default {
           // 请求完成后的回调
           this.afterSearch(params, data)
           // 列表赋值
-          const list = data.list
+          // 如果筛选范围同时包含标题和正文,那么按照正文包括高亮在前,标题在后
+          const resDataList = data.list && data.list.length > 0 ? JSON.parse(JSON.stringify(data.list)) : []
+          const list =this.sortListTitleDetail(resDataList)
           const count = data.count
           const total = data.total
           if (Array.isArray(list)) {
@@ -1769,11 +1851,75 @@ export default {
         // 保存更多关键词到历史记录中
         this.saveAdditionalWordsToHistory()
         // 关键词为空,则提示,输入关键词
-        if (!params.keyWords && !params.additionalWords) {
+        if (!params.keyWords && !params.additionalWords && !this.isWhiteList) {
           this.$toast('请输入关键词')
         }
       }
     },
+    // p640需求,如果筛选类型同时包含标题和正文,先展示标题包含关键词,再展示内容包含关键词的数据
+    // 如果不同时包含标题和正文,则不处理
+    sortListTitleDetail (baseArr) {
+      const _this = this
+      if( this.filters.scope.includes('title') &&  this.filters.scope.includes('content')) {
+        // 辅助函数:检查字符串是否包含关键词数组中的任何一个关键词
+        function containsKeyword(str, keywords) {
+          return keywords.some(keyword => str ? str.includes(keyword) : '');
+        }
+        // 先按照发布时间倒序分组排序
+        let arr = []
+        const baseTimeMap = {}
+        const baseTimeKeys = []
+        baseArr.forEach(v => {
+          const ymd = dayjs(v.publishTime * 1000).startOf('day').unix()
+          if (!Array.isArray(baseTimeMap[ymd])) {
+            baseTimeMap[ymd] = []
+          }
+          baseTimeMap[ymd].push(v)
+          if (!baseTimeKeys.includes(ymd)) {
+            baseTimeKeys.push(ymd)
+          }
+        })
+
+        const sortBaseTimeKeys = baseTimeKeys.sort((a,b) => b - a)
+        // 再按照标题正文排序
+        function sortItem (arr) {
+          const matchKeys = _this.pageState.splitKeys
+          const titleMatches = arr.filter(item => containsKeyword(item.title, matchKeys))
+          const contentMatches = arr.filter(item => _this.setDetailText(item.detail, matchKeys) && !titleMatches.some(tm => tm.title === item.title))
+          const noMatches = arr.filter(item => !containsKeyword(item.title, matchKeys) && !_this.setDetailText(item.detail, matchKeys));
+          // 合并结果
+          const resultArr = [...titleMatches, ...contentMatches, ...noMatches]
+          return resultArr
+        }
+
+        for (let i = 0; i < sortBaseTimeKeys.length; i++) {
+          const baseTimeMapKey = sortBaseTimeKeys[i]
+          const sortItemArr = sortItem(baseTimeMap[baseTimeMapKey])
+          arr = arr.concat(sortItemArr)
+        }
+        return arr
+      } else {
+        return baseArr
+      }
+    },
+    // 正文开头和高亮词之间,最多保留10个字符
+    setDetailText(detailStr, matchKeys) {
+      // 正则匹配标签置为空
+      if (!detailStr) return ''
+      const detail = detailStr.replace(/<[^>]+>/g, '')
+      const detailLower = detail.toLowerCase()
+      const keyLower = matchKeys[0]?.toLowerCase()
+      let postion = detailLower.indexOf(keyLower)
+      if (postion !== -1) {
+        if (postion - 10 > 0) {
+          return detail.substring(postion - 10)
+        } else {
+          return detail
+        }
+      } else {
+        return ''
+      }
+    },
     /**
      * 检查是否需要切换模糊搜索、是否展示提示
      * 1. 精准搜索无数据 (自动切换模糊搜索)
@@ -2104,7 +2250,7 @@ export default {
       return true
     },
     // 收藏
-    async doCollection(item, index) {
+    async doCollection(item, index, type) {
       if (!this.isLogin) {
         this.pageState.cacheStar = `${index}`
         this.saveState()
@@ -2129,6 +2275,28 @@ export default {
         }
       })
     },
+    // 收藏
+    async doBdwCollection(item) {
+      if (!this.isLogin) {
+        return openLinkOfOther(LINKS.APP登录页.app, {
+          query: {
+            to: 'back'
+          }
+        })
+      }
+      this.$keep.action({
+        status: item.star,
+        id: item.id,
+        beforeRedirect: () => {
+        },
+        complete: ({ type, message }) => {
+          if (type) {
+            item.star = !item.star
+            this.$forceUpdate()
+          }
+        }
+      })
+    },
     // 老接口通用的参数
     getFilterParams2() {
       const { moreKeywordsMode } = this.filters
@@ -2423,7 +2591,6 @@ export default {
     // 获取已存筛选的参数
     getSaveFilterToHistoryParams() {
       const params = this.getFilterParams2()
-
       delete params.scope
       // 存的时候要把<拟建项目>替换成<拟建>
       if (params.subtype) {
@@ -2554,11 +2721,11 @@ export default {
           if (this.topSearch.input) {
             return v && v.text
           } else {
-            return !v.label.includes('搜索范围') && v.text
+            return  v.text
           }
         })
         .map((item) => item.text)
-      if (searchModeText && this.topSearch.input) {
+      if (searchModeText) {
         labelList = [searchModeText].concat(labelList)
       }
       // console.log(formattedList, 'formattedList')
@@ -2942,7 +3109,7 @@ export default {
         // 市场分析报告插到第一条数据后面位置
         this.$nextTick(() => {
           const recommend = document.querySelector('.recommend-card-container')
-          const cellList = document.querySelectorAll('.project-cell')
+          const cellList = document.querySelectorAll('.more-list .list-wrapper>.project-cell')
           const recommendChild = document.querySelector('.recommend-bg')
           if (recommend && cellList[0]) {
             const parentNode = cellList[0].parentNode
@@ -2954,6 +3121,33 @@ export default {
           }
         })
       }
+    },
+    // 在列表第五条插入项目名称/标的物引流广告
+    insertProjectBdwAd(num) {
+      this.$nextTick(() => {
+        const adModule = document.querySelector('.project-bdw-ad-container')
+        const cellList = document.querySelectorAll('.more-list .project-cell')
+        if (adModule && cellList[num]) {
+          const parentNode = cellList[num].parentNode
+          parentNode.insertBefore(adModule, cellList[num].nextSibling)
+          this.pageState.bdwCardLoaded = true
+        }
+      })
+    },
+    // 计算标的物引流标讯的信息(标签信息等)
+    calcProjectBdwTag() {
+      this.preSortItem(this.pageState.projectBdwInfo.sInfo)
+    },
+    // 开通超级订阅
+    openSvip () {
+      if (!this.isLogin) {
+        return openLinkOfOther(LINKS.APP登录页.app, {
+          query: {
+            url: '/jy_mobile/common/order/create/svip'
+          }
+        })
+      }
+      this.$router.push('/common/order/create/svip')
     }
   }
 }
@@ -3429,4 +3623,39 @@ export default {
 .customer-corner {
   z-index: 10;
 }
+.project-bdw-ad-container{
+  border-radius: 12px;
+  background: linear-gradient(180deg, #fff4e8 0%, #fff 100%);
+  padding:12px;
+  margin:8px;
+  .top-tip {
+    font-weight:bold;
+    font-size:13px;
+    line-height:20px;
+    strong {
+      color:#FF9F40;
+      margin:0 2px;
+    }
+  }
+  .follow-text{
+    background: #FF9F40;
+    color: #fff;
+    border-radius: 4px;
+    padding: 2px 11px;
+    font-size:12px;
+    line-height:20px;
+    margin-top:10px;
+    cursor: pointer;
+  }
+  ::v-deep{
+    .project-cell{
+      padding:0;
+      margin:0;
+      background: unset;
+    }
+    .jy-hairline--bottom::after{
+      border: none;
+    }
+  }
+}
 </style>

+ 112 - 47
apps/mobile/src/views/tabbar/Subscribe.vue

@@ -497,31 +497,38 @@
       </div>
     </main>
     <footer class="j-footer">
-      <NoticeBar
-        v-if="pushMaxNoticeBarShow"
-        right-text="了解详情"
-        @clickLeft="noticeBar.pushMaxShow = false"
-        @clickRight="toVipIntroducePage"
-      >
-        本次推送已达150条信息上限!开通超级订阅,支持每天最多推送2000条信息
-      </NoticeBar>
-      <NoticeBar
-        v-if="vipNoticeBarShow"
-        theme="red"
-        :right-text="noticeBar.vipRightText"
-        @clickLeft="onVipNoticeClickLeft"
-        @clickRight="toBuyVip"
-      >
-        {{ noticeBar.vipContentText }}
-      </NoticeBar>
-      <NoticeBar
-        v-if="keywordsSettingNoticeBarShow"
-        right-text="去设置"
-        @clickLeft="noticeBar.settingKeywordsShow = false"
-        @clickRight="toKeysetPage"
-      >
-        您未设置关键词,设置后接收信息更精准!
-      </NoticeBar>
+      <div v-if="urlPushTime" class="lookMoretips">
+        <span>当前筛选查看的为此次推送的信息,</span>
+        <span class="tipsBtn" @click="lookAlldata">点击查看全部></span>
+      </div>
+      <!-- 从模版消息进来其他底部提示不显示 -->
+      <div v-else>
+        <NoticeBar
+          v-if="pushMaxNoticeBarShow"
+          right-text="了解详情"
+          @clickLeft="noticeBar.pushMaxShow = false"
+          @clickRight="toVipIntroducePage"
+        >
+          本次推送已达150条信息上限!开通超级订阅,支持每天最多推送2000条信息
+        </NoticeBar>
+        <NoticeBar
+          v-if="vipNoticeBarShow"
+          theme="red"
+          :right-text="noticeBar.vipRightText"
+          @clickLeft="onVipNoticeClickLeft"
+          @clickRight="toBuyVip"
+        >
+          {{ noticeBar.vipContentText }}
+        </NoticeBar>
+        <NoticeBar
+          v-if="keywordsSettingNoticeBarShow"
+          right-text="去设置"
+          @clickLeft="noticeBar.settingKeywordsShow = false"
+          @clickRight="toKeysetPage"
+        >
+          您未设置关键词,设置后接收信息更精准!
+        </NoticeBar>
+      </div>
     </footer>
     <!--    客服 -->
     <CustomerCorner
@@ -578,14 +585,14 @@ import { mapActions, mapGetters, mapState } from 'vuex'
 import {
   Badge,
   Button,
+  Icon,
   List,
+  Overlay,
+  Popover,
   Popup,
   Tab,
   Tabs,
-  Tag,
-  Overlay,
-  Popover,
-  Icon
+  Tag
 } from 'vant'
 import dayjs from 'dayjs'
 import qs from 'qs'
@@ -632,19 +639,19 @@ import {
 import {
   ajaxCanBiaoAction,
   ajaxCanBiaoStatus,
+  ajaxGetTipInfo,
   checkDataReportTip,
   checkHasReportData,
   getConfiguration,
   getPushListDataExportId,
+  getPushSet,
   getSubscribePageAreaPackTip,
   getUserSubscribeKeywords,
   getUserSubscribeList,
   getUserSubscribeSomeInfo,
   selectEnt,
   setSubscribePageAreaPackTipClose,
-  setUserSubscribeListVisited,
-  ajaxGetTipInfo,
-  getPushSet
+  setUserSubscribeListVisited
 } from '@/api/modules'
 import { leadGetDate } from '@/api/modules/leadGeneration'
 
@@ -946,7 +953,8 @@ export default {
       // 预加载提醒文案
       preloadTipText: '',
       // 市场分析报告是否加载完成
-      cardLoaded: false
+      cardLoaded: false,
+      urlPushTime: ''
     }
   },
   computed: {
@@ -1232,6 +1240,7 @@ export default {
     this.checkIOSCompatible()
     // 检查筛选器权限
     this.changeFiltersPower(this.disabledFilters)
+    this.getStoragepushTime()
     await this.getUserIdentityList()
     if (this.restored) {
       // do something
@@ -1287,6 +1296,11 @@ export default {
     jumpBidPage() {
       this.$router.push('/course/index')
     },
+    lookAlldata() {
+      this.urlPushTime = ''
+      sessionStorage.removeItem('dy-urlPushTime')
+      this.getList()
+    },
     async getConfigurationApi() {
       const { error_code: code, data } = await getConfiguration()
       if (code === 0) {
@@ -1313,6 +1327,11 @@ export default {
         this.popupHeight = 'height: 440px'
       }
     },
+    getStoragepushTime() {
+      if (!this.urlPushTime && sessionStorage.getItem('dy-urlPushTime')) {
+        this.urlPushTime = sessionStorage.getItem('dy-urlPushTime')
+      }
+    },
     // 获取url参数
     async getQueryString() {
       // 参数说明:
@@ -1322,15 +1341,25 @@ export default {
       //   4. advertcode // 广告位代码
       this.cacheQuery = this.$route.query // 缓存一份query参数
       const { f, pushtime, times, t, advertcode, tab } = this.$route.query
+
       if (pushtime) {
-        this.setFirstTimeSelectSearch(false)
-        this.filters.selectTime = dayjs.unix(pushtime).format('YYYY/MM/DD')
+        this.urlPushTime = pushtime
+        sessionStorage.setItem('dy-urlPushTime', this.urlPushTime)
+        // this.setFirstTimeSelectSearch(false)
+        // 不再回显时间筛选
+        // this.filters.selectTime = dayjs.unix(pushtime).format('YYYY/MM/DD')
         // 修改时间筛选文字
-        this.changeDropdownMenuText({ key: 'selectTime' })
+        // this.changeDropdownMenuText({ key: 'selectTime' })
       } else if (times) {
-        this.setFirstTimeSelectSearch(false)
-        this.filters.selectTime = dayjs.unix(times).format('YYYY/MM/DD')
-        this.changeDropdownMenuText({ key: 'selectTime' })
+        this.urlPushTime = times
+        sessionStorage.setItem('dy-urlPushTime', this.urlPushTime)
+        // this.setFirstTimeSelectSearch(false)
+        // 不再回显时间筛选
+        // this.filters.selectTime = dayjs.unix(times).format('YYYY/MM/DD')
+        // this.changeDropdownMenuText({ key: 'selectTime' })
+      } else {
+        this.urlPushTime = ''
+        sessionStorage.removeItem('dy-urlPushTime')
       }
       const { subscribeTypeList } = this.tabConf
       if (tab && subscribeTypeList.includes(tab)) {
@@ -1355,7 +1384,6 @@ export default {
           this.subscribeTypeActive = 'my'
         }
       }
-
       if (f === 'push') {
         // 从推送过来,url参数取完后,把url.query清空
         this.$router.replace({
@@ -1610,7 +1638,8 @@ export default {
       const params = {
         ...query,
         selectTime: selectTime || 'all',
-        vt: isEnt ? 'q' : this.vSwitch
+        vt: isEnt ? 'q' : this.vSwitch,
+        pushTime: this.urlPushTime
       }
       return params
     },
@@ -1672,6 +1701,7 @@ export default {
             params[key] = ''
             if (!firstTimeSelectSearch) {
               if (values) {
+                console.log(values)
                 const sTime = dayjs(values)
                 const start = sTime.startOf('day').unix()
                 const end = sTime.endOf('day').unix()
@@ -1868,16 +1898,30 @@ export default {
           end: dateFormatter(params.selectTime.split('_')[1] * 1000)
         })
       }
-
+      if (this.urlPushTime) {
+        const { data = {} } = await getUserSubscribeList(
+          this.restfulApiUserTypeWitchVSwitch,
+          {
+            ...params,
+            pushTime: this.urlPushTime // 推送信息进入页面时的时间参数
+          }
+        )
+        // 没有数据或者企业版推送全部数据 清空PushTime参数
+        if (!data.count || this.subscribeTypeActive === 'ent') {
+          // 检查是否有数据
+          this.urlPushTime = ''
+          sessionStorage.removeItem('dy-urlPushTime')
+        }
+      }
       try {
         const {
           data = {},
           error_code: code = 0,
           error_msg: msg
-        } = await getUserSubscribeList(
-          this.restfulApiUserTypeWitchVSwitch,
-          params
-        )
+        } = await getUserSubscribeList(this.restfulApiUserTypeWitchVSwitch, {
+          ...params,
+          pushTime: this.urlPushTime // 推送信息进入页面时的时间参数
+        })
         if (code === 0 && data) {
           // 判断是否为刷新
           if (t.refreshing) {
@@ -2440,6 +2484,11 @@ export default {
     },
     // 筛选器确定事件
     onFilterConfirm({ option, index, $ref }) {
+      // 若是从微信推送进入页面,重新确定筛选就移除推送时间参数
+      if (this.urlPushTime) {
+        this.urlPushTime = ''
+        sessionStorage.removeItem('dy-urlPushTime')
+      }
       if (option.key === 'selectTime') {
         this.setFirstTimeSelectSearch(false)
       }
@@ -3285,7 +3334,8 @@ export default {
   border-radius: 8px;
   border: 1px solid transparent;
   background-clip: padding-box, border-box;
-  background-image: linear-gradient(#e8ffff 40%, #ffffff 100%),
+  background-image:
+    linear-gradient(#e8ffff 40%, #ffffff 100%),
     linear-gradient(to right, #2abed1, #4de4f84c, #2abed1);
   background-origin: padding-box, border-box;
 
@@ -3544,4 +3594,19 @@ export default {
     }
   }
 }
+.lookMoretips {
+  width: 100%;
+  background-color: #fff4e8;
+  padding: 12px;
+  font-size: 13px;
+  line-height: 20px;
+  text-align: center;
+  color: #ff9f40;
+  // position: fixed;
+  // left: 0;
+  // bottom: 50px;
+}
+.tipsBtn {
+  color: #2abed1;
+}
 </style>

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 431 - 231
pnpm-lock.yaml


Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно