123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566 |
- <template>
- <!-- 对应页面: app崩溃 -->
- <view class="fix-top-window">
- <view class="uni-header">
- <uni-stat-breadcrumb class="uni-stat-breadcrumb-on-phone" />
- <view class="uni-group hide-on-phone">
- <!-- <view class="uni-title">崩溃分析</view> -->
- <view class="uni-sub-title">开发者可以在这里快速查询原生应用最近出现的具体崩溃内容,了解崩溃概况信息,以便快速修复问题</view>
- </view>
- </view>
- <view class="uni-container">
- <view class="uni-stat--x flex">
- <uni-data-select collection="opendb-app-list" field="appid as value, name as text" orderby="text asc"
- :defItem="1" label="应用选择" v-model="query.appid" :clear="false" />
- <uni-data-select collection="opendb-app-versions" :where="versionQuery"
- field="_id as value, version as text" orderby="text asc" label="版本选择" v-model="query.version_id" />
- <uni-stat-tabs label="平台选择" type="boldLine" :all="false" mode="platform-channel"
- v-model="query.platform_id" @change="changePlatform" />
- <view class="flex">
- <uni-stat-tabs label="日期选择" :current="currentDateTab" :yesterday="false" mode="date"
- @change="changeTimeRange" />
- <uni-datetime-picker type="daterange" :end="new Date().getTime()" v-model="query.start_time"
- returnType="timestamp" :clearIcon="false" class="uni-stat-datetime-picker"
- :class="{'uni-stat__actived': currentDateTab < 0 && !!query.start_time.length}"
- @change="useDatetimePicker" />
- </view>
- </view>
- <view class="uni-stat--x" style="padding: 15px 0;">
- <uni-stat-panel :items="panelData" class="uni-stat-panel" />
- <uni-stat-tabs type="box" v-model="chartTab" :tabs="chartTabs" class="mb-l" />
- <view class="uni-charts-box">
- <qiun-data-charts type="area" :chartData="chartData" :eopts="{notMerge:true}" echartsH5 echartsApp
- tooltipFormat="tooltipCustom" />
- </view>
- </view>
- <view class="uni-stat--x p-m">
- <view class="flex-between">
- <view class="uni-stat-card-header">信息列表</view>
- <view class="uni-group">
- <!-- #ifdef H5 -->
- <download-excel class="hide-on-phone" :fields="exportExcel.fields" :data="exportExcelData"
- :type="exportExcel.type" :name="exportExcel.filename">
- <button class="uni-button" type="primary" size="mini">导出 Excel</button>
- </download-excel>
- <!-- #endif -->
- </view>
- </view>
- <unicloud-db ref="udb" :collection="collectionList"
- field="appid,version,platform,channel,sdk_version,device_id,device_net,device_os,device_os_version,device_vendor,device_model,device_is_root,device_os_name,device_batt_level,device_batt_temp,device_memory_use_size,device_memory_total_size,device_disk_use_size,device_disk_total_size,device_abis,app_count,app_use_memory_size,app_webview_count,app_use_duration,app_run_fore,package_name,package_version,page_url,error_msg,create_time"
- :where="where" page-data="replace" :orderby="orderby" :getcount="true" :page-size="options.pageSize"
- :page-current="options.pageCurrent" loadtime="manual"
- v-slot:default="{data,pagination,loading,error,options}" :options="options" @load="onqueryload">
- <uni-table ref="table" :loading="loading" border stripe :emptyText="$t('common.empty')"
- style="overflow-y: scroll;">
- <uni-tr>
- <template v-for="(mapper, index) in fieldsMap">
- <!-- todo: schema table -->
- <!-- <uni-th v-if="mapper.title" :key="index" :filter-type="mapper.filter"
- @filter-change="filterChange($event, mapper.field)" sortable
- @sort-change="sortChange($event, mapper.field)" align="center"
- :style="`min-width: ${mapper.title.length * 15 + 80}px;`"> -->
- <uni-th v-if="mapper.title" :key="index" align="center"
- :style="{'minWidth':`${mapper.title.length * 15 + 80}px`}">
- <!-- #ifdef MP -->
- {{mapper.title}}
- <!-- #endif -->
- <!-- #ifndef MP -->
- <uni-tooltip>
- {{mapper.title}}
- <uni-icons v-if="mapper.tooltip" type="help" color="#666" />
- <template v-if="mapper.tooltip" v-slot:content>
- <view class="uni-stat-tooltip-s">
- {{mapper.tooltip}}
- </view>
- </template>
- </uni-tooltip>
- <!-- #endif -->
- </uni-th>
- </template>
- </uni-tr>
- <uni-tr v-for="(item ,i) in tableData" :key="i">
- <template v-for="(mapper, index) in fieldsMap">
- <uni-td v-if="mapper.field === 'error_msg'" :key="mapper.field" align="left"
- style="min-width: 500px;">
- <!-- #ifdef MP -->
- {{item.error_msg ? item.error_msg.substring(0, 100) + '...' : '-'}}
- <!-- #endif -->
- <!-- #ifndef MP -->
- <uni-tooltip>
- {{item.error_msg ? item.error_msg.substring(0, 100) + '...' : ''}}
- <uni-icons v-if="item.error_msg" type="help" color="#666" />
- <template v-if="item.error_msg" v-slot:content>
- <view class="uni-stat-tooltip-l">
- {{item.error_msg}}
- </view>
- </template>
- </uni-tooltip>
- <!-- #endif -->
- </uni-td>
- <uni-td v-else-if="mapper.field === 'create_time'" :key="mapper.field" align="center">
- <uni-dateformat :threshold="[0, 0]" :date="item.create_time"></uni-dateformat>
- </uni-td>
- <uni-td v-else :key="mapper.field" align="center">
- {{item[mapper.field] !== undefined ? item[mapper.field] : '-'}}
- </uni-td>
- </template>
- </uni-tr>
- </uni-table>
- <view class="uni-pagination-box">
- <uni-pagination show-icon :page-size="pagination.size" v-model="pagination.current"
- :total="pagination.count" @change="onPageChanged" />
- </view>
- </unicloud-db>
- </view>
- </view>
- <!-- #ifndef H5 -->
- <fix-window />
- <!-- #endif -->
- </view>
- </template>
- <script>
- import {
- mapfields,
- stringifyQuery,
- getTimeOfSomeDayAgo,
- division,
- format,
- formatDate,
- parseDateTime,
- debounce,
- getAllDateCN
- } from '@/js_sdk/uni-stat/util.js'
- import {
- fieldsMap,
- } from './fieldsMap.js'
- const panelOption = [{
- title: '崩溃总数',
- field: 'count',
- value: 0,
- formatter: ',',
- tooltip: '指原生应用在某个时间段内出现崩溃的总数'
- }, {
- title: '崩溃率',
- field: 'count/app_launch_count',
- computed: 'count/app_launch_count',
- formatter: '%',
- value: 0,
- tooltip: '时间范围内的总崩溃数/原生应用启动次数,如果小于0.01%,默认显示为0'
- }]
- import {
- enumConverter,
- filterToWhere
- } from '@/js_sdk/validator/uni-stat-app-crash-logs.js';
- const db = uniCloud.database()
- // 表查询配置
- const dbOrderBy = 'create_time desc' // 排序字段
- const dbSearchFields = [] // 模糊搜索字段,支持模糊搜索的字段列表。联表查询格式: 主表字段名.副表字段名,例如用户表关联角色表 role.role_name
- // 分页配置
- const pageSize = 20
- const pageCurrent = 1
- const orderByMapping = {
- "ascending": "asc",
- "descending": "desc"
- }
- export default {
- data() {
- return {
- fieldsMap,
- //todo:要与schema 生成页面一起工作,stringifyQuery 需要与 schema 查询逻辑相容
- query: {
- type: "crash",
- dimension: "day",
- appid: "",
- platform_id: '',
- uni_platform: '',
- version_id: '',
- start_time: [],
- },
- options: {
- pageCurrent: 1, // 当前页
- total: 0, // 数据总量
- pageSizeIndex: 0, // 与 pageSizeRange 一起计算得出 pageSize
- pageSizeRange: [10, 20, 50, 100],
- },
- loading: false,
- popupLoading: false,
- currentDateTab: 0,
- // currentChartTab: ,
- tableData: [],
- popupTableData: [],
- panelData: JSON.parse(JSON.stringify(panelOption)),
- chartData: {},
- chartTab: 'errorCount',
- chartTabs: [{
- _id: 'errorCount',
- name: '崩溃次数'
- }, {
- _id: 'errorRate',
- name: '崩溃率'
- }],
- collectionList: "uni-stat-app-crash-logs",
- schemaQuery: '',
- where: this.tableData,
- orderby: dbOrderBy,
- orderByFieldName: "",
- selectedIndexs: [],
- options: {
- pageSize,
- pageCurrent,
- filterData: {},
- ...enumConverter
- },
- exportExcel: {
- "filename": "uni-stat-app-crash-logs.xls",
- "type": "xls",
- "fields": {
- "appid": "appid",
- "version": "version",
- "platform": "platform",
- "channel": "channel",
- "sdk_version": "sdk_version",
- "device_id": "device_id",
- "device_net": "device_net",
- "device_os": "device_os",
- "device_os_version": "device_os_version",
- "device_vendor": "device_vendor",
- "device_model": "device_model",
- "device_is_root": "device_is_root",
- "device_os_name": "device_os_name",
- "device_batt_level": "device_batt_level",
- "device_batt_temp": "device_batt_temp",
- "device_memory_use_size": "device_memory_use_size",
- "device_memory_total_size": "device_memory_total_size",
- "device_disk_use_size": "device_disk_use_size",
- "device_disk_total_size": "device_disk_total_size",
- "device_abis": "device_abis",
- "app_count": "app_count",
- "app_use_memory_size": "app_use_memory_size",
- "app_webview_count": "app_webview_count",
- "app_use_duration": "app_use_duration",
- "app_run_fore": "app_run_fore",
- "package_name": "package_name",
- "package_version": "package_version",
- "page_url": "page_url",
- "error_msg": "error_msg",
- "create_time": "create_time"
- }
- },
- exportExcelData: []
- }
- },
- computed: {
- queryStr() {
- return stringifyQuery(this.query)
- },
- tableQuery() {
- const {
- appid,
- platform_id,
- version_id,
- start_time
- } = this.query
- // 从本地存储中取到数据做过滤
- const platforms = uni.getStorageSync('platform_channel_last_data')
- const versions = uni.getStorageSync('uni-stat-app-versions_last_data')
- const p = Array.isArray(platforms) && platforms.find(p => p._id === platform_id)
- const v = Array.isArray(versions) && versions.find(v => v._id === version_id)
- const query = stringifyQuery({
- appid,
- create_time: start_time,
- platform: p && p.code || '',
- version: v && v.text || ''
- })
- return query
- },
- versionQuery() {
- const {
- appid,
- uni_platform
- } = this.query
- const query = stringifyQuery({
- appid,
- uni_platform,
- type: 'native_app'
- })
- return query
- }
- },
- created() {
- this.debounceGet = debounce(() => {
- this.getAllData(this.queryStr)
- this.where = this.tableQuery
- this.$nextTick(() => {
- this.$refs.udb && this.$refs.udb.loadData()
- }, 200)
- })
- },
- watch: {
- query: {
- deep: true,
- handler(val) {
- this.options.pageCurrent = 1 // 重置分页
- this.debounceGet()
- }
- },
- chartTab(val) {
- this.getChartData(this.queryStr)
- }
- },
- onLoad() {
- this._filter = {}
- },
- methods: {
- onqueryload(data) {
- this.exportExcelData = data
- this.tableData = data
- },
- getWhere() {
- const query = this.schemaQuery.trim()
- if (!query) {
- return ''
- }
- const queryRe = new RegExp(query, 'i')
- return dbSearchFields.map(name => queryRe + '.test(' + name + ')').join(' || ')
- },
- loadData(clear = true) {
- this.$refs.udb.loadData({
- clear
- })
- },
- onPageChanged(e) {
- this.selectedIndexs.length = 0
- this.$refs.table.clearSelection()
- this.$refs.udb.loadData({
- current: e.current
- })
- },
- sortChange(e, name) {
- this.orderByFieldName = name;
- if (e.order) {
- this.orderby = name + ' ' + orderByMapping[e.order]
- } else {
- this.orderby = ''
- }
- this.$refs.table.clearSelection()
- this.$nextTick(() => {
- this.$refs.udb.loadData()
- })
- },
- filterChange(e, name) {
- this._filter[name] = {
- type: e.filterType,
- value: e.filter
- }
- let newWhere = filterToWhere(this._filter, db.command)
- if (Object.keys(newWhere).length) {
- this.where = newWhere
- } else {
- this.where = ''
- // this.where = this.tableQuery
- }
- this.$nextTick(() => {
- this.$refs.udb.loadData()
- })
- },
- useDatetimePicker() {
- this.currentDateTab = -1
- },
- changePlatform(id, index, name, item) {
- this.query.version_id = 0
- this.query.uni_platform = item.code
- },
- changeTimeRange(id, index) {
- this.currentDateTab = index
- const start = getTimeOfSomeDayAgo(id),
- end = getTimeOfSomeDayAgo(0) - 1
- this.query.start_time = [start, end]
- },
- getAllData(query) {
- this.getPanelData(query)
- this.getChartData(query)
- },
- getPanelData(query) {
- // console.log(query);
- let querystr = stringifyQuery(this.query, false, ['uni_platform'])
- const db = uniCloud.database()
- console.log('queryStr', querystr);
- db.collection('uni-stat-error-result')
- .where(querystr)
- .field('count as temp_count, app_launch_count as temp_app_launch_count, appid')
- .groupBy('appid')
- .groupField('sum(temp_count) as count, sum(temp_app_launch_count) as app_launch_count')
- .get({
- getCount: true
- })
- .then(res => {
- const {
- count,
- data
- } = res.result
- const item = res.result.data[0]
- // this.panelData = []
- let queryTemp = Object.assign({}, this.query)
- delete queryTemp.type
- console.log('---- query ', queryTemp);
- this.getTotalLaunch(stringifyQuery(queryTemp, false, ['uni_platform'])).then(res => {
- const total = res.result.data[0]
- console.log('result total---', total);
- if (item) {
- let launch_count = total && total.total_app_launch_count
- item.app_launch_count = launch_count
- this.panelData = mapfields(panelOption, item)
- }
- })
- })
- },
- getTotalLaunch(query) {
- const db = uniCloud.database()
- return db.collection('uni-stat-result')
- .where(query)
- .groupBy('appid')
- .groupField('sum(app_launch_count) as total_app_launch_count')
- .get()
- },
- getChartData(query, field = 'day_count') {
- let querystr = stringifyQuery(this.query, false, ['uni_platform'])
- this.chartData = {}
- const {
- pageCurrent
- } = this.options
- const db = uniCloud.database()
- const [start_time, end_tiem] = this.query.start_time
- // 时间补全
- const timeAll = getAllDateCN(new Date(start_time), new Date(end_tiem))
- db.collection('uni-stat-error-result')
- .where(querystr)
- .field('count as temp_count, app_launch_count as temp_app_launch_count, start_time')
- .groupBy('start_time')
- .groupField('sum(temp_count) as count, sum(temp_app_launch_count) as app_launch_count')
- .orderBy('start_time', 'asc')
- .get({
- getCount: true
- })
- .then(res => {
- const {
- count,
- data
- } = res.result
- let dataAll = []
- timeAll.forEach(v => {
- let item = data.find(item => item.start_time === v)
- console.log(item);
- if (item) {
- dataAll.push(item)
- } else {
- dataAll.push({
- app_launch_count: 0,
- count: 0,
- start_time: v
- })
- }
- })
- const options = {
- categories: [],
- series: [{
- name: '暂无数据',
- data: []
- }]
- }
- if (this.chartTab === 'errorCount') {
- const countLine = options.series[0] = {
- name: '崩溃次数',
- data: []
- }
- const xAxis = options.categories
- for (const item of dataAll) {
- let date = item.start_time
- const x = formatDate(date, 'day')
- const countY = item.count
- xAxis.push(x)
- countLine.data.push(countY)
- }
- this.chartData = options
- } else {
- const rateLine = options.series[0] = {
- name: '崩溃率(%)',
- data: [],
- lineStyle: {
- color: '#EE6666',
- width: 1,
- },
- itemStyle: {
- borderWidth: 1,
- borderColor: '#EE6666',
- color: '#EE6666'
- },
- areaStyle: {
- color: {
- colorStops: [{
- offset: 0,
- color: '#EE6666', // 0% 处的颜色
- }, {
- offset: 1,
- color: '#FFFFFF' // 100% 处的颜色
- }]
- }
- }
- }
- const xAxis = options.categories
- for (const item of dataAll) {
- const {
- count,
- app_launch_count
- } = item
- let date = item.start_time
- const x = formatDate(date, 'day')
- console.log('---', x);
- xAxis.push(x)
- let y = count / app_launch_count
- y = !y ? 0 : y.toFixed(2)
- rateLine.data.push(y)
- }
- this.chartData = options
- }
- }).finally(() => {})
- }
- }
- }
- </script>
- <style>
- .flex-between {
- margin-bottom: 10px;
- display: flex;
- justify-content: space-between;
- align-items: center;
- }
- .uni-stat-panel {
- box-shadow: unset;
- border-bottom: 1px solid #eee;
- padding: 0;
- margin: 0 15px;
- }
- .uni-stat-tooltip-s {
- width: 160px;
- white-space: normal;
- }
- </style>
|