当前位置:   article > 正文

计算机毕业设计选题推荐-办公管理系统-Python项目实战_python的办公室审批管理

python的办公室审批管理

作者主页:IT研究室✨
个人简介:曾从事计算机专业培训教学,擅长Java、Python、微信小程序、Golang、安卓Android等。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。
☑文末获取源码☑
精彩专栏推荐⬇⬇⬇
Java项目
Python项目
安卓项目
微信小程序项目

一、前言

随着企业规模的扩大和业务复杂性的增加,办公管理系统对于企业的运营至关重要。本课题基于角色权限管理的办公管理系统,旨在解决企业运营过程中的一系列管理问题,提高工作效率,保障信息安全,提升企业运营的规范性。

当前,许多企业的办公管理系统存在以下问题:
缺乏统一的角色权限管理,导致权限分配混乱,容易引发信息安全问题;
系统功能模块独立,缺乏协同性,无法满足企业复杂多变的业务需求;
系统可扩展性差,难以适应企业快速发展的需要;
缺乏智能化决策支持,难以实现数据驱动的管理决策。

本课题基于角色权限管理的办公管理系统,能够实现以下功能:
员工管理:支持员工信息的录入、修改、删除等操作,支持员工账号的创建和权限分配;
部门信息管理:维护企业组织架构,记录部门相关信息;
通知公告管理:发布通知公告,实时传递企业最新消息;
员工打卡管理:记录员工的考勤情况,便于薪资计算和管理;
员工请假管理:支持员工请假申请和审批,提高工作效率;
员工档案管理:存储员工档案信息,方便查询和管理;
下派任务管理:支持任务的下达、分配、跟踪和反馈,提高工作效率;
完成任务管理:记录员工完成任务的情况,便于考核和评价;
员工评价管理:对员工工作表现进行评价,激励员工提升工作效率和质量;
员工工资管理:根据员工考勤、工作表现等因素计算员工工资。

本课题的研究意义在于:
通过建立统一的角色权限管理体系,解决权限分配混乱的信息安全问题;
通过整合各个功能模块,实现各模块之间的协同工作,满足企业复杂多变的业务需求;
通过优化系统架构和算法设计,提高系统的可扩展性和性能,适应企业快速发展的需要;
通过引入智能化决策支持功能,实现数据驱动的管理决策,提高企业的决策效率和准确性。

二、开发环境

  • 开发语言:Python
  • 数据库:MySQL
  • 系统架构:B/S
  • 后端:Django
  • 前端:Vue

三、系统功能模块

  • 角色:员工、管理员
  • 功能:
    员工
    通知公告、员工打卡管理、员工请假管理、员工档案管理、下派任务管理、完成任务管理、员工评价管理、员工工资管理;
    管理员
    员工管理、部门信息管理、通知公告管理、员工打卡管理、员工请假管理、员工档案管理、下派任务管理、完成任务管理、员工评价管理、员工工资管理。

四、系统界面展示

  • 办公管理系统-界面展示:
    办公管理系统-员工打卡
    办公管理系统-员工请假
    办公管理系统-员工档案信息-员工
    办公管理系统-下派任务管理-员工
    办公管理系统-完成任务管理-员工
    办公管理系统-员工打卡管理-管理员
    办公管理系统-员工请假管理-管理员
    办公管理系统-下派任务管理-管理员
    办公管理系统-完成任务管理-管理员
    办公管理系统-员工工资管理-管理员

五、代码参考

  • Python项目实战-代码参考:
class ValueListAdmin(generic.BOAdmin):
    CODE_NUMBER_WIDTH = 3
    CODE_PREFIX = 'S'
    list_display = ['code', 'name', 'module', 'status']
    fields = (('code',),('name',),('module',),('status','init','locked',),('locked_by','lock_time',))
    raw_id_fields = ['module']
    readonly_fields = ['locked_by','lock_time']
    inlines = [ValueListItemInline]
    search_fields = ['code','name']

    def save_model(self, request, obj, form, change):
        super(ValueListAdmin,self).save_model(request,obj,form,change)
        obj.valuelistitem_set.update(group_code=obj.code)


class AddressAdmin(generic.BOAdmin):
    list_display = ['address','phone','contacts']
    exclude = ['content_type','object_id','creator','modifier','creation','modification','begin','end']


class AddressInline(GenericTabularInline):
    model = Address
    exclude = ['content_type','object_id','creator','modifier','creation','modification','begin','end']
    extra = 1


class BankAccountInline(admin.TabularInline):
    model = BankAccount
    fields = ['account','title','memo']

    def get_extra(self, request, obj=None, **kwargs):
        if obj:
            return 0
        else:
            return 1


class PartnerForm(models.ModelForm):
    tax_address = fields.CharField(widget=TextInput(attrs={'size': 119,}),required=False,label=_("tax address"))
    memo = fields.CharField(widget=Textarea(attrs={'rows':3,'cols':85}),required=False,label=_("memo"))

    class Meta:
        model = Partner
        fields = '__all__'


class PartnerAdmin(generic.BOAdmin):
    list_display = ['code','name','partner_type','level']
    list_display_links = ['code','name']

    fields = (('code','name',),('short','pinyin',),('partner_type','level'),('tax_num','tax_account',),
              ('tax_address',),('contacts','phone',),('memo',),)
    search_fields = ['code','name','pinyin']
    form = PartnerForm
    save_on_top = True
    inlines = [AddressInline,BankAccountInline]

    def get_queryset(self, request):
        if request.user.is_superuser or (request.user.has_perm('basedate.view_all_customer') and request.user.has_perm('basedate.view_all_supplier')):
            return super(PartnerAdmin,self).get_queryset(request)
        elif request.user.has_perm('basedata.view_all_customer'):
            return super(PartnerAdmin,self).get_queryset(request).filter(partner_type='C')
        else:
           return super(PartnerAdmin,self).get_queryset(request).filter(partner_type='S')


class ProjectForm(models.ModelForm):
    income = fields.DecimalField(required=False,widget=TextInput(attrs={'readonly':'true'}))
    expand = fields.DecimalField(required=False,widget=TextInput(attrs={'readonly':'true'}))

    class Meta:
        model = Project
        fields = '__all__'


class ProjectAdmin(generic.BOAdmin):
    CODE_PREFIX = 'PJ'
    list_display = ['code','name','status','income','expand']
    list_display_links = ['code','name']
    fields = (
        ('code','name',),('short','pinyin',),
        ('partner',),('status','prj_type',),
        ('description',),
        ('budget','income','expand',),('blueprint',),('offer',),('business',),('users',),
    )
    search_fields = ['code','name']
    readonly_fields = ['status']
    raw_id_fields = ['partner']
    filter_horizontal = ['users']
    form = ProjectForm


class WarehouseAdmin(admin.ModelAdmin):
    list_display = ['code','name','location']
    filter_horizontal = ['users']

    def save_model(self, request, obj, form, change):
        super(WarehouseAdmin,self).save_model(request,obj,form,change)
        try:
            code = getattr(obj,'code')
            if not code:
                obj.code = '%s%02d' % ('A',obj.id)
                obj.save()
        except Exception,e:
            self.message_user(request,'ERROR:%s' % e,level=messages.ERROR)


class BrandAdmin(admin.ModelAdmin):
    list_display = ['name','pinyin']


class MeasureAdmin(admin.ModelAdmin):
    list_display = ['code','name','status']


class CategoryAdmin(admin.ModelAdmin):
    list_display = ['code','name','path']

    def save_model(self, request, obj, form, change):
        super(CategoryAdmin,self).save_model(request,obj,form,change)
        try:
            code = getattr(obj,'code')
            if not code:
                obj.code = '%s%02d' % ('F',obj.id)
                obj.save()
            if obj.parent:
                if obj.parent.path:
                    obj.path = obj.parent.path + '/'+obj.parent.name
                else:
                    obj.path = obj.parent.name
                obj.save()
        except Exception,e:
            self.message_user(request,'ERROR:%s' % e,level=messages.ERROR)


class MaterialForm(models.ModelForm):
    name = fields.CharField(widget=TextInput(attrs={"size":"119"}),label=_("material name"))
    spec = fields.CharField(widget=TextInput(attrs={"size":"119"}),required=False,label=_("specifications"))

    class Mata:
        model = Material
        fields = '__all__'


class ExtraParamInline(admin.TabularInline):
    model = ExtraParam
    fields = ['name','data_type','data_source']

    def get_extra(self, request, obj=None, **kwargs):
        if obj:
            return 0
        else:
            return 1


class MaterialAdmin(generic.BOAdmin):
    CODE_PREFIX = 'IT'
    CODE_NUMBER_WIDTH = 5
    list_display = ['code','name','spec','tp']
    list_display_links = ['code','name']
    list_filter = ['brand','tp']
    search_fields = ['code','name']
    fields = (
        ('code','barcode'),('name',),('spec',),
        ('brand',),('category',),('status','is_equip','can_sale','is_virtual',),
        ('warehouse',),('tp',),('measure',),('stock_price','purchase_price','sale_price',),
    )
    filter_horizontal = ['measure']
    inlines = [ExtraParamInline]
    form = MaterialForm


class TechParamValueInline(admin.TabularInline):
    model = TechnicalParameterValue


class TechParamNameAdmin(admin.ModelAdmin):
    list_display = ['name','category']
    inlines = [TechParamValueInline]


class TradeAdmin(admin.ModelAdmin):
    list_display = ['code','name','parent']


class ExpenseAdmin(generic.BOAdmin):
    CODE_PREFIX = 'FC'
    list_display = ['code','name','category']
    list_display_links = ['code','name']
    list_filter = ['category']
    search_fields = ['name']


class FamilyForm(models.ModelForm):
    name = fields.CharField(widget=TextInput(attrs={"size":"25"}),label=_("name"))
    phone = fields.CharField(widget=TextInput(attrs={"size":"25"}),label=_("phone"))

    class Meta:
        model = Family
        fields = '__all__'


class FamilyInline(admin.TabularInline):
    model = Family
    exclude = ['creator','modifier','creation','modification','begin','end']
    form = FamilyForm
    extra = 1


class EducationInline(admin.TabularInline):
    model = Education
    exclude = ['creator','modifier','creation','modification']
    extra = 0


class WorkExperienceInline(admin.TabularInline):
    model = WorkExperience
    exclude = ['creator','modifier','creation','modification']
    extra = 1


class EmployeeAdmin(generic.BOAdmin):
    CODE_PREFIX = '1'
    list_display = ['code','name','position','gender','idcard','age','work_age','literacy','phone','email']
    search_fields = ['code','name','idcard','pinyin']
    fieldsets = [
        (None,{'fields':[('code','phone',),('name','pinyin',),('gender','birthday',),('idcard','country',),
                         ('position',),('rank','category'),('status','ygxs',),('workday','startday',)]}),
        (_('other info'),{'fields':[('hometown','address',),('banknum','bankname',),('email','office',),
        ('emergency','literacy',),('religion','marital',),('party','nation',),('spjob','health',),
        ('major','degree',),('tag1','tag2',),('tag3','tag4',),('user',),],'classes':['collapse']}),
    ]
    readonly_fields = ['status','ygxs','rank','category']
    inlines = [FamilyInline,EducationInline,WorkExperienceInline]
    raw_id_fields = ['user']

    def get_queryset(self, request):
        if request.user.is_superuser or request.user.has_perm('basedata.view_all_employee'):
            return super(EmployeeAdmin,self).get_queryset(request)
        else:
            return super(EmployeeAdmin,self).get_queryset(request).filter(user=request.user)

    def get_readonly_fields(self, request, obj=None):
        if request.user.is_superuser:
            return []
        else:
            return ['status','ygxs','rank','category','position','user']


class DataImportAdmin(generic.BOAdmin):
    list_display = ['imp_date','title','status']
    list_display_links = ['imp_date','title']
    raw_id_fields = ['content_type']
    readonly_fields = ['status']
    extra_buttons = [{'href':'action','title':_('import')}]

    def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
        if object_id:
            obj = DataImport.objects.get(id=object_id)
            if obj.status == '1':
                extra_context = extra_context or {}
                extra_context.update(dict(readonly=True))
        return super(DataImportAdmin,self).changeform_view(request,object_id,form_url,extra_context)


class DocumentForm(models.ModelForm):
    title = fields.CharField(widget=TextInput(attrs={"size":"119"}),label=_("title"))
    keywords = fields.CharField(widget=TextInput(attrs={"size":"119"}),label=_("keywords"))

    class Meta:
        model = Document
        fields = '__all__'


class DocumentAdmin(generic.BOAdmin):
    CODE_PREFIX = 'FD'
    CODE_NUMBER_WIDTH = 4
    list_display = ['code','title','keywords','tp','business_domain','status','creation']
    list_display_links = ['code','title']
    fields = (('code','status',),('title',),('keywords',),('description',),('business_domain','tp',),('attach',))
    readonly_fields = ['status']
    list_filter = ['tp','business_domain']
    search_fields = ['title','keywords','code']
    form = DocumentForm
    actions = ['publish']
    date_hierarchy = 'begin'

    def get_readonly_fields(self, request, obj=None):
        if obj and obj.status=='1':
            return ['code','status','title','keywords','description','business_domain','tp','attach',]
        else:
            return ['status']

    def publish(self,request,queryset):
        import datetime
        cnt = queryset.filter(status='0').update(status='1',pub_date=datetime.datetime.now())
        self.message_user(request,u'%s 个文档发布成功'%cnt)

    publish.short_description = _('publish selected %(verbose_name_plural)s')

# admin.site.register(Address,AddressAdmin)
admin.site.register(ValueList,ValueListAdmin)
admin.site.register(Partner,PartnerAdmin)
admin.site.register(Project,ProjectAdmin)
admin.site.register(Material,MaterialAdmin)
admin.site.register(Warehouse,WarehouseAdmin)
admin.site.register(Brand,BrandAdmin)
admin.site.register(Measure,MeasureAdmin)
admin.site.register(Category,CategoryAdmin)
admin.site.register(TechnicalParameterName,TechParamNameAdmin)
admin.site.register(Trade,TradeAdmin)
admin.site.register(ExpenseAccount,ExpenseAdmin)
admin.site.register(Employee,EmployeeAdmin)
admin.site.register(DataImport,DataImportAdmin)
admin.site.register(Document,DocumentAdmin)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
def compile_node_handler(request,obj,next_node):
    """
    :param request:
    :param obj:
    :param handler:
    :return:
    """

    handler = next_node.handler
    next_user_handler = next_node.next_user_handler
    # next_user_handler 具有最高优先级
    if next_user_handler:
        # print 'it is here'
        klass = NextUserManager().handlers.get(next_user_handler)
        if klass and isinstance(klass,NextUserHandler):
            return klass.handle(request,obj,next_node)

    if handler and handler != '':
        handler = handler.replace("submitter()", request.user.username)
        handler = handler.replace("suber()", request.user.username)
        fields = obj._meta.fields
        for field in fields:
            name = field.name
            temp = "{{%s}}" % name
            val = getattr(obj,name,None)
            if val:
                if type(val)!=str:
                    val = str(val)
                handler = handler.replace(temp,val)
        cursor = connection.cursor()
        cursor.execute(handler)
        rows = [row for row in cursor.fetchall()]
        return rows
    else:
        tp = next_node.handler_type
        if tp == 1 and next_node.users:
            # user
            users = [user for user in next_node.users.all()]
            return users
        elif tp == 2 and next_node.positions:
            # position
            users = []
            for position in next_node.positions.all():
                for employee in position.employee_set.all():
                    users.append(employee.user)
            return users
        elif tp == 3 and next_node.roles:
            # role
            users = []
            for role in next_node.roles.all():
                for user in role.users.all():
                    users.append(user)
            return users
        elif tp == 4:
            # submitter
            return request.user
        else:
            return None


def start(request,app,model,object_id):
    """

    :param request:
    :return:
    """
    import datetime
    content_type = ContentType.objects.get(app_label=app,model=model)
    obj = content_type.get_object_for_this_type(id=int(object_id))
    title = _("Are you sure?")
    opts = obj._meta
    objects_name = force_text(opts.verbose_name)
    has_workflow = False
    queryset = Modal.objects.filter(content_type=content_type,end__gt=datetime.date.today()).order_by('-end')
    cnt = queryset.count()
    workflow_modal = None
    next_node = None
    next_users = []
    has_next_user = False
    if cnt > 0:
        has_workflow = True
        workflow_modal = queryset[0]
        query_start_node = workflow_modal.node_set.filter(start=1)
        query_first_node = workflow_modal.node_set.order_by('id')
        if query_start_node.count() > 0:
            next_node = query_start_node[0]
        elif query_first_node.count()>0:
            next_node = query_first_node[0]
        if next_node:
            next_users = compile_node_handler(request,obj,next_node)
            if len(next_users) > 0:
                has_next_user = True
    else:
        title = _("No workflow model was found")

    try:
        tmp = Instance.objects.get(modal = workflow_modal,object_id=object_id)
        messages.warning(request,_("the object is already in workflow"))
        return HttpResponseRedirect("/admin/%s/%s/%s"%(app,model,object_id))
    except Exception:
        pass

    if request.POST.get("post"):
        val = request.POST.getlist(SELECTED_CHECKBOX_NAME)
        workflow_inst = Instance.objects.create(modal=workflow_modal,object_id=object_id,starter=request.user)
        workflow_inst.current_nodes.add(next_node)
        workflow_inst.save()
        workflow_history = History.objects.create(inst=workflow_inst,user=request.user)
        for user in User.objects.filter(id__in=val):
            todo = TodoList.objects.create(inst=workflow_inst,node=next_node,user=user,app_name=app,model_name=model)
        TodoList.objects.create(inst=workflow_inst,user=request.user,app_name=app,model_name=model,is_read=True,
                                read_time=datetime.datetime.now(),status=True)
        if next_node.status_field and next_node.status_value:
            try:
                setattr(obj,next_node.status_field,next_node.status_value)
                obj.save()
            except Exception,e:
                pass
        messages.success(request,_('workflow started successfully'))
        return HttpResponseRedirect("/admin/%s/%s/%s"%(app,model,object_id))

    context = dict(
        site.each_context(request),
        title=title,
        opts=opts,
        objects_name=objects_name,
        object=obj,
        has_workflow = has_workflow,
        workflow_modal = workflow_modal,
        next_node = next_node,
        has_next_user = has_next_user,
        next_users = next_users,
        checkbox_name = SELECTED_CHECKBOX_NAME,
    )
    request.current_app = site.name

    return TemplateResponse(request,'default/workflow/workflow_start_confirmation.html', context)


def approve(request,app,model,object_id,operation):
    """

    :param request:
    :param operation:
    :return:
    """
    if operation not in ('1','3','4'):
        messages.warning(request,_("unkown workflow operation"))
        return HttpResponseRedirect("/admin/%s/%s/%s"%(app,model,object_id))

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150

六、论文参考

  • 计算机毕业设计选题推荐-办公管理系统-论文参考:
    计算机毕业设计选题推荐-办公管理系统-论文参考

七、系统视频

办公管理系统-项目视频:

基于Python的办公管理系统

结语

计算机毕业设计选题推荐-办公管理系统-Python项目实战
大家可以帮忙点赞、收藏、关注、评论啦~
源码获取:私信我

精彩专栏推荐⬇⬇⬇
Java项目
Python项目
安卓项目
微信小程序项目

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/371208
推荐阅读
相关标签
  

闽ICP备14008679号