增加下载码展示

qrnn
youngS 3 years ago
parent 7932dde3b4
commit c8a69acb84
  1. 18
      fir_client/src/components/FirDownload.vue
  2. 3
      fir_client/src/components/FirHeader.vue
  3. 19
      fir_client/src/components/ShortDownload.vue
  4. 29
      fir_client/src/components/apps/FirAppInfosBase.vue
  5. 6
      fir_client/src/components/apps/FirApps.vue
  6. 19
      fir_client/src/components/user/FirSuperSignBase.vue
  7. 319
      fir_client/src/components/user/FirUserQrcode.vue
  8. 15
      fir_client/src/restful/index.js
  9. 7
      fir_client/src/router/index.js
  10. 3
      fir_ser/api/urls.py
  11. 33
      fir_ser/api/views/apps.py
  12. 3
      fir_ser/api/views/supersign.py

@ -1672,6 +1672,24 @@ button:focus {
}
}
@media only screen and (min-device-width: 768px) and (max-device-width: 1024px) and (orientation: landscape) {
#actions {
display: block
}
}
@media only screen and (min-device-width: 768px) and (max-device-width: 1024px) and (orientation: portrait) {
#actions {
display: block
}
}
@media only screen and (min-device-width: 1024px) and (max-device-width: 1366px) and (orientation: landscape) {
#actions {
display: block
}
}
#actions.type-android {
display: block
}

@ -59,6 +59,7 @@
</el-dropdown-item>
<el-dropdown-item command="myorder">订单信息</el-dropdown-item>
<el-dropdown-item command="contact">联系我们</el-dropdown-item>
<el-dropdown-item command="qrcode">下载码</el-dropdown-item>
<el-dropdown-item command="exit">退出</el-dropdown-item>
</el-dropdown-menu>
@ -137,6 +138,8 @@ export default {
this.$router.push({"name": 'FirUserAdvert'})
} else if (command === 'myorder') {
this.$router.push({"name": 'FirUserOrders'})
} else if (command === 'qrcode') {
this.$router.push({"name": 'FirUserQrcode'})
} else if (command === 'contact') {
this.$router.push({"name": 'FirContact'})
} else if (command === 'exit') {

@ -1780,6 +1780,7 @@ button:focus {
background: 0
}
@media only screen and (min-device-width: 320px) and (max-device-width: 480px) and (-webkit-min-device-pixel-ratio: 2) {
#actions {
display: block
@ -1816,6 +1817,24 @@ button:focus {
}
}
@media only screen and (min-device-width: 768px) and (max-device-width: 1024px) and (orientation: landscape) {
#actions {
display: block
}
}
@media only screen and (min-device-width: 768px) and (max-device-width: 1024px) and (orientation: portrait) {
#actions {
display: block
}
}
@media only screen and (min-device-width: 1024px) and (max-device-width: 1366px) and (orientation: landscape) {
#actions {
display: block
}
}
#actions.type-android {
display: block
}

@ -13,20 +13,6 @@
class="bundleid short"
>&nbsp;{{ short_full_url }}</span>
</el-tooltip>
<span>{{ master_release.release_type |getapptype }}</span>
<el-tooltip content="下载量" placement="top">
<span><i class="el-icon-cloudy"/><b class="short">{{ appinfos.count_hits }}</b></span>
</el-tooltip>
<span class="bundleid ng-binding">BundleID<b class="ng-binding">
<el-tooltip content="复制到剪切板" placement="top">
<span v-clipboard:copy="appinfos.bundle_id"
v-clipboard:success="copy_success"
class="bundleid short"
>&nbsp;{{ appinfos.bundle_id }}</span>
</el-tooltip>
</b></span>
<span class="version ng-scope">{{ master_release.minimum_os_version }}&nbsp; 或者高版本</span>
<span v-if="appinfos.issupersign" class="short ng-scope">超级签</span>
<el-popover
v-if="$store.state.userinfo&&$store.state.userinfo.role >1 &&$store.state.userinfo.qrcode_domain_name.length>3 "
@ -47,6 +33,21 @@
</el-popover>
<span>{{ master_release.release_type |getapptype }}</span>
<el-tooltip content="下载量" placement="top">
<span><i class="el-icon-cloudy"/><b class="short">{{ appinfos.count_hits }}</b></span>
</el-tooltip>
<span class="bundleid ng-binding">BundleID<b class="ng-binding">
<el-tooltip content="复制到剪切板" placement="top">
<span v-clipboard:copy="appinfos.bundle_id"
v-clipboard:success="copy_success"
class="bundleid short"
>&nbsp;{{ appinfos.bundle_id }}</span>
</el-tooltip>
</b></span>
<span class="version ng-scope">{{ master_release.minimum_os_version }}&nbsp; 或者高版本</span>
<span v-if="appinfos.issupersign" class="short ng-scope">超级签</span>
</div>
<div class="actions">
<el-button v-if="appinfos.status!==1" type="danger">该应用被封禁,请联系管理员</el-button>

@ -1063,9 +1063,6 @@ export default {
},
onUploadChange(file, fileList) {
if (fileList && fileList.length > 1) {
this.multiupload = true;
}
this.multiFileList = fileList;
// eslint-disable-next-line no-unused-vars
this.timer = setTimeout(data => {
@ -1075,6 +1072,9 @@ export default {
}
clearTimeout(this.timer);
}, 300);
if (fileList && fileList.length > 1) {
this.multiupload = true;
}
},
delApp() {
let loadingobj = this.$loading({

@ -744,7 +744,9 @@
<el-button icon="el-icon-search" type="primary" @click="handleCurrentChange(1)">
搜索
</el-button>
<el-link :underline="false" style="margin-top:10px;text-align: center;float: right"> 总消耗设备数
{{ app_rank_number }}
</el-link>
<el-table
v-loading="loading"
:data="app_rank_lists"
@ -825,6 +827,7 @@ export default {
app_devices_lists: [],
app_bill_lists: [],
app_rank_lists: [],
app_rank_number: 0,
app_bill_info_lists: [],
app_udid_lists: [],
activeName: "iosdeveloper",
@ -992,7 +995,18 @@ export default {
}
},
udidDeleteFun(scope) {
this.iosdevicesudidFun('DELETE', {id: scope.row.id, aid: scope.row.app_id}, scope);
this.$confirm('此操作会禁用该苹果开发者账户下面的该设备,可能会导致超级签包的闪退, 是否继续?', '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.iosdevicesudidFun('DELETE', {id: scope.row.id, aid: scope.row.app_id}, scope);
}).catch(() => {
this.$message({
type: 'info',
message: '已取消删除'
});
});
},
handleSizeChange(val) {
this.pagination.pagesize = val;
@ -1271,6 +1285,7 @@ export default {
if (data.code === 1000) {
this.app_rank_lists = data.data;
this.pagination.total = data.count;
this.app_rank_number = data.number
} else {
this.$message.error("信息获取失败了 " + data.msg);
}

@ -0,0 +1,319 @@
<template>
<el-main>
<el-row style="margin-bottom: 5px">
<el-col :span="3">
<el-radio-group v-model="searchfromtype">
<el-radio-button icon="el-icon-mobile-phone" label="android"><i
class="iconfont icon-android2"/>
</el-radio-button>
<el-radio-button label="ios"><i class="iconfont icon-ios"/>
</el-radio-button>
</el-radio-group>
</el-col>
<el-col :span="8">
<el-row>
<el-col :span="18">
<el-input
v-model="keysearch"
clearable
placeholder="请输入名称搜索"
@click="searchapps"
@keyup.enter.native="searchapps">
</el-input>
</el-col>
<el-col :span="6">
<el-button icon="el-icon-search" @click="searchFun">
</el-button>
</el-col>
</el-row>
</el-col>
<el-col :offset="5" :span="8">
<el-row>
<el-col :span="8">
<el-checkbox v-model="checkAll" :indeterminate="isIndeterminate" style="margin-top: 10px"
@change="handleCheckAllChange">当前页全选{{ checkedQrcodes.length }}
</el-checkbox>
</el-col>
<el-col :span="13" style="float: right">
<el-button @click="savemany">批量保存选中下载码到本地</el-button>
</el-col>
</el-row>
</el-col>
</el-row>
<el-row id="showqr">
<el-checkbox-group v-model="checkedQrcodes" @change="handlecheckedQrcodesChange">
<el-col v-for="appinfo in qrcode_info_list" :key="appinfo.app_id" :span="5" style="margin-left: 40px">
<el-checkbox :label="appinfo.app_id" border style="height: 320px">
<el-card :body-style="{textAlign:'center',padding:'8px'}" shadow="hover">
<vue-qr :callback="qrback"
:correctLevel="qrinfo.correctLevel" :logoCornerRadius="qrinfo.logoCornerRadius"
:logoScale="qrinfo.logoScale"
:logoSrc="appinfo.master_release.icon_url"
:margin="qrinfo.margin" :qid="appinfo.app_id"
:size="200"
:text="short_url(appinfo)">
</vue-qr>
<div style="margin: 5px 0 5px">
<i v-if="appinfo.type === 1" class=" type-icon iconfont icon-ios"/>
<i v-if="appinfo.type === 0" class="type-icon iconfont icon-android2"/>
&nbsp;
<span>{{ appinfo.name }}</span>
<div class="bottom clearfix">
<el-button plain size="small" type="primary" @click="go_download(appinfo)">预览</el-button>
<el-button plain size="small" type="primary" @click="save_qr(appinfo)">保存本地</el-button>
</div>
</div>
</el-card>
</el-checkbox>
</el-col>
</el-checkbox-group>
</el-row>
</el-main>
</template>
<script>
import {qrcodeinfo} from "@/restful";
import {getScrollHeight, getScrollTop, getUserInfoFun, getWindowHeight} from '@/utils'
import VueQr from 'vue-qr';
export default {
name: "FirUserQrcode",
components: {
VueQr
},
data() {
return {
has_next: false,
firstloadflag: true,
query: {'page': 1, size: 20},
searchflag: false,
keysearch: '',
searchfromtype: '',
loadingobj: null,
checkAll: false,
checkedQrcodes: [],
isIndeterminate: false,
allQrcodeAppid: [],
qrcode_info_list: [],
orgqrcode_info_list: [],
qrcode_img_info: {},
qrinfo: {
logoScale: 0.3,
logoCornerRadius: 12,
correctLevel: 3,
margin: 10
},
}
},
methods: {
auto_load() {
if (getScrollTop() + getWindowHeight() >= getScrollHeight()) {
if (this.has_next) { //
if (this.autoloadflag) {
this.autoloadflag = false;
if (this.qrcode_info_list.length === 0) {
this.query.page = 1;
} else {
this.query.page += 1;
}
if (this.searchfromtype !== '') {
this.query.type = this.searchfromtype;
}
this.UserQrcodeFun(this.query);
}
}
}
},
init_value() {
this.checkAll = false;
this.checkedQrcodes = [];
this.isIndeterminate = false;
this.qrcode_info_list = [];
this.orgqrcode_info_list = [];
this.checkedQrcodes = [];
this.allQrcodeAppid = [];
this.qrcode_img_info = {};
},
searchFun() {
let keysearch = this.keysearch.replace(/^\s+|\s+$/g, "");
if (keysearch === '') {
this.searchflag = false;
this.init_value();
this.query.page = 1;
if (this.searchfromtype) {
this.UserQrcodeFun({"type": this.searchfromtype});
} else {
this.UserQrcodeFun({});
}
} else {
this.searchflag = true
}
if (this.searchflag) {
this.qrcode_info_list = [];
this.orgqrcode_info_list = [];
if (this.searchfromtype) {
this.UserQrcodeFun({"type": this.searchfromtype, 'page': 1, size: 999});
} else {
this.UserQrcodeFun({'page': 1, size: 999});
}
}
},
searchapps() {
let keysearch = this.keysearch.replace(/^\s+|\s+$/g, "");
let newqrcode_info_list = [];
for (let i = 0; i < this.orgqrcode_info_list.length; i++) {
if (this.orgqrcode_info_list[i].name.search(keysearch) >= 0) {
newqrcode_info_list.push(this.orgqrcode_info_list[i]);
}
}
if (keysearch === "") {
this.qrcode_info_list = this.orgqrcode_info_list.slice();
} else {
this.qrcode_info_list = newqrcode_info_list.slice();
}
},
get_appinfo_from_appid(app_id) {
for (let i = 0; i < this.qrcode_info_list.length; i++) {
if (app_id === this.qrcode_info_list[i].app_id) {
return this.qrcode_info_list[i];
}
}
},
savemany() {
for (let i = 0; i < this.checkedQrcodes.length; i++) {
this.save_qr(this.get_appinfo_from_appid(this.allQrcodeAppid[i]))
}
},
handleCheckAllChange(val) {
this.checkedQrcodes = val ? this.allQrcodeAppid : [];
this.isIndeterminate = false;
},
handlecheckedQrcodesChange(value) {
let checkedCount = value.length;
this.checkAll = checkedCount === this.allQrcodeAppid.length;
this.isIndeterminate = checkedCount > 0 && checkedCount < this.allQrcodeAppid.length;
},
go_download(appinfo) {
let routeData = this.$router.resolve({name: 'FirDownload', params: {short: appinfo.short}});
let p_url = routeData.href;
if (appinfo.preview_url && appinfo.preview_url.length > 6) {
p_url = appinfo.preview_url + p_url
}
window.open(p_url, '_blank', '');
},
qrback(dataUrl, id) {
this.qrcode_img_info[id] = dataUrl;
this.allQrcodeAppid.push(id);
},
short_url(appinfo) {
const userinfo = this.$store.state.userinfo;
if (userinfo.qrcode_domain_name && userinfo.qrcode_domain_name.length > 3) {
return 'http://' + userinfo.qrcode_domain_name + '/' + appinfo.short;
}
return appinfo.preview_url;
},
save_qr(appinfo) {
let dtype = "I";
if (appinfo.master_release.release_type === 0) {
dtype = "A";
}
let a = document.createElement('a');
//
a.download = appinfo.name + '_' + dtype + "_下载码";
a.href = this.qrcode_img_info[appinfo.app_id];
//
a.dispatchEvent(new MouseEvent('click'))
},
UserQrcodeFun(params) {
this.loadingobj = this.$loading({
lock: true,
text: '加载中',
spinner: 'el-icon-loading',
// background: 'rgba(0, 0, 0, 0.7)'
});
qrcodeinfo(data => {
if (data.code === 1000) {
if (this.firstloadflag) {
window.addEventListener('scroll', this.auto_load);
this.firstloadflag = false
}
this.autoloadflag = true;
this.qrcode_info_list = this.qrcode_info_list.concat(data.data);
this.has_next = data.has_next;
this.orgqrcode_info_list = this.qrcode_info_list.slice(); //
this.hdata = data.hdata;
this.searchapps();
} else {
this.$message.error("信息获取失败")
}
this.loadingobj.close();
}, {methods: 'GET', data: params})
},
}, mounted() {
getUserInfoFun(this);
this.UserQrcodeFun()
},
watch: {
// eslint-disable-next-line no-unused-vars
keysearch: function (val, oldVal) {
// this.searchapps()
let keysearch = this.keysearch.replace(/^\s+|\s+$/g, "");
if (keysearch === "") {
this.searchFun()
}
},
// eslint-disable-next-line no-unused-vars
searchfromtype: function (val, oldVal) {
this.qrcode_info_list = [];
this.query.page = 1;
// this.keysearch='';
this.searchFun();
// this.getappsFun({"type": this.searchfromtype});
},
}
}
</script>
<style scoped>
.el-main {
margin: 20px auto 100px;
width: 1166px;
position: relative;
padding-bottom: 1px;
color: #9b9b9b;
-webkit-font-smoothing: antialiased;
border-radius: 1%;
}
.bottom {
margin-top: 10px;
}
.clearfix:before,
.clearfix:after {
display: table;
content: "";
}
.clearfix:after {
clear: both
}
#showqr /deep/ .el-checkbox__input {
display: none;
}
</style>

@ -709,6 +709,21 @@ export function advertinfo(callBack, params, load = true) {
);
}
/**下载码大屏 */
export function qrcodeinfo(callBack, params, load = true) {
getData(
params.methods,
USERSEVER + '/qrcode',
params.data,
data => {
callBack(data);
},
load,
true,
true
);
}
/**签名账单 */
export function DeviceBillInfo(callBack, params, load = true) {
getData(

@ -164,6 +164,12 @@ const router = new VueRouter({
meta: {label: '自定义广告'},
component: () => import("@/components/user/FirUserAdvert"),
},
{
path: 'qrcode',
name: 'FirUserQrcode',
meta: {label: '下载码'},
component: () => import("@/components/user/FirUserQrcode"),
},
{
path: 'supersign-help',
name: 'FirSuperSignHelp',
@ -194,6 +200,7 @@ const router = new VueRouter({
path: '/:short',
name: 'FirDownload',
component: () => import("@/components/FirDownload"),
// component: () => import("@/components/ShortDownload"),
},
]

@ -16,7 +16,7 @@ Including another URLconf
from django.urls import re_path
from api.views.advert import UserAdInfoView
from api.views.apps import AppsView, AppInfoView, AppReleaseInfoView
from api.views.apps import AppsView, AppInfoView, AppReleaseInfoView, AppsQrcodeShowView
from api.views.domain import DomainCnameView, DomainInfoView
from api.views.download import ShortDownloadView
from api.views.login import LoginView, UserInfoView, RegistView, AuthorizationView, ChangeAuthorizationView, \
@ -55,6 +55,7 @@ urlpatterns = [
re_path("^analyse$", AppAnalyseView.as_view()),
re_path("^advert$", UserAdInfoView.as_view()),
re_path("^report$", ReportView.as_view()),
re_path("^qrcode$", AppsQrcodeShowView.as_view()),
re_path("^supersign/developer$", DeveloperView.as_view()),
re_path("^supersign/devices$", SuperSignUsedView.as_view()),
re_path("^supersign/udid$", AppUDIDUsedView.as_view()),

@ -98,8 +98,6 @@ class AppsView(APIView):
app_serializer = AppsListSerializer(app_page_serializer, many=True, context={"storage": Storage(request.user)})
res.userinfo = {}
res.has_next = {}
res.data = app_serializer.data
res.has_next = page_obj.page.has_next()
return Response(res.dict)
@ -372,3 +370,34 @@ class AppReleaseInfoView(APIView):
res = get_release_apps(request, res, app_serializer, app_obj, Storage(request.user))
return Response(res.dict)
class AppsQrcodeShowView(APIView):
authentication_classes = [ExpiringTokenAuthentication, ]
def get(self, request):
app_type = request.query_params.get("type", None)
act_type = request.query_params.get("act", None)
res = BaseResponse()
if app_type == "android":
filter_data = {"user_id": request.user, "type": 0}
elif app_type == "ios":
filter_data = {"user_id": request.user, "type": 1}
else:
filter_data = {"user_id": request.user}
if act_type == "combo":
filter_data["has_combo"] = None
apps_obj = Apps.objects.filter(**filter_data)
page_obj = AppsPageNumber()
app_page_serializer = page_obj.paginate_queryset(queryset=apps_obj.order_by("-updated_time"), request=request,
view=self)
app_serializer = AppsListSerializer(app_page_serializer, many=True, context={"storage": Storage(request.user)})
res.data = app_serializer.data
res.has_next = page_obj.page.has_next()
return Response(res.dict)

@ -6,7 +6,7 @@
import datetime
import logging
from django.db.models import Count, Q
from django.db.models import Count, Q, Sum
from django.http.response import FileResponse
from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response
@ -443,6 +443,7 @@ class DeviceUsedRankInfoView(APIView):
app_used_sign_objs = app_used_sign_objs.values('app_id__app_id', 'app_id__name', 'app_id__bundle_id').annotate(
count=Count('app_id__app_id')).order_by('-count')
res.count = app_used_sign_objs.count()
res.number = app_used_sign_objs.aggregate(Sum('count')).get('count__sum')
app_used_sign_infos = page_obj.paginate_queryset(queryset=app_used_sign_objs,
request=request, view=self)

Loading…
Cancel
Save