现有系统显示超级管理员的所有剧本的总数的统计:

希望优化为:
增加分组名和分组总数,以及所有的总数
且每个个数都支持点击,跳转到全部剧本列表中
先去要能从Django后端获取到
所有的Script的用户的组
每个组的组员,以及组员的剧本个数(已通过,未通过)
以及每个组的加起来的总数
现有的api接口:
return request(`${apiPrefix}/scripts/user_scripts_count/?${qsParamStr}`, {后端代码是:
def user_scripts_count(self, request):
"""
组下面的成员的 scripts 统计
全部用户的 scripts 统计
url 参数
function_group 传 id
"""
logger.info("user_scripts_count: request=%s", request)
queryParams = request.query_params
logger.info("queryParams=%s", queryParams)
function_group_id = queryParams.get('function_group_id', None)
logger.info("function_group_id=%s", function_group_id)
if function_group_id:
function_group = FunctionGroup.objects.get(pk=function_group_id)
logger.info("function_group=%s", function_group)
user_queryset = function_group.members.all()
logger.info("user_queryset=%s", user_queryset)
else:
user_queryset = User.objects.filter(is_active=True)
user_names = list(user_queryset.values_list('username', flat=True))
user_ids = list(user_queryset.values_list('id', flat=True))
all_user_scripts_count = [i.user_scripts.filter(Q(version=1)).count() for i in user_queryset]
published_user_scripts_count = [i.user_scripts.filter(Q(publish_status='1')).count() for i in user_queryset]
scriptSummaryInfoList = []
for index, value in enumerate(user_names):
item = {}
item['username'] = value
item['user_id'] = user_ids[index]
item['all_scripts_count'] = all_user_scripts_count[index]
item['published_scripts_count'] = published_user_scripts_count[index]
if int(published_user_scripts_count[index]) == 0:
item['published_ratio'] = '0%'
else:
item['published_ratio'] = str(float('%.2f' % (100 * float(published_user_scripts_count[index])/float(all_user_scripts_count[index])))) + '%'
scriptSummaryInfoList.append(item)
logger.info("scriptSummaryInfoList=%s", scriptSummaryInfoList)
scriptSummaryInfoListLen = len(scriptSummaryInfoList)
logger.info("scriptSummaryInfoListLen=%s", scriptSummaryInfoListLen)
return Response(
{
'results': scriptSummaryInfoList,
'count': scriptSummaryInfoListLen
},
status=status.HTTP_200_OK
)所返回的数据是:
[{
'username': 'lxxx',
'user_id': UUID('1ed0a0d8-2abb-401d-9956-1493714200e3'),
'all_scripts_count': 7,
'published_scripts_count': 1,
'published_ratio': '14.29%'
}, {
'username': 'xxx',
'user_id': UUID('44c3de7f-a0e4-4a62-8f64-720044ad6d03'),
'all_scripts_count': 101,
'published_scripts_count': 0,
'published_ratio': '0%'
}, {
'username': 'irene',
'user_id': UUID('4c271812-d36e-4585-a1af-bd7ea6c6d449'),
'all_scripts_count': 24,
'published_scripts_count': 0,
'published_ratio': '0%'
}, {
'username': 'xxx',
'user_id': UUID('4c875831-f6d4-446a-8acb-9cd2fac75ac5'),
'all_scripts_count': 102,
'published_scripts_count': 0,
'published_ratio': '0%'
}, {
'username': 'Maggie',
'user_id': UUID('78c7b3c4-8a7c-4f1f-96ce-dc469fedad40'),
'all_scripts_count': 121,
'published_scripts_count': 121,
'published_ratio': '100.0%'
}, {
'username': 'xxx',
'user_id': UUID('7dff71b1-e48f-4b94-ade3-c832742a8ed7'),
'all_scripts_count': 26,
'published_scripts_count': 0,
'published_ratio': '0%'
}, {
'username': 'xxx',
'user_id': UUID('7e8832bc-c02d-4bef-a303-ed9488fb654a'),
'all_scripts_count': 1,
'published_scripts_count': 1,
'published_ratio': '100.0%'
}, {
'username': 'victor',
'user_id': UUID('bb6d17b3-4fbd-431a-bcb1-0f134ca8509c'),
'all_scripts_count': 0,
'published_scripts_count': 0,
'published_ratio': '0%'
}, {
'username': 'xxx',
'user_id': UUID('face7292-6b42-4858-a092-7b771512cba7'),
'all_scripts_count': 124,
'published_scripts_count': 123,
'published_ratio': '99.19%'
}]而此处,针对于全部的用户的列表,需要去调整返回数据的格式
希望前后端配置,显示这种的效果:

所以现在问题转化为:
【已解决】Antd Pro中如何动态自定义生成带合并单元格的表头
然后继续去优化自动生成Table的columns和data
以及优化从Django后台返回的数据的格式
期间:
【已解决】js中float浮点数转换为百分比后保留1位小数再转换为字符串
【总结】
此处已经可以通过:
前端reactjs:
import React, { PureComponent, Fragment } from 'react';
import { connect } from 'dva';
import {
Card,
Form,
Table,
} from 'antd';
import { routerRedux } from 'dva/router';
// import SimpleTable from 'components/SimpleTable';
import PageHeaderLayout from '../../layouts/PageHeaderLayout';
import styles from '../List/TableList.less';
import { isEmptyObj } from '../../utils/utils';
// @connect(({ script, loading }) => ({
@connect(({ script}) => ({
script,
// loading: loading.models.script,
}))
@Form.create()
export default class AllUserScriptCount extends PureComponent {
state = {
formValues: {},
currentPage: 1,
pageSize: 20,
};
componentDidMount() {
console.log("AllUserScriptCount componentDidMount")
this.props.dispatch({
type: 'script/fetchUserScriptsCount',
});
}
componentWillReceiveProps(nextProps){
console.log("AllUserScriptCount componentWillReceiveProps: nextProps=", nextProps)
}
handleStandardTableChange = (pagination) => {
const { dispatch } = this.props;
const { formValues } = this.state;
this.setState({
currentPage: pagination.current,
pageSize: pagination.pageSize,
});
const params = {
page: pagination.current,
page_size: pagination.pageSize,
...formValues,
};
dispatch({
type: 'script/fetchUserScriptsCount',
payload: params,
});
};
gotoAllScriptList = (searchPara) => {
console.log("gotoAllScriptList: searchPara=", searchPara)
const { dispatch } = this.props;
dispatch(routerRedux.push({
pathname: '/script/all-script-list',
// search: `?author_id=${authorId}`,
search: searchPara,
}));
};
floatToPercentage = (value, record, index) => {
console.log(`value=${value}, index=${index}`, "record=", record)
// const percentValue = (value * 100).toFixed(1)
const percentValue = (value * 100).toFixed(0)
console.log("percentValue=", percentValue)
return `${percentValue}%`
}
scriptListLink = (value, searchPara) => {
console.log(`value=${value}, searchPara=${searchPara}`)
return <a href="javascript:;" onClick={() => this.gotoAllScriptList(searchPara)} >{value}</a>
}
// merge `mergeRowCount` rows start from `mergeRowStartIdx`
mergeRows = (value, record, index, searchPara) => {
console.log(`value=${value}, index=${index}, searchPara=${searchPara}`, "record=", record)
const obj = {
// children: value,
children: this.scriptListLink(value, searchPara),
props: {},
}
const mergeRowCount = record.viewInfo.mergeRowCount
const mergeRowStartIdx = record.viewInfo.mergeRowStartIdx
const endRowIdx = mergeRowStartIdx + (mergeRowCount - 1)
if (mergeRowCount > 0) {
if (index === mergeRowStartIdx) {
obj.props.rowSpan = mergeRowCount
} else if ((index >= (mergeRowStartIdx+1)) && (index <= endRowIdx)) {
// other row are merged into first row cell
obj.props.rowSpan = 0
}
}
console.log("obj=", obj)
return obj
}
searchGroupOwner = (value, record, index) => {
const searchPara = `?author_id=${record.groupOwner.userId}`
return this.scriptListLink(value, searchPara)
}
searchMemberUser = (value, record, index) => {
const searchPara = `?author_id=${record.memberInfo.userId}`
console.log("searchMemberUser: searchPara=", searchPara)
return this.scriptListLink(value, searchPara)
}
searchMemberUserPublished = (value, record, index) => {
const searchPara = `?author_id=${record.memberInfo.userId}&publish_status=1`
return this.scriptListLink(value, searchPara)
}
searchMemberUserUnpublished = (value, record, index) => {
const searchPara = `?author_id=${record.memberInfo.userId}&publish_status=0`
return this.scriptListLink(value, searchPara)
}
mergeRowsAndSearchGroup = (value, record, index) => {
const searchPara = `?group_id=${record.groupId}`
console.log("mergeRowsAndSearchGroup: searchPara=", searchPara)
return this.mergeRows(value, record, index, searchPara)
}
mergeRowsAndSearchUser = (value, record, index, authorId) => {
const searchPara = `?author_id=${authorId}`
return this.mergeRows(value, record, index, searchPara)
}
mergeRowsAndSearchMemberUser = (value, record, index) => {
return this.mergeRowsAndSearchUser(value, record, index, record.memberInfo.userId)
}
mergeRowsAndSearchGroupOwner = (value, record, index) => {
return this.mergeRowsAndSearchUser(value, record, index, record.groupOwner.userId)
}
render() {
const { script: { userScriptsCount }, loading } = this.props;
const { currentPage, pageSize } = this.state;
console.log("currentPage=", currentPage, ",pageSize=", pageSize)
const columns = [
{
title: '序号',
// dataIndex: 'number',
rowKey: 'number',
render(value, record, index) {
const number = (currentPage - 1) * pageSize
return number + index + 1;
},
},
{
title: '剧本组',
rowKey: 'scriptGroup',
children: [
{
title: '组名',
dataIndex: 'groupName',
rowKey: 'groupName',
width: 140,
render: this.mergeRowsAndSearchGroup,
},
{
title: '组长',
dataIndex: 'groupOwner.username',
rowKey: 'groupOwner.username',
render: this.mergeRowsAndSearchGroupOwner,
},
{
title: '剧本总数',
dataIndex: 'totalScriptCount',
rowKey: 'totalScriptCount',
width: 90,
render: this.mergeRowsAndSearchGroup,
},
],
},
{
title: '组员',
rowKey: 'memberInfo',
dataIndex: 'memberInfo',
children: [
{
title: '用户名',
dataIndex: 'memberInfo.username',
rowKey: 'memberInfo.username',
render: this.searchMemberUser,
},
{
title: '剧本总数',
dataIndex: 'memberInfo.totalScriptCount',
rowKey: 'memberInfo.totalScriptCount',
render: this.searchMemberUser,
},
{
title: '已通过',
dataIndex: 'memberInfo.publishedScriptCount',
rowKey: 'memberInfo.publishedScriptCount',
render: this.searchMemberUserPublished,
},
{
title: '未通过',
dataIndex: 'memberInfo.unpublishedScriptCount',
rowKey: 'memberInfo.unpublishedScriptCount',
render: this.searchMemberUserUnpublished,
},
{
title: '通过率',
dataIndex: 'memberInfo.publishedScriptRatio',
rowKey: 'memberInfo.publishedScriptRatio',
render: this.floatToPercentage,
},
// {
// title: '未通过率',
// dataIndex: 'memberInfo.unpublishedScriptRatio',
// rowKey: 'memberInfo.unpublishedScriptRatio',
// render: this.floatToPercentage,
// },
],
},
]
let data = []
// let data = [
// {
// key: '1',
// viewInfo: {
// "mergeRowCount": 3,
// "mergeRowStartIdx": 0,
// },
// // number: 1,
// groupId: "5",
// groupName: 'Maggie剧本组',
// groupOwner: {
// "userId": "78c7b3c48a7c4f1f96cedc469fedad40",
// "username": 'Maggie',
// },
// totalScriptCount: 248,
// memberInfo : {
// 'username': 'Maggie',
// 'userId': "78c7b3c48a7c4f1f96cedc469fedad40",
// 'totalScriptCount': 121,
// 'publishedScriptCount': 100,
// "unpublishedScriptCount": 0,
// 'publishedScriptRatio': 0.8264,
// // 'unpublishedScriptRatio': 0.1735,
// },
// },
// {
// key: '2',
// viewInfo: {
// "mergeRowCount": 3,
// "mergeRowStartIdx": 0,
// },
// // number: 2,
// groupId: "5",
// groupName: 'Maggie剧本组',
// groupOwner: {
// "userId": "78c7b3c48a7c4f1f96cedc469fedad40",
// "username": 'Maggie',
// },
// totalScriptCount: 248,
// memberInfo : {
// 'username': 'xxx',
// 'userId': "44c3de7fa0e44a628f64720044ad6d03",
// 'totalScriptCount': 101,
// 'publishedScriptCount': 101,
// "unpublishedScriptCount": 0,
// 'publishedScriptRatio': 1.00,
// // 'unpublishedScriptRatio': 0.0,
// },
// },
// {
// key: '3',
// viewInfo: {
// "mergeRowCount": 3,
// "mergeRowStartIdx": 0,
// },
// // number: 3,
// groupId: "5",
// groupName: 'Maggie剧本组',
// groupOwner: {
// "userId": "78c7b3c48a7c4f1f96cedc469fedad40",
// "username": 'Maggie',
// },
// totalScriptCount: 248,
// memberInfo : {
// 'username': 'xxx',
// 'userId': "7dff71b1e48f4b94ade3c832742a8ed7",
// 'totalScriptCount': 26,
// 'publishedScriptCount': 26,
// "unpublishedScriptCount": 0,
// 'publishedScriptRatio': 0.0,
// // 'unpublishedScriptRatio': 1.00,
// },
// },
// ];
console.log("userScriptsCount=", userScriptsCount)
if (isEmptyObj(userScriptsCount)){
data = []
} else {
data = userScriptsCount
}
console.log("table data=", data)
return (
<PageHeaderLayout title="全部用户剧本统计">
<Card bordered={false}>
<div className={styles.tableList}>
{/* <SimpleTable
loading={loading}
data={userScriptsCount}
columns={columns}
/> */}
<Table
bordered
columns={columns}
dataSource={data}
/>
</div>
</Card>
</PageHeaderLayout>
);
}
}
后端Django:
@list_route(
methods=['get'],
url_path='user_scripts_count',
url_name='user_scripts_count',
permission_classes=[
IsAuthenticated, IsUserScriptFunctionGroup
])
def user_scripts_count(self, request):
"""
组下面的成员的 scripts 统计
全部用户的 scripts 统计
url 参数
function_group 传 id
"""
logger.info("user_scripts_count: request=%s", request)
queryParams = request.query_params
logger.info("queryParams=%s", queryParams)
function_group_id = queryParams.get('function_group_id', None)
logger.info("function_group_id=%s", function_group_id)
userScripSummaryList = []
functionGroupList = []
if function_group_id:
# singleFunctionGroup = FunctionGroup.objects.get(pk=function_group_id)
singleFunctionGroup = FunctionGroup.objects.get(pk=function_group_id, function=FunctionGroup.TYPE_VALUE_SCRIPT)
logger.info("singleFunctionGroup=%s", singleFunctionGroup)
# user_queryset = singleFunctionGroup.members.all()
# logger.info("user_queryset=%s", user_queryset)
functionGroupList.append(singleFunctionGroup)
else:
# user_queryset = User.objects.filter(is_active=True)
allScriptGroup = FunctionGroup.objects.filter(function=FunctionGroup.TYPE_VALUE_SCRIPT)
logger.info("allScriptGroup=%s", allScriptGroup)
functionGroupList = allScriptGroup
logger.info("functionGroupList=%s", functionGroupList)
# # old code
# user_names = list(user_queryset.values_list('username', flat=True))
# user_ids = list(user_queryset.values_list('id', flat=True))
# all_user_scripts_count = [i.user_scripts.filter(Q(version=1)).count() for i in user_queryset]
# published_user_scripts_count = [i.user_scripts.filter(Q(publish_status='1')).count() for i in user_queryset]
# scriptSummaryInfoList = []
# for index, value in enumerate(user_names):
# item = {}
# item['username'] = value
# item['user_id'] = user_ids[index]
# item['all_scripts_count'] = all_user_scripts_count[index]
# item['published_scripts_count'] = published_user_scripts_count[index]
# if int(published_user_scripts_count[index]) == 0:
# item['published_ratio'] = '0%'
# else:
# item['published_ratio'] = str(float('%.2f' % (100 * float(published_user_scripts_count[index])/float(all_user_scripts_count[index])))) + '%'
# scriptSummaryInfoList.append(item)
# logger.info("scriptSummaryInfoList=%s", scriptSummaryInfoList)
# scriptSummaryInfoListLen = len(scriptSummaryInfoList)
# logger.info("scriptSummaryInfoListLen=%s", scriptSummaryInfoListLen)
# userScripSummaryList = {
# 'results': scriptSummaryInfoList,
# 'count': scriptSummaryInfoListLen
# }
# allScriptGroupSerializer = FunctionGroupSerializer(allScriptGroup, many=True)
# allScriptGroupSerializeData = allScriptGroupSerializer.data
# logger.info("allScriptGroupSerializeData=%s", allScriptGroupSerializeData)
# userScripSummaryList = allScriptGroupSerializeData
allGroupAllScriptCount = 0
allGroupPublishedScriptCount = 0
allGroupUnpublishedScriptCount = 0
allGroupPublishedScriptRatio = 0.0
# allGroupUnpublishedScriptRatio = 0.0
curRowIdx = 0
for curGroupIdx, curFunctionGroup in enumerate(functionGroupList):
logger.info("=== [%d] curFunctionGroup=%s", curGroupIdx, curFunctionGroup)
curGroupAllMembers = curFunctionGroup.members.all()
logger.info("curGroupAllMembers=%s", curGroupAllMembers)
groupMemerCount = len(curGroupAllMembers)
logger.info("groupMemerCount=%s", groupMemerCount)
groupOwner = curFunctionGroup.owner
logger.info("groupOwner=%s", groupOwner)
# update group total script count
curGroupTotalScriptCount = 0
for eachMember in curGroupAllMembers:
curUserTotalScriptCount = eachMember.user_scripts.filter(Q(version=1)).count()
curGroupTotalScriptCount += curUserTotalScriptCount
logger.info("curGroupTotalScriptCount=%s", curGroupTotalScriptCount)
allGroupAllScriptCount += curGroupTotalScriptCount
curGroupDict = {
"viewInfo": {
"mergeRowCount": groupMemerCount,
"mergeRowStartIdx": curRowIdx,
},
"groupId": curFunctionGroup.id,
"groupName": curFunctionGroup.name,
"groupOwner": {
"userId": groupOwner.id,
"username": groupOwner.username,
},
"totalScriptCount": curGroupTotalScriptCount,
"memberInfo" : {}
}
logger.info("curGroupDict=%s", curGroupDict)
for curMemberIdx, curMember in enumerate(curGroupAllMembers):
logger.info("curMemberIdx=%s,curMember=%s", curMemberIdx, curMember)
curUserAllScripts = curMember.user_scripts
logger.info("curUserAllScripts=%s", curUserAllScripts)
totalScriptCount = curUserAllScripts.filter(Q(version=1)).count()
publishedScriptCount = curUserAllScripts.filter(Q(publish_status=Script.PUBLISH_STATUS_PUBLISHED)).count()
unpublishedScriptCount = curUserAllScripts.filter(Q(publish_status=Script.PUBLISH_STATUS_UNPUBLISHED)).count()
allGroupPublishedScriptCount += publishedScriptCount
allGroupUnpublishedScriptCount += unpublishedScriptCount
if (totalScriptCount > 0):
publishedScriptRatio = float(publishedScriptCount)/float(totalScriptCount)
# unpublishedScriptRatio = float(unpublishedScriptCount)/float(totalScriptCount)
else:
publishedScriptRatio = 0.0
# unpublishedScriptRatio = 0.0
curUserDict = copy.deepcopy(curGroupDict)
curUserDict["memberInfo"] = {
'username': curMember.username,
'userId': curMember.id,
'totalScriptCount': totalScriptCount,
'publishedScriptCount': publishedScriptCount,
"unpublishedScriptCount": unpublishedScriptCount,
'publishedScriptRatio': publishedScriptRatio,
# 'unpublishedScriptRatio': unpublishedScriptRatio,
}
curUserDict["key"] = curRowIdx
logger.info("--- [%d] curUserDict=%s", curRowIdx, curUserDict)
userScripSummaryList.append(curUserDict)
curRowIdx += 1
# generate last total summary item
if (allGroupAllScriptCount > 0):
allGroupPublishedScriptRatio = float(allGroupPublishedScriptCount)/float(allGroupAllScriptCount)
# allGroupUnpublishedScriptRatio = float(allGroupUnpublishedScriptCount)/float(allGroupAllScriptCount)
else:
allGroupPublishedScriptRatio = 0.0
# allGroupUnpublishedScriptRatio = 0.0
lastTotalDict = {
"viewInfo": {
"mergeRowCount": 0,
"mergeRowStartIdx": 0,
},
"groupId": "",
"groupName": "总计",
"groupOwner": {
"userId": "",
"username": "",
},
"totalScriptCount": allGroupAllScriptCount,
"memberInfo" : {
'username': "",
'userId': "",
'totalScriptCount': allGroupAllScriptCount,
'publishedScriptCount': allGroupPublishedScriptCount,
"unpublishedScriptCount": allGroupUnpublishedScriptCount,
'publishedScriptRatio': allGroupPublishedScriptRatio,
# 'unpublishedScriptRatio': allGroupUnpublishedScriptRatio,
}
}
userScripSummaryList.append(lastTotalDict)
logger.info("userScripSummaryList=%s", userScripSummaryList)
userScripSummaryCount = len(userScripSummaryList)
logger.info("userScripSummaryCount=%s", userScripSummaryCount)
return Response(userScripSummaryList, status=status.HTTP_200_OK)实现了全部剧本统计的页面:

且点击对应的内容,可以跳转到全部剧本列表页且传递对应参数:

【后记 180803】
折腾:
【已解决】Django出错:TypeError: object of type ‘ManyRelatedManager’ has no len()
后,更加了解了,many to many的manager,直接打印的值是None
但是可以用queryset去获取自己需要的值的
回头再去看上面的代码:
curUserAllScripts = curMember.user_scripts
logger.info("curUserAllScripts=%s", curUserAllScripts)
totalScriptCount = curUserAllScripts.filter(Q(version=1)).count()
publishedScriptCount = curUserAllScripts.filter(Q(publish_status=Script.PUBLISH_STATUS_PUBLISHED)).count()
unpublishedScriptCount = curUserAllScripts.filter(Q(publish_status=Script.PUBLISH_STATUS_UNPUBLISHED)).count()通过调试发现:
RelatedManager
的问题
且log中都是:
curUserAllScripts=script.Script.None
之类的None的值,而不是我们希望看到的值
才更加明白,其实curUserAllScripts也是个manager
其中定义是:
class Script(TimestampedModel): author = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.PROTECT, related_name='user_scripts', null=False, blank=False)
通过打印type:
type(curUserAllScripts)=<class 'django.db.models.fields.related_descriptors.create_reverse_many_to_one_manager.<locals>.RelatedManager'>
是属于many to one的类型
所以应该去改名:
curUserAllScriptsRelatedManager = curMember.user_scripts
logger.info("type(curUserAllScriptsRelatedManager)=%s", type(curUserAllScriptsRelatedManager))
logger.info("curUserAllScriptsRelatedManager=%s", curUserAllScriptsRelatedManager)
totalScriptCount = curUserAllScriptsRelatedManager.filter(Q(version=1)).count()
publishedScriptCount = curUserAllScriptsRelatedManager.filter(Q(publish_status=Script.PUBLISH_STATUS_PUBLISHED)).count()
unpublishedScriptCount = curUserAllScriptsRelatedManager.filter(Q(publish_status=Script.PUBLISH_STATUS_UNPUBLISHED)).count()转载请注明:在路上 » 【已解决】剧本编写系统中优化超级管理员的全部剧本统计页