jiaweiqi 3 năm trước cách đây
mục cha
commit
438b3e9064

+ 1 - 26
apps/base.py

@@ -33,29 +33,4 @@ class Formater():
 
 
     @staticmethod
     @staticmethod
     def formatAmountShow(value):
     def formatAmountShow(value):
-        return '%.2f' % (float(value or 0) / 10000.0 + 0.0000001)
-
-class CustomFormaterByUser():
-    @staticmethod
-    def formatEmptyStr(value,user):
-        return Formater.formatEmptyStr(value)
-
-    @staticmethod
-    def formatEmptyFloat(value, user):
-        #return round(value or 0, 2)
-        return Formater.formatEmptyFloat(value)
-
-    @staticmethod
-    def formatTime(value,user):
-        return Formater.formatTime(value)
-
-    @staticmethod
-    def formatDate(value, user):
-        return Formater.formatDate(value)
-
-    @staticmethod
-    def formatCountShow(value,user):
-        return Formater.formatCountShow(value)
-    @staticmethod
-    def formatPartAmountShow(value, user):
-        return Formater.formatAmountShow(value)
+        return '%.2f' % (float(value or 0) / 10000.0 + 0.0000001)

+ 16 - 1
apps/collection/models.py

@@ -12,6 +12,7 @@ class Collection(models.Model):
     customer = models.ForeignKey(Customer, verbose_name=u'收藏人', on_delete=models.PROTECT)
     customer = models.ForeignKey(Customer, verbose_name=u'收藏人', on_delete=models.PROTECT)
     commodity = models.ForeignKey(Commodity, verbose_name=u'收藏商品', on_delete=models.PROTECT)
     commodity = models.ForeignKey(Commodity, verbose_name=u'收藏商品', on_delete=models.PROTECT)
     create_time = models.DateTimeField(verbose_name=u'收藏时间', default=timezone.now, editable=False)
     create_time = models.DateTimeField(verbose_name=u'收藏时间', default=timezone.now, editable=False)
+    delete = models.BooleanField(verbose_name=u'删除', default=False, editable=False)
 
 
     class Meta:
     class Meta:
         db_table = "collection"
         db_table = "collection"
@@ -19,4 +20,18 @@ class Collection(models.Model):
         ordering = ['-id']
         ordering = ['-id']
         default_permissions = ()
         default_permissions = ()
 
 
-
+    @staticmethod
+    def is_collection(customer, commodity):
+        count = Collection.objects.filter(customer=customer, commodity=commodity, delete=False).count()
+        return count != 0
+
+    @staticmethod
+    def addnew(customer, commodity):
+        instance = Collection.objects.filter(customer=customer, commodity=commodity).first()
+        if instance:
+            instance.delete = False
+            instance.create_time = timezone.now()
+            instance.save()
+        else:
+            instance = Collection.objects.create(customer=customer, commodity=commodity, create_time=timezone.now())
+        return instance

+ 2 - 1
apps/commodity/models.py

@@ -28,7 +28,7 @@ class Commodity(models.Model):
     type = models.PositiveSmallIntegerField(verbose_name=u'类型', choices=TYPE_CHOICE, default=CASH)
     type = models.PositiveSmallIntegerField(verbose_name=u'类型', choices=TYPE_CHOICE, default=CASH)
     price = models.BigIntegerField(verbose_name=u'销售价格', default=0)
     price = models.BigIntegerField(verbose_name=u'销售价格', default=0)
     vip_price = models.BigIntegerField(verbose_name=u'会员价格', default=0)
     vip_price = models.BigIntegerField(verbose_name=u'会员价格', default=0)
-    point_price = models.BigIntegerField(verbose_name=u'积分价格', default=0)
+    point_price = models.IntegerField(verbose_name=u'积分价格', default=0)
     status = models.PositiveSmallIntegerField(choices=settings.SALES_STATUS_CHOICES, verbose_name=u"状态", default=settings.ONLINE)
     status = models.PositiveSmallIntegerField(choices=settings.SALES_STATUS_CHOICES, verbose_name=u"状态", default=settings.ONLINE)
     notes = models.CharField(max_length=500, verbose_name=u"备注", null=True, blank=True)
     notes = models.CharField(max_length=500, verbose_name=u"备注", null=True, blank=True)
     create_user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=u'创建人', editable=False, on_delete=models.PROTECT)
     create_user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=u'创建人', editable=False, on_delete=models.PROTECT)
@@ -62,6 +62,7 @@ class CommodityImage(models.Model):
     class Meta:
     class Meta:
         db_table = "commodity_image"
         db_table = "commodity_image"
         verbose_name = u'商品图片'
         verbose_name = u'商品图片'
+        ordering = ['-id']
         default_permissions = ()
         default_permissions = ()
         permissions = []
         permissions = []
 
 

+ 1 - 16
apps/commodity/serializers.py

@@ -21,7 +21,6 @@ class CommoditySerializer(serializers.ModelSerializer):
     create_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M', read_only=True)
     create_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M', read_only=True)
     price = serializers.SerializerMethodField()
     price = serializers.SerializerMethodField()
     vip_price = serializers.SerializerMethodField()
     vip_price = serializers.SerializerMethodField()
-    point_price = serializers.SerializerMethodField()
 
 
     show_image_url = serializers.CharField(source='show_image.picture', read_only=True)
     show_image_url = serializers.CharField(source='show_image.picture', read_only=True)
     detail_image = serializers.SerializerMethodField()
     detail_image = serializers.SerializerMethodField()
@@ -37,9 +36,6 @@ class CommoditySerializer(serializers.ModelSerializer):
     def get_vip_price(self, obj):
     def get_vip_price(self, obj):
         return Formater.formatPriceShow(obj.vip_price)
         return Formater.formatPriceShow(obj.vip_price)
 
 
-    def get_point_price(self, obj):
-        return Formater.formatPriceShow(obj.point_price)
-
     def get_detail_image(self, obj):
     def get_detail_image(self, obj):
         count = CommodityImage.objects.filter(commodity=obj, type=CommodityImage.DETAILS).count()
         count = CommodityImage.objects.filter(commodity=obj, type=CommodityImage.DETAILS).count()
         return count
         return count
@@ -59,7 +55,7 @@ class CommoditySerializer(serializers.ModelSerializer):
         if 'vip_price' in self.initial_data:
         if 'vip_price' in self.initial_data:
             attrs['vip_price'] = Formater.formatPrice(self.initial_data['vip_price'])
             attrs['vip_price'] = Formater.formatPrice(self.initial_data['vip_price'])
         if 'point_price' in self.initial_data:
         if 'point_price' in self.initial_data:
-            attrs['point_price'] = Formater.formatPrice(self.initial_data['point_price'])
+            attrs['point_price'] = self.initial_data['point_price']
 
 
         if attrs['type'] == Commodity.CASH:
         if attrs['type'] == Commodity.CASH:
             attrs['point_price'] = 0
             attrs['point_price'] = 0
@@ -78,14 +74,3 @@ class CommoditySerializer(serializers.ModelSerializer):
             raise CustomError(u'该商品已删除,禁止操作!')
             raise CustomError(u'该商品已删除,禁止操作!')
         instance = super(CommoditySerializer, self).update(instance, validated_data)
         instance = super(CommoditySerializer, self).update(instance, validated_data)
         return instance
         return instance
-
-
-class CommodityImageSerializer(serializers.ModelSerializer):
-    img_url = serializers.SerializerMethodField()
-
-    def get_img_url(self, obj):
-        return '%s%s' % (settings.MEDIA_URL, obj.img.picture)
-
-    class Meta:
-        model = CommodityImage
-        fields = ('img_url', 'id',)

+ 1 - 1
apps/commodity/views.py

@@ -15,7 +15,7 @@ from apps.log.models import BizLog
 from apps.images.models import Images
 from apps.images.models import Images
 from apps.commodity.models import Commodity, CommodityImage
 from apps.commodity.models import Commodity, CommodityImage
 from apps.commodity.filters import CommodityFilter, CommodityImageFilter
 from apps.commodity.filters import CommodityFilter, CommodityImageFilter
-from .serializers import CommoditySerializer, CommodityImageSerializer
+from apps.commodity.serializers import CommoditySerializer
 from apps.option.models import Option
 from apps.option.models import Option
 from apps.base import Formater
 from apps.base import Formater
 
 

+ 0 - 0
apps/customer/commodity/__init__.py


+ 93 - 0
apps/customer/commodity/serializers.py

@@ -0,0 +1,93 @@
+# coding=utf-8
+
+from django.conf import settings
+from rest_framework import serializers
+from apps.commodity.models import Commodity, CommodityImage
+from apps.collection.models import Collection
+from apps.base import Formater
+from apps.customer.models import Customer
+from apps.option.models import Option
+
+
+class CommoditySerializer(serializers.ModelSerializer):
+    show_image_url = serializers.SerializerMethodField()
+    price = serializers.SerializerMethodField()
+    vip_price = serializers.SerializerMethodField()
+    sale_count = serializers.SerializerMethodField()
+
+    def get_sale_count(self, obj):
+        count = (obj.total_sales or 0) + (obj.initial_sale_count or 0)
+        return count
+
+    def get_price(self, obj):
+        return Formater.formatPriceShow(obj.price)
+
+    def get_vip_price(self, obj):
+        return Formater.formatPriceShow(obj.vip_price)
+
+    def get_show_image_url(self, obj):
+        if obj.show_image:
+            return obj.show_image.get_path()
+        return ''
+
+    class Meta:
+        model = Commodity
+        fields = ('id', 'show_image_url', 'price', 'vip_price', 'sale_count', 'point_price', 'type', 'name', )
+
+
+class CommodityDetailSerializer(serializers.ModelSerializer):
+    sale_count = serializers.SerializerMethodField()
+    is_collection = serializers.SerializerMethodField()
+    carousel_urls = serializers.SerializerMethodField()
+    details_urls = serializers.SerializerMethodField()
+    price = serializers.SerializerMethodField()
+    vip_price = serializers.SerializerMethodField()
+    # 评论
+
+    def get_sale_count(self, obj):
+        count = (obj.total_sales or 0) + (obj.initial_sale_count or 0)
+        return count
+
+    def get_is_collection(self, obj):
+        if not self.context['request'].user or not self.context['request'].user.is_authenticated or not self.context['request'].user.is_customer():
+            return False
+        customer = Customer.objects.filter(user=self.context['request'].user).first()
+        if not customer:
+            return False
+        return Collection.is_collection(customer, obj)
+
+    def get_carousel_urls(self, obj):
+        result = []
+        rows = CommodityImage.objects.filter(commodity=obj, type=CommodityImage.CAROUSEL).values('image__picture')
+        for row in rows:
+            url = '%s%s' % (settings.SERVER_DOMAIN, row['image__picture'])
+            result.append(url)
+        return result
+
+    def get_details_urls(self, obj):
+        result = []
+        rows = CommodityImage.objects.filter(commodity=obj, type=CommodityImage.DETAILS).values('image__picture')
+        for row in rows:
+            url = '%s%s' % (settings.SERVER_DOMAIN, row['image__picture'])
+            result.append(url)
+        return result
+
+    def get_price(self, obj):
+        return Formater.formatPriceShow(obj.price)
+
+    def get_vip_price(self, obj):
+        return Formater.formatPriceShow(obj.vip_price)
+
+    class Meta:
+        model = Commodity
+        fields = (
+            'id', 'name', 'price', 'vip_price', 'point_price', 'sale_count', 'is_collection', 'carousel_urls', 'details_urls',
+        )
+
+
+class CommodityCategorySerializer(serializers.ModelSerializer):
+    label = serializers.CharField(source='name', read_only=True)
+
+    class Meta:
+        model = Option
+        fields = ('id', 'label', )

+ 16 - 0
apps/customer/commodity/urls.py

@@ -0,0 +1,16 @@
+# coding=utf-8
+
+from django.conf.urls import url, include
+from rest_framework.routers import SimpleRouter
+
+from .views import *
+
+urlpatterns = [
+    url(r'^category/$', CommodityCategoryListView.as_view()),
+    url(r'^list/$', CommodityListView.as_view()),
+    url(r'^detail/(?P<pk>[0-9]+)/$', CommodityDetailView.as_view()),
+]
+
+router = SimpleRouter()
+router.register(r'', CollectionViewSet)
+urlpatterns += router.urls

+ 103 - 0
apps/customer/commodity/views.py

@@ -0,0 +1,103 @@
+# coding=utf-8
+
+from django.db import transaction
+from django.db.models import F
+from django.conf import settings
+from django.utils import timezone
+
+from rest_framework import generics, mixins
+from rest_framework.viewsets import GenericViewSet
+from rest_framework.views import APIView
+from rest_framework.decorators import action
+from rest_framework.exceptions import NotFound
+
+from utils import response_ok
+from utils.permission import IsCustomer
+from utils.exceptions import CustomError
+
+from apps.commodity.models import Commodity
+from apps.commodity.filters import CommodityFilter
+from apps.log.models import BizLog
+from apps.customer.models import Customer
+from apps.collection.models import Collection
+from apps.option.filters import OptionFilter
+from apps.option.models import Option
+
+from .serializers import CommodityCategorySerializer, CommoditySerializer, CommodityDetailSerializer
+
+
+class CollectionViewSet(GenericViewSet, mixins.ListModelMixin, mixins.RetrieveModelMixin):
+    permission_classes = [IsCustomer, ]
+    queryset = Commodity.objects.filter(status=settings.ONLINE, delete=False)
+    serializer_class = CommoditySerializer
+
+    @action(methods=['get'], detail=True)
+    def collection(self, request, pk):
+        instance = self.get_object()
+        if instance.delete:
+            raise CustomError(u'该商品已被删除!')
+        if instance.status != settings.ONLINE:
+            raise CustomError(u'该商品已下架!')
+        customer = request.customer
+        if Collection.is_collection(customer, instance):
+            raise CustomError(u'该商品已收藏!')
+        with transaction.atomic():
+            Collection.addnew(customer, instance)
+        return response_ok()
+
+    @action(methods=['get'], detail=True)
+    def uncollection(self, request, pk):
+        instance = self.get_object()
+        customer = request.customer
+        collection = Collection.objects.filter(customer=customer, commodity=instance).first()
+        if not collection or collection.delete:
+            raise CustomError(u'该商品尚未收藏!')
+        with transaction.atomic():
+            Collection.objects.filter(customer=customer, commodity=instance).update(delete=True)
+        return response_ok()
+
+
+class CommodityCategoryListView(generics.ListAPIView):
+    queryset = Option.objects.filter(enable=True, delete=False, type=Option.COMMODITY_CATEGORY).order_by('sort')
+    serializer_class = CommodityCategorySerializer
+
+    def filter_queryset(self, queryset):
+        queryset = queryset.filter()
+        f = OptionFilter(self.request.GET, queryset=queryset)
+        return f.qs
+
+    def list(self, request, *args, **kwargs):
+        try:
+            data = super(CommodityCategoryListView, self).list(request)
+        except NotFound:
+            return response_ok([])
+        return data
+
+
+class CommodityListView(generics.ListAPIView):
+    queryset = Commodity.objects.filter(status=settings.ONLINE, delete=False)
+    serializer_class = CommoditySerializer
+
+    def filter_queryset(self, queryset):
+        queryset = queryset.filter().order_by('type', 'sort', '-id')
+        f = CommodityFilter(self.request.GET, queryset=queryset)
+        return f.qs
+
+    def list(self, request, *args, **kwargs):
+        try:
+            data = super(CommodityListView, self).list(request)
+        except NotFound:
+            return response_ok([])
+        return data
+
+
+class CommodityDetailView(generics.RetrieveAPIView):
+    queryset = Commodity.objects.filter(status=settings.ONLINE, delete=False)
+    serializer_class = CommodityDetailSerializer
+
+    def retrieve(self, request, *args, **kwargs):
+        instance = self.get_object()
+        if not instance:
+            raise CustomError(u'未找到相应的商品!')
+        serializer = self.get_serializer(instance)
+        return response_ok(serializer.data)

+ 0 - 1
apps/customer/filters.py

@@ -17,7 +17,6 @@ class CustomerFilter(django_filters.FilterSet):
             queryset = queryset.filter(user__date_joined__gte=range_time[0], user__date_joined__lte=end_time)
             queryset = queryset.filter(user__date_joined__gte=range_time[0], user__date_joined__lte=end_time)
         return queryset
         return queryset
 
 
-
     class Meta:
     class Meta:
         model = Customer
         model = Customer
         fields = '__all__'
         fields = '__all__'

+ 75 - 39
apps/customer/models.py

@@ -1,6 +1,6 @@
 # coding=utf-8
 # coding=utf-8
 
 
-from django.db import models
+from django.db import models, transaction
 from django.utils import timezone
 from django.utils import timezone
 from django.conf import settings
 from django.conf import settings
 from django.contrib.auth import get_user_model
 from django.contrib.auth import get_user_model
@@ -28,7 +28,7 @@ class Customer(models.Model):
 
 
     success_count = models.IntegerField(verbose_name=u'成交次数', default=0, editable=False)
     success_count = models.IntegerField(verbose_name=u'成交次数', default=0, editable=False)
     total_amount = models.BigIntegerField(verbose_name=u'累计消费金额', default=0, editable=False)
     total_amount = models.BigIntegerField(verbose_name=u'累计消费金额', default=0, editable=False)
-    total_point = models.BigIntegerField(verbose_name=u'累计消费积分', default=0, editable=False)
+    total_point = models.IntegerField(verbose_name=u'累计消费积分', default=0, editable=False)
 
 
     class Meta:
     class Meta:
         db_table = "customer"
         db_table = "customer"
@@ -41,6 +41,36 @@ class Customer(models.Model):
         default_permissions = ()
         default_permissions = ()
         permissions = []
         permissions = []
 
 
+    @staticmethod
+    def get_or_create_customer(user, referrer):
+        '''
+
+        :param user: 用户
+        :param referrer: 推荐人 被推荐人是新用户的时候 要创建上级分销记录
+        :return:
+        '''
+        customer = Customer.objects.filter(user=user).first()
+        if customer:
+            return customer
+        with transaction.atomic():
+            customer = Customer.objects.create(user=user, name=user.username, tel=user.username)
+            if referrer:
+                customer.create_distributor(referrer)
+        return customer
+
+    def create_distributor(self, re_customer):
+        referrer = Customer.objects.filter(id=re_customer).first()
+        if not referrer:
+            return ''
+        distributor = SuperiorDistributor.objects.create(customer=self, one_level=referrer)
+        re_distributor = SuperiorDistributor.objects.filter(customer=referrer).first()
+        if re_distributor:
+            distributor.two_level = re_distributor.one_level
+            distributor.three_level = re_distributor.two_level
+            distributor.four_level = re_distributor.three_level
+            distributor.five_level = re_distributor.four_level
+            distributor.save()
+
     def setInfo(self, appid, openid, encryptedData, iv):
     def setInfo(self, appid, openid, encryptedData, iv):
         wx_customer = CustomerWechat.objects.filter(openid=openid, wechat_app__authorizer_appid=appid).first()
         wx_customer = CustomerWechat.objects.filter(openid=openid, wechat_app__authorizer_appid=appid).first()
         if not wx_customer:
         if not wx_customer:
@@ -58,14 +88,45 @@ class Customer(models.Model):
         self.save()
         self.save()
         return self.face, self.name
         return self.face, self.name
 
 
-
+'''
+-------------------------------------------------分销商备注---------------------------------------
+A——>B
+只有B是新用户的时候B才能成为A的一级分销商
+A——>B
+AB都不是分销商 B也是A的一级分销商
+
+A——>B——>C——>D——>E——>F——>G——>H                     按分销商级别记录
+0   1   2   3   4   5
+BCDEF分别是A的五级分销商
+F消费
+F是A的五级分销商  A按五级返利比例获取返利
+F是B的四级分销商  B按四级返利比例获取返利......
+计算返利时 我要去找F分别是谁的几级分销商 不好计算
+A——>B——>C——>D——>E——>F——>G——>H                      按各级分销商获取返利的返利级别记录(计算方便所以选择这种方式)
+5   4   3   2   1   0
+F消费
+F是A的五级分销商  A按五级返利比例获取返利
+F是B的四级分销商  B按四级返利比例获取返利......
+计算返利时 只需要找F的各级返利是谁 好计算返利
+A——>B——>C——>D——>E——>F——>G——>H
+5   4   3   2   1   0
+如果F消费 要根据系统设置比例分别给他的上级分销商(F/E/D/C/B/A)返利
+如果E不是分销商那么E就不获得返利
+如果E不是分销商那么D是按二级返利比例获取返利
+如果E后来成为分销商后 F再消费E还能获取返利
+
+要查看X的一级分销商都有谁  只需要查一级返利关联X的客户
+
+查分销商Y下级分销商
+SuperiorDistributor.objects.filter(Q(one_level=Y) | Q(two_level=Y) | Q(three_level=Y) | Q(four_level=Y) | Q(five_level=Y))的customer就是Y的下级分销
+'''
 class SuperiorDistributor(models.Model):
 class SuperiorDistributor(models.Model):
     customer = models.ForeignKey(Customer, verbose_name=u'客户', editable=False, related_name='superior_distributor_customer', on_delete=models.PROTECT)
     customer = models.ForeignKey(Customer, verbose_name=u'客户', editable=False, related_name='superior_distributor_customer', on_delete=models.PROTECT)
-    one_level = models.ForeignKey(Customer, verbose_name=u'一级分销', related_name='superior_distributor_one_level', on_delete=models.PROTECT, null=True, blank=True)
-    tow_level = models.ForeignKey(Customer, verbose_name=u'二级分销', related_name='superior_distributor_tow_level', on_delete=models.PROTECT, null=True, blank=True)
-    three_level = models.ForeignKey(Customer, verbose_name=u'三级分销', related_name='superior_distributor_three_level', on_delete=models.PROTECT, null=True, blank=True)
-    four_level = models.ForeignKey(Customer, verbose_name=u'四级分销', related_name='superior_distributor_four_level', on_delete=models.PROTECT, null=True, blank=True)
-    five_level = models.ForeignKey(Customer, verbose_name=u'五级分销', related_name='superior_distributor_five_level', on_delete=models.PROTECT, null=True, blank=True)
+    one_level = models.ForeignKey(Customer, verbose_name=u'一级返利', related_name='superior_distributor_one_level', on_delete=models.PROTECT, null=True, blank=True)
+    two_level = models.ForeignKey(Customer, verbose_name=u'二级返利', related_name='superior_distributor_tow_level', on_delete=models.PROTECT, null=True, blank=True)
+    three_level = models.ForeignKey(Customer, verbose_name=u'三级返利', related_name='superior_distributor_three_level', on_delete=models.PROTECT, null=True, blank=True)
+    four_level = models.ForeignKey(Customer, verbose_name=u'四级返利', related_name='superior_distributor_four_level', on_delete=models.PROTECT, null=True, blank=True)
+    five_level = models.ForeignKey(Customer, verbose_name=u'五级返利', related_name='superior_distributor_five_level', on_delete=models.PROTECT, null=True, blank=True)
 
 
     class Meta:
     class Meta:
         db_table = "superior_distributor"
         db_table = "superior_distributor"
@@ -108,7 +169,7 @@ class CustomerWechat(models.Model):
         return instance
         return instance
 
 
     @staticmethod
     @staticmethod
-    def bindWechat(appid, openid, phoneEncryptedData, phoneIv):
+    def bindWechat(appid, openid, phoneEncryptedData, phoneIv, referrer):
         '''
         '''
         微信登录小程序
         微信登录小程序
         判断是否有推荐人,如果有且推荐人是分销商,就要创建上级分销表,同时查询推荐人是否存在上级分销表 如果存在就要将推荐人的上级分销相应的添加到本人的上级分销表里
         判断是否有推荐人,如果有且推荐人是分销商,就要创建上级分销表,同时查询推荐人是否存在上级分销表 如果存在就要将推荐人的上级分销相应的添加到本人的上级分销表里
@@ -135,44 +196,19 @@ class CustomerWechat(models.Model):
             user = User.objects.create_customer(tel, password=tel, **{'is_active': True})
             user = User.objects.create_customer(tel, password=tel, **{'is_active': True})
         customer = Customer.objects.filter(user=user).first()
         customer = Customer.objects.filter(user=user).first()
         if not customer:
         if not customer:
-            customer = Customer.objects.create(user=user, name=tel, tel=tel)
+            customer = Customer.get_or_create_customer(user, referrer)
         wx_customer.customer = customer
         wx_customer.customer = customer
         wx_customer.save()
         wx_customer.save()
         return customer
         return customer
 
 
 
 
-class Area(models.Model):
-    PROVINCE = 0
-    CITY = 1
-    COUNTY = 2
-    LEVEL_CHOICES = (
-        (PROVINCE, u'省级'),
-        (CITY, u'市级'),
-        (COUNTY, u'县区级'),
-    )
-
-    name = models.CharField(verbose_name=u'名称', max_length=50, blank=True, null=True)
-    level = models.IntegerField(verbose_name=u'层级', choices=LEVEL_CHOICES)
-    province_name = models.CharField(u'所属省', max_length=50, blank=True, null=True)
-    city_name = models.CharField(u'所属地市', max_length=50, blank=True, null=True)
-    province = models.ForeignKey('Area', verbose_name=u'所属省', related_name='province_children', on_delete=models.PROTECT, blank=True, null=True)
-    city = models.ForeignKey('Area', verbose_name=u'所属地市', related_name='city_children', on_delete=models.PROTECT, blank=True, null=True)
-
-    def __unicode__(self):
-        return '%s' % (self.name)
-
-    class Meta:
-        verbose_name = u"地区"
-        db_table = "area"
-
-
 class CustomerAddress(models.Model):
 class CustomerAddress(models.Model):
     customer = models.ForeignKey(Customer, verbose_name=u'客户', on_delete=models.PROTECT, editable=False)
     customer = models.ForeignKey(Customer, verbose_name=u'客户', on_delete=models.PROTECT, editable=False)
     name = models.CharField(verbose_name=u'收货人姓名', max_length=20)
     name = models.CharField(verbose_name=u'收货人姓名', max_length=20)
     tel = models.CharField(verbose_name=u'收货人电话', max_length=11)
     tel = models.CharField(verbose_name=u'收货人电话', max_length=11)
-    province = models.ForeignKey(Area, verbose_name=u'省', related_name='address_province', on_delete=models.PROTECT)
-    city = models.ForeignKey(Area, verbose_name=u'地市', related_name='address_city', null=True, on_delete=models.PROTECT)
-    county = models.ForeignKey(Area, verbose_name=u'区县', related_name='address_county', null=True, on_delete=models.PROTECT)
+    province = models.CharField(verbose_name=u'省', max_length=100, null=True)
+    city = models.CharField(verbose_name=u'市', max_length=100, null=True)
+    area = models.CharField(verbose_name=u'县/区', max_length=100, null=True)
     address = models.CharField(verbose_name=u'详细地址', max_length=500)
     address = models.CharField(verbose_name=u'详细地址', max_length=500)
     default = models.BooleanField(verbose_name=u'默认', default=False)
     default = models.BooleanField(verbose_name=u'默认', default=False)
     delete = models.BooleanField(verbose_name=u'删除', default=False, editable=False)
     delete = models.BooleanField(verbose_name=u'删除', default=False, editable=False)
@@ -185,7 +221,7 @@ class CustomerAddress(models.Model):
         default_permissions = ()
         default_permissions = ()
 
 
     def get_address(self):
     def get_address(self):
-        return '{}{}{}{}'.format(self.province.name, self.city.name, self.county.name, self.address)
+        return '{}{}{}{}'.format(self.province, self.city, self.area, self.address)
 
 
     def destory(self, queryset):
     def destory(self, queryset):
         if self.default:
         if self.default:

+ 0 - 0
apps/customer/order/__init__.py


+ 0 - 0
apps/customer/order/serializers.py


+ 20 - 0
apps/customer/order/urls.py

@@ -0,0 +1,20 @@
+# coding=utf-8
+
+from django.conf.urls import url, include
+from rest_framework.routers import SimpleRouter
+
+from .views import *
+
+urlpatterns = [
+    # url(r'^code2Session/$', WxLoginView.as_view()),
+    # url(r'^wxbind/$', WxBindView.as_view()),
+    # url(r'^setUserInfo/$', SetUserInfoView.as_view()),
+    # url(r'^token/refresh/$', CustomerRefreshTokenView),
+    # url(r'^token/verify/$', CustomerVerifyTokenView),
+    #
+    # url(r'^commodity/', include('apps.customer.commodity.urls'))
+]
+
+# router = SimpleRouter()
+# router.register(r'', CustomerViewSet)
+# urlpatterns += router.urls

+ 0 - 0
apps/customer/order/views.py


+ 20 - 3
apps/customer/serializers.py

@@ -7,6 +7,7 @@ from rest_framework_jwt.settings import api_settings
 
 
 from apps.customer.models import *
 from apps.customer.models import *
 from apps.log.models import BizLog
 from apps.log.models import BizLog
+from apps.base import Formater
 
 
 
 
 User = get_user_model()
 User = get_user_model()
@@ -41,7 +42,7 @@ class WechatLoginSerializer(serializers.Serializer):
                 'name': wx_customer.customer.name or '',
                 'name': wx_customer.customer.name or '',
                 'tel': wx_customer.customer.tel or '',
                 'tel': wx_customer.customer.tel or '',
                 'face': wx_customer.customer.face and wx_customer.customer.face.get_path() or '',
                 'face': wx_customer.customer.face and wx_customer.customer.face.get_path() or '',
-                'gender': wx_customer.customer.gender or 0,
+                'customer_id': wx_customer.customer.id,
             }
             }
 
 
         else:
         else:
@@ -55,9 +56,10 @@ class WechatBindSerializer(serializers.Serializer):
         openid = self.initial_data.get('openid')
         openid = self.initial_data.get('openid')
         phoneEncryptedData = self.initial_data.get('encryptedData')
         phoneEncryptedData = self.initial_data.get('encryptedData')
         phoneIv = self.initial_data.get('iv')
         phoneIv = self.initial_data.get('iv')
+        referrer = self.initial_data.get('referrer')
 
 
         if openid and phoneEncryptedData and phoneIv:
         if openid and phoneEncryptedData and phoneIv:
-            customer = CustomerWechat.bindWechat(appid, openid, phoneEncryptedData, phoneIv)
+            customer = CustomerWechat.bindWechat(appid, openid, phoneEncryptedData, phoneIv, referrer)
             user = customer.user
             user = customer.user
             payload = jwt_payload_handler(user)
             payload = jwt_payload_handler(user)
             BizLog.objects.addnew(user, BizLog.INSERT, u'用户微信登录,username=%s' % user.username)
             BizLog.objects.addnew(user, BizLog.INSERT, u'用户微信登录,username=%s' % user.username)
@@ -66,7 +68,7 @@ class WechatBindSerializer(serializers.Serializer):
                 'name': customer.name or '',
                 'name': customer.name or '',
                 'tel': customer.tel or '',
                 'tel': customer.tel or '',
                 'face': customer.face and customer.face.get_path() or '',
                 'face': customer.face and customer.face.get_path() or '',
-                'gender': customer.gender or 0
+                'customer_id': customer.id,
             }
             }
 
 
         else:
         else:
@@ -75,8 +77,23 @@ class WechatBindSerializer(serializers.Serializer):
 
 
 
 
 class CustomerSerializer(serializers.ModelSerializer):
 class CustomerSerializer(serializers.ModelSerializer):
+    gender_text = serializers.CharField(source='get_gender_display', read_only=True)
+    is_distributor_text = serializers.SerializerMethodField()
+    balance = serializers.SerializerMethodField()
+    total_amount = serializers.SerializerMethodField()
+    date_join = serializers.DateTimeField(source='user.date_joined', format='%Y-%m-%d %H:%M', read_only=True)
 
 
     class Meta:
     class Meta:
         model = Customer
         model = Customer
         fields = '__all__'
         fields = '__all__'
 
 
+    def get_is_distributor_text(self, obj):
+        if obj.is_distributor:
+            return u'是'
+        return u'否'
+
+    def get_balance(self, obj):
+        return Formater.formatAmountShow(obj.balance)
+
+    def get_total_amount(self, obj):
+        return Formater.formatAmountShow(obj.total_amount)

+ 8 - 2
apps/customer/urls.py

@@ -9,6 +9,12 @@ urlpatterns = [
     url(r'^code2Session/$', WxLoginView.as_view()),
     url(r'^code2Session/$', WxLoginView.as_view()),
     url(r'^wxbind/$', WxBindView.as_view()),
     url(r'^wxbind/$', WxBindView.as_view()),
     url(r'^setUserInfo/$', SetUserInfoView.as_view()),
     url(r'^setUserInfo/$', SetUserInfoView.as_view()),
-    url(r'^token/refresh/', CustomerRefreshTokenView),
-    url(r'^token/verify/', CustomerVerifyTokenView),
+    url(r'^token/refresh/$', CustomerRefreshTokenView),
+    url(r'^token/verify/$', CustomerVerifyTokenView),
+
+    url(r'^commodity/', include('apps.customer.commodity.urls'))
 ]
 ]
+
+router = SimpleRouter()
+router.register(r'', CustomerViewSet)
+urlpatterns += router.urls

+ 44 - 2
apps/customer/views.py

@@ -1,16 +1,22 @@
 # coding=utf-8
 # coding=utf-8
 
 
 from django.db import transaction
 from django.db import transaction
+from django.db.models import Sum
 
 
 from rest_framework.views import APIView
 from rest_framework.views import APIView
 from rest_framework_jwt.views import VerifyJSONWebToken,RefreshJSONWebToken
 from rest_framework_jwt.views import VerifyJSONWebToken,RefreshJSONWebToken
 from rest_framework.serializers import ValidationError
 from rest_framework.serializers import ValidationError
+from rest_framework.decorators import action
 
 
 from utils import response_ok, response_error
 from utils import response_ok, response_error
-from utils.permission import IsCustomer
+from utils.permission import IsCustomer, IsEmployee
+from utils.custom_modelviewset import CustomModelViewSet
 
 
-from apps.customer.serializers import WechatLoginSerializer, WechatBindSerializer
+from apps.customer.serializers import WechatLoginSerializer, WechatBindSerializer, CustomerSerializer
+from apps.customer.models import Customer
+from apps.customer.filters import CustomerFilter
 from apps.log.models import BizLog
 from apps.log.models import BizLog
+from apps.base import Formater
 
 
 
 
 class WxLoginView(APIView):
 class WxLoginView(APIView):
@@ -67,3 +73,39 @@ class CustomerVerifyTokenView(VerifyJSONWebToken):
                 return response_ok({'token': ser.validated_data['token']})
                 return response_ok({'token': ser.validated_data['token']})
         except ValidationError as e:
         except ValidationError as e:
             return response_error(u'登录状态失效,请重新登录[' + e.detail['error'][0] + ']')
             return response_error(u'登录状态失效,请重新登录[' + e.detail['error'][0] + ']')
+
+
+class CustomerViewSet(CustomModelViewSet):
+    permission_classes = [IsEmployee, ]
+    queryset = Customer.objects.filter()
+    serializer_class = CustomerSerializer
+
+    def filter_queryset(self, queryset):
+        queryset = queryset.filter()
+        f = CustomerFilter(self.request.GET, queryset=queryset)
+        return f.qs
+
+    def list(self, request, *args, **kwargs):
+        footer = {}
+        queryset = self.filter_queryset(self.get_queryset())
+        total_row = queryset.aggregate(
+            balance=Sum('balance'), points=Sum('points'), success_count=Sum('success_count'),
+            total_amount=Sum('total_amount'), total_point=Sum('total_point')
+        )
+        footer['total_balance'] = Formater.formatAmountShow(total_row['balance'] or 0)
+        footer['total_points'] = str(total_row['points'] or 0)
+        footer['total_success_count'] = str(total_row['success_count'] or 0)
+        footer['all_total_amount'] = Formater.formatAmountShow(total_row['total_amount'] or 0)
+        footer['all_total_point'] = str(total_row['total_point'] or 0)
+        page = self.paginate_queryset(queryset)
+        if page is not None:
+            serializer = self.get_serializer(page, many=True)
+            return self.get_paginated_response({'data': serializer.data, 'footer': footer})
+        serializer = self.get_serializer(queryset, many=True)
+        return response_ok({'data': serializer.data, 'footer': footer})
+
+    @action(methods=['get'], detail=False)
+    def export(self, request):
+        queryset = self.filter_queryset(self.queryset)
+        serializer = self.get_serializer(queryset, many=True)
+        return response_ok(serializer.data)

+ 1 - 1
apps/option/views.py

@@ -20,7 +20,7 @@ class DictView(APIView):
 
 
     def get(self, request):
     def get(self, request):
         ret = {
         ret = {
-            'types':Option.TYPE_CHOICES,
+            'types': Option.TYPE_CHOICES,
         }
         }
         return response_ok(ret)
         return response_ok(ret)
 
 

+ 11 - 46
apps/order/filters.py

@@ -1,32 +1,25 @@
 # coding=utf-8
 # coding=utf-8
 import django_filters
 import django_filters
 
 
-from .models import *
-from apps.base import clean_datetime_range
+from apps.order.models import *
+
 
 
 class OrderFilter(django_filters.FilterSet):
 class OrderFilter(django_filters.FilterSet):
     no = django_filters.CharFilter(field_name='no', lookup_expr='icontains')
     no = django_filters.CharFilter(field_name='no', lookup_expr='icontains')
-    name = django_filters.CharFilter(field_name='agent__name', lookup_expr='icontains')
-    create_time_0 = django_filters.DateTimeFilter(field_name='create_time', lookup_expr='gte')
-    create_time_1 = django_filters.DateTimeFilter(field_name='create_time', lookup_expr='lte')
-    create_user_name = django_filters.CharFilter(field_name='create_user__name', lookup_expr='icontains')
-    pay_no = django_filters.CharFilter(field_name='pay__pay_no', lookup_expr='icontains')
-    area_manager = django_filters.CharFilter(field_name='agent__area_manager__name', lookup_expr='icontains')
+    customer_tel = django_filters.CharFilter(field_name='customer__tel', lookup_expr='icontains')
+    tel = django_filters.CharFilter(field_name='tel', lookup_expr='icontains')
+    range_time = django_filters.CharFilter(method='find_base_range_time')
 
 
     class Meta:
     class Meta:
         model = Order
         model = Order
         fields = '__all__'
         fields = '__all__'
 
 
-    def __init__(self, data=None, *args, **kwargs):
-        data = clean_datetime_range(data, 'create_time')
-        super(OrderFilter, self).__init__(data, *args, **kwargs)
-
-class LogisticsFilter(django_filters.FilterSet):
-    no = django_filters.CharFilter(field_name='no', lookup_expr='icontains')
-
-    class Meta:
-        model = Logistics
-        fields = '__all__'
+    def find_base_range_time(self, queryset, *args):
+        if args[1]:
+            range_time = args[1].split(' - ')
+            end_time = range_time[1] + ' 23:59:59'
+            queryset = queryset.filter(create_time__gte=range_time[0], create_time__lte=end_time)
+        return queryset
 
 
 
 
 class OrderDetailsFilter(django_filters.FilterSet):
 class OrderDetailsFilter(django_filters.FilterSet):
@@ -42,31 +35,3 @@ class ShoppingCartFilter(django_filters.FilterSet):
     class Meta:
     class Meta:
         model = ShoppingCart
         model = ShoppingCart
         fields = '__all__'
         fields = '__all__'
-
-class OrderPaymentVoucherFilter(django_filters.FilterSet):
-
-    class Meta:
-        model = OrderPaymentVoucher
-        fields = '__all__'
-
-
-class CustomerOrderFilter(django_filters.FilterSet):
-    no = django_filters.CharFilter(field_name='no', lookup_expr='icontains')
-    name = django_filters.CharFilter(field_name='agent__name', lookup_expr='icontains')
-    create_time_0 = django_filters.DateTimeFilter(field_name='create_time', lookup_expr='gte')
-    create_time_1 = django_filters.DateTimeFilter(field_name='create_time', lookup_expr='lte')
-
-    class Meta:
-        model = CustomerOrder
-        fields = '__all__'
-
-    def __init__(self, data=None, *args, **kwargs):
-        data = clean_datetime_range(data, 'create_time')
-        super(CustomerOrderFilter, self).__init__(data, *args, **kwargs)
-
-
-class CustomerShoppingCartFilter(django_filters.FilterSet):
-
-    class Meta:
-        model = CustomerShoppingCart
-        fields = '__all__'

+ 21 - 23
apps/order/models.py

@@ -55,44 +55,42 @@ class ShoppingCart(models.Model):
 
 
 class Order(models.Model):
 class Order(models.Model):
     WAIT_PAY = 0
     WAIT_PAY = 0
-    CONFIRM_PAY = 1
-    WAIT_DISPATCH = 2
-    CONFIRM_DISPATCH = 3
-    FINISH = 4
-    CANCEL = 5
+    WAIT_DISPATCH = 1
+    WAIT_EVALUATE = 2
+    EVALUATED = 3
+    CANCEL = 4
     STATUS_CHOICES = (
     STATUS_CHOICES = (
         (WAIT_PAY, u'待付款'),
         (WAIT_PAY, u'待付款'),
-        (CONFIRM_PAY, u'已付款'),
         (WAIT_DISPATCH, u'待发货'),
         (WAIT_DISPATCH, u'待发货'),
-        (CONFIRM_DISPATCH, u'已发货'),
-        (FINISH, u'已完成'),
+        (WAIT_EVALUATE, u'待评价'),
+        (EVALUATED, u'已评价'),
         (CANCEL, u'已取消'),
         (CANCEL, u'已取消'),
     )
     )
 
 
-    no = models.CharField(max_length=50, verbose_name=u'订单号', blank=True)
+    no = models.CharField(max_length=50, verbose_name=u'订单号', editable=False)
     pay = models.ForeignKey(Pay, verbose_name=u'支付信息', on_delete=models.PROTECT, null=True, editable=False)
     pay = models.ForeignKey(Pay, verbose_name=u'支付信息', on_delete=models.PROTECT, null=True, editable=False)
 
 
     total_count = models.IntegerField(verbose_name=u'总数量', default=0, editable=False)
     total_count = models.IntegerField(verbose_name=u'总数量', default=0, editable=False)
     total_amount = models.BigIntegerField(verbose_name=u'总金额', default=0, editable=False)
     total_amount = models.BigIntegerField(verbose_name=u'总金额', default=0, editable=False)
-    total_point = models.BigIntegerField(verbose_name=u'总积分', default=0, editable=False)
+    total_point = models.IntegerField(verbose_name=u'总积分', default=0, editable=False)
     status = models.PositiveSmallIntegerField(choices=STATUS_CHOICES, verbose_name=u"订单状态", default=WAIT_PAY)
     status = models.PositiveSmallIntegerField(choices=STATUS_CHOICES, verbose_name=u"订单状态", default=WAIT_PAY)
-    notes = models.CharField(max_length=500, verbose_name=u"备注", null=True)
-
+    notes = models.CharField(max_length=500, verbose_name=u"备注", null=True, blank=True)
     customer = models.ForeignKey(Customer, verbose_name=u'创建人', editable=False, on_delete=models.PROTECT)
     customer = models.ForeignKey(Customer, verbose_name=u'创建人', editable=False, on_delete=models.PROTECT)
     create_time = models.DateTimeField(verbose_name=u"创建时间", default=timezone.now, editable=False)
     create_time = models.DateTimeField(verbose_name=u"创建时间", default=timezone.now, editable=False)
-    cancel = models.BooleanField(verbose_name=u"取消", default=False)
+
     cancel_reason = models.CharField(max_length=100, verbose_name=u"取消原因", null=True, blank=True)
     cancel_reason = models.CharField(max_length=100, verbose_name=u"取消原因", null=True, blank=True)
-    cancel_user =models.ForeignKey(settings.AUTH_USER_MODEL, related_name='order_cancel_user', verbose_name=u"取消人", on_delete=models.PROTECT, null=True, blank=True)
+    cancel_user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='order_cancel_user', verbose_name=u"取消人", on_delete=models.PROTECT, null=True, blank=True)
     cancel_time = models.DateTimeField(verbose_name=u"取消时间", null=True, blank=True)
     cancel_time = models.DateTimeField(verbose_name=u"取消时间", null=True, blank=True)
 
 
-    address = models.ForeignKey(CustomerAddress, verbose_name=u'收货地址', related_name='order_address', null=True, on_delete=models.PROTECT)
-    name = models.CharField(verbose_name=u'姓名', max_length=20, null=True)
-    tel = models.CharField(verbose_name=u'手机', max_length=15, null=True)
-    user_address = models.CharField(verbose_name=u'详细地址', max_length=200, null=True)
+    address = models.ForeignKey(CustomerAddress, verbose_name=u'收货地址', related_name='order_address', null=True, blank=True, on_delete=models.PROTECT)
+    name = models.CharField(verbose_name=u'收货人', max_length=20, null=True, blank=True)
+    tel = models.CharField(verbose_name=u'收货电话', max_length=15, null=True, blank=True)
+    user_address = models.CharField(verbose_name=u'详细地址', max_length=200, null=True, blank=True)
 
 
-    express_no = models.CharField(max_length=50, verbose_name=u'快递单号', blank=True)
-    express_company = models.ForeignKey(Option, verbose_name=u'快递公司', on_delete=models.PROTECT)
-    dispatch_time = models.DateField(verbose_name=u'发货时间', null=True)
+    express_no = models.CharField(max_length=50, verbose_name=u'快递单号', null=True, blank=True)
+    express_company = models.ForeignKey(Option, verbose_name=u'快递公司', on_delete=models.PROTECT, null=True, blank=True)
+    dispatch_time = models.DateTimeField(verbose_name=u'发货时间', null=True, blank=True)
+    dispatch_user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='order_dispatch_user', verbose_name=u"发货人", on_delete=models.PROTECT, null=True, blank=True)
 
 
     class Meta:
     class Meta:
         db_table = "order"
         db_table = "order"
@@ -106,11 +104,11 @@ class OrderDetails(models.Model):
 
 
     order = models.ForeignKey(Order, verbose_name=u'订单', on_delete=models.PROTECT)
     order = models.ForeignKey(Order, verbose_name=u'订单', on_delete=models.PROTECT)
     commodity = models.ForeignKey(Commodity, verbose_name=u'商品', on_delete=models.PROTECT)
     commodity = models.ForeignKey(Commodity, verbose_name=u'商品', on_delete=models.PROTECT)
-    price = models.IntegerField(verbose_name=u'价格', null=True, default=0)
+    price = models.BigIntegerField(verbose_name=u'价格', null=True, default=0)
     point = models.IntegerField(verbose_name=u'积分', null=True, default=0)
     point = models.IntegerField(verbose_name=u'积分', null=True, default=0)
     count = models.IntegerField(verbose_name=u'数量', null=True, default=0)
     count = models.IntegerField(verbose_name=u'数量', null=True, default=0)
     amount = models.BigIntegerField(verbose_name=u'总金额', null=True, default=0)
     amount = models.BigIntegerField(verbose_name=u'总金额', null=True, default=0)
-    point_amount = models.BigIntegerField(verbose_name=u'总积分', null=True, default=0)
+    point_amount = models.IntegerField(verbose_name=u'总积分', null=True, default=0)
 
 
     class Meta:
     class Meta:
         db_table = u'order_details'
         db_table = u'order_details'

+ 59 - 0
apps/order/serializers.py

@@ -0,0 +1,59 @@
+# coding=utf-8
+
+import json
+
+from django.conf import settings
+from rest_framework import serializers
+from django.db.models import Q
+
+from apps.order.models import *
+from apps.base import Formater
+
+from utils.exceptions import CustomError
+
+
+class OrderSerializer(serializers.ModelSerializer):
+    status_text = serializers.CharField(source='get_status_display', read_only=True)
+    customer_name = serializers.CharField(source='customer.name', read_only=True)
+    customer_tel = serializers.CharField(source='customer.tel', read_only=True)
+    total_amount = serializers.SerializerMethodField()
+    create_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M', read_only=True)
+    express_info = serializers.SerializerMethodField()
+    dispatch_user_text = serializers.CharField(source='dispatch_user.employee.name', read_only=True)
+    dispatch_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M', read_only=True)
+    cancel_user_text = serializers.CharField(source='cancel_user.employee.name', read_only=True)
+    cancel_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M', read_only=True)
+
+    class Meta:
+        model = Order
+        fields = '__all__'
+
+    def get_total_amount(self, obj):
+        return Formater.formatPriceShow(obj.total_amount)
+
+    def get_express_info(self, obj):
+        if obj.express_no and obj.express_company:
+            return '{},{}'.format(obj.express_no, obj.express_company.name)
+        return ''
+
+
+class OrderDetailsSerializer(serializers.ModelSerializer):
+    commodity_name = serializers.CharField(source='commodity.name', read_only=True)
+    price = serializers.SerializerMethodField()
+    amount = serializers.SerializerMethodField()
+    images = serializers.SerializerMethodField()
+
+    class Meta:
+        model = OrderDetails
+        fields = '__all__'
+
+    def get_price(self, obj):
+        return Formater.formatPriceShow(obj.price)
+
+    def get_amount(self, obj):
+        return Formater.formatPriceShow(obj.amount)
+
+    def get_images(self, obj):
+        if obj.commodity.show_image:
+            return obj.commodity.show_image.get_path()
+        return ''

+ 14 - 0
apps/order/urls.py

@@ -0,0 +1,14 @@
+# coding=utf-8
+
+from django.conf.urls import url, include
+from rest_framework.routers import SimpleRouter
+
+from .views import *
+
+urlpatterns = [
+    url(r'^dict/$', OrderDict.as_view()),
+]
+
+router = SimpleRouter()
+router.register(r'', OrderViewSet)
+urlpatterns += router.urls

+ 110 - 0
apps/order/views.py

@@ -0,0 +1,110 @@
+# coding=utf-8
+
+from django.db import transaction
+from django.utils import timezone
+
+from rest_framework.decorators import action
+from rest_framework.views import APIView
+from rest_framework import generics
+
+from utils import response_ok
+from utils.permission import IsEmployee
+from utils.exceptions import CustomError
+from utils.custom_modelviewset import CustomModelViewSet
+
+from apps.log.models import BizLog
+from apps.images.models import Images
+from apps.order.models import *
+from apps.order.filters import *
+from apps.order.serializers import *
+from apps.option.models import Option
+from apps.base import Formater
+
+
+class OrderDict(APIView):
+    permission_classes = [IsEmployee, ]
+
+    def get(self, request):
+        rows = Option.objects.filter(type=Option.EXPRESS_COMPANY, enable=True, delete=False).order_by('sort').values('id', 'name')
+        data = [{'value': row['id'], 'name': row['name']} for row in rows]
+        ret = {
+            'express_company': data
+        }
+        return response_ok(ret)
+
+
+class OrderViewSet(CustomModelViewSet):
+    permission_classes = [IsEmployee, ]
+    queryset = Order.objects.filter()
+    serializer_class = OrderSerializer
+
+    def filter_queryset(self, queryset):
+        queryset = queryset.filter()
+        f = OrderFilter(self.request.GET, queryset=queryset)
+        return f.qs
+
+    @action(methods=['get'], detail=True)
+    def get_details(self, request, pk):
+        '''
+        获取详细订单
+        :param request:
+        :param pk:
+        :return:
+        '''
+        order = self.get_object()
+        details = OrderDetails.objects.filter(order=order).order_by('-id')
+        data = OrderDetailsSerializer(details, many=True).data
+        return response_ok(data)
+
+    @action(methods=['post'], detail=True)
+    def submit_express(self, request, pk):
+        '''
+        快递信息添加/修改
+        :param request:
+        :param pk:
+        :return:
+        '''
+        order = self.get_object()
+        express_no =request.POST.get('express_no')
+        express_company = request.POST.get('express_company')
+        if order.status != Order.WAIT_DISPATCH:
+            raise CustomError(u'该订单状态禁止发货!')
+        try:
+            express_company_id = int(express_company)
+        except:
+            raise CustomError(u'无效的快递公司ID!')
+        express_company = Option.objects.filter(id=express_company_id).first()
+        if not express_company.enable:
+            raise CustomError(u'该快递公司已被禁用!')
+        if express_company.delete:
+            raise CustomError(u'该快递公司已被删除!')
+        with transaction.atomic():
+            order.status = Order.WAIT_EVALUATE
+            order.dispatch_time = timezone.now()
+            order.dispatch_user = request.user
+            order.express_no = express_no
+            order.express_company = express_company
+            order.save()
+        return response_ok()
+
+    @action(methods=['post'], detail=True)
+    def cancel_order(self, request, pk):
+        '''
+        取消订单
+        :param request:
+        :param pk:
+        :return:
+        '''
+        cancel_reason = request.POST.get('cancel_reason')
+        order = self.get_object()
+        if order.status == Order.CANCEL:
+            raise CustomError(u'该订单已取消,禁止重复操作!')
+        if order.status != Order.WAIT_PAY:
+            raise CustomError(u'该订单已付款,禁止取消!')
+        with transaction.atomic():
+            order.status = Order.CANCEL
+            order.cancel_reason = cancel_reason
+            order.cancel_user = request.user
+            order.cancel_time = timezone.now()
+            order.save()
+        return response_ok()

+ 1 - 1
cosmetics_shop/settings.py

@@ -181,7 +181,7 @@ MEDIA_ROOT = os.path.join(BASE_DIR, "uis/up/")
 UIS_URL = '/'
 UIS_URL = '/'
 UIS_ROOT = os.path.join(BASE_DIR, "uis/")
 UIS_ROOT = os.path.join(BASE_DIR, "uis/")
 
 
-SERVER_DOMAIN = 'http://127.0.0.1:8089'
+SERVER_DOMAIN = 'http://192.168.2.164:8089'
 
 
 JWT_AUTH = {
 JWT_AUTH = {
     'JWT_EXPIRATION_DELTA': datetime.timedelta(days=30),
     'JWT_EXPIRATION_DELTA': datetime.timedelta(days=30),

+ 1 - 0
cosmetics_shop/urls.py

@@ -26,6 +26,7 @@ urlpatterns = [
     url(r'^config/', include('apps.config.urls')),
     url(r'^config/', include('apps.config.urls')),
     url(r'^commodity/', include('apps.commodity.urls')),
     url(r'^commodity/', include('apps.commodity.urls')),
     url(r'^option/', include('apps.option.urls')),
     url(r'^option/', include('apps.option.urls')),
+    url(r'^order/', include('apps.order.urls')),
 ]
 ]
 
 
 urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
 urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)

+ 5 - 5
uis/views/commodity/edit.html

@@ -68,7 +68,7 @@
                         <div class="layui-form-item" id="point_price_item" hidden>
                         <div class="layui-form-item" id="point_price_item" hidden>
                             <label class="layui-form-label"><font color='red' size="4">*</font>积分价格:</label>
                             <label class="layui-form-label"><font color='red' size="4">*</font>积分价格:</label>
                             <div class="layui-input-block">
                             <div class="layui-input-block">
-                                <input type="text" id="id_point_price" name="point_price" lay-verify="numberGeZ" placeholder="请输入销售限价折扣" autocomplete="off" class="layui-input">
+                                <input type="text" id="id_point_price" name="point_price" lay-verify="intGtz" placeholder="请输入销售限价折扣" autocomplete="off" class="layui-input">
                             </div>
                             </div>
                         </div>
                         </div>
 
 
@@ -145,10 +145,10 @@
 
 
                 $('#point_price_item').hide();
                 $('#point_price_item').hide();
                 $('#id_point_price').val('');
                 $('#id_point_price').val('');
-                $('#id_point_price').attr('lay-verify', 'numberGeZ');
+                $('#id_point_price').attr('lay-verify', 'intGtz');
             } else if (val === '2') {
             } else if (val === '2') {
                 $('#point_price_item').show();
                 $('#point_price_item').show();
-                $('#id_point_price').attr('lay-verify', 'numberGtZ|required');
+                $('#id_point_price').attr('lay-verify', 'intGtz|required');
                 $('#id_point_price').val(point_price);
                 $('#id_point_price').val(point_price);
 
 
                 $('#vip_price_item').hide();
                 $('#vip_price_item').hide();
@@ -194,10 +194,10 @@
                 $('#point_price_item').hide();
                 $('#point_price_item').hide();
 
 
                 $('#id_point_price').val('');
                 $('#id_point_price').val('');
-                $('#id_point_price').attr('lay-verify', 'numberGeZ');
+                $('#id_point_price').attr('lay-verify', 'intGtz');
             } else if (editdata.type === 2) {
             } else if (editdata.type === 2) {
                 $('#point_price_item').show();
                 $('#point_price_item').show();
-                $('#id_point_price').attr('lay-verify', 'numberGtZ|required');
+                $('#id_point_price').attr('lay-verify', 'intGtz|required');
 
 
                 $('#vip_price_item').hide();
                 $('#vip_price_item').hide();
                 $('#id_vip_price').val('');
                 $('#id_vip_price').val('');

+ 21 - 11
uis/views/config/index.html

@@ -31,7 +31,7 @@
                     <div class="layui-inline">
                     <div class="layui-inline">
                         <label class="layui-form-label " style="width: 120px">赠送积分比例:</label>
                         <label class="layui-form-label " style="width: 120px">赠送积分比例:</label>
                         <div class="layui-input-inline" style="width: 100px">
                         <div class="layui-input-inline" style="width: 100px">
-                            <input type="text" name="point_rule" lay-verify="numberGeZ|required" class="layui-input" placeholder="数"/>
+                            <input type="text" name="point_rule" lay-verify="numberGeZ|required" class="layui-input" placeholder="数"/>
                         </div>
                         </div>
                         <div class="layui-form-mid ">%</div>
                         <div class="layui-form-mid ">%</div>
                     </div>
                     </div>
@@ -44,7 +44,7 @@
                     <div class="layui-inline">
                     <div class="layui-inline">
                         <label class="layui-form-label " style="width: 120px">一级返现比例:</label>
                         <label class="layui-form-label " style="width: 120px">一级返现比例:</label>
                         <div class="layui-input-inline" style="width: 100px">
                         <div class="layui-input-inline" style="width: 100px">
-                            <input type="text" name="first_lv1" lay-verify="numberGeZ|required" class="layui-input" placeholder="数"/>
+                            <input type="text" name="first_lv1" lay-verify="numberGeZ|required" class="layui-input" placeholder="数"/>
                         </div>
                         </div>
                         <div class="layui-form-mid ">%</div>
                         <div class="layui-form-mid ">%</div>
                     </div>
                     </div>
@@ -52,7 +52,7 @@
                     <div class="layui-inline">
                     <div class="layui-inline">
                         <label class="layui-form-label " style="width: 120px">二级返现比例:</label>
                         <label class="layui-form-label " style="width: 120px">二级返现比例:</label>
                         <div class="layui-input-inline" style="width: 100px">
                         <div class="layui-input-inline" style="width: 100px">
-                            <input type="text" name="first_lv2" lay-verify="numberGeZ|required" class="layui-input" placeholder="数"/>
+                            <input type="text" name="first_lv2" lay-verify="numberGeZ|required" class="layui-input" placeholder="数"/>
                         </div>
                         </div>
                         <div class="layui-form-mid ">%</div>
                         <div class="layui-form-mid ">%</div>
                     </div>
                     </div>
@@ -60,7 +60,7 @@
                     <div class="layui-inline">
                     <div class="layui-inline">
                         <label class="layui-form-label " style="width: 120px">三级返现比例:</label>
                         <label class="layui-form-label " style="width: 120px">三级返现比例:</label>
                         <div class="layui-input-inline" style="width: 100px">
                         <div class="layui-input-inline" style="width: 100px">
-                            <input type="text" name="first_lv3" lay-verify="numberGeZ|required" class="layui-input" placeholder="数"/>
+                            <input type="text" name="first_lv3" lay-verify="numberGeZ|required" class="layui-input" placeholder="数"/>
                         </div>
                         </div>
                         <div class="layui-form-mid ">%</div>
                         <div class="layui-form-mid ">%</div>
                     </div>
                     </div>
@@ -68,7 +68,7 @@
                     <div class="layui-inline">
                     <div class="layui-inline">
                         <label class="layui-form-label " style="width: 120px">四级返积分比例:</label>
                         <label class="layui-form-label " style="width: 120px">四级返积分比例:</label>
                         <div class="layui-input-inline" style="width: 100px">
                         <div class="layui-input-inline" style="width: 100px">
-                            <input type="text" name="first_lv4" lay-verify="numberGeZ|required" class="layui-input" placeholder="数"/>
+                            <input type="text" name="first_lv4" lay-verify="numberGeZ|required" class="layui-input" placeholder="数"/>
                         </div>
                         </div>
                         <div class="layui-form-mid ">%</div>
                         <div class="layui-form-mid ">%</div>
                     </div>
                     </div>
@@ -76,7 +76,7 @@
                     <div class="layui-inline">
                     <div class="layui-inline">
                         <label class="layui-form-label " style="width: 120px">五级返积分比例:</label>
                         <label class="layui-form-label " style="width: 120px">五级返积分比例:</label>
                         <div class="layui-input-inline" style="width: 100px">
                         <div class="layui-input-inline" style="width: 100px">
-                            <input type="text" name="first_lv5" lay-verify="numberGeZ|required" class="layui-input" placeholder="数"/>
+                            <input type="text" name="first_lv5" lay-verify="numberGeZ|required" class="layui-input" placeholder="数"/>
                         </div>
                         </div>
                         <div class="layui-form-mid ">%</div>
                         <div class="layui-form-mid ">%</div>
                     </div>
                     </div>
@@ -89,7 +89,7 @@
                     <div class="layui-inline">
                     <div class="layui-inline">
                         <label class="layui-form-label " style="width: 120px">一级返现比例:</label>
                         <label class="layui-form-label " style="width: 120px">一级返现比例:</label>
                         <div class="layui-input-inline" style="width: 100px">
                         <div class="layui-input-inline" style="width: 100px">
-                            <input type="text" name="again_lv1" lay-verify="numberGeZ|required" class="layui-input" placeholder="数"/>
+                            <input type="text" name="again_lv1" lay-verify="numberGeZ|required" class="layui-input" placeholder="数"/>
                         </div>
                         </div>
                         <div class="layui-form-mid ">%</div>
                         <div class="layui-form-mid ">%</div>
                     </div>
                     </div>
@@ -97,7 +97,7 @@
                     <div class="layui-inline">
                     <div class="layui-inline">
                         <label class="layui-form-label " style="width: 120px">二级返现比例:</label>
                         <label class="layui-form-label " style="width: 120px">二级返现比例:</label>
                         <div class="layui-input-inline" style="width: 100px">
                         <div class="layui-input-inline" style="width: 100px">
-                            <input type="text" name="again_lv2" lay-verify="numberGeZ|required" class="layui-input" placeholder="数"/>
+                            <input type="text" name="again_lv2" lay-verify="numberGeZ|required" class="layui-input" placeholder="数"/>
                         </div>
                         </div>
                         <div class="layui-form-mid ">%</div>
                         <div class="layui-form-mid ">%</div>
                     </div>
                     </div>
@@ -105,7 +105,7 @@
                     <div class="layui-inline">
                     <div class="layui-inline">
                         <label class="layui-form-label " style="width: 120px">三级返现比例:</label>
                         <label class="layui-form-label " style="width: 120px">三级返现比例:</label>
                         <div class="layui-input-inline" style="width: 100px">
                         <div class="layui-input-inline" style="width: 100px">
-                            <input type="text" name="again_lv3" lay-verify="numberGeZ|required" class="layui-input" placeholder="数"/>
+                            <input type="text" name="again_lv3" lay-verify="numberGeZ|required" class="layui-input" placeholder="数"/>
                         </div>
                         </div>
                         <div class="layui-form-mid ">%</div>
                         <div class="layui-form-mid ">%</div>
                     </div>
                     </div>
@@ -113,7 +113,7 @@
                     <div class="layui-inline">
                     <div class="layui-inline">
                         <label class="layui-form-label " style="width: 120px">四级返积分比例:</label>
                         <label class="layui-form-label " style="width: 120px">四级返积分比例:</label>
                         <div class="layui-input-inline" style="width: 100px">
                         <div class="layui-input-inline" style="width: 100px">
-                            <input type="text" name="again_lv4" lay-verify="numberGeZ|required" class="layui-input" placeholder="数"/>
+                            <input type="text" name="again_lv4" lay-verify="numberGeZ|required" class="layui-input" placeholder="数"/>
                         </div>
                         </div>
                         <div class="layui-form-mid ">%</div>
                         <div class="layui-form-mid ">%</div>
                     </div>
                     </div>
@@ -121,10 +121,20 @@
                     <div class="layui-inline">
                     <div class="layui-inline">
                         <label class="layui-form-label " style="width: 120px">五级返积分比例:</label>
                         <label class="layui-form-label " style="width: 120px">五级返积分比例:</label>
                         <div class="layui-input-inline" style="width: 100px">
                         <div class="layui-input-inline" style="width: 100px">
-                            <input type="text" name="again_lv5" lay-verify="numberGeZ|required" class="layui-input" placeholder="数"/>
+                            <input type="text" name="again_lv5" lay-verify="numberGeZ|required" class="layui-input" placeholder="数"/>
                         </div>
                         </div>
                         <div class="layui-form-mid ">%</div>
                         <div class="layui-form-mid ">%</div>
                     </div>
                     </div>
+                    <br>
+                    <div class="layui-inline">
+                        <label class="layui-form-label " style="width: 10px"></label>
+                        <div class="layui-form-mid " style="color: green">设置成为分销商需购买的商品</div>
+                    </div>
+                    <div>
+                        <div class="layui-input-block">
+                            <button type="button" class="layui-btn layui-btn-primary layui-border-blue" id="pay_picture">设置商品</button>
+                        </div>
+                    </div>
 
 
                     <div class="layui-input-block" style="margin-top: 20px">
                     <div class="layui-input-block" style="margin-top: 20px">
                         <button class="layui-btn" lay-submit lay-filter="component-form-element">保存</button>
                         <button class="layui-btn" lay-submit lay-filter="component-form-element">保存</button>

+ 192 - 0
uis/views/customer/index.html

@@ -0,0 +1,192 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>人员管理</title>
+  <meta name="renderer" content="webkit">
+  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=0">
+  <link rel="stylesheet" href="../../layuiadmin/layui/css/layui.css" media="all">
+  <link rel="stylesheet" href="../../layuiadmin/style/admin.css" media="all">
+  <link rel="stylesheet" type="text/css" href="../../layuiadmin/style/formSelects-v4.css"/>
+    <style type="text/css">
+    .LAY-btns .layui-nav {padding-left:0;padding-right:10px;top:-4px;margin: 0 10px;border: 0;background-color: #009688;}
+    .LAY-btns .layui-nav .layui-nav-item{line-height: 30px;}
+    .LAY-btns .layui-nav .layui-nav-child{top:34px;}
+    .LAY-btns .layui-nav .layui-nav-bar{display: none;}
+    .LAY-btns .layui-nav .layui-nav-child dd.layui-this a{color:#333;background-color:#fff;}
+    .LAY-btns .layui-nav .layui-nav-child dd.layui-this a:hover {background-color: #f2f2f2;color: #000;}
+    .seach_items {float:right;margin-left: 5px;display: inline-block; margin-top: 5px;}
+  </style>
+</head>
+<body>
+
+  <div class="layui-fluid">
+    <div class="layui-card">
+        <div class="layui-card-body" pad15>
+        <div class="layui-row layui-col-space15">
+          <div class="layui-col-md12">
+            <div class="LAY-btns" style="margin-bottom: 10px;">
+              <div class="layui-col-xs12">
+                  <div style="float: left">
+                      <button class="layui-btn" id="btn_download" ><i class="layui-icon layui-icon-download-circle"></i>导出</button>
+                  </div>
+                    <form class="layui-form" lay-filter="query-form-element">
+                        <div class="seach_items">
+                            <button class="layui-btn" lay-submit lay-filter="query-form-element"><i class="layui-icon layui-icon-search"></i>查询</button>
+                        </div>
+                        <div class="seach_items">
+                            <input type="text"  name="tel" autocomplete="off" class="layui-input" placeholder="客户电话"/>
+                        </div>
+                        <div class="seach_items">
+                            <input type="text"  name="name" autocomplete="off" class="layui-input" placeholder="客户姓名"/>
+                        </div>
+                        <div class="seach_items">
+                            <div class="layui-inline">
+                              <div class="layui-input-inline">
+                                <input type="text" name="range_time" class="layui-input" id="range_time" placeholder="注册时间">
+                              </div>
+                            </div>
+                        </div>
+                    </form>
+                </div>
+                <div style="clear: both;"></div>
+            </div>
+            <table class="layui-hide" id="datagrid" lay-filter="datagrid-operate"></table>
+
+            <script type="text/html" id="datagrid-operate-bar">
+                <div class="layui-btn-group">
+
+                </div>
+            </script>
+          </div>
+        </div>
+        </div>
+    </div>
+  </div>
+
+  <script src="../../layuiadmin/layui/layui.js?t=1"></script>
+  <script>
+  var _params = '';
+  layui.config({
+    base: '../../../layuiadmin/' //静态资源所在路径
+  }).extend({
+    index: 'lib/index' //主入口模块
+     ,formSelects: 'formSelects-v4'
+  }).use(['index', 'table', 'form', 'formSelects', 'upload', 'laydate'], function(){
+    var $ = layui.$
+            ,table = layui.table
+            ,laydate = layui.laydate
+            ,form = layui.form;
+    table.render({
+      elem: '#datagrid'
+      ,url: '/customer/'
+      ,title: '客户信息'
+      ,id: 'datagrid'
+      ,cols: [[
+        {field:'name', title:'名称',width: 120}
+       ,{field:'tel', title:'电话',width: 150}
+       ,{field:'gender_text', title:'性别',width: 80}
+       ,{field:'is_distributor_text', title:'分销商',width: 80}
+       ,{field:'balance', align: 'right', title:'余额',width: 120}
+       ,{field:'points', align:'right', title:'积分', width:120}
+       ,{field:'success_count', align: 'right', title:'成交数',width: 120}
+       ,{field:'total_amount', align: 'right', title:'累计消费金额',width: 120}
+       ,{field:'total_point', align: 'right', title:'累计消费积分',width: 120}
+       ,{field:'date_join', title:'注册时间',width: 150}
+        ,{width:150, align:'center', fixed: 'right', toolbar: '#datagrid-operate-bar'}
+      ]]
+      ,totalRow:true
+      ,parseData:function(res) {
+        if(res.code === 1){
+            return;
+        }
+        var cols = this.cols[0];
+        for (var i in cols) {
+            if (cols[i].field === 'balance') {
+                cols[i].totalRowText = res.data.footer.total_balance;
+            }
+            if (cols[i].field === 'points') {
+                cols[i].totalRowText = res.data.footer.total_points
+            }
+            if (cols[i].field === 'success_count') {
+                cols[i].totalRowText = res.data.footer.total_success_count
+            }
+            if (cols[i].field === 'total_amount') {
+                cols[i].totalRowText = res.data.footer.all_total_amount
+            }
+            if (cols[i].field === 'total_point') {
+                cols[i].totalRowText = res.data.footer.all_total_point
+            }
+        }
+        return {
+          "code": res.code, //解析接口状态
+          "count": res.count, //解析数据长度
+          "data": res.data.data //解析数据列表
+        };
+      }
+      , done: function () {
+        layui.index.removeNoPermButtons()
+      }
+      ,page: true
+      ,height: 'full-108'
+    });
+    laydate.render({
+        elem: '#range_time'
+        ,range:true
+    });
+    form.on('submit(query-form-element)', function(data){
+      //layer.msg(JSON.stringify(data.field));
+      table.reload('datagrid', {
+          where: data.field
+          ,page:{curr:1}
+      });
+      _params = data.field;
+      layer.closeAll();
+      return false;
+    });
+    //监听工具条
+    table.on('tool(datagrid-operate)', function(obj){
+      var data = obj.data;
+        if(obj.event === 'edit'){
+        table.editdata = data;
+        layer.open({
+          type: 2,
+          title: '修改',
+          shadeClose: false,
+          area: ['500px', '500px'],
+          btn: ['保存', '取消'],
+          yes: function (index, dom) {
+            layui.onSubmitChild = function (data) {
+                layer.close(index);
+                table.reload('datagrid', {});
+              };
+              layui.submitChild();
+          },
+          btn2: function(index, layero){
+            layer.close(index);//关闭当前按钮
+          },
+          content: 'edit.html?id='+data.id
+        });
+      }
+    });
+    // 导出数据
+    $('#btn_download').on('click', function(){
+        $.get({
+            url: '/customer/export/',
+            dataType: 'json',
+            data: _params,
+            success: function (res) {
+                if(res.code === 1){
+                    layer.msg(res.msg);
+                    return;
+                }
+                table.exportFile('datagrid', res.data, 'xlsx')
+            }
+        })
+    });
+  });
+  </script>
+</body>
+</html>
+

+ 6 - 70
uis/views/index.html

@@ -99,80 +99,16 @@
                         </dl>
                         </dl>
                     </li>
                     </li>
                     <li data-name="set" class="layui-nav-item">
                     <li data-name="set" class="layui-nav-item">
-                        <a href="javascript:;" lay-tips="代理商管理" lay-direction="2">
+                        <a href="javascript:;" lay-tips="客户管理" lay-direction="2">
                             <i class="layui-icon layui-icon-set"></i>
                             <i class="layui-icon layui-icon-set"></i>
-                            <cite>代理商管理</cite>
+                            <cite>客户管理</cite>
                         </a>
                         </a>
                         <dl class="layui-nav-child">
                         <dl class="layui-nav-child">
-                            <dd data-name="nav" data-permission="agent.view_agent_apply">
-                                <a lay-href="agent/agent_apply.html">代理商申请</a>
-                            </dd>
-                            <dd data-name="nav" data-permission="agent.view_agent">
-                                <a lay-href="agent/index.html">代理商管理</a>
-                            </dd>
-                            <dd data-name="nav" data-permission="agent.view_agent_commodity">
-                                <a lay-href="agent/commodity_index.html">代理商商品</a>
-                            </dd>
-                            <dd data-name="nav" data-permission="order.view_order">
-                                <a lay-href="order/index.html">代理商订单</a>
-                            </dd>
-                            <dd data-name="nav" data-permission="order.order_statistics">
-                                <a lay-href="statistics/home.html">订单统计</a>
-                            </dd>
-                            <dd data-name="nav" data-permission="order.commodity_detail_statistics">
-                                <a lay-href="statistics/commodity_detail.html">商品明细月销</a>
-                            </dd>
-                            <dd data-name="nav" data-permission="order.view_order">
-                                <a lay-href="order/customer.html">用户订单</a>
-                            </dd>
-                            <dd data-name="nav" data-permission="agent.view_agent">
-                                <a lay-href="agent/school_index.html">学校管理</a>
-                            </dd>
-                            <dd data-name="nav" data-permission="agent.view_agent">
-                                <a lay-href="agent/class_index.html">班级管理</a>
-                            </dd>
-                        </dl>
-                    </li>
-                    <li data-name="set" class="layui-nav-item">
-                        <a href="javascript:;" lay-tips="代理商小程序" lay-direction="2">
-                            <i class="layui-icon layui-icon-set"></i>
-                            <cite>代理商小程序</cite>
-                        </a>
-                        <dl class="layui-nav-child">
-                            <dd data-name="nav" data-permission="WechatApplet.manage_wechat_applet">
-                                <a lay-href="register_applet/index.html">注册小程序</a>
-                            </dd>
-                            <dd data-name="nav" data-permission="WechatApplet.manage_wechat_applet">
-                                <a lay-href="applet/index.html">小程序管理</a>
-                            </dd>
-                            <dd data-name="nav" data-permission="WechatApplet.manage_wechat_applet">
-                                <a lay-href="thirdPart/index.html">第三方平台</a>
-                            </dd>
-                            <dd data-name="nav" data-permission="WechatApplet.manage_wechat_applet">
-                                <a lay-href="applet/msg_template_index.html">消息模板</a>
-                            </dd>
-                        </dl>
-                    </li>
-                    <li data-name="set" class="layui-nav-item">
-                        <a href="javascript:;" lay-tips="防窜货管理" lay-direction="2">
-                            <i class="layui-icon layui-icon-set"></i>
-                            <cite>防窜货管理</cite>
-                        </a>
-                        <dl class="layui-nav-child">
-                            <dd data-name="nav" data-permission="deliver.manage_gfps">
-                                <a lay-href="deliver/index.html">出库作业</a>
-                            </dd>
-                            <dd data-name="nav" data-permission="deliver.manage_gfps">
-                                <a lay-href="back/index.html">退货作业</a>
-                            </dd>
-                            <dd data-name="button" data-permission="deliver.manage_gfps">
-                                <a lay-href="query/index.html">条码查询</a>
-                            </dd>
-                            <dd data-name="nav" data-permission="deliver.manage_gfps">
-                                <a lay-href="product/index.html">产品管理</a>
+                            <dd data-name="nav">
+                                <a lay-href="customer/index.html">用户管理</a>
                             </dd>
                             </dd>
-                            <dd data-name="nav" data-permission="deliver.manage_gfps">
-                                <a lay-href="distributor/index.html">经销商管理</a>
+                            <dd data-name="nav">
+                                <a lay-href="order/index.html">订单管理</a>
                             </dd>
                             </dd>
                         </dl>
                         </dl>
                     </li>
                     </li>

+ 221 - 0
uis/views/order/index.html

@@ -0,0 +1,221 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>人员管理</title>
+  <meta name="renderer" content="webkit">
+  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=0">
+  <link rel="stylesheet" href="../../layuiadmin/layui/css/layui.css" media="all">
+  <link rel="stylesheet" href="../../layuiadmin/style/admin.css" media="all">
+  <link rel="stylesheet" type="text/css" href="../../layuiadmin/style/formSelects-v4.css"/>
+    <style type="text/css">
+    .LAY-btns .layui-nav {padding-left:0;padding-right:10px;top:-4px;margin: 0 10px;border: 0;background-color: #009688;}
+    .LAY-btns .layui-nav .layui-nav-item{line-height: 30px;}
+    .LAY-btns .layui-nav .layui-nav-child{top:34px;}
+    .LAY-btns .layui-nav .layui-nav-bar{display: none;}
+    .LAY-btns .layui-nav .layui-nav-child dd.layui-this a{color:#333;background-color:#fff;}
+    .LAY-btns .layui-nav .layui-nav-child dd.layui-this a:hover {background-color: #f2f2f2;color: #000;}
+    .seach_items {float:right;margin-left: 5px;display: inline-block; margin-top: 5px;}
+  </style>
+</head>
+<body>
+
+  <div class="layui-fluid">
+    <div class="layui-card">
+        <div class="layui-card-body" pad15>
+        <div class="layui-row layui-col-space15">
+          <div class="layui-col-md12">
+            <div class="LAY-btns" style="margin-bottom: 10px;">
+              <div class="layui-col-xs12">
+                  <!--<div style="float: left">
+                      <button class="layui-btn" id="btn_download" ><i class="layui-icon layui-icon-download-circle"></i>导出</button>
+                  </div>-->
+                    <form class="layui-form" lay-filter="query-form-element">
+                        <div class="seach_items">
+                            <button class="layui-btn" lay-submit lay-filter="query-form-element"><i class="layui-icon layui-icon-search"></i>查询</button>
+                        </div>
+                        <div class="seach_items">
+                            <div class="layui-inline">
+                              <div class="layui-input-inline">
+                                <input type="text" name="range_time" class="layui-input" id="range_time" placeholder="下单时间">
+                              </div>
+                            </div>
+                        </div>
+                        <div class="seach_items" style="width: 150px;">
+                            <select name="status">
+                                <option value="">订单状态</option>
+                                <option value="0">待付款</option>
+                                <option value="1">待发货</option>
+                                <option value="2">待评价</option>
+                                <option value="3">已评价</option>
+                                <option value="4">已取消</option>
+                            </select>
+                        </div>
+                        <div class="seach_items">
+                            <input type="text"  name="tel" autocomplete="off" class="layui-input" placeholder="收货电话"/>
+                        </div>
+                        <div class="seach_items">
+                            <input type="text"  name="customer_tel" autocomplete="off" class="layui-input" placeholder="下单电话"/>
+                        </div>
+                        <div class="seach_items">
+                            <input type="text"  name="no" autocomplete="off" class="layui-input" placeholder="订单号"/>
+                        </div>
+                    </form>
+                </div>
+                <div style="clear: both;"></div>
+            </div>
+            <table class="layui-hide" id="datagrid" lay-filter="datagrid-operate"></table>
+
+            <script type="text/html" id="datagrid-operate-bar">
+                <div class="layui-btn-group">
+                    <a class="layui-btn layui-btn-xs " lay-event="order_detail">明细</a>
+                    {{# if(d.status == 1){ }}
+                    <a class="layui-btn layui-btn-xs layui-btn-normal" lay-event="dispatch">发货</a>
+                    {{# } }}
+                    {{# if(d.status == 0){ }}
+                    <a class="layui-btn layui-btn-xs layui-btn-danger" lay-event="cancel">取消</a>
+                    {{# } }}
+                </div>
+            </script>
+          </div>
+        </div>
+        </div>
+    </div>
+  </div>
+
+  <script src="../../layuiadmin/layui/layui.js?t=1"></script>
+  <script>
+  var _params = '';
+  layui.config({
+    base: '../../../layuiadmin/' //静态资源所在路径
+  }).extend({
+    index: 'lib/index' //主入口模块
+     ,formSelects: 'formSelects-v4'
+  }).use(['index', 'table', 'form', 'formSelects', 'upload', 'laydate'], function(){
+    var $ = layui.$
+            ,table = layui.table
+            ,laydate = layui.laydate
+            , admin = layui.admin
+            ,form = layui.form;
+    table.render({
+      elem: '#datagrid'
+      ,url: '/order/'
+      ,title: '订单信息'
+      ,id: 'datagrid'
+      ,cols: [[
+        {field:'no', title:'订单号',width: 150, fixed: 'left'}
+       ,{field:'customer_name', title:'下单人',width: 100, fixed: 'left'}
+       ,{field:'customer_tel', title:'电话',width: 120, fixed: 'left'}
+       ,{field:'total_count', title:'数量',width: 60}
+       ,{field:'total_amount', title:'金额',width: 100}
+       ,{field:'total_point', title:'积分',width: 60}
+       ,{field:'status_text', title:'订单状态',width: 90}
+       ,{field:'create_time', title:'下单时间',width: 150}
+       ,{field:'name', title:'收货人',width: 80}
+       ,{field:'tel', title:'收货电话',width: 120}
+       ,{field:'user_address', title:'收货地址',width: 200}
+       ,{field:'express_info', title:'快递信息',width: 200}
+       ,{field:'dispatch_user_text', title:'发货人',width: 80}
+       ,{field:'dispatch_time', title:'发货时间',width: 150}
+       ,{field:'cancel_reason', title:'取消订单原因',width: 150}
+       ,{field:'cancel_user_text', title:'取消人',width: 80}
+       ,{field:'cancel_time', title:'取消时间',width: 150}
+       ,{field:'notes', title:'备注',width: 150}
+        ,{width:150, align:'center', fixed: 'right', toolbar: '#datagrid-operate-bar'}
+      ]]
+      ,totalRow:false
+      , done: function () {
+        layui.index.removeNoPermButtons()
+      }
+      ,page: true
+      ,height: 'full-108'
+    });
+    laydate.render({
+        elem: '#range_time'
+        ,range:true
+    });
+    form.on('submit(query-form-element)', function(data){
+      //layer.msg(JSON.stringify(data.field));
+      table.reload('datagrid', {
+          where: data.field
+          ,page:{curr:1}
+      });
+      _params = data.field;
+      layer.closeAll();
+      return false;
+    });
+    //监听工具条
+    table.on('tool(datagrid-operate)', function(obj){
+      var data = obj.data;
+      table.editdata = data;
+      if(obj.event === 'order_detail'){
+        layer.open({
+          type: 2,
+          title: '订单明细',
+          shadeClose: false,
+          area: ['60%', '80%'],
+          btn: ['关闭'],
+          yes: function (index, dom) {
+            layer.close(index);//关闭当前按钮
+          },
+          content: 'order_detail.html?id='+data.id
+        });
+      } else if (obj.event === 'dispatch') {
+          layer.open({
+              type: 2,
+              title: '发货',
+              area: ['40%', '60%'],
+              btn: ['保存', '取消'],
+              yes: function (index, dom) {
+                  layui.onSubmitChild = function (data) {
+                      layer.close(index);
+                      table.reload('datagrid', {});
+                  };
+                  layui.submitChild();
+              },
+              btn2: function (index, layero) {
+                  layer.close(index);
+              },
+              content: 'order_dispatch.html?id=' + data.id
+          })
+      } else if (obj.event === 'cancel') {
+          layer.open({
+              type: 2,
+              title: '取消订单',
+              area: ['40%', '250px'],
+                btn: ['保存', '取消'],
+                yes: function (index, dom) {
+                  layui.onSubmitChild = function (data) {
+                      layer.close(index);
+                      table.reload('datagrid', {});
+                  };
+                  layui.submitChild();
+              },
+              btn2: function (index, layero) {
+                  layer.close(index);
+              },
+              content: 'order_cancel.html?id=' + data.id
+          });
+      }
+    });
+    // 导出数据
+    $('#btn_download').on('click', function(){
+        $.get({
+            url: '/order/export/',
+            dataType: 'json',
+            data: _params,
+            success: function (res) {
+                if(res.code === 1){
+                    layer.msg(res.msg);
+                    return;
+                }
+                table.exportFile('datagrid', res.data, 'xlsx')
+            }
+        })
+    });
+  });
+  </script>
+</body>
+</html>
+

+ 75 - 0
uis/views/order/order_cancel.html

@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>取消订单</title>
+  <meta name="renderer" content="webkit">
+  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=0">
+  <link rel="stylesheet" href="../../layuiadmin/layui/css/layui.css" media="all">
+  <link rel="stylesheet" href="../../layuiadmin/style/admin.css" media="all">
+  <link rel="stylesheet" type="text/css" href="../../layuiadmin/style/formSelects-v4.css"/>
+</head>
+<body>
+
+  <div class="layui-fluid">
+    <div class="layui-row layui-col-space15">
+      <div class="layui-col-md6">
+        <div class="layui-card">
+
+          <div class="layui-card-body">
+            <form class="layui-form" action="" lay-filter="component-form-element">
+              <div class="layui-row layui-col-space10 layui-form-item">
+
+                <div class="layui-col-lg6">
+                  <label class="layui-form-label"><font color='red' size="4">*</font>取消原因:</label>
+                  <div class="layui-input-block">
+                    <input type="text" name="cancel_reason" lay-verify="required" placeholder="请输入取消原因" autocomplete="off" class="layui-input">
+                  </div>
+                </div>
+                <button class="layui-btn" id="id_order_cancel" lay-submit lay-filter="component-form-element" style="display: none">保存</button>
+              </div>
+            </form>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+
+
+  <script src="../../layuiadmin/layui/layui.js"></script>
+  <script>
+  layui.config({
+    base: '../../../layuiadmin/' //静态资源所在路径
+  }).extend({
+    index: 'lib/index',
+    formSelects: 'formSelects-v4'
+  }).use(['index', 'form', 'utils'], function(){
+    var $ = layui.$
+    ,admin = layui.admin
+    ,form = layui.form;
+    var id = layui.view.getParameterByName('id');
+
+    var editdata = JSON.parse(JSON.stringify(parent.layui.table.editdata)); // 框架有Bug所以这么转换
+    form.val("component-form-element", editdata);
+
+    form.on('submit(component-form-element)', function(data){
+      admin.req({
+        url: '/order/' + id + '/cancel_order/'
+        ,data: data.field
+        ,type: 'post'
+        ,done: function(res){
+            parent.layui.onSubmitChild(res);
+        }
+      });
+
+      return false;
+    });
+
+    parent.layui.submitChild = function () {
+      $("#id_order_cancel").click();
+    };
+  });
+  </script>
+</body>
+</html>

+ 150 - 0
uis/views/order/order_detail.html

@@ -0,0 +1,150 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="utf-8">
+    <title>订单商品明细</title>
+    <meta name="renderer" content="webkit">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+    <meta name="viewport"
+          content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=0">
+    <link rel="stylesheet" href="../../layuiadmin/layui/css/layui.css" media="all">
+    <link rel="stylesheet" href="../../layuiadmin/style/admin.css" media="all">
+    <style type="text/css">
+        /*您可以将下列样式写入自己的样式表中*/
+        /*layui 元素样式改写*/
+        .layui-btn-sm {
+            line-height: normal;
+            font-size: 12.5px;
+        }
+
+        .layui-table-view .layui-table-body {
+            min-height: 256px;
+        }
+
+        .layui-table-cell .layui-input.layui-unselect {
+            height: 30px;
+            line-height: 30px;
+        }
+
+        /*设置 layui 表格中单元格内容溢出可见样式*/
+        .table-overlay .layui-table-view,
+        .table-overlay .layui-table-box,
+        .table-overlay .layui-table-body {
+            overflow: visible;
+        }
+
+        .table-overlay .layui-table-cell {
+            height: auto;
+            overflow: visible;
+        }
+
+        .imgStyle {
+            padding-left: 10px;
+            width: 80px;
+            height: 50px;
+        }
+
+        .layui-table-cell {
+            font-size: 14px;
+            padding: 0 5px;
+            height: auto;
+            overflow: visible;
+            text-overflow: inherit;
+            white-space: normal;
+            word-break: break-all;
+        }
+    </style>
+</head>
+<body>
+
+<div class="layui-fluid">
+    <div class="layui-col-md12">
+        <div class="layui-card">
+            <div class="layui-card-body">
+                <div>收货信息:<font id="id_address" style="margin-right: 30px"></font>
+                </div>
+                <div style="height: 5px"></div>
+                <div id="tableRes" class="table-overlay">
+                    <table id="dataTable" lay-filter="dataTable" class="layui-hide"></table>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+
+<script src="../../layuiadmin/layui/layui.js"></script>
+<script type="text/html" id="paramImage">
+    {{# if(d.images){ }}
+    <img src="{{ d.images }}" class="imgStyle"/>
+    {{# } }}
+</script>
+<script>
+    layui.config({
+        base: '../../../layuiadmin/' //静态资源所在路径
+    }).extend({
+        index: 'lib/index' //主入口模块
+    }).use(['index', 'table', 'layer',], function () {
+        var $ = layui.$
+            , admin = layui.admin
+            , table = layui.table
+            , layer = layui.layer;
+
+        var id = layui.view.getParameterByName('id');
+        var editdata = JSON.parse(JSON.stringify(parent.layui.table.editdata)); // 框架有Bug所以这么转换
+        $('#id_address').html(editdata.name + ',' + editdata.tel + ',' + editdata.user_address);
+        var tbWidth = $("#tableRes").width();
+        var layTableId = "layTable";
+        var tableIns = table.render({
+            elem: '#dataTable',
+            id: layTableId,
+            data: [],
+            width: tbWidth,
+            page: false,
+            limit: 100,
+            loading: true,
+            even: true, //不开启隔行背景
+            cols: [[
+                {title: '序号', type: 'numbers'},
+                {field: 'commodity_name', title: '名称', width: '20%',},
+                {field: 'price', title: '价格', width: '10%',},
+                {field: 'point', title: '积分', width: '10%',},
+                {field: 'count', title: '数量', width: '10%',},
+                {field: 'amount', title: '总金额', width: '10%',},
+                {field: 'point_amount', title: '总积分', width: '10%',},
+                {field: 'images', title: '缩略图', templet: '#paramImage', width: '20%',},
+            ]]
+        });
+
+        admin.req({
+            url: '/order/' + id + '/get_details/',
+            done: function (res) {
+                if (res.code === 0) {
+                    var rows = res.data;
+                    var oldData = table.cache[layTableId];
+                    for (var k in rows) {
+                        oldData.push(
+                            {
+                                commodity_name: rows[k].commodity_name,
+                                price: rows[k].price,
+                                point: rows[k].point,
+                                count: rows[k].count,
+                                amount: rows[k].amount,
+                                point_amount: rows[k].point_amount,
+                                images: rows[k].images,
+                            }
+                        )
+                    }
+                    tableIns.reload({
+                        data: oldData
+                    });
+                }else{
+                    layer.msg('获取订单明细失败', {icon: 5});
+                }
+            }
+        });
+
+    });
+
+</script>
+</body>
+</html>

+ 125 - 0
uis/views/order/order_dispatch.html

@@ -0,0 +1,125 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="utf-8">
+    <title>物流信息</title>
+    <meta name="renderer" content="webkit">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+    <meta name="viewport"
+          content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=0">
+    <link rel="stylesheet" href="../../layuiadmin/layui/css/layui.css" media="all">
+    <link rel="stylesheet" href="../../layuiadmin/style/admin.css" media="all">
+    <style>
+        .input {
+            border-radius: 2px;
+            height: 38px;
+            border-width: 1px;
+            border-color: lightgray;
+            border-style: solid;
+            background-color: white;
+            padding-left: 3px;
+        }
+
+        .layui-form-label {
+            width: 100px !important;
+        }
+
+        .layui-input-block {
+            margin-left: 135px;
+        }
+    </style>
+</head>
+<body>
+
+<div class="layui-fluid">
+    <div class="layui-row layui-col-space15">
+        <div class="layui-card">
+
+            <div class="layui-card-body" pad15>
+                <form class="layui-form" action="" lay-filter="component-form-element">
+                    <div class="layui-row layui-col-space10 layui-form-item">
+                        <div>
+                            <label class="layui-form-label"><font color='red' size="4">*</font>快递公司:</label>
+                            <div class="layui-input-block">
+                                <select lay-verify="required" name="express_company" id="id_company">
+                                    <option value="" >请选择快递公司</option>
+                                </select>
+                            </div>
+                        </div>
+                        <div>
+                            <label class="layui-form-label"><font color='red' size="4">*</font>快递单号:</label>
+                            <div class="layui-input-block">
+                                <input type="text" name="express_no" lay-verify="required" placeholder="请输入快递单号" autocomplete="off" class="layui-input">
+                            </div>
+                        </div>
+
+                        <button id="id_save" class="layui-btn" lay-submit lay-filter="component-form-element" style="display: none">保存
+                        </button>
+                    </div>
+                </form>
+            </div>
+        </div>
+    </div>
+</div>
+<script src="../../layuiadmin/layui/layui.js"></script>
+<script>
+    layui.config({
+        base: '../../../layuiadmin/' //静态资源所在路径
+    }).extend({
+        index: 'lib/index',
+    }).use(['index', 'form'], function () {
+        var $ = layui.$
+            , admin = layui.admin
+            , form = layui.form;
+
+        var id = layui.view.getParameterByName('id');
+
+
+        if (id) {
+            var editdata = JSON.parse(JSON.stringify(parent.layui.table.editdata)); // 框架有Bug所以这么转换
+
+            form.val("component-form-element", editdata);
+        }
+
+
+        admin.req({
+            url: '/order/dict/',
+            done: function (res) {
+                var data_company = res.data.express_company;
+                var company_node = $('#id_company');
+                for (var i in data_company) {
+                    var pid = data_company[i].value;
+                    var name = data_company[i].name;
+                    if (editdata.express_company === pid) {
+                        company_node.append("<option value='" + pid + "'selected >" + name + "</option>");
+                    } else {
+                        company_node.append("<option value='" + pid + "'>" + name + "</option>");
+                    }
+                }
+                form.render()
+            }
+        });
+
+        form.on('submit(component-form-element)', function (data) {
+            if(typeof parent.layui.onSubmitChild !== "function"){
+                return false;
+            }
+            admin.req({
+                url: '/order/' + id + '/submit_express/'
+                , data: data.field
+                , type: 'post'
+                , done: function (res) {
+                    parent.layui.onSubmitChild(res);
+                }
+            });
+
+            return false;
+        });
+
+        parent.layui.submitChild = function () {
+            $("#id_save").click();
+        };
+    });
+</script>
+</body>
+</html>